From e62b7525168d2d65182ff85b71bf00d85a8e2a35 Mon Sep 17 00:00:00 2001 From: Patrick Mueller Date: Thu, 7 Oct 2021 15:48:29 -0400 Subject: [PATCH 01/74] [actions] update doc on customHostSettings config (#101768) Improved the doc on the Kibana config for actions customHostSettings Co-authored-by: ymao1 --- docs/settings/alert-action-settings.asciidoc | 28 +++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index 3a94e652d2ea..599e8c54643c 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -46,10 +46,13 @@ entry. + In the following example, two custom host settings are defined. The first provides a custom host setting for mail server -`mail.example.com` using port 465 that supplies server certificate authorization +`mail.example.com` using port 465 that supplies server certificate authentication data from both a file and inline, and requires TLS for the connection. The second provides a custom host setting for https server -`webhook.example.com` which turns off server certificate authorization. +`webhook.example.com` which turns off server certificate authentication, +that will allow Kibana to connect to the server if it's using a self-signed +certificate. The individual properties that can be used in the settings are +documented below. + [source,yaml] -- @@ -66,11 +69,16 @@ xpack.actions.customHostSettings: requireTLS: true - url: https://webhook.example.com ssl: - // legacy - rejectUnauthorized: false verificationMode: 'none' -- +The settings in `xpack.actions.customHostSettings` can be used to override the +global option `xpack.actions.ssl.verificationMode` and provide customized TLS +settings on a per-server basis. Set `xpack.actions.ssl.verificationMode` to the +value to be used by default for all servers, then add an entry in +`xpack.actions.customHostSettings` for every server that requires customized +settings. + `xpack.actions.customHostSettings[n].url` {ess-icon}:: A URL associated with this custom host setting. Should be in the form of `protocol://hostname:port`, where `protocol` is `https` or `smtp`. If the @@ -91,10 +99,12 @@ values. `xpack.actions.customHostSettings[n].smtp.ignoreTLS` {ess-icon}:: A boolean value indicating that TLS must not be used for this connection. The options `smtp.ignoreTLS` and `smtp.requireTLS` can not both be set to true. +Default: `false`. `xpack.actions.customHostSettings[n].smtp.requireTLS` {ess-icon}:: A boolean value indicating that TLS must be used for this connection. The options `smtp.ignoreTLS` and `smtp.requireTLS` can not both be set to true. +Default: `false`. `xpack.actions.customHostSettings[n].ssl.rejectUnauthorized`:: Deprecated. Use <> instead. A boolean value indicating whether to bypass server certificate validation. @@ -141,8 +151,8 @@ Specifies HTTP headers for the proxy, if using a proxy for actions. Default: {}. `xpack.actions.proxyRejectUnauthorizedCertificates` {ess-icon}:: Deprecated. Use <> instead. Set to `false` to bypass certificate validation for the proxy, if using a proxy for actions. Default: `true`. -[[action-config-proxy-verification-mode]]`xpack.actions[n].ssl.proxyVerificationMode` {ess-icon}:: -Controls the verification for the proxy server certificate that {hosted-ems} receives when making an outbound SSL/TLS connection to the proxy server. Valid values are `full`, `certificate`, and `none`. +[[action-config-proxy-verification-mode]]`xpack.actions.ssl.proxyVerificationMode` {ess-icon}:: +Controls the verification for the proxy server certificate that Kibana receives when making an outbound SSL/TLS connection to the proxy server. Valid values are `full`, `certificate`, and `none`. Use `full` to perform hostname verification, `certificate` to skip hostname verification, and `none` to skip verification. Default: `full`. <>. `xpack.actions.rejectUnauthorized` {ess-icon}:: @@ -151,12 +161,12 @@ Deprecated. Use <>. + -As an alternative to setting `xpack.actions.ssl.verificationMode`, you can use the setting -`xpack.actions.customHostSettings` to set SSL options for specific servers. +This setting can be overridden for specific URLs by using the setting +`xpack.actions.customHostSettings[n].ssl.verificationMode` (described above) to a different value. `xpack.actions.maxResponseContentLength` {ess-icon}:: Specifies the max number of bytes of the http response for requests to external resources. Default: 1000000 (1MB). From 0b14195b691e92e5a97d13c98c863b1e701a00f2 Mon Sep 17 00:00:00 2001 From: Pete Harverson Date: Thu, 7 Oct 2021 21:08:13 +0100 Subject: [PATCH 02/74] [ML] Fix deletion of models that are not used by pipelines (#114107) * [ML] Fix deletion of models that are not used by pipelines * [ML] Edits from review * [ML] Fix jest test for index switch in delete job modal * [ML] Fix API test calls to createTestTrainedModels * [ML] Remove unnecessary async from jest test --- .../action_delete/delete_action_name.test.tsx | 7 +- .../action_delete/use_delete_action.tsx | 4 +- .../models_management/delete_models_modal.tsx | 13 +- .../models_management/models_list.tsx | 20 +- .../apis/ml/trained_models/delete_model.ts | 2 +- .../ml/trained_models/get_model_pipelines.ts | 2 +- .../apis/ml/trained_models/get_model_stats.ts | 2 +- .../apis/ml/trained_models/get_models.ts | 2 +- .../ml/data_frame_analytics/trained_models.ts | 112 ++++++++- x-pack/test/functional/services/ml/api.ts | 4 +- x-pack/test/functional/services/ml/index.ts | 3 + .../functional/services/ml/trained_models.ts | 19 +- .../services/ml/trained_models_table.ts | 212 ++++++++++++++++++ 13 files changed, 376 insertions(+), 26 deletions(-) create mode 100644 x-pack/test/functional/services/ml/trained_models_table.ts diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_name.test.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_name.test.tsx index 696eeefa6cc1..6b26e3823d2e 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_name.test.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/delete_action_name.test.tsx @@ -69,7 +69,7 @@ describe('DeleteAction', () => { }); describe('When delete model is open', () => { - it('should allow to delete target index by default.', () => { + it('should not allow to delete target index by default.', () => { const mock = jest.spyOn(CheckPrivilige, 'checkPermission'); mock.mockImplementation((p) => p === 'canDeleteDataFrameAnalytics'); @@ -101,10 +101,9 @@ describe('DeleteAction', () => { const deleteButton = getByTestId('mlAnalyticsJobDeleteButton'); fireEvent.click(deleteButton); expect(getByTestId('mlAnalyticsJobDeleteModal')).toBeInTheDocument(); - expect(getByTestId('mlAnalyticsJobDeleteIndexSwitch')).toBeInTheDocument(); - const mlAnalyticsJobDeleteIndexSwitch = getByTestId('mlAnalyticsJobDeleteIndexSwitch'); - expect(mlAnalyticsJobDeleteIndexSwitch).toHaveAttribute('aria-checked', 'true'); + expect(queryByTestId('mlAnalyticsJobDeleteIndexSwitch')).toBeNull(); expect(queryByTestId('mlAnalyticsJobDeleteIndexPatternSwitch')).toBeNull(); + mock.mockRestore(); }); }); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx index 4abe70435d37..91871015d2ad 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx @@ -89,9 +89,9 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => { ); } }; - const checkUserIndexPermission = () => { + const checkUserIndexPermission = async () => { try { - const userCanDelete = canDeleteIndex(indexName, toastNotificationService); + const userCanDelete = await canDeleteIndex(indexName, toastNotificationService); if (userCanDelete) { setUserCanDeleteIndex(true); } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/delete_models_modal.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/delete_models_modal.tsx index d93baee97c53..0db4c5d30fbe 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/delete_models_modal.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/delete_models_modal.tsx @@ -30,7 +30,11 @@ export const DeleteModelsModal: FC = ({ models, onClose .map((model) => model.model_id); return ( - + = ({ models, onClose /> - + { description: i18n.translate('xpack.ml.trainedModels.modelsList.deleteModelActionLabel', { defaultMessage: 'Delete model', }), + 'data-test-subj': 'mlModelsTableRowDeleteAction', icon: 'trash', type: 'icon', color: 'danger', @@ -353,7 +355,7 @@ export const ModelsList: FC = () => { enabled: (item) => { // TODO check for permissions to delete ingest pipelines. // ATM undefined means pipelines fetch failed server-side. - return !item.pipelines; + return !isPopulatedObject(item.pipelines); }, }, ]; @@ -389,6 +391,7 @@ export const ModelsList: FC = () => { iconType={itemIdToExpandedRowMap[item.model_id] ? 'arrowUp' : 'arrowDown'} /> ), + 'data-test-subj': 'mlModelsTableRowDetailsToggle', }, { field: ModelsTableToConfigMapping.id, @@ -397,6 +400,7 @@ export const ModelsList: FC = () => { }), sortable: true, truncateText: true, + 'data-test-subj': 'mlModelsTableColumnId', }, { field: ModelsTableToConfigMapping.description, @@ -406,6 +410,7 @@ export const ModelsList: FC = () => { }), sortable: false, truncateText: true, + 'data-test-subj': 'mlModelsTableColumnDescription', }, { field: ModelsTableToConfigMapping.type, @@ -418,11 +423,14 @@ export const ModelsList: FC = () => { {types.map((type) => ( - {type} + + {type} + ))} ), + 'data-test-subj': 'mlModelsTableColumnType', }, { field: ModelsTableToConfigMapping.createdAt, @@ -432,12 +440,14 @@ export const ModelsList: FC = () => { dataType: 'date', render: timeFormatter, sortable: true, + 'data-test-subj': 'mlModelsTableColumnCreatedAt', }, { name: i18n.translate('xpack.ml.trainedModels.modelsList.actionsHeader', { defaultMessage: 'Actions', }), actions, + 'data-test-subj': 'mlModelsTableColumnActions', }, ]; @@ -492,8 +502,7 @@ export const ModelsList: FC = () => { defaultMessage: 'Select a model', }); } - - if (Array.isArray(item.pipelines) && item.pipelines.length > 0) { + if (isPopulatedObject(item.pipelines)) { return i18n.translate('xpack.ml.trainedModels.modelsList.disableSelectableMessage', { defaultMessage: 'Model has associated pipelines', }); @@ -507,7 +516,7 @@ export const ModelsList: FC = () => { return ''; }, - selectable: (item) => !item.pipelines && !isBuiltInModel(item), + selectable: (item) => !isPopulatedObject(item.pipelines) && !isBuiltInModel(item), onSelectionChange: (selectedItems) => { setSelectedModels(selectedItems); }, @@ -574,6 +583,7 @@ export const ModelsList: FC = () => { pagination={pagination} onTableChange={onTableChange} sorting={sorting} + data-test-subj={isLoading ? 'mlModelsTable loading' : 'mlModelsTable loaded'} /> {modelsToDelete.length > 0 && ( diff --git a/x-pack/test/api_integration/apis/ml/trained_models/delete_model.ts b/x-pack/test/api_integration/apis/ml/trained_models/delete_model.ts index 3848330a95fb..7124a9566c71 100644 --- a/x-pack/test/api_integration/apis/ml/trained_models/delete_model.ts +++ b/x-pack/test/api_integration/apis/ml/trained_models/delete_model.ts @@ -17,7 +17,7 @@ export default ({ getService }: FtrProviderContext) => { describe('DELETE trained_models', () => { before(async () => { await ml.testResources.setKibanaTimeZoneToUTC(); - await ml.api.createdTestTrainedModels('regression', 2); + await ml.api.createTestTrainedModels('regression', 2); }); after(async () => { diff --git a/x-pack/test/api_integration/apis/ml/trained_models/get_model_pipelines.ts b/x-pack/test/api_integration/apis/ml/trained_models/get_model_pipelines.ts index cc347056f02a..9600972e3e8b 100644 --- a/x-pack/test/api_integration/apis/ml/trained_models/get_model_pipelines.ts +++ b/x-pack/test/api_integration/apis/ml/trained_models/get_model_pipelines.ts @@ -19,7 +19,7 @@ export default ({ getService }: FtrProviderContext) => { before(async () => { await ml.testResources.setKibanaTimeZoneToUTC(); - testModelIds = await ml.api.createdTestTrainedModels('regression', 2, true); + testModelIds = await ml.api.createTestTrainedModels('regression', 2, true); }); after(async () => { diff --git a/x-pack/test/api_integration/apis/ml/trained_models/get_model_stats.ts b/x-pack/test/api_integration/apis/ml/trained_models/get_model_stats.ts index 76f108836996..48040959f0e4 100644 --- a/x-pack/test/api_integration/apis/ml/trained_models/get_model_stats.ts +++ b/x-pack/test/api_integration/apis/ml/trained_models/get_model_stats.ts @@ -17,7 +17,7 @@ export default ({ getService }: FtrProviderContext) => { describe('GET trained_models/_stats', () => { before(async () => { await ml.testResources.setKibanaTimeZoneToUTC(); - await ml.api.createdTestTrainedModels('regression', 2); + await ml.api.createTestTrainedModels('regression', 2); }); after(async () => { diff --git a/x-pack/test/api_integration/apis/ml/trained_models/get_models.ts b/x-pack/test/api_integration/apis/ml/trained_models/get_models.ts index 604dff6a98a9..ec33ef316828 100644 --- a/x-pack/test/api_integration/apis/ml/trained_models/get_models.ts +++ b/x-pack/test/api_integration/apis/ml/trained_models/get_models.ts @@ -19,7 +19,7 @@ export default ({ getService }: FtrProviderContext) => { before(async () => { await ml.testResources.setKibanaTimeZoneToUTC(); - testModelIds = await ml.api.createdTestTrainedModels('regression', 5, true); + testModelIds = await ml.api.createTestTrainedModels('regression', 5, true); await ml.api.createModelAlias('dfa_regression_model_n_0', 'dfa_regression_model_alias'); await ml.api.createIngestPipeline('dfa_regression_model_alias'); }); diff --git a/x-pack/test/functional/apps/ml/data_frame_analytics/trained_models.ts b/x-pack/test/functional/apps/ml/data_frame_analytics/trained_models.ts index 43130651cb12..b302e0bfb114 100644 --- a/x-pack/test/functional/apps/ml/data_frame_analytics/trained_models.ts +++ b/x-pack/test/functional/apps/ml/data_frame_analytics/trained_models.ts @@ -12,8 +12,8 @@ export default function ({ getService }: FtrProviderContext) { describe('trained models', function () { before(async () => { - await ml.trainedModels.createdTestTrainedModels('classification', 15); - await ml.trainedModels.createdTestTrainedModels('regression', 15); + await ml.trainedModels.createTestTrainedModels('classification', 15, true); + await ml.trainedModels.createTestTrainedModels('regression', 15); await ml.securityUI.loginAsMlPowerUser(); await ml.navigation.navigateToTrainedModels(); }); @@ -22,10 +22,116 @@ export default function ({ getService }: FtrProviderContext) { await ml.api.cleanMlIndices(); }); + // 'Created at' will be different on each run, + // so we will just assert that the value is in the expected timestamp format. + const builtInModelData = { + modelId: 'lang_ident_model_1', + description: 'Model used for identifying language from arbitrary input text.', + modelTypes: ['classification', 'built-in'], + }; + + const modelWithPipelineData = { + modelId: 'dfa_classification_model_n_0', + description: '', + modelTypes: ['classification'], + }; + + const modelWithoutPipelineData = { + modelId: 'dfa_regression_model_n_0', + description: '', + modelTypes: ['regression'], + }; + it('renders trained models list', async () => { - await ml.trainedModels.assertRowsNumberPerPage(10); + await ml.testExecution.logTestStep( + 'should display the stats bar with the total number of models' + ); // +1 because of the built-in model await ml.trainedModels.assertStats(31); + + await ml.testExecution.logTestStep('should display the table'); + await ml.trainedModels.assertTableExists(); + await ml.trainedModels.assertRowsNumberPerPage(10); + }); + + it('displays the built-in model and no actions are enabled', async () => { + await ml.testExecution.logTestStep('should display the model in the table'); + await ml.trainedModelsTable.filterWithSearchString(builtInModelData.modelId, 1); + + await ml.testExecution.logTestStep('displays expected row values for the model in the table'); + await ml.trainedModelsTable.assertModelsRowFields(builtInModelData.modelId, { + id: builtInModelData.modelId, + description: builtInModelData.description, + modelTypes: builtInModelData.modelTypes, + }); + + await ml.testExecution.logTestStep( + 'should not show collapsed actions menu for the model in the table' + ); + await ml.trainedModelsTable.assertModelCollapsedActionsButtonExists( + builtInModelData.modelId, + false + ); + + await ml.testExecution.logTestStep( + 'should not show delete action for the model in the table' + ); + await ml.trainedModelsTable.assertModelDeleteActionButtonExists( + builtInModelData.modelId, + false + ); + }); + + it('displays a model with an ingest pipeline and delete action is disabled', async () => { + await ml.testExecution.logTestStep('should display the model in the table'); + await ml.trainedModelsTable.filterWithSearchString(modelWithPipelineData.modelId, 1); + + await ml.testExecution.logTestStep('displays expected row values for the model in the table'); + await ml.trainedModelsTable.assertModelsRowFields(modelWithPipelineData.modelId, { + id: modelWithPipelineData.modelId, + description: modelWithPipelineData.description, + modelTypes: modelWithPipelineData.modelTypes, + }); + + await ml.testExecution.logTestStep( + 'should show disabled delete action for the model in the table' + ); + + await ml.trainedModelsTable.assertModelDeleteActionButtonEnabled( + modelWithPipelineData.modelId, + false + ); + }); + + it('displays a model without an ingest pipeline and model can be deleted', async () => { + await ml.testExecution.logTestStep('should display the model in the table'); + await ml.trainedModelsTable.filterWithSearchString(modelWithoutPipelineData.modelId, 1); + + await ml.testExecution.logTestStep('displays expected row values for the model in the table'); + await ml.trainedModelsTable.assertModelsRowFields(modelWithoutPipelineData.modelId, { + id: modelWithoutPipelineData.modelId, + description: modelWithoutPipelineData.description, + modelTypes: modelWithoutPipelineData.modelTypes, + }); + + await ml.testExecution.logTestStep( + 'should show enabled delete action for the model in the table' + ); + + await ml.trainedModelsTable.assertModelDeleteActionButtonEnabled( + modelWithoutPipelineData.modelId, + true + ); + + await ml.testExecution.logTestStep('should show the delete modal'); + await ml.trainedModelsTable.clickDeleteAction(modelWithoutPipelineData.modelId); + + await ml.testExecution.logTestStep('should delete the model'); + await ml.trainedModelsTable.confirmDeleteModel(); + await ml.trainedModelsTable.assertModelDisplayedInTable( + modelWithoutPipelineData.modelId, + false + ); }); }); } diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts index 7add4a024b46..abde3bf36538 100644 --- a/x-pack/test/functional/services/ml/api.ts +++ b/x-pack/test/functional/services/ml/api.ts @@ -981,11 +981,11 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { .expect(200) .then((res: any) => res.body); - log.debug('> Trained model crated'); + log.debug('> Trained model created'); return model; }, - async createdTestTrainedModels( + async createTestTrainedModels( modelType: ModelType, count: number = 10, withIngestPipelines = false diff --git a/x-pack/test/functional/services/ml/index.ts b/x-pack/test/functional/services/ml/index.ts index e12289c63069..d50ec371d7c2 100644 --- a/x-pack/test/functional/services/ml/index.ts +++ b/x-pack/test/functional/services/ml/index.ts @@ -51,6 +51,7 @@ import { SwimLaneProvider } from './swim_lane'; import { MachineLearningDashboardJobSelectionTableProvider } from './dashboard_job_selection_table'; import { MachineLearningDashboardEmbeddablesProvider } from './dashboard_embeddables'; import { TrainedModelsProvider } from './trained_models'; +import { TrainedModelsTableProvider } from './trained_models_table'; import { MachineLearningJobAnnotationsProvider } from './job_annotations_table'; export function MachineLearningProvider(context: FtrProviderContext) { @@ -121,6 +122,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { const alerting = MachineLearningAlertingProvider(context, commonUI); const swimLane = SwimLaneProvider(context); const trainedModels = TrainedModelsProvider(context, api, commonUI); + const trainedModelsTable = TrainedModelsTableProvider(context); return { anomaliesTable, @@ -168,5 +170,6 @@ export function MachineLearningProvider(context: FtrProviderContext) { testExecution, testResources, trainedModels, + trainedModelsTable, }; } diff --git a/x-pack/test/functional/services/ml/trained_models.ts b/x-pack/test/functional/services/ml/trained_models.ts index 7a1fa1714ca1..a15ec9fb1ecd 100644 --- a/x-pack/test/functional/services/ml/trained_models.ts +++ b/x-pack/test/functional/services/ml/trained_models.ts @@ -18,15 +18,26 @@ export function TrainedModelsProvider( mlCommonUI: MlCommonUI ) { const testSubjects = getService('testSubjects'); + const retry = getService('retry'); return { - async createdTestTrainedModels(modelType: ModelType, count: number = 10) { - await mlApi.createdTestTrainedModels(modelType, count); + async createTestTrainedModels( + modelType: ModelType, + count: number = 10, + withIngestPipelines = false + ) { + await mlApi.createTestTrainedModels(modelType, count, withIngestPipelines); }, async assertStats(expectedTotalCount: number) { - const actualStats = await testSubjects.getVisibleText('mlInferenceModelsStatsBar'); - expect(actualStats).to.eql(`Total trained models: ${expectedTotalCount}`); + await retry.tryForTime(5 * 1000, async () => { + const actualStats = await testSubjects.getVisibleText('mlInferenceModelsStatsBar'); + expect(actualStats).to.eql(`Total trained models: ${expectedTotalCount}`); + }); + }, + + async assertTableExists() { + await testSubjects.existOrFail('~mlModelsTable'); }, async assertRowsNumberPerPage(rowsNumber: 10 | 25 | 100) { diff --git a/x-pack/test/functional/services/ml/trained_models_table.ts b/x-pack/test/functional/services/ml/trained_models_table.ts new file mode 100644 index 000000000000..11a97a4fed8f --- /dev/null +++ b/x-pack/test/functional/services/ml/trained_models_table.ts @@ -0,0 +1,212 @@ +/* + * 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 { ProvidedType } from '@kbn/test'; + +import { WebElementWrapper } from 'test/functional/services/lib/web_element_wrapper'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export interface TrainedModelRowData { + id: string; + description: string; + modelTypes: string[]; +} + +export type MlTrainedModelsTable = ProvidedType; + +export function TrainedModelsTableProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + + return new (class ModelsTable { + public async parseModelsTable() { + const table = await testSubjects.find('~mlModelsTable'); + const $ = await table.parseDomContent(); + const rows = []; + + for (const tr of $.findTestSubjects('~mlModelsTableRow').toArray()) { + const $tr = $(tr); + + const $types = $tr.findTestSubjects('mlModelType'); + const modelTypes = []; + for (const el of $types.toArray()) { + modelTypes.push($(el).text().trim()); + } + + const rowObject: { + id: string; + description: string; + modelTypes: string[]; + createdAt: string; + } = { + id: $tr + .findTestSubject('mlModelsTableColumnId') + .find('.euiTableCellContent') + .text() + .trim(), + description: $tr + .findTestSubject('mlModelsTableColumnDescription') + .find('.euiTableCellContent') + .text() + .trim(), + modelTypes, + createdAt: $tr + .findTestSubject('mlModelsTableColumnCreatedAt') + .find('.euiTableCellContent') + .text() + .trim(), + }; + + rows.push(rowObject); + } + + return rows; + } + + public rowSelector(modelId: string, subSelector?: string) { + const row = `~mlModelsTable > ~row-${modelId}`; + return !subSelector ? row : `${row} > ${subSelector}`; + } + + public async waitForRefreshButtonLoaded() { + await testSubjects.existOrFail('~mlAnalyticsRefreshListButton', { timeout: 10 * 1000 }); + await testSubjects.existOrFail('mlAnalyticsRefreshListButton loaded', { timeout: 30 * 1000 }); + } + + public async refreshModelsTable() { + await this.waitForRefreshButtonLoaded(); + await testSubjects.click('~mlAnalyticsRefreshListButton'); + await this.waitForRefreshButtonLoaded(); + await this.waitForModelsToLoad(); + } + + public async waitForModelsToLoad() { + await testSubjects.existOrFail('~mlModelsTable', { timeout: 60 * 1000 }); + await testSubjects.existOrFail('mlModelsTable loaded', { timeout: 30 * 1000 }); + } + + async getModelsSearchInput(): Promise { + const tableListContainer = await testSubjects.find('mlModelsTableContainer'); + return await tableListContainer.findByClassName('euiFieldSearch'); + } + + public async assertModelsSearchInputValue(expectedSearchValue: string) { + const searchBarInput = await this.getModelsSearchInput(); + const actualSearchValue = await searchBarInput.getAttribute('value'); + expect(actualSearchValue).to.eql( + expectedSearchValue, + `Trained models search input value should be '${expectedSearchValue}' (got '${actualSearchValue}')` + ); + } + + public async filterWithSearchString(filter: string, expectedRowCount: number = 1) { + await this.waitForModelsToLoad(); + const searchBarInput = await this.getModelsSearchInput(); + await searchBarInput.clearValueWithKeyboard(); + await searchBarInput.type(filter); + await this.assertModelsSearchInputValue(filter); + + const rows = await this.parseModelsTable(); + const filteredRows = rows.filter((row) => row.id === filter); + expect(filteredRows).to.have.length( + expectedRowCount, + `Filtered trained models table should have ${expectedRowCount} row(s) for filter '${filter}' (got matching items '${filteredRows}')` + ); + } + + public async assertModelDisplayedInTable(modelId: string, shouldBeDisplayed: boolean) { + await retry.tryForTime(5 * 1000, async () => { + await this.filterWithSearchString(modelId, shouldBeDisplayed === true ? 1 : 0); + }); + } + + public async assertModelsRowFields(modelId: string, expectedRow: TrainedModelRowData) { + await this.refreshModelsTable(); + const rows = await this.parseModelsTable(); + const modelRow = rows.filter((row) => row.id === modelId)[0]; + expect(modelRow.id).to.eql( + expectedRow.id, + `Expected trained model row ID to be '${expectedRow.id}' (got '${modelRow.id}')` + ); + expect(modelRow.description).to.eql( + expectedRow.description, + `Expected trained model row description to be '${expectedRow.description}' (got '${modelRow.description}')` + ); + expect(modelRow.modelTypes.sort()).to.eql( + expectedRow.modelTypes.sort(), + `Expected trained model row types to be '${JSON.stringify( + expectedRow.modelTypes + )}' (got '${JSON.stringify(modelRow.modelTypes)}')` + ); + // 'Created at' will be different on each run, + // so we will just assert that the value is in the expected timestamp format. + expect(modelRow.createdAt).to.match( + /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/, + `Expected trained model row created at time to have same format as '2019-12-05 12:28:34' (got '${modelRow.createdAt}')` + ); + } + + public async assertModelCollapsedActionsButtonExists(modelId: string, expectedValue: boolean) { + const actionsExists = await testSubjects.exists( + this.rowSelector(modelId, 'euiCollapsedItemActionsButton') + ); + expect(actionsExists).to.eql( + expectedValue, + `Expected row collapsed actions menu button for trained model '${modelId}' to be ${ + expectedValue ? 'visible' : 'hidden' + } (got ${actionsExists ? 'visible' : 'hidden'})` + ); + } + + public async assertModelDeleteActionButtonExists(modelId: string, expectedValue: boolean) { + const actionsExists = await testSubjects.exists( + this.rowSelector(modelId, 'mlModelsTableRowDeleteAction') + ); + expect(actionsExists).to.eql( + expectedValue, + `Expected row delete action button for trained model '${modelId}' to be ${ + expectedValue ? 'visible' : 'hidden' + } (got ${actionsExists ? 'visible' : 'hidden'})` + ); + } + + public async assertModelDeleteActionButtonEnabled(modelId: string, expectedValue: boolean) { + await this.assertModelDeleteActionButtonExists(modelId, true); + const isEnabled = await testSubjects.isEnabled( + this.rowSelector(modelId, 'mlModelsTableRowDeleteAction') + ); + expect(isEnabled).to.eql( + expectedValue, + `Expected row delete action button for trained model '${modelId}' to be '${ + expectedValue ? 'enabled' : 'disabled' + }' (got '${isEnabled ? 'enabled' : 'disabled'}')` + ); + } + + public async assertDeleteModalExists() { + await testSubjects.existOrFail('mlModelsDeleteModal', { timeout: 60 * 1000 }); + } + + public async assertDeleteModalNotExists() { + await testSubjects.missingOrFail('mlModelsDeleteModal', { timeout: 60 * 1000 }); + } + + public async confirmDeleteModel() { + await retry.tryForTime(30 * 1000, async () => { + await this.assertDeleteModalExists(); + await testSubjects.click('mlModelsDeleteModalConfirmButton'); + await this.assertDeleteModalNotExists(); + }); + } + + public async clickDeleteAction(modelId: string) { + await testSubjects.click(this.rowSelector(modelId, 'mlModelsTableRowDeleteAction')); + await this.assertDeleteModalExists(); + } + })(); +} From 2e6ce134f4c1f1860ca556e1f5da8c21bd0be559 Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Thu, 7 Oct 2021 23:14:20 +0300 Subject: [PATCH 03/74] Revert "[TSVB] Rename Index pattern to Data view (#110253)" (#114278) This reverts commit 51df1e58a38892e4db648678899c185a9f44e596. --- .../index_pattern_select.tsx | 37 +++++++++---------- .../switch_mode_popover.tsx | 9 +++-- .../application/components/series_config.js | 4 +- .../use_index_patter_mode_callout.tsx | 12 +++--- .../components/vis_types/timeseries/config.js | 4 +- .../translations/translations/ja-JP.json | 13 ++++++- .../translations/translations/zh-CN.json | 13 ++++++- 7 files changed, 54 insertions(+), 38 deletions(-) diff --git a/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx index d28e2c5e0fb9..1029ac67cc43 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/index_pattern_select.tsx @@ -33,28 +33,22 @@ export interface IndexPatternSelectProps { | null; } -const queryAllIndicesHelpText = ( - *, - }} - /> +const defaultIndexPatternHelpText = i18n.translate( + 'visTypeTimeseries.indexPatternSelect.defaultIndexPatternText', + { + defaultMessage: 'Default index pattern is used.', + } ); -const getIndexPatternHelpText = (useKibanaIndices: boolean) => ( - +const queryAllIndexesHelpText = i18n.translate( + 'visTypeTimeseries.indexPatternSelect.queryAllIndexesText', + { + defaultMessage: 'To query all indexes use *', + } ); const indexPatternLabel = i18n.translate('visTypeTimeseries.indexPatternSelect.label', { - defaultMessage: 'Data view', + defaultMessage: 'Index pattern', }); export const IndexPatternSelect = ({ @@ -109,14 +103,17 @@ export const IndexPatternSelect = ({ diff --git a/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx index 16eeaff30c20..f33f51f60d04 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/index_pattern_select/switch_mode_popover.tsx @@ -79,7 +79,7 @@ export const SwitchModePopover = ({ onModeChange, useKibanaIndices }: PopoverPro aria-label={i18n.translate( 'visTypeTimeseries.indexPatternSelect.switchModePopover.areaLabel', { - defaultMessage: 'Configure data view selection mode', + defaultMessage: 'Configure index pattern selection mode', } )} onClick={onButtonClick} @@ -97,13 +97,14 @@ export const SwitchModePopover = ({ onModeChange, useKibanaIndices }: PopoverPro > {i18n.translate('visTypeTimeseries.indexPatternSelect.switchModePopover.title', { - defaultMessage: 'Data view mode', + defaultMessage: 'Index pattern selection mode', })} { { } iconType="cheer" @@ -42,13 +42,13 @@ export const UseIndexPatternModeCallout = () => { >

@@ -59,7 +59,7 @@ export const UseIndexPatternModeCallout = () => { diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/config.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/config.js index 208f9af9bb25..4257c35a6d4c 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/config.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/config.js @@ -538,8 +538,8 @@ export const TimeseriesConfig = injectI18n(function (props) { Date: Thu, 7 Oct 2021 23:14:53 +0300 Subject: [PATCH 04/74] Revert "[Visualize] Wizard rename Index pattern to Data view (#110251)" (#114276) This reverts commit a8084b7f194758734db024891012cf555f0e0a4f. --- .../public/wizard/search_selection/search_selection.tsx | 4 ++-- x-pack/plugins/translations/translations/ja-JP.json | 1 + x-pack/plugins/translations/translations/zh-CN.json | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx b/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx index 1d297a8d9ebc..81709abb800b 100644 --- a/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx +++ b/src/plugins/visualizations/public/wizard/search_selection/search_selection.tsx @@ -71,9 +71,9 @@ export class SearchSelection extends React.Component { type: 'index-pattern', getIconForSavedObject: () => 'indexPatternApp', name: i18n.translate( - 'visualizations.newVisWizard.searchSelection.savedObjectType.dataView', + 'visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern', { - defaultMessage: 'Data view', + defaultMessage: 'Index pattern', } ), }, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 2c1929400ca2..74e96b98dce7 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -5899,6 +5899,7 @@ "visualizations.newVisWizard.newVisTypeTitle": "新規 {visTypeName}", "visualizations.newVisWizard.readDocumentationLink": "ドキュメンテーションを表示", "visualizations.newVisWizard.searchSelection.notFoundLabel": "一致インデックスまたは保存した検索が見つかりません。", + "visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "インデックスパターン", "visualizations.newVisWizard.searchSelection.savedObjectType.search": "保存検索", "visualizations.newVisWizard.title": "新規ビジュアライゼーション", "visualizations.newVisWizard.toolsGroupTitle": "ツール", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3ae51ef369fd..7e88077b631f 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -5944,6 +5944,7 @@ "visualizations.newVisWizard.readDocumentationLink": "阅读文档", "visualizations.newVisWizard.resultsFound": "{resultCount, plural, other {类型}}已找到", "visualizations.newVisWizard.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。", + "visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "索引模式", "visualizations.newVisWizard.searchSelection.savedObjectType.search": "已保存搜索", "visualizations.newVisWizard.title": "新建可视化", "visualizations.newVisWizard.toolsGroupTitle": "工具", From f945ccfbbf299c961a759ce11c539bef3201ca7e Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Thu, 7 Oct 2021 23:15:47 +0300 Subject: [PATCH 05/74] Revert "[Lens] Rename Index pattern to Data view (#110252)" (#114277) This reverts commit 93522e5fa8b4d73dffaf1aaaa3e1d5d9703b29d4. --- .../editor_frame/config_panel/add_layer.tsx | 2 +- .../workspace_panel/workspace_panel.tsx | 12 ++++++------ .../editor_frame_service/error_helper.ts | 5 +++-- .../change_indexpattern.tsx | 4 ++-- .../indexpattern_datasource/datapanel.tsx | 18 +++++++++--------- .../dimensions_editor_helpers.tsx | 2 +- .../indexpattern_datasource/field_item.tsx | 8 ++++---- .../indexpattern_datasource/indexpattern.tsx | 4 ++-- .../indexpattern_datasource/layerpanel.tsx | 4 ++-- .../no_fields_callout.test.tsx | 2 +- .../no_fields_callout.tsx | 2 +- .../operations/definitions/last_value.test.tsx | 2 +- .../operations/definitions/last_value.tsx | 8 ++++---- .../operations/layer_helpers.ts | 4 +++- .../plugins/lens/server/routes/field_stats.ts | 2 +- .../translations/translations/ja-JP.json | 16 ++++++++++++++++ .../translations/translations/zh-CN.json | 18 ++++++++++++++++++ 17 files changed, 75 insertions(+), 38 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx index b0c10abb7581..e052e06f1b2f 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/add_layer.tsx @@ -57,7 +57,7 @@ export function AddLayerButton({ })} content={i18n.translate('xpack.lens.xyChart.addLayerTooltip', { defaultMessage: - 'Use multiple layers to combine visualization types or visualize different data views.', + 'Use multiple layers to combine visualization types or visualize different index patterns.', })} position="bottom" > diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index f1161b83c228..e4816870b438 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -151,9 +151,9 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({ ? [ { shortMessage: '', - longMessage: i18n.translate('xpack.lens.indexPattern.missingDataView', { + longMessage: i18n.translate('xpack.lens.indexPattern.missingIndexPattern', { defaultMessage: - 'The {count, plural, one {data view} other {data views}} ({count, plural, one {id} other {ids}}: {indexpatterns}) cannot be found', + 'The {count, plural, one {index pattern} other {index patterns}} ({count, plural, one {id} other {ids}}: {indexpatterns}) cannot be found', values: { count: missingIndexPatterns.length, indexpatterns: missingIndexPatterns.join(', '), @@ -569,8 +569,8 @@ export const VisualizationWrapper = ({ })} data-test-subj="configuration-failure-reconfigure-indexpatterns" > - {i18n.translate('xpack.lens.editorFrame.dataViewReconfigure', { - defaultMessage: `Recreate it in the data view management page`, + {i18n.translate('xpack.lens.editorFrame.indexPatternReconfigure', { + defaultMessage: `Recreate it in the index pattern management page`, })} @@ -580,8 +580,8 @@ export const VisualizationWrapper = ({ <>

diff --git a/x-pack/plugins/lens/public/editor_frame_service/error_helper.ts b/x-pack/plugins/lens/public/editor_frame_service/error_helper.ts index 9df48d99ce76..b19a295b6840 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/error_helper.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/error_helper.ts @@ -160,8 +160,9 @@ export function getMissingCurrentDatasource() { } export function getMissingIndexPatterns(indexPatternIds: string[]) { - return i18n.translate('xpack.lens.editorFrame.expressionMissingDataView', { - defaultMessage: 'Could not find the {count, plural, one {data view} other {data views}}: {ids}', + return i18n.translate('xpack.lens.editorFrame.expressionMissingIndexPattern', { + defaultMessage: + 'Could not find the {count, plural, one {index pattern} other {index pattern}}: {ids}', values: { count: indexPatternIds.length, ids: indexPatternIds.join(', ') }, }); } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx index ca44e833981a..64d7f5efc9c4 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/change_indexpattern.tsx @@ -69,8 +69,8 @@ export function ChangeIndexPattern({ >

- {i18n.translate('xpack.lens.indexPattern.changeDataViewTitle', { - defaultMessage: 'Data view', + {i18n.translate('xpack.lens.indexPattern.changeIndexPatternTitle', { + defaultMessage: 'Index pattern', })}

@@ -642,7 +642,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ iconType="boxesHorizontal" data-test-subj="lnsIndexPatternActions" aria-label={i18n.translate('xpack.lens.indexPatterns.actionsPopoverLabel', { - defaultMessage: 'Data view settings', + defaultMessage: 'Index pattern settings', })} onClick={() => { setPopoverOpen(!popoverOpen); @@ -663,7 +663,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ }} > {i18n.translate('xpack.lens.indexPatterns.addFieldButton', { - defaultMessage: 'Add field to data view', + defaultMessage: 'Add field to index pattern', })} , {i18n.translate('xpack.lens.indexPatterns.manageFieldButton', { - defaultMessage: 'Manage data view fields', + defaultMessage: 'Manage index pattern fields', })} , ]} @@ -709,7 +709,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ data-test-subj="lnsIndexPatternFieldSearch" placeholder={i18n.translate('xpack.lens.indexPatterns.filterByNameLabel', { defaultMessage: 'Search field names', - description: 'Search the list of fields in the data view for the provided text', + description: 'Search the list of fields in the index pattern for the provided text', })} value={localState.nameFilter} onChange={(e) => { @@ -717,7 +717,7 @@ export const InnerIndexPatternDataPanel = function InnerIndexPatternDataPanel({ }} aria-label={i18n.translate('xpack.lens.indexPatterns.filterByNameLabel', { defaultMessage: 'Search field names', - description: 'Search the list of fields in the data view for the provided text', + description: 'Search the list of fields in the index pattern for the provided text', })} aria-describedby={fieldSearchDescriptionId} /> diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx index dc6dc6dc31c8..a39f3705fd23 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimensions_editor_helpers.tsx @@ -217,7 +217,7 @@ export function getErrorMessage( } if (fieldInvalid) { return i18n.translate('xpack.lens.indexPattern.invalidFieldLabel', { - defaultMessage: 'Invalid field. Check your data view or pick another field.', + defaultMessage: 'Invalid field. Check your index pattern or pick another field.', }); } } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index ee6065aabf9d..9c22ec9d4bb0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -348,7 +348,7 @@ function FieldPanelHeader({ @@ -366,7 +366,7 @@ function FieldPanelHeader({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index c408d0130825..2138b06a4c34 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -98,8 +98,8 @@ export function getIndexPatternDatasource({ const uiSettings = core.uiSettings; const onIndexPatternLoadError = (err: Error) => core.notifications.toasts.addError(err, { - title: i18n.translate('xpack.lens.indexPattern.dataViewLoadError', { - defaultMessage: 'Error loading data view', + title: i18n.translate('xpack.lens.indexPattern.indexPatternLoadError', { + defaultMessage: 'Error loading index pattern', }), }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.tsx index 28f2921ccc77..12536e556f30 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/layerpanel.tsx @@ -23,8 +23,8 @@ export function LayerPanel({ state, layerId, onChangeIndexPattern }: IndexPatter const indexPattern = state.indexPatterns[layer.indexPatternId]; - const notFoundTitleLabel = i18n.translate('xpack.lens.layerPanel.missingDataView', { - defaultMessage: 'Data view not found', + const notFoundTitleLabel = i18n.translate('xpack.lens.layerPanel.missingIndexPattern', { + defaultMessage: 'Index pattern not found', }); return ( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/no_fields_callout.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/no_fields_callout.test.tsx index 635c06691a73..69dc150922b4 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/no_fields_callout.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/no_fields_callout.test.tsx @@ -16,7 +16,7 @@ describe('NoFieldCallout', () => { `); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/no_fields_callout.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/no_fields_callout.tsx index 073b21c700cc..6b434e8cd41a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/no_fields_callout.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/no_fields_callout.tsx @@ -32,7 +32,7 @@ export const NoFieldsCallout = ({ size="s" color="warning" title={i18n.translate('xpack.lens.indexPatterns.noFieldsLabel', { - defaultMessage: 'No fields exist in this data view.', + defaultMessage: 'No fields exist in this index pattern.', })} /> ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx index d0dd8a438ed1..77af42ab4188 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx @@ -343,7 +343,7 @@ describe('last_value', () => { 'data' ); expect(disabledStatus).toEqual( - 'This function requires the presence of a date field in your data view' + 'This function requires the presence of a date field in your index' ); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx index 9a3ba9a04414..88c9d82092e2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx @@ -134,7 +134,7 @@ export const lastValueOperation: OperationDefinition @@ -284,7 +284,7 @@ export const lastValueOperation: OperationDefinition) { const field = indexPattern.fields.find((f) => f.name === fieldName); if (!field) { - throw new Error(`Field {fieldName} not found in data view ${indexPattern.title}`); + throw new Error(`Field {fieldName} not found in index pattern ${indexPattern.title}`); } const filter = timeFieldName diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 74e96b98dce7..476ba3485659 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -238,6 +238,8 @@ "xpack.lens.editorFrame.expressionMissingDatasource": "ビジュアライゼーションのデータソースが見つかりませんでした", "xpack.lens.editorFrame.expressionMissingVisualizationType": "ビジュアライゼーションタイプが見つかりません。", "xpack.lens.editorFrame.goToForums": "リクエストとフィードバック", + "xpack.lens.editorFrame.indexPatternNotFound": "インデックスパターンが見つかりませんでした", + "xpack.lens.editorFrame.indexPatternReconfigure": "インデックスパターン管理ページで再作成", "xpack.lens.editorFrame.invisibleIndicatorLabel": "このディメンションは現在グラフに表示されません", "xpack.lens.editorFrame.networkErrorMessage": "ネットワークエラーです。しばらくたってから再試行するか、管理者に連絡してください。", "xpack.lens.editorFrame.noColorIndicatorLabel": "このディメンションには個別の色がありません", @@ -364,6 +366,7 @@ "xpack.lens.indexPattern.cardinality": "ユニークカウント", "xpack.lens.indexPattern.cardinality.signature": "フィールド:文字列", "xpack.lens.indexPattern.cardinalityOf": "{name} のユニークカウント", + "xpack.lens.indexPattern.changeIndexPatternTitle": "インデックスパターン", "xpack.lens.indexPattern.chooseField": "フィールドを選択", "xpack.lens.indexPattern.chooseFieldLabel": "この関数を使用するには、フィールドを選択してください。", "xpack.lens.indexPattern.chooseSubFunction": "サブ関数を選択", @@ -403,6 +406,7 @@ "xpack.lens.indexPattern.derivative": "差異", "xpack.lens.indexPattern.derivativeOf": "{name} の差異", "xpack.lens.indexPattern.differences.signature": "メトリック:数値", + "xpack.lens.indexPattern.editFieldLabel": "インデックスパターンフィールドを編集", "xpack.lens.indexPattern.emptyDimensionButton": "空のディメンション", "xpack.lens.indexPattern.emptyFieldsLabel": "空のフィールド", "xpack.lens.indexPattern.emptyFieldsLabelHelp": "空のフィールドには、フィルターに基づく最初の 500 件のドキュメントの値が含まれていませんでした。", @@ -463,12 +467,15 @@ "xpack.lens.indexPattern.functionsLabel": "関数を選択", "xpack.lens.indexPattern.groupByDropdown": "グループ分けの条件", "xpack.lens.indexPattern.incompleteOperation": "(未完了)", + "xpack.lens.indexPattern.indexPatternLoadError": "インデックスパターンの読み込み中にエラーが発生", "xpack.lens.indexPattern.intervals": "間隔", + "xpack.lens.indexPattern.invalidFieldLabel": "無効なフィールドです。インデックスパターンを確認するか、別のフィールドを選択してください。", "xpack.lens.indexPattern.invalidInterval": "無効な間隔値", "xpack.lens.indexPattern.invalidOperationLabel": "選択した関数はこのフィールドで動作しません。", "xpack.lens.indexPattern.invalidReferenceConfiguration": "ディメンション\"{dimensionLabel}\"の構成が正しくありません", "xpack.lens.indexPattern.invalidTimeShift": "無効な時間シフトです。正の整数の後に単位s、m、h、d、w、M、yのいずれかを入力します。例:3時間は3hです", "xpack.lens.indexPattern.lastValue": "最終値", + "xpack.lens.indexPattern.lastValue.disabled": "この関数には、インデックスの日付フィールドが必要です", "xpack.lens.indexPattern.lastValue.invalidTypeSortField": "フィールド {invalidField} は日付フィールドではないため、並べ替えで使用できません", "xpack.lens.indexPattern.lastValue.signature": "フィールド:文字列", "xpack.lens.indexPattern.lastValue.sortField": "日付フィールドで並べ替え", @@ -504,6 +511,8 @@ "xpack.lens.indexPattern.movingAverage.windowLimitations": "ウィンドウには現在の値が含まれません。", "xpack.lens.indexPattern.movingAverageOf": "{name} の移動平均", "xpack.lens.indexPattern.multipleDateHistogramsError": "\"{dimensionLabel}\"は唯一の日付ヒストグラムではありません。時間シフトを使用するときには、1つの日付ヒストグラムのみを使用していることを確認してください。", + "xpack.lens.indexPattern.noPatternsDescription": "インデックスパターンを作成するか、別のデータソースに切り替えてください", + "xpack.lens.indexPattern.noPatternsLabel": "インデックスパターンがありません", "xpack.lens.indexPattern.numberFormatLabel": "数字", "xpack.lens.indexPattern.ofDocumentsLabel": "ドキュメント", "xpack.lens.indexPattern.otherDocsLabel": "その他", @@ -547,6 +556,7 @@ "xpack.lens.indexPattern.referenceFunctionPlaceholder": "サブ関数", "xpack.lens.indexPattern.removeColumnAriaLabel": "フィールドを追加するか、{groupLabel}までドラッグアンドドロップします", "xpack.lens.indexPattern.removeColumnLabel": "「{groupLabel}」から構成を削除", + "xpack.lens.indexPattern.removeFieldLabel": "インデックスパターンを削除", "xpack.lens.indexPattern.sortField.invalid": "無効なフィールドです。インデックスパターンを確認するか、別のフィールドを選択してください。", "xpack.lens.indexpattern.suggestions.nestingChangeLabel": "各 {outerOperation} の {innerOperation}", "xpack.lens.indexpattern.suggestions.overallLabel": "全体の {operation}", @@ -592,8 +602,12 @@ "xpack.lens.indexPattern.timeShiftSmallWarning": "{label}は{columnTimeShift}の時間シフトを使用しています。これは{interval}の日付ヒストグラム間隔よりも小さいです。不一致のデータを防止するには、時間シフトとして{interval}を使用します。", "xpack.lens.indexPattern.uniqueLabel": "{label} [{num}]", "xpack.lens.indexPattern.useAsTopLevelAgg": "最初にこのフィールドでグループ化", + "xpack.lens.indexPatterns.actionsPopoverLabel": "インデックスパターン設定", + "xpack.lens.indexPatterns.addFieldButton": "フィールドをインデックスパターンに追加", "xpack.lens.indexPatterns.clearFiltersLabel": "名前とタイプフィルターを消去", "xpack.lens.indexPatterns.fieldFiltersLabel": "タイプでフィルタリング", + "xpack.lens.indexPatterns.filterByNameLabel": "検索フィールド名", + "xpack.lens.indexPatterns.manageFieldButton": "インデックスパターンを管理", "xpack.lens.indexPatterns.noAvailableDataLabel": "データを含むフィールドはありません。", "xpack.lens.indexPatterns.noDataLabel": "フィールドがありません。", "xpack.lens.indexPatterns.noEmptyDataLabel": "空のフィールドがありません。", @@ -601,12 +615,14 @@ "xpack.lens.indexPatterns.noFields.fieldTypeFilterBullet": "別のフィールドフィルターを使用", "xpack.lens.indexPatterns.noFields.globalFiltersBullet": "グローバルフィルターを変更", "xpack.lens.indexPatterns.noFields.tryText": "試行対象:", + "xpack.lens.indexPatterns.noFieldsLabel": "このインデックスパターンにはフィールドがありません。", "xpack.lens.indexPatterns.noFilteredFieldsLabel": "選択したフィルターと一致するフィールドはありません。", "xpack.lens.indexPatterns.noMetaDataLabel": "メタフィールドがありません。", "xpack.lens.indexPatternSuggestion.removeLayerLabel": "{indexPatternTitle}のみを表示", "xpack.lens.indexPatternSuggestion.removeLayerPositionLabel": "レイヤー{layerNumber}のみを表示", "xpack.lens.labelInput.label": "ラベル", "xpack.lens.layerPanel.layerVisualizationType": "レイヤービジュアライゼーションタイプ", + "xpack.lens.layerPanel.missingIndexPattern": "インデックスパターンが見つかりませんでした", "xpack.lens.lensSavedObjectLabel": "レンズビジュアライゼーション", "xpack.lens.metric.addLayer": "ビジュアライゼーションレイヤーを追加", "xpack.lens.metric.groupLabel": "表形式の値と単一の値", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 7e88077b631f..88f3004c0bc0 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -241,8 +241,11 @@ "xpack.lens.editorFrame.expressionFailureMessage": "请求错误:{type},{reason}", "xpack.lens.editorFrame.expressionFailureMessageWithContext": "请求错误:{type},{context} 中的 {reason}", "xpack.lens.editorFrame.expressionMissingDatasource": "无法找到可视化的数据源", + "xpack.lens.editorFrame.expressionMissingIndexPattern": "找不到{count, plural, other {索引模式}}:{ids}", "xpack.lens.editorFrame.expressionMissingVisualizationType": "找不到可视化类型。", "xpack.lens.editorFrame.goToForums": "提出请求并提供反馈", + "xpack.lens.editorFrame.indexPatternNotFound": "未找到索引模式", + "xpack.lens.editorFrame.indexPatternReconfigure": "在索引模式管理页面中重新创建", "xpack.lens.editorFrame.invisibleIndicatorLabel": "此维度当前在图表中不可见", "xpack.lens.editorFrame.networkErrorMessage": "网络错误,请稍后重试或联系管理员。", "xpack.lens.editorFrame.noColorIndicatorLabel": "此维度没有单独的颜色", @@ -371,6 +374,7 @@ "xpack.lens.indexPattern.cardinality": "唯一计数", "xpack.lens.indexPattern.cardinality.signature": "field: string", "xpack.lens.indexPattern.cardinalityOf": "{name} 的唯一计数", + "xpack.lens.indexPattern.changeIndexPatternTitle": "索引模式", "xpack.lens.indexPattern.chooseField": "选择字段", "xpack.lens.indexPattern.chooseFieldLabel": "要使用此函数,请选择字段。", "xpack.lens.indexPattern.chooseSubFunction": "选择子函数", @@ -410,6 +414,7 @@ "xpack.lens.indexPattern.derivative": "差异", "xpack.lens.indexPattern.derivativeOf": "{name} 的差异", "xpack.lens.indexPattern.differences.signature": "指标:数字", + "xpack.lens.indexPattern.editFieldLabel": "编辑索引模式字段", "xpack.lens.indexPattern.emptyDimensionButton": "空维度", "xpack.lens.indexPattern.emptyFieldsLabel": "空字段", "xpack.lens.indexPattern.emptyFieldsLabelHelp": "空字段在基于您的筛选的前 500 个文档中不包含任何值。", @@ -471,12 +476,15 @@ "xpack.lens.indexPattern.functionsLabel": "选择函数", "xpack.lens.indexPattern.groupByDropdown": "分组依据", "xpack.lens.indexPattern.incompleteOperation": "(不完整)", + "xpack.lens.indexPattern.indexPatternLoadError": "加载索引模式时出错", "xpack.lens.indexPattern.intervals": "时间间隔", + "xpack.lens.indexPattern.invalidFieldLabel": "字段无效。检查索引模式或选取其他字段。", "xpack.lens.indexPattern.invalidInterval": "时间间隔值无效", "xpack.lens.indexPattern.invalidOperationLabel": "此字段不适用于选定函数。", "xpack.lens.indexPattern.invalidReferenceConfiguration": "维度“{dimensionLabel}”配置不正确", "xpack.lens.indexPattern.invalidTimeShift": "时间偏移无效。输入正整数数量,后跟以下单位之一:s、m、h、d、w、M、y。例如,3h 表示 3 小时", "xpack.lens.indexPattern.lastValue": "最后值", + "xpack.lens.indexPattern.lastValue.disabled": "此功能要求索引中存在日期字段", "xpack.lens.indexPattern.lastValue.invalidTypeSortField": "字段 {invalidField} 不是日期字段,不能用于排序", "xpack.lens.indexPattern.lastValue.signature": "field: string", "xpack.lens.indexPattern.lastValue.sortField": "按日期字段排序", @@ -496,6 +504,7 @@ "xpack.lens.indexPattern.min.description": "单值指标聚合,返回从聚合文档提取的数值中的最小值。", "xpack.lens.indexPattern.minOf": "{name} 的最小值", "xpack.lens.indexPattern.missingFieldLabel": "缺失字段", + "xpack.lens.indexPattern.missingIndexPattern": "找不到{count, plural, other {索引模式}} ({count, plural, other {id}}:{indexpatterns})", "xpack.lens.indexPattern.missingReferenceError": "“{dimensionLabel}”配置不完整", "xpack.lens.indexPattern.moveToWorkspace": "将 {field} 添加到工作区", "xpack.lens.indexPattern.moveToWorkspaceDisabled": "此字段无法自动添加到工作区。您仍可以在配置面板中直接使用它。", @@ -512,6 +521,8 @@ "xpack.lens.indexPattern.movingAverage.windowLimitations": "时间窗不包括当前值。", "xpack.lens.indexPattern.movingAverageOf": "{name} 的移动平均值", "xpack.lens.indexPattern.multipleDateHistogramsError": "“{dimensionLabel}”不是唯一的 Date Histogram。使用时间偏移时,请确保仅使用一个 Date Histogram。", + "xpack.lens.indexPattern.noPatternsDescription": "请创建索引模式或切换到其他数据源", + "xpack.lens.indexPattern.noPatternsLabel": "无索引模式", "xpack.lens.indexPattern.numberFormatLabel": "数字", "xpack.lens.indexPattern.ofDocumentsLabel": "文档", "xpack.lens.indexPattern.operationsNotFound": "未找到{operationLength, plural, other {运算}} {operationsList}", @@ -556,6 +567,7 @@ "xpack.lens.indexPattern.referenceFunctionPlaceholder": "子函数", "xpack.lens.indexPattern.removeColumnAriaLabel": "将字段添加或拖放到 {groupLabel}", "xpack.lens.indexPattern.removeColumnLabel": "从“{groupLabel}”中删除配置", + "xpack.lens.indexPattern.removeFieldLabel": "移除索引模式字段", "xpack.lens.indexPattern.sortField.invalid": "字段无效。检查索引模式或选取其他字段。", "xpack.lens.indexpattern.suggestions.nestingChangeLabel": "每个 {outerOperation} 的 {innerOperation}", "xpack.lens.indexpattern.suggestions.overallLabel": "总体 {operation}", @@ -601,9 +613,13 @@ "xpack.lens.indexPattern.timeShiftSmallWarning": "{label} 使用的时间偏移 {columnTimeShift} 小于 Date Histogram 时间间隔 {interval} 。要防止数据不匹配,请使用 {interval} 的倍数作为时间偏移。", "xpack.lens.indexPattern.uniqueLabel": "{label} [{num}]", "xpack.lens.indexPattern.useAsTopLevelAgg": "先按此字段分组", + "xpack.lens.indexPatterns.actionsPopoverLabel": "索引模式设置", + "xpack.lens.indexPatterns.addFieldButton": "将字段添加到索引模式", "xpack.lens.indexPatterns.clearFiltersLabel": "清除名称和类型筛选", "xpack.lens.indexPatterns.fieldFiltersLabel": "按类型筛选", "xpack.lens.indexPatterns.fieldSearchLiveRegion": "{availableFields} 个可用{availableFields, plural, other {字段}}。{emptyFields} 个空{emptyFields, plural, other {字段}}。{metaFields} 个元{metaFields, plural,other {字段}}。", + "xpack.lens.indexPatterns.filterByNameLabel": "搜索字段名称", + "xpack.lens.indexPatterns.manageFieldButton": "管理索引模式字段", "xpack.lens.indexPatterns.noAvailableDataLabel": "没有包含数据的可用字段。", "xpack.lens.indexPatterns.noDataLabel": "无字段。", "xpack.lens.indexPatterns.noEmptyDataLabel": "无空字段。", @@ -611,12 +627,14 @@ "xpack.lens.indexPatterns.noFields.fieldTypeFilterBullet": "使用不同的字段筛选", "xpack.lens.indexPatterns.noFields.globalFiltersBullet": "更改全局筛选", "xpack.lens.indexPatterns.noFields.tryText": "尝试:", + "xpack.lens.indexPatterns.noFieldsLabel": "在此索引模式中不存在任何字段。", "xpack.lens.indexPatterns.noFilteredFieldsLabel": "没有字段匹配选定筛选。", "xpack.lens.indexPatterns.noMetaDataLabel": "无元字段。", "xpack.lens.indexPatternSuggestion.removeLayerLabel": "仅显示 {indexPatternTitle}", "xpack.lens.indexPatternSuggestion.removeLayerPositionLabel": "仅显示图层 {layerNumber}", "xpack.lens.labelInput.label": "标签", "xpack.lens.layerPanel.layerVisualizationType": "图层可视化类型", + "xpack.lens.layerPanel.missingIndexPattern": "未找到索引模式", "xpack.lens.lensSavedObjectLabel": "Lens 可视化", "xpack.lens.metric.addLayer": "添加可视化图层", "xpack.lens.metric.groupLabel": "表和单值", From ba11cb344f3c701ffbc7c2029aaffc3fd802ea73 Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Thu, 7 Oct 2021 13:35:24 -0700 Subject: [PATCH 06/74] [Reporting] Re-enable test for large CSV export from Discover (#113675) * [Reporting] Re-enable test for large CSV export from Discover * update snapshot Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/test/functional/apps/discover/reporting.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/x-pack/test/functional/apps/discover/reporting.ts b/x-pack/test/functional/apps/discover/reporting.ts index 99bdb20580ce..3c27aaeffa71 100644 --- a/x-pack/test/functional/apps/discover/reporting.ts +++ b/x-pack/test/functional/apps/discover/reporting.ts @@ -98,8 +98,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(res.text).to.be(`\n`); }); - // FIXME: https://github.com/elastic/kibana/issues/112186 - it.skip('generates a large export', async () => { + it('generates a large export', async () => { const fromTime = 'Apr 27, 2019 @ 23:56:51.374'; const toTime = 'Aug 23, 2019 @ 16:18:51.821'; await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); @@ -112,7 +111,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // match file length, the beginning and the end of the csv file contents const { text: csvFile } = await getReport(); - expect(csvFile.length).to.be(4954749); + expect(csvFile.length).to.be(5093456); expectSnapshot(csvFile.slice(0, 5000)).toMatch(); expectSnapshot(csvFile.slice(-5000)).toMatch(); }); From bd7d54642d38eb1bcad882a55d63c890e19cd78e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 7 Oct 2021 16:15:16 -0500 Subject: [PATCH 07/74] Update babel to ^7.15.8 (master) (#114195) Co-authored-by: Renovate Bot --- package.json | 12 +++--- yarn.lock | 109 ++++++++++++++++++++++++++++++++++----------------- 2 files changed, 78 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 6662c55629bb..585d6489551b 100644 --- a/package.json +++ b/package.json @@ -414,19 +414,19 @@ }, "devDependencies": { "@babel/cli": "^7.15.7", - "@babel/core": "^7.15.5", - "@babel/eslint-parser": "^7.15.7", + "@babel/core": "^7.15.8", + "@babel/eslint-parser": "^7.15.8", "@babel/eslint-plugin": "^7.14.5", - "@babel/generator": "^7.15.4", - "@babel/parser": "^7.15.7", + "@babel/generator": "^7.15.8", + "@babel/parser": "^7.15.8", "@babel/plugin-proposal-class-properties": "^7.14.5", "@babel/plugin-proposal-export-namespace-from": "^7.14.5", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", "@babel/plugin-proposal-object-rest-spread": "^7.15.6", "@babel/plugin-proposal-optional-chaining": "^7.14.5", "@babel/plugin-proposal-private-methods": "^7.14.5", - "@babel/plugin-transform-runtime": "^7.15.0", - "@babel/preset-env": "^7.15.6", + "@babel/plugin-transform-runtime": "^7.15.8", + "@babel/preset-env": "^7.15.8", "@babel/preset-react": "^7.14.5", "@babel/preset-typescript": "^7.15.0", "@babel/register": "^7.15.3", diff --git a/yarn.lock b/yarn.lock index 0d363d6402aa..66aeb2808031 100644 --- a/yarn.lock +++ b/yarn.lock @@ -46,6 +46,13 @@ dependencies: "@babel/highlight" "^7.14.5" +"@babel/code-frame@^7.15.8": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.15.8.tgz#45990c47adadb00c03677baa89221f7cc23d2503" + integrity sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg== + dependencies: + "@babel/highlight" "^7.14.5" + "@babel/compat-data@^7.12.5", "@babel/compat-data@^7.12.7": version "7.12.7" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.12.7.tgz#9329b4782a7d6bbd7eef57e11addf91ee3ef1e41" @@ -119,20 +126,20 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.15.5": - version "7.15.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.15.5.tgz#f8ed9ace730722544609f90c9bb49162dc3bf5b9" - integrity sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg== +"@babel/core@^7.15.8": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.15.8.tgz#195b9f2bffe995d2c6c159e72fe525b4114e8c10" + integrity sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og== dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" + "@babel/code-frame" "^7.15.8" + "@babel/generator" "^7.15.8" "@babel/helper-compilation-targets" "^7.15.4" - "@babel/helper-module-transforms" "^7.15.4" + "@babel/helper-module-transforms" "^7.15.8" "@babel/helpers" "^7.15.4" - "@babel/parser" "^7.15.5" + "@babel/parser" "^7.15.8" "@babel/template" "^7.15.4" "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" + "@babel/types" "^7.15.6" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -140,10 +147,10 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/eslint-parser@^7.15.7": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.15.7.tgz#2dc3d0ff0ea22bb1e08d93b4eeb1149bf1c75f2d" - integrity sha512-yJkHyomClm6A2Xzb8pdAo4HzYMSXFn1O5zrCYvbFP0yQFvHueLedV8WiEno8yJOKStjUXzBZzJFeWQ7b3YMsqQ== +"@babel/eslint-parser@^7.15.8": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.15.8.tgz#8988660b59d739500b67d0585fd4daca218d9f11" + integrity sha512-fYP7QFngCvgxjUuw8O057SVH5jCXsbFFOoE77CFDcvzwBVgTOkMD/L4mIC5Ud1xf8chK/no2fRbSSn1wvNmKuQ== dependencies: eslint-scope "^5.1.1" eslint-visitor-keys "^2.1.0" @@ -174,6 +181,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.15.8": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.8.tgz#fa56be6b596952ceb231048cf84ee499a19c0cd1" + integrity sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g== + dependencies: + "@babel/types" "^7.15.6" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.10": version "7.12.10" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz#54ab9b000e60a93644ce17b3f37d313aaf1d115d" @@ -405,6 +421,20 @@ "@babel/traverse" "^7.15.4" "@babel/types" "^7.15.6" +"@babel/helper-module-transforms@^7.15.8": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz#d8c0e75a87a52e374a8f25f855174786a09498b2" + integrity sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg== + dependencies: + "@babel/helper-module-imports" "^7.15.4" + "@babel/helper-replace-supers" "^7.15.4" + "@babel/helper-simple-access" "^7.15.4" + "@babel/helper-split-export-declaration" "^7.15.4" + "@babel/helper-validator-identifier" "^7.15.7" + "@babel/template" "^7.15.4" + "@babel/traverse" "^7.15.4" + "@babel/types" "^7.15.6" + "@babel/helper-optimise-call-expression@^7.10.4", "@babel/helper-optimise-call-expression@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" @@ -602,11 +632,16 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.9.tgz#ca34cb95e1c2dd126863a84465ae8ef66114be99" integrity sha512-nEUfRiARCcaVo3ny3ZQjURjHQZUo/JkEw7rLlSZy/psWGnvwXFtPcr6jb7Yb41DVW5LTe6KRq9LGleRNsg1Frw== -"@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.15.7": +"@babel/parser@^7.15.4": version "7.15.7" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.7.tgz#0c3ed4a2eb07b165dfa85b3cc45c727334c4edae" integrity sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g== +"@babel/parser@^7.15.8": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.8.tgz#7bacdcbe71bdc3ff936d510c15dcea7cf0b99016" + integrity sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA== + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.15.4.tgz#dbdeabb1e80f622d9f0b583efb2999605e0a567e" @@ -625,10 +660,10 @@ "@babel/helper-remap-async-to-generator" "^7.12.1" "@babel/plugin-syntax-async-generators" "^7.8.0" -"@babel/plugin-proposal-async-generator-functions@^7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.15.4.tgz#f82aabe96c135d2ceaa917feb9f5fca31635277e" - integrity sha512-2zt2g5vTXpMC3OmK6uyjvdXptbhBXfA77XGrd3gh93zwG8lZYBLOBImiGBEG0RANu3JqKEACCz5CGk73OJROBw== +"@babel/plugin-proposal-async-generator-functions@^7.15.8": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.15.8.tgz#a3100f785fab4357987c4223ab1b02b599048403" + integrity sha512-2Z5F2R2ibINTc63mY7FLqGfEbmofrHU9FitJW1Q7aPaKFhiPvSq6QEt/BoWN5oME3GVyjcRuNNSRbb9LC0CSWA== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-remap-async-to-generator" "^7.15.4" @@ -1561,15 +1596,15 @@ resolve "^1.8.1" semver "^5.5.1" -"@babel/plugin-transform-runtime@^7.15.0": - version "7.15.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.15.0.tgz#d3aa650d11678ca76ce294071fda53d7804183b3" - integrity sha512-sfHYkLGjhzWTq6xsuQ01oEsUYjkHRux9fW1iUA68dC7Qd8BS1Unq4aZ8itmQp95zUzIcyR2EbNMTzAicFj+guw== +"@babel/plugin-transform-runtime@^7.15.8": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.15.8.tgz#9d15b1e94e1c7f6344f65a8d573597d93c6cd886" + integrity sha512-+6zsde91jMzzvkzuEA3k63zCw+tm/GvuuabkpisgbDMTPQsIMHllE3XczJFFtEHLjjhKQFZmGQVRdELetlWpVw== dependencies: - "@babel/helper-module-imports" "^7.14.5" + "@babel/helper-module-imports" "^7.15.4" "@babel/helper-plugin-utils" "^7.14.5" babel-plugin-polyfill-corejs2 "^0.2.2" - babel-plugin-polyfill-corejs3 "^0.2.2" + babel-plugin-polyfill-corejs3 "^0.2.5" babel-plugin-polyfill-regenerator "^0.2.2" semver "^6.3.0" @@ -1595,13 +1630,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" -"@babel/plugin-transform-spread@^7.14.6": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.14.6.tgz#6bd40e57fe7de94aa904851963b5616652f73144" - integrity sha512-Zr0x0YroFJku7n7+/HH3A2eIrGMjbmAIbJSVv0IZ+t3U2WUQUA64S/oeied2e+MaGSjmt4alzBCsK9E8gh+fag== +"@babel/plugin-transform-spread@^7.15.8": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.15.8.tgz#79d5aa27f68d700449b2da07691dfa32d2f6d468" + integrity sha512-/daZ8s2tNaRekl9YJa9X4bzjpeRZLt122cpgFnQPLGUe61PH8zMEBmYqKkW5xF5JUEh5buEGXJoQpqBmIbpmEQ== dependencies: "@babel/helper-plugin-utils" "^7.14.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.14.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.15.4" "@babel/plugin-transform-sticky-regex@^7.12.7", "@babel/plugin-transform-sticky-regex@^7.2.0": version "7.12.7" @@ -1819,17 +1854,17 @@ core-js-compat "^3.8.0" semver "^5.5.0" -"@babel/preset-env@^7.15.6": - version "7.15.6" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.15.6.tgz#0f3898db9d63d320f21b17380d8462779de57659" - integrity sha512-L+6jcGn7EWu7zqaO2uoTDjjMBW+88FXzV8KvrBl2z6MtRNxlsmUNRlZPaNNPUTgqhyC5DHNFk/2Jmra+ublZWw== +"@babel/preset-env@^7.15.8": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.15.8.tgz#f527ce5bcb121cd199f6b502bf23e420b3ff8dba" + integrity sha512-rCC0wH8husJgY4FPbHsiYyiLxSY8oMDJH7Rl6RQMknbN9oDDHhM9RDFvnGM2MgkbUJzSQB4gtuwygY5mCqGSsA== dependencies: "@babel/compat-data" "^7.15.0" "@babel/helper-compilation-targets" "^7.15.4" "@babel/helper-plugin-utils" "^7.14.5" "@babel/helper-validator-option" "^7.14.5" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.15.4" - "@babel/plugin-proposal-async-generator-functions" "^7.15.4" + "@babel/plugin-proposal-async-generator-functions" "^7.15.8" "@babel/plugin-proposal-class-properties" "^7.14.5" "@babel/plugin-proposal-class-static-block" "^7.15.4" "@babel/plugin-proposal-dynamic-import" "^7.14.5" @@ -1884,7 +1919,7 @@ "@babel/plugin-transform-regenerator" "^7.14.5" "@babel/plugin-transform-reserved-words" "^7.14.5" "@babel/plugin-transform-shorthand-properties" "^7.14.5" - "@babel/plugin-transform-spread" "^7.14.6" + "@babel/plugin-transform-spread" "^7.15.8" "@babel/plugin-transform-sticky-regex" "^7.14.5" "@babel/plugin-transform-template-literals" "^7.14.5" "@babel/plugin-transform-typeof-symbol" "^7.14.5" @@ -1893,7 +1928,7 @@ "@babel/preset-modules" "^0.1.4" "@babel/types" "^7.15.6" babel-plugin-polyfill-corejs2 "^0.2.2" - babel-plugin-polyfill-corejs3 "^0.2.2" + babel-plugin-polyfill-corejs3 "^0.2.5" babel-plugin-polyfill-regenerator "^0.2.2" core-js-compat "^3.16.0" semver "^6.3.0" @@ -8926,7 +8961,7 @@ babel-plugin-polyfill-corejs2@^0.2.2: "@babel/helper-define-polyfill-provider" "^0.2.2" semver "^6.1.1" -babel-plugin-polyfill-corejs3@^0.2.2: +babel-plugin-polyfill-corejs3@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.5.tgz#2779846a16a1652244ae268b1e906ada107faf92" integrity sha512-ninF5MQNwAX9Z7c9ED+H2pGt1mXdP4TqzlHKyPIYmJIYz0N+++uwdM7RnJukklhzJ54Q84vA4ZJkgs7lu5vqcw== From 9bb8f2246c04b5540c2218ead2f1d334164f5bb0 Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 7 Oct 2021 16:37:42 -0500 Subject: [PATCH 08/74] [ts] support building refs for one project (#114345) Co-authored-by: spalger --- src/dev/typescript/build_ts_refs.ts | 7 +++-- src/dev/typescript/build_ts_refs_cli.ts | 35 ++++++++++++++++++------ src/dev/typescript/run_type_check_cli.ts | 17 ++++++++---- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/dev/typescript/build_ts_refs.ts b/src/dev/typescript/build_ts_refs.ts index db51ff999ccc..aaa8c0d12fa4 100644 --- a/src/dev/typescript/build_ts_refs.ts +++ b/src/dev/typescript/build_ts_refs.ts @@ -11,17 +11,20 @@ import Path from 'path'; import { ToolingLog, REPO_ROOT, ProcRunner } from '@kbn/dev-utils'; import { ROOT_REFS_CONFIG_PATH } from './root_refs_config'; +import { Project } from './project'; -export async function buildAllTsRefs({ +export async function buildTsRefs({ log, procRunner, verbose, + project, }: { log: ToolingLog; procRunner: ProcRunner; verbose?: boolean; + project?: Project; }): Promise<{ failed: boolean }> { - const relative = Path.relative(REPO_ROOT, ROOT_REFS_CONFIG_PATH); + const relative = Path.relative(REPO_ROOT, project ? project.tsConfigPath : ROOT_REFS_CONFIG_PATH); log.info(`Building TypeScript projects refs for ${relative}...`); try { diff --git a/src/dev/typescript/build_ts_refs_cli.ts b/src/dev/typescript/build_ts_refs_cli.ts index a1f1d1ac7028..c68424c2a98f 100644 --- a/src/dev/typescript/build_ts_refs_cli.ts +++ b/src/dev/typescript/build_ts_refs_cli.ts @@ -8,11 +8,11 @@ import Path from 'path'; -import { run, REPO_ROOT } from '@kbn/dev-utils'; +import { run, REPO_ROOT, createFlagError } from '@kbn/dev-utils'; import del from 'del'; import { RefOutputCache } from './ref_output_cache'; -import { buildAllTsRefs } from './build_ts_refs'; +import { buildTsRefs } from './build_ts_refs'; import { updateRootRefsConfig, ROOT_REFS_CONFIG_PATH } from './root_refs_config'; import { Project } from './project'; import { PROJECT_CACHE } from './projects'; @@ -41,25 +41,35 @@ export async function runBuildRefsCli() { return; } + const projectFilter = flags.project; + if (projectFilter && typeof projectFilter !== 'string') { + throw createFlagError('expected --project to be a string'); + } + // if the tsconfig.refs.json file is not self-managed then make sure it has // a reference to every composite project in the repo await updateRootRefsConfig(log); - // load all the projects referenced from the root refs config deeply, so we know all - // the ts projects we are going to be cleaning or populating with caches - const projects = Project.load( - ROOT_REFS_CONFIG_PATH, + const rootProject = Project.load( + projectFilter ? projectFilter : ROOT_REFS_CONFIG_PATH, {}, { skipConfigValidation: true, } - ).getProjectsDeep(PROJECT_CACHE); + ); + // load all the projects referenced from the root project deeply, so we know all + // the ts projects we are going to be cleaning or populating with caches + const projects = rootProject.getProjectsDeep(PROJECT_CACHE); const cacheEnabled = process.env.BUILD_TS_REFS_CACHE_ENABLE !== 'false' && !!flags.cache; const doCapture = process.env.BUILD_TS_REFS_CACHE_CAPTURE === 'true'; const doClean = !!flags.clean || doCapture; const doInitCache = cacheEnabled && !doCapture; + if (doCapture && projectFilter) { + throw createFlagError('--project can not be combined with cache capture'); + } + statsMeta.set('buildTsRefsEnabled', enabled); statsMeta.set('buildTsRefsCacheEnabled', cacheEnabled); statsMeta.set('buildTsRefsDoCapture', doCapture); @@ -87,7 +97,12 @@ export async function runBuildRefsCli() { } try { - await buildAllTsRefs({ log, procRunner, verbose: !!flags.verbose }); + await buildTsRefs({ + log, + procRunner, + verbose: !!flags.verbose, + project: rootProject, + }); log.success('ts refs build successfully'); } catch (error) { const typeFailure = isTypeFailure(error); @@ -110,13 +125,15 @@ export async function runBuildRefsCli() { } }, { - description: 'Build TypeScript projects', + description: 'Build TypeScript project references', flags: { boolean: ['clean', 'force', 'cache', 'ignore-type-failures'], + string: ['project'], default: { cache: true, }, help: ` + --project Only build the TS Refs for a specific project --force Run the build even if the BUILD_TS_REFS_DISABLE is set to "true" --clean Delete outDirs for each ts project before building --no-cache Disable fetching/extracting outDir caches based on the mergeBase with upstream diff --git a/src/dev/typescript/run_type_check_cli.ts b/src/dev/typescript/run_type_check_cli.ts index 6a2863132285..472b9c04757c 100644 --- a/src/dev/typescript/run_type_check_cli.ts +++ b/src/dev/typescript/run_type_check_cli.ts @@ -16,7 +16,7 @@ import { run, createFailError } from '@kbn/dev-utils'; import { lastValueFrom } from '@kbn/std'; import { PROJECTS } from './projects'; -import { buildAllTsRefs } from './build_ts_refs'; +import { buildTsRefs } from './build_ts_refs'; import { updateRootRefsConfig } from './root_refs_config'; export async function runTypeCheckCli() { @@ -26,11 +26,6 @@ export async function runTypeCheckCli() { // a reference to every composite project in the repo await updateRootRefsConfig(log); - const { failed } = await buildAllTsRefs({ log, procRunner, verbose: !!flags.verbose }); - if (failed) { - throw createFailError('Unable to build TS project refs'); - } - const projectFilter = flags.project && typeof flags.project === 'string' ? Path.resolve(flags.project) @@ -40,6 +35,16 @@ export async function runTypeCheckCli() { return !p.disableTypeCheck && (!projectFilter || p.tsConfigPath === projectFilter); }); + const { failed } = await buildTsRefs({ + log, + procRunner, + verbose: !!flags.verbose, + project: projects.length === 1 ? projects[0] : undefined, + }); + if (failed) { + throw createFailError('Unable to build TS project refs'); + } + if (!projects.length) { if (projectFilter) { throw createFailError(`Unable to find project at ${flags.project}`); From a5a8bb29d624c49129271a76b02b87ec7fb81d2b Mon Sep 17 00:00:00 2001 From: Jason Stoltzfus Date: Thu, 7 Oct 2021 17:38:05 -0400 Subject: [PATCH 09/74] [App Search] Wired up action buttons for suggestion detail view (#114183) --- .../components/suggestions_logic.test.tsx | 1 + .../app_search/components/curations/types.ts | 1 + .../curation_action_bar.test.tsx | 20 +- .../curation_action_bar.tsx | 21 +- .../curation_actions_popover.test.tsx | 42 ++- .../curation_actions_popover.tsx | 30 +-- .../curation_suggestion.tsx | 5 +- .../curation_suggestion_logic.test.ts | 250 +++++++++++++++++- .../curation_suggestion_logic.ts | 174 +++++++++++- 9 files changed, 463 insertions(+), 81 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/suggestions_logic.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/suggestions_logic.test.tsx index bf64101527fd..4248eb62e33f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/suggestions_logic.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/suggestions_logic.test.tsx @@ -45,6 +45,7 @@ const MOCK_RESPONSE: SuggestionsAPIResponse = { updated_at: '2021-07-08T14:35:50Z', promoted: ['1', '2'], status: 'applied', + operation: 'create', }, ], }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts index 6b087c1dcacd..f00da5deec7e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts @@ -14,6 +14,7 @@ export interface CurationSuggestion { promoted: string[]; status: 'pending' | 'applied' | 'automated' | 'rejected' | 'disabled'; curation_id?: string; + operation: 'create' | 'update' | 'delete'; override_curation_id?: string; } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_action_bar.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_action_bar.test.tsx index 4bd586d9d2e9..62cbc479f1d8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_action_bar.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_action_bar.test.tsx @@ -5,6 +5,8 @@ * 2.0. */ +import { setMockActions } from '../../../../../__mocks__/kea_logic'; + import React from 'react'; import { shallow } from 'enzyme'; @@ -12,18 +14,22 @@ import { shallow } from 'enzyme'; import { CurationActionBar } from './curation_action_bar'; describe('CurationActionBar', () => { - const handleAcceptClick = jest.fn(); - const handleRejectClick = jest.fn(); + const actions = { + acceptSuggestion: jest.fn(), + rejectSuggestion: jest.fn(), + }; + + beforeAll(() => { + setMockActions(actions); + }); it('renders', () => { - const wrapper = shallow( - - ); + const wrapper = shallow(); wrapper.find('[data-test-subj="rejectButton"]').simulate('click'); - expect(handleRejectClick).toHaveBeenCalled(); + expect(actions.rejectSuggestion).toHaveBeenCalled(); wrapper.find('[data-test-subj="acceptButton"]').simulate('click'); - expect(handleAcceptClick).toHaveBeenCalled(); + expect(actions.acceptSuggestion).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_action_bar.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_action_bar.tsx index 42f4cbbb7d7a..536d30637549 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_action_bar.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_action_bar.tsx @@ -7,17 +7,17 @@ import React from 'react'; +import { useActions } from 'kea'; + import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { CurationActionsPopover } from './curation_actions_popover'; +import { CurationSuggestionLogic } from './curation_suggestion_logic'; -interface Props { - onAcceptClick: (event: React.MouseEvent) => void; - onRejectClick: (event: React.MouseEvent) => void; -} +export const CurationActionBar: React.FC = () => { + const { acceptSuggestion, rejectSuggestion } = useActions(CurationSuggestionLogic); -export const CurationActionBar: React.FC = ({ onAcceptClick, onRejectClick }) => { return ( @@ -41,7 +41,7 @@ export const CurationActionBar: React.FC = ({ onAcceptClick, onRejectClic color="danger" iconType="crossInACircleFilled" data-test-subj="rejectButton" - onClick={onRejectClick} + onClick={rejectSuggestion} > {i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.rejectButtonLabel', @@ -55,7 +55,7 @@ export const CurationActionBar: React.FC = ({ onAcceptClick, onRejectClic color="success" iconType="checkInCircleFilled" data-test-subj="acceptButton" - onClick={onAcceptClick} + onClick={acceptSuggestion} > {i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.acceptButtonLabel', @@ -64,12 +64,7 @@ export const CurationActionBar: React.FC = ({ onAcceptClick, onRejectClic - {}} - onAutomate={() => {}} - onReject={() => {}} - onTurnOff={() => {}} - /> + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_actions_popover.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_actions_popover.test.tsx index 33d00ca2b789..c5caff68bc0f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_actions_popover.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_actions_popover.test.tsx @@ -5,6 +5,8 @@ * 2.0. */ +import { setMockActions } from '../../../../../__mocks__/kea_logic'; + import React from 'react'; import { shallow } from 'enzyme'; @@ -12,48 +14,40 @@ import { shallow } from 'enzyme'; import { CurationActionsPopover } from './curation_actions_popover'; describe('CurationActionsPopover', () => { - const handleAccept = jest.fn(); - const handleAutomate = jest.fn(); - const handleReject = jest.fn(); - const handleTurnOff = jest.fn(); + const actions = { + acceptSuggestion: jest.fn(), + acceptAndAutomateSuggestion: jest.fn(), + rejectSuggestion: jest.fn(), + rejectAndDisableSuggestion: jest.fn(), + }; + + beforeAll(() => { + setMockActions(actions); + }); beforeEach(() => { jest.clearAllMocks(); }); it('renders', () => { - const wrapper = shallow( - - ); + const wrapper = shallow(); expect(wrapper.isEmptyRender()).toBe(false); wrapper.find('[data-test-subj="acceptButton"]').simulate('click'); - expect(handleAccept).toHaveBeenCalled(); + expect(actions.acceptSuggestion).toHaveBeenCalled(); wrapper.find('[data-test-subj="automateButton"]').simulate('click'); - expect(handleAutomate).toHaveBeenCalled(); + expect(actions.acceptAndAutomateSuggestion).toHaveBeenCalled(); wrapper.find('[data-test-subj="rejectButton"]').simulate('click'); - expect(handleReject).toHaveBeenCalled(); + expect(actions.rejectSuggestion).toHaveBeenCalled(); wrapper.find('[data-test-subj="turnoffButton"]').simulate('click'); - expect(handleTurnOff).toHaveBeenCalled(); + expect(actions.rejectAndDisableSuggestion).toHaveBeenCalled(); }); it('can open and close', () => { - const wrapper = shallow( - - ); + const wrapper = shallow(); expect(wrapper.prop('isOpen')).toBe(false); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_actions_popover.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_actions_popover.tsx index ef7b42fb705f..0ae923830cd7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_actions_popover.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_actions_popover.tsx @@ -7,6 +7,8 @@ import React, { useState } from 'react'; +import { useActions } from 'kea'; + import { EuiButtonIcon, EuiListGroup, @@ -16,20 +18,16 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -interface Props { - onAccept: () => void; - onAutomate: () => void; - onReject: () => void; - onTurnOff: () => void; -} +import { CurationSuggestionLogic } from './curation_suggestion_logic'; -export const CurationActionsPopover: React.FC = ({ - onAccept, - onAutomate, - onReject, - onTurnOff, -}) => { +export const CurationActionsPopover: React.FC = () => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const { + acceptSuggestion, + acceptAndAutomateSuggestion, + rejectSuggestion, + rejectAndDisableSuggestion, + } = useActions(CurationSuggestionLogic); const onButtonClick = () => setIsPopoverOpen(!isPopoverOpen); const closePopover = () => setIsPopoverOpen(false); @@ -63,7 +61,7 @@ export const CurationActionsPopover: React.FC = ({ 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.actionsAcceptButtonLabel', { defaultMessage: 'Accept this suggestion' } )} - onClick={onAccept} + onClick={acceptSuggestion} data-test-subj="acceptButton" /> = ({ 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.actionsAutomateButtonLabel', { defaultMessage: 'Automate - always accept new suggestions for this query' } )} - onClick={onAutomate} + onClick={acceptAndAutomateSuggestion} data-test-subj="automateButton" /> = ({ 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.actionsRejectButtonLabel', { defaultMessage: 'Reject this suggestion' } )} - onClick={onReject} + onClick={rejectSuggestion} data-test-subj="rejectButton" /> = ({ 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.actionsTurnOffButtonLabel', { defaultMessage: 'Reject and turn off suggestions for this query' } )} - onClick={onTurnOff} + onClick={rejectAndDisableSuggestion} data-test-subj="turnoffButton" /> diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.tsx index 07184f6d668e..8e1e6487197f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.tsx @@ -65,10 +65,7 @@ export const CurationSuggestion: React.FC = () => { pageTitle: suggestionQuery, }} > - alert('Accepted')} - onRejectClick={() => alert('Rejected')} - /> + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.test.ts index af694c3756fd..2ace55133d6f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.test.ts @@ -9,6 +9,7 @@ import { LogicMounter, mockFlashMessageHelpers, mockHttpValues, + mockKibanaValues, } from '../../../../../__mocks__/kea_logic'; import { set } from 'lodash/fp'; @@ -32,7 +33,8 @@ const suggestion: CurationSuggestion = { query: 'foo', updated_at: '2021-07-08T14:35:50Z', promoted: ['1', '2', '3'], - status: 'applied', + status: 'pending', + operation: 'create', }; const curation = { @@ -115,13 +117,51 @@ const MOCK_DOCUMENTS_RESPONSE = { describe('CurationSuggestionLogic', () => { const { mount } = new LogicMounter(CurationSuggestionLogic); - const { flashAPIErrors } = mockFlashMessageHelpers; + const { flashAPIErrors, setQueuedErrorMessage } = mockFlashMessageHelpers; + const { navigateToUrl } = mockKibanaValues; + const mountLogic = (props: object = {}) => { mount(props, { query: 'foo-query' }); }; const { http } = mockHttpValues; + const itHandlesInlineErrors = (callback: () => void) => { + it('handles inline errors', async () => { + http.put.mockReturnValueOnce( + Promise.resolve({ + results: [ + { + error: 'error', + }, + ], + }) + ); + mountLogic({ + suggestion, + }); + + callback(); + await nextTick(); + + expect(flashAPIErrors).toHaveBeenCalledWith('error'); + }); + }; + + const itHandlesErrors = (httpMethod: any, callback: () => void) => { + it('handles errors', async () => { + httpMethod.mockReturnValueOnce(Promise.reject('error')); + mountLogic({ + suggestion, + }); + + callback(); + await nextTick(); + + expect(flashAPIErrors).toHaveBeenCalledWith('error'); + }); + }; + beforeEach(() => { jest.clearAllMocks(); }); @@ -207,7 +247,8 @@ describe('CurationSuggestionLogic', () => { query: 'foo', updated_at: '2021-07-08T14:35:50Z', promoted: ['1', '2', '3'], - status: 'applied', + status: 'pending', + operation: 'create', }, // Note that these were re-ordered to match the 'promoted' list above, and since document // 3 was not found it is not included in this list @@ -243,9 +284,7 @@ describe('CurationSuggestionLogic', () => { ); http.post.mockReturnValueOnce(Promise.resolve(MOCK_DOCUMENTS_RESPONSE)); http.get.mockReturnValueOnce(Promise.resolve(curation)); - mountLogic({ - suggestion: set('curation_id', 'cur-6155e69c7a2f2e4f756303fd', suggestion), - }); + mountLogic(); jest.spyOn(CurationSuggestionLogic.actions, 'onSuggestionLoaded'); CurationSuggestionLogic.actions.loadSuggestion(); @@ -255,7 +294,6 @@ describe('CurationSuggestionLogic', () => { '/internal/app_search/engines/some-engine/curations/cur-6155e69c7a2f2e4f756303fd', { query: { skip_record_analytics: 'true' } } ); - await nextTick(); expect(CurationSuggestionLogic.actions.onSuggestionLoaded).toHaveBeenCalledWith({ suggestion: expect.any(Object), @@ -264,14 +302,204 @@ describe('CurationSuggestionLogic', () => { }); }); - it('handles errors', async () => { - http.post.mockReturnValueOnce(Promise.reject('error')); - mount(); + // This could happen if a user applies a suggestion and then navigates back to a detail page via + // the back button, etc. The suggestion still exists, it's just not in a "pending" state + // so we can show it.ga + it('will redirect if the suggestion is not found', async () => { + http.post.mockReturnValueOnce(Promise.resolve(set('results', [], MOCK_RESPONSE))); + mountLogic(); + CurationSuggestionLogic.actions.loadSuggestion(); + await nextTick(); + expect(setQueuedErrorMessage).toHaveBeenCalled(); + expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations'); + }); + itHandlesErrors(http.post, () => { CurationSuggestionLogic.actions.loadSuggestion(); + }); + }); + + describe('acceptSuggestion', () => { + it('will make an http call to apply the suggestion, and then navigate to that detail page', async () => { + http.put.mockReturnValueOnce( + Promise.resolve({ + results: [ + { + ...suggestion, + status: 'accepted', + curation_id: 'cur-6155e69c7a2f2e4f756303fd', + }, + ], + }) + ); + mountLogic({ + suggestion, + }); + + CurationSuggestionLogic.actions.acceptSuggestion(); + await nextTick(); + + expect(http.put).toHaveBeenCalledWith( + '/internal/app_search/engines/some-engine/search_relevance_suggestions', + { + body: JSON.stringify([ + { + query: 'foo', + type: 'curation', + status: 'applied', + }, + ]), + } + ); + + expect(navigateToUrl).toHaveBeenCalledWith( + '/engines/some-engine/curations/cur-6155e69c7a2f2e4f756303fd' + ); + }); + + itHandlesErrors(http.put, () => { + CurationSuggestionLogic.actions.acceptSuggestion(); + }); + + itHandlesInlineErrors(() => { + CurationSuggestionLogic.actions.acceptSuggestion(); + }); + }); + + describe('acceptAndAutomateSuggestion', () => { + it('will make an http call to apply the suggestion, and then navigate to that detail page', async () => { + http.put.mockReturnValueOnce( + Promise.resolve({ + results: [ + { + ...suggestion, + status: 'accepted', + curation_id: 'cur-6155e69c7a2f2e4f756303fd', + }, + ], + }) + ); + mountLogic({ + suggestion, + }); + + CurationSuggestionLogic.actions.acceptAndAutomateSuggestion(); + await nextTick(); + + expect(http.put).toHaveBeenCalledWith( + '/internal/app_search/engines/some-engine/search_relevance_suggestions', + { + body: JSON.stringify([ + { + query: 'foo', + type: 'curation', + status: 'automated', + }, + ]), + } + ); + + expect(navigateToUrl).toHaveBeenCalledWith( + '/engines/some-engine/curations/cur-6155e69c7a2f2e4f756303fd' + ); + }); + + itHandlesErrors(http.put, () => { + CurationSuggestionLogic.actions.acceptAndAutomateSuggestion(); + }); + + itHandlesInlineErrors(() => { + CurationSuggestionLogic.actions.acceptAndAutomateSuggestion(); + }); + }); + + describe('rejectSuggestion', () => { + it('will make an http call to apply the suggestion, and then navigate back the curations page', async () => { + http.put.mockReturnValueOnce( + Promise.resolve({ + results: [ + { + ...suggestion, + status: 'rejected', + curation_id: 'cur-6155e69c7a2f2e4f756303fd', + }, + ], + }) + ); + mountLogic({ + suggestion, + }); + + CurationSuggestionLogic.actions.rejectSuggestion(); + await nextTick(); + + expect(http.put).toHaveBeenCalledWith( + '/internal/app_search/engines/some-engine/search_relevance_suggestions', + { + body: JSON.stringify([ + { + query: 'foo', + type: 'curation', + status: 'rejected', + }, + ]), + } + ); + + expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations'); + }); + + itHandlesErrors(http.put, () => { + CurationSuggestionLogic.actions.rejectSuggestion(); + }); + + itHandlesInlineErrors(() => { + CurationSuggestionLogic.actions.rejectSuggestion(); + }); + }); + + describe('rejectAndDisableSuggestion', () => { + it('will make an http call to apply the suggestion, and then navigate back the curations page', async () => { + http.put.mockReturnValueOnce( + Promise.resolve({ + results: [ + { + ...suggestion, + status: 'disabled', + curation_id: 'cur-6155e69c7a2f2e4f756303fd', + }, + ], + }) + ); + mountLogic({ + suggestion, + }); + + CurationSuggestionLogic.actions.rejectAndDisableSuggestion(); await nextTick(); - expect(flashAPIErrors).toHaveBeenCalledWith('error'); + expect(http.put).toHaveBeenCalledWith( + '/internal/app_search/engines/some-engine/search_relevance_suggestions', + { + body: JSON.stringify([ + { + query: 'foo', + type: 'curation', + status: 'disabled', + }, + ]), + } + ); + + expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations'); + }); + + itHandlesErrors(http.put, () => { + CurationSuggestionLogic.actions.rejectAndDisableSuggestion(); + }); + + itHandlesInlineErrors(() => { + CurationSuggestionLogic.actions.rejectAndDisableSuggestion(); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.ts index 3c3af8bfd96c..6749b510edeb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.ts @@ -8,12 +8,23 @@ import { kea, MakeLogicType } from 'kea'; import { HttpSetup } from 'kibana/public'; -import { flashAPIErrors } from '../../../../../shared/flash_messages'; +import { i18n } from '@kbn/i18n'; + +import { + flashAPIErrors, + setQueuedErrorMessage, + setQueuedSuccessMessage, +} from '../../../../../shared/flash_messages'; import { HttpLogic } from '../../../../../shared/http'; -import { EngineLogic } from '../../../engine'; +import { KibanaLogic } from '../../../../../shared/kibana'; +import { ENGINE_CURATIONS_PATH, ENGINE_CURATION_PATH } from '../../../../routes'; +import { EngineLogic, generateEnginePath } from '../../../engine'; import { Result } from '../../../result/types'; import { Curation, CurationSuggestion } from '../../types'; +interface Error { + error: string; +} interface CurationSuggestionValues { dataLoading: boolean; suggestion: CurationSuggestion | null; @@ -36,6 +47,10 @@ interface CurationSuggestionActions { suggestedPromotedDocuments: Result[]; curation: Curation; }; + acceptSuggestion(): void; + acceptAndAutomateSuggestion(): void; + rejectSuggestion(): void; + rejectAndDisableSuggestion(): void; } interface CurationSuggestionProps { @@ -53,6 +68,10 @@ export const CurationSuggestionLogic = kea< suggestedPromotedDocuments, curation, }), + acceptSuggestion: true, + acceptAndAutomateSuggestion: true, + rejectSuggestion: true, + rejectAndDisableSuggestion: true, }), reducers: () => ({ dataLoading: [ @@ -81,13 +100,14 @@ export const CurationSuggestionLogic = kea< }, ], }), - listeners: ({ actions, props }) => ({ + listeners: ({ actions, values, props }) => ({ loadSuggestion: async () => { const { http } = HttpLogic.values; const { engineName } = EngineLogic.values; try { - const suggestion = await getSuggestions(http, engineName, props.query); + const suggestion = await getSuggestion(http, engineName, props.query); + if (!suggestion) return; const promotedIds: string[] = suggestion.promoted; const documentDetailsResopnse = getDocumentDetails(http, engineName, promotedIds); @@ -116,14 +136,144 @@ export const CurationSuggestionLogic = kea< flashAPIErrors(e); } }, + acceptSuggestion: async () => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + const { suggestion } = values; + + try { + const updatedSuggestion = await updateSuggestion( + http, + engineName, + suggestion!.query, + 'applied' + ); + + setQueuedSuccessMessage( + i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyAppliedMessage', + { defaultMessage: 'Suggestion was succefully applied.' } + ) + ); + KibanaLogic.values.navigateToUrl( + generateEnginePath(ENGINE_CURATION_PATH, { + curationId: updatedSuggestion.curation_id, + }) + ); + } catch (e) { + flashAPIErrors(e); + } + }, + acceptAndAutomateSuggestion: async () => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + const { suggestion } = values; + + try { + const updatedSuggestion = await updateSuggestion( + http, + engineName, + suggestion!.query, + 'automated' + ); + + setQueuedSuccessMessage( + i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyAutomatedMessage', + { + defaultMessage: + 'Suggestion was succefully applied and all future suggestions for the query "{query}" will be automatically applied.', + values: { query: suggestion!.query }, + } + ) + ); + KibanaLogic.values.navigateToUrl( + generateEnginePath(ENGINE_CURATION_PATH, { + curationId: updatedSuggestion.curation_id, + }) + ); + } catch (e) { + flashAPIErrors(e); + } + }, + rejectSuggestion: async () => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + const { suggestion } = values; + + try { + await updateSuggestion(http, engineName, suggestion!.query, 'rejected'); + + setQueuedSuccessMessage( + i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyRejectedMessage', + { + defaultMessage: 'Suggestion was succefully rejected.', + } + ) + ); + KibanaLogic.values.navigateToUrl(generateEnginePath(ENGINE_CURATIONS_PATH)); + } catch (e) { + flashAPIErrors(e); + } + }, + rejectAndDisableSuggestion: async () => { + const { http } = HttpLogic.values; + const { engineName } = EngineLogic.values; + const { suggestion } = values; + + try { + await updateSuggestion(http, engineName, suggestion!.query, 'disabled'); + + setQueuedSuccessMessage( + i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyDisabledMessage', + { + defaultMessage: + 'Suggestion was succefully rejected and you will no longer receive suggestions for the query "{query}".', + values: { query: suggestion!.query }, + } + ) + ); + KibanaLogic.values.navigateToUrl(generateEnginePath(ENGINE_CURATIONS_PATH)); + } catch (e) { + flashAPIErrors(e); + } + }, }), }); -const getSuggestions = async ( +const updateSuggestion = async ( + http: HttpSetup, + engineName: string, + query: string, + status: string +) => { + const response = await http.put<{ results: Array }>( + `/internal/app_search/engines/${engineName}/search_relevance_suggestions`, + { + body: JSON.stringify([ + { + query, + type: 'curation', + status, + }, + ]), + } + ); + + if (response.results[0].hasOwnProperty('error')) { + throw (response.results[0] as Error).error; + } + + return response.results[0] as CurationSuggestion; +}; + +const getSuggestion = async ( http: HttpSetup, engineName: string, query: string -): Promise => { +): Promise => { const response = await http.post( `/internal/app_search/engines/${engineName}/search_relevance_suggestions/${query}`, { @@ -140,6 +290,18 @@ const getSuggestions = async ( } ); + if (response.results.length < 1) { + const message = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.notFoundError', + { + defaultMessage: 'Could not find suggestion, it may have already been applied or rejected.', + } + ); + setQueuedErrorMessage(message); + KibanaLogic.values.navigateToUrl(generateEnginePath(ENGINE_CURATIONS_PATH)); + return; + } + const suggestion = response.results[0] as CurationSuggestion; return suggestion; }; From 0442c87591029c6a874f40c61ccf31ad52047bca Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Thu, 7 Oct 2021 17:42:05 -0400 Subject: [PATCH 10/74] High level walkthrough of repo folder structure (#114293) * Create code_walkthrough.mdx * Remove unused utilities folder and mention of it in the repo structure doc. * Update dev_docs/contributing/code_walkthrough.mdx Co-authored-by: Luke Elmers * Update dev_docs/contributing/code_walkthrough.mdx Co-authored-by: Luke Elmers * Update dev_docs/contributing/code_walkthrough.mdx omg my grammar is horrible. Co-authored-by: Luke Elmers * Update dev_docs/contributing/code_walkthrough.mdx Co-authored-by: Luke Elmers * Update dev_docs/contributing/code_walkthrough.mdx Co-authored-by: Luke Elmers * Update dev_docs/contributing/code_walkthrough.mdx Co-authored-by: Luke Elmers * Update dev_docs/contributing/code_walkthrough.mdx Co-authored-by: Luke Elmers * review updates * Update dev_docs/contributing/code_walkthrough.mdx Co-authored-by: Brandon Kobel * Update dev_docs/contributing/code_walkthrough.mdx Co-authored-by: Brandon Kobel Co-authored-by: Luke Elmers Co-authored-by: Brandon Kobel --- dev_docs/contributing/code_walkthrough.mdx | 139 ++++++++ .../visual_regression_gallery.handlebars | 297 ------------------ utilities/visual_regression.js | 141 --------- 3 files changed, 139 insertions(+), 438 deletions(-) create mode 100644 dev_docs/contributing/code_walkthrough.mdx delete mode 100644 utilities/templates/visual_regression_gallery.handlebars delete mode 100644 utilities/visual_regression.js diff --git a/dev_docs/contributing/code_walkthrough.mdx b/dev_docs/contributing/code_walkthrough.mdx new file mode 100644 index 000000000000..47eb05a95c42 --- /dev/null +++ b/dev_docs/contributing/code_walkthrough.mdx @@ -0,0 +1,139 @@ +--- +id: kibRepoStructure +slug: /kibana-dev-docs/contributing/repo-structure +title: Repository structure +summary: High level walk-through of our repository structure. +date: 2021-10-07 +tags: ['contributor', 'dev', 'github', 'getting started', 'onboarding', 'kibana'] +--- + +A high-level walk through of the folder structure of our [repository](https://github.com/elastic/kibana). + +Tip: Look for a `README.md` in a folder to learn about its contents. + +## [.buildkite](https://github.com/elastic/kibana/tree/master/.buildkite) + +Managed by the operations team to set up a new buildkite ci system. Can be ignored by folks outside the Operations team. + +## [.ci](https://github.com/elastic/kibana/tree/master/.ci) + +Managed by the operations team to contain Jenkins settings. Can be ignored by folks outside the Operations team. + +## [.github](https://github.com/elastic/kibana/tree/master/.github) + +Contains GitHub configuration settings. This file contains issue templates, and the [CODEOWNERS](https://github.com/elastic/kibana/blob/master/.github/CODEOWNERS) file. It's important for teams to keep the CODEOWNERS file up-to-date so the right team is pinged for a code owner review on PRs that edit certain files. Note that the `CODEOWNERS` file only exists on the main/master branch, and is not backported to other branches in the repo. + +## [api_docs](https://github.com/elastic/kibana/tree/master/api_docs) + +Every file in here is auto-generated by the and used to render our API documentation. If you edit a public plugin or package API and run `node scripts/build_api_docs` you will see files changed in this folder. Do not edit the contents of this folder directly! + +Note that currently you may see _a lot_ of changes because that command is not run on every PR and so the content in this folder is often outdated. + +## [config](https://github.com/elastic/kibana/tree/master/config) + +This contains the base configuration file, `kibana.yml`. If you want to tweak any settings, create a `kibana.dev.yml` in here which will get picked up during development, and not checked into GitHub. + +## [docs](https://github.com/elastic/kibana/tree/master/docs) + +Every folder in here _except_ the [development one](https://github.com/elastic/kibana/tree/master/docs/development) contains manually generated asciidocs that end up hosted in our [Elastic guide](https://www.elastic.co/guide/). + +The `development` folder contains markdown that is auto-generated with our legacy API docs tool. We are aiming to remove it shortly after 8.0FF. + +## [dev_docs](https://github.com/elastic/kibana/tree/master/dev_docs) + +This is where a lot of manually written content for our Developer Guide resides. Developers may also keep the information closer to what it's describing, but it's a good spot for high-level information, or information that describes how multiple plugins work together. + +## [examples](https://github.com/elastic/kibana/tree/master/examples) + +These are our tested example plugins that also get built and hosted [here](https://demo.kibana.dev/8.0/app/developerExamples). If a plugin is written for testing purposes only, it won't go in here. These example plugins should be written with the intention of helping developers understand how to use our services. + +## [legacy_rfcs](https://github.com/elastic/kibana/tree/master/legacy_rfcs) + +We used to write RFCs in `md` format and keep them in the repository, but we have since moved to Google Docs. We kept the folder around, since some folks still read these old docs. If you are an internal contributor you can visit to read about our current RFC process. + +## [licenses](https://github.com/elastic/kibana/tree/master/licenses) + +Contains our two license header texts, one for the Elastic license and one for the Elastic+SSPL license. All code files inside x-pack should have the Elastic license text at the top, all code files outside x-pack should contain the other. If you have your environment set up to auto-fix on save, eslint should take care of adding it for you. If you don't have it, ci will fail. Can be ignored for the most part, this rarely changes. + +## [packages](https://github.com/elastic/kibana/tree/master/packages) + +The packages folder contains a mixture of build-time related code (like the [code needed to build the api docs](https://github.com/elastic/kibana/tree/master/packages/kbn-docs-utils)), as well as static code that some plugins rely on (like the [kbn-monaco package](https://github.com/elastic/kibana/tree/master/packages/kbn-monaco)). covers how packages differ from plugins. + +## [plugins](https://github.com/elastic/kibana/tree/master/plugins) + +This is an empty folder in GitHub. It's where third party developers should put their plugin folders. Internal developers can ignore this folder. + +## [scripts](https://github.com/elastic/kibana/tree/master/scripts) + +Contains a bunch of developer scripts. These are usually very small files with just two lines that kick off a command, the logic of which resides elsewhere (sometimes `src/dev`, sometimes inside `packages`). +Example: +``` +require('../src/setup_node_env'); +require('@kbn/es-archiver').runCli(); +``` + +## [src](https://github.com/elastic/kibana/tree/master/src) + +This folder and the packages folder contain the most code and where developers usually find themselves. I'll touch on a few of the subfolder, the rest can generally be ignored, or are build/ops related code. + +### [src/cli*](https://github.com/elastic/kibana/tree/master/src/cli) + +Maintained primarily by the Operations team, this folder contains code that initializes the Kibana runtime and a bit of work to handle authorization for interactive setup mode. Most devs should be able to ignore this code. + +### [src/core](https://github.com/elastic/kibana/tree/master/src/core) + +This code primarily belongs to the Core team and contains the plugin infrastructure, as well as a bunch of fundamental services like migrations, saved objects, and some UI utilities (toasts, flyouts, etc.). + +### [src/dev](https://github.com/elastic/kibana/tree/master/src/dev) + +Maintained by the Operations team, this code contains build and development tooling related code. This folder existed before `packages`, so contains mostly older code that hasn't been migrated to packages. Prefer creating a `package` if possible. Can be ignored for the most part if you are not on the Ops team. Prefer + +### [src/plugins](https://github.com/elastic/kibana/tree/master/src/plugins) + +Contains all of our Basic-licensed plugins. Most folders in this directory will contain `README.md` files explaining what they do. If there are none, you can look at the `owner.gitHub` field inside all `kibana.json`s that will tell you which team to get in touch with for questions. + +Note that as plugins can be nested, each folder in this directory may contain multiple plugins. + +## [test](https://github.com/elastic/kibana/tree/master/test) + +Contains functional tests and related FTR (functional test runner) code for the plugins inside `src/plugins`, although there is a push to move the tests to reside inside the plugins themselves. + +## [typings](https://github.com/elastic/kibana/tree/master/typings) + +Maintained by Ops and Core, this contains global typings for dependencies that do not provide their own types, or don't have types available via [DefinitelyTyped](https://definitelytyped.org). This directory is intended to be minimal; types should only be added here as a last resort. + +## [vars](https://github.com/elastic/kibana/tree/master/vars) + +A bunch of groovy scripts maintained by the Operations team. + +## [x-pack](https://github.com/elastic/kibana/tree/master/x-pack) + +Contains all code and infrasturcture that powers our gold+ (non-basic) features that are provided under a more restrictive license. + +### [x-pack/build_chromium](https://github.com/elastic/kibana/tree/master/x-pack/build_chromium) + +Maintained by the App Services UX team, this contains Reporting-related code for building Chromium in order to take server side screenshots. + +### [x-pack/dev-tools](https://github.com/elastic/kibana/tree/master/x-pack/dev-tools) + +Maintained by the Operations team. + +### [x-pack/examples](https://github.com/elastic/kibana/tree/master/x-pack/examples) + +Contains all example plugins that rely on gold+ features. + +### [x-pack/plugins](https://github.com/elastic/kibana/tree/master/x-pack/plugins) + +Contains code for all the plugins that power our gold+ features. + +### [x-pack/scripts](https://github.com/elastic/kibana/tree/master/x-pack/scripts) + +Maintained by the Ops team, this folder contains some scripts for running x-pack utilities, like the functional test runner that runs with a license higher than Basic. + +### [x-pack/test](https://github.com/elastic/kibana/tree/master/x-pack/test) + +Functional tests for our gold+ features. + + + + diff --git a/utilities/templates/visual_regression_gallery.handlebars b/utilities/templates/visual_regression_gallery.handlebars deleted file mode 100644 index 60817dec5fcf..000000000000 --- a/utilities/templates/visual_regression_gallery.handlebars +++ /dev/null @@ -1,297 +0,0 @@ - - - - Kibana Visual Regression Gallery - - - - - - - - -
- Kibana Visual Regression Gallery -
- -
- {{branch}} - {{date}} -
- - {{#each comparisons as |comparison|}} -
- -
- ({{comparison.percentage}}%) {{comparison.name}} -
- -
-
Diff
- -
- Baseline -
-
- -
- -
- -
- - - - - -
- -
-
-
- {{/each}} - - - - - diff --git a/utilities/visual_regression.js b/utilities/visual_regression.js deleted file mode 100644 index fa4d48a2280d..000000000000 --- a/utilities/visual_regression.js +++ /dev/null @@ -1,141 +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 bluebird, { promisify } from 'bluebird'; -import Handlebars from 'handlebars'; -import fs from 'fs'; -import path from 'path'; -import { PNG } from 'pngjs'; -import pixelmatch from 'pixelmatch'; -import moment from 'moment'; -import SimpleGit from 'simple-git'; - -const readDirAsync = promisify(fs.readdir); -const readFileAsync = promisify(fs.readFile); -const writeFileAsync = promisify(fs.writeFile); - -Handlebars.registerHelper('lte', function lessThanEquals(value, threshold, options) { - if (value <= threshold) { - return options.fn(this); - } - return options.inverse(this); -}); - -Handlebars.registerHelper('gte', function greaterThanEquals(value, threshold, options) { - if (value >= threshold) { - return options.fn(this); - } - return options.inverse(this); -}); - -async function buildGallery(comparisons) { - const simpleGit = new SimpleGit(); - const asyncBranch = promisify(simpleGit.branch, simpleGit); - const branch = await asyncBranch(); - - const template = Handlebars.compile( - await readFileAsync( - path.resolve('./utilities/templates/visual_regression_gallery.handlebars'), - 'utf8' - ), - { knownHelpersOnly: true } - ); - - const html = template({ - date: moment().format('MMMM Do YYYY, h:mm:ss a'), - branch: branch.current, - hiddenThreshold: 0, - warningThreshold: 0.03, - comparisons, - }); - - return writeFileAsync( - path.resolve('./test/functional/screenshots/visual_regression_gallery.html'), - html - ); -} - -async function compareScreenshots() { - const SCREENSHOTS_DIR = 'test/functional/screenshots'; - const BASELINE_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'baseline'); - const DIFF_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'diff'); - const SESSION_SCREENSHOTS_DIR = path.resolve(SCREENSHOTS_DIR, 'session'); - - // We don't need to create the baseline dir because it's committed. - fs.mkdirSync(DIFF_SCREENSHOTS_DIR, { recursive: true }); - fs.mkdirSync(SESSION_SCREENSHOTS_DIR, { recursive: true }); - const files = await readDirAsync(SESSION_SCREENSHOTS_DIR); - const screenshots = files.filter((file) => file.indexOf('.png') !== -1); - - // We'll use this data to build a screenshot gallery in HTML. - return await bluebird.map(screenshots, async (screenshot) => { - // We're going to load image data and cache it in this object. - const comparison = { - name: screenshot, - change: undefined, - percentage: undefined, - imageData: { - session: undefined, - baseline: undefined, - diff: undefined, - }, - }; - - const sessionImagePath = path.resolve(SESSION_SCREENSHOTS_DIR, screenshot); - - const baselineImagePath = path.resolve(BASELINE_SCREENSHOTS_DIR, screenshot); - - const diffImagePath = path.resolve(DIFF_SCREENSHOTS_DIR, screenshot); - - const sessionImage = PNG.sync.read(await readFileAsync(sessionImagePath)); - const baselineImage = PNG.sync.read(await readFileAsync(baselineImagePath)); - const { width, height } = sessionImage; - const diff = new PNG({ width, height }); - - const numDiffPixels = pixelmatch( - sessionImage.data, - baselineImage.data, - diff.data, - width, - height, - { threshold: 0 } - ); - - await writeFileAsync(diffImagePath, PNG.sync.write(diff)); - - const change = numDiffPixels / (width * height); - const changePercentage = (change * 100).toFixed(2); - console.log(`(${changePercentage}%) ${screenshot}`); - comparison.percentage = changePercentage; - comparison.change = change; - - // Once the images have been diffed, we can load and store the image data. - comparison.imageData.session = await readFileAsync(sessionImagePath, 'base64'); - - comparison.imageData.baseline = await readFileAsync(baselineImagePath, 'base64'); - - comparison.imageData.diff = await readFileAsync(diffImagePath, 'base64'); - - return comparison; - }); -} - -export function run(done) { - compareScreenshots().then( - (screenshotComparisons) => { - // Once all of the data has been loaded, we can build the gallery. - buildGallery(screenshotComparisons).then(() => { - done(); - }); - }, - (error) => { - console.error(error); - done(false); - } - ); -} From 67cbccf9a7abed0d6c9731b674f43c33ececeba3 Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Thu, 7 Oct 2021 18:27:16 -0400 Subject: [PATCH 11/74] [Fleet] Revert max fields value for Fleet component templates to 10k (#114299) * Revert max fields value for Fleet component templates to 10k * Skip endpoint tests * fix failing spec Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../epm/elasticsearch/template/default_settings.test.ts | 5 +++++ .../services/epm/elasticsearch/template/default_settings.ts | 5 +++++ .../test/fleet_api_integration/apis/epm/install_overrides.ts | 5 +++++ x-pack/test/fleet_api_integration/apis/epm/update_assets.ts | 5 +++++ .../test/security_solution_endpoint/apps/endpoint/index.ts | 2 +- 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.test.ts index 30f2909b2403..feab9cb88239 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.test.ts @@ -46,6 +46,11 @@ describe('buildDefaultSettings', () => { "lifecycle": Object { "name": "logs", }, + "mapping": Object { + "total_fields": Object { + "limit": "10000", + }, + }, "query": Object { "default_field": Array [ "field1Keyword", diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.ts index a9ae65e9e97b..84ec75b9da06 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/default_settings.ts @@ -67,6 +67,11 @@ export function buildDefaultSettings({ }, // What should be our default for the compression? codec: 'best_compression', + mapping: { + total_fields: { + limit: '10000', + }, + }, // All the default fields which should be queried have to be added here. // So far we add all keyword and text fields here if there are any, otherwise diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_overrides.ts b/x-pack/test/fleet_api_integration/apis/epm/install_overrides.ts index 34917528560b..02820f85e8ee 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_overrides.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_overrides.ts @@ -121,6 +121,11 @@ export default function (providerContext: FtrProviderContext) { lifecycle: { name: 'overridden by user', }, + mapping: { + total_fields: { + limit: '10000', + }, + }, number_of_shards: '3', }, }, diff --git a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts index a8ccb5e301d5..f46dcdb761e6 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts @@ -220,6 +220,11 @@ export default function (providerContext: FtrProviderContext) { index: { lifecycle: { name: 'reference2' }, codec: 'best_compression', + mapping: { + total_fields: { + limit: '10000', + }, + }, query: { default_field: ['logs_test_name', 'new_field_name'], }, diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts index 70d60ba5c1b6..5ff206b8ad67 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts @@ -15,7 +15,7 @@ import { export default function (providerContext: FtrProviderContext) { const { loadTestFile, getService } = providerContext; - describe('endpoint', function () { + describe.skip('endpoint', function () { const ingestManager = getService('ingestManager'); const log = getService('log'); const endpointTestResources = getService('endpointTestResources'); From 0861a5d6a0c91d6a63425dfc1eca19dddd64a29a Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Thu, 7 Oct 2021 19:03:37 -0500 Subject: [PATCH 12/74] [build] Fix os package kibana.yml (#114372) --- src/dev/build/tasks/os_packages/create_os_package_kibana_yml.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev/build/tasks/os_packages/create_os_package_kibana_yml.ts b/src/dev/build/tasks/os_packages/create_os_package_kibana_yml.ts index be21bc493059..ebd62f83798d 100644 --- a/src/dev/build/tasks/os_packages/create_os_package_kibana_yml.ts +++ b/src/dev/build/tasks/os_packages/create_os_package_kibana_yml.ts @@ -23,7 +23,7 @@ export async function createOSPackageKibanaYML(config: Config, build: Build) { [ [/#pid.file:.*/g, 'pid.file: /run/kibana/kibana.pid'], [ - /#logging.dest:.*/g, + /#logging.appenders.default:.*kibana\.log\n/gs, dump({ logging: { appenders: { From 080b2199f41e7905eab6e345bd03554beea9093f Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Thu, 7 Oct 2021 20:09:19 -0400 Subject: [PATCH 13/74] Legacy url conflict UI improvements (#114172) --- ...-plugin-core-public.doclinksstart.links.md | 4 + ...kibana-plugin-core-public.doclinksstart.md | 2 +- .../public/doc_links/doc_links_service.ts | 8 ++ src/core/public/public.api.md | 4 + .../public/embeddable/embeddable.test.tsx | 2 +- .../lens/public/embeddable/embeddable.tsx | 5 +- .../lens/public/lens_attribute_service.ts | 11 +- .../init_middleware/load_initial.ts | 2 +- .../lens/public/state_management/types.ts | 2 +- x-pack/plugins/lens/public/types.ts | 2 +- .../maps/public/embeddable/map_embeddable.tsx | 5 +- .../maps/public/map_attribute_service.ts | 12 +- x-pack/plugins/spaces/public/constants.ts | 4 + x-pack/plugins/spaces/public/index.ts | 3 +- .../embeddable_legacy_url_conflict.tsx | 22 +++ ...mbeddable_legacy_url_conflict_internal.tsx | 92 ++++++++++++ .../{lib => legacy_urls/components}/index.ts | 3 +- .../components/legacy_url_conflict.tsx | 0 .../legacy_url_conflict_internal.test.tsx | 0 .../legacy_url_conflict_internal.tsx | 135 ++++++++++++++++++ .../utils => legacy_urls}/index.ts | 3 + .../redirect_legacy_url.test.ts | 0 .../redirect_legacy_url.ts | 10 +- .../spaces/public/legacy_urls/types.ts | 46 ++++++ .../public/lib/documentation_links.test.ts | 27 ---- .../spaces/public/lib/documentation_links.ts | 20 --- x-pack/plugins/spaces/public/mocks.ts | 2 +- .../components/constants.ts | 12 -- .../get_saved_object_conflict_message.tsx | 19 --- .../components/index.ts | 2 - .../legacy_url_conflict_internal.tsx | 116 --------------- .../saved_object_conflict_message.tsx | 56 -------- .../components/selectable_spaces_control.tsx | 7 +- .../components/share_mode_control.tsx | 8 +- .../share_to_space_flyout_internal.tsx | 2 +- .../share_saved_objects_to_space/index.ts | 14 +- .../share_saved_objects_to_space/types.ts | 32 ----- .../spaces/public/ui_api/components.tsx | 11 +- x-pack/plugins/spaces/public/ui_api/index.ts | 2 +- x-pack/plugins/spaces/public/ui_api/types.ts | 17 ++- .../translations/translations/ja-JP.json | 6 - .../translations/translations/zh-CN.json | 6 - 42 files changed, 364 insertions(+), 372 deletions(-) create mode 100644 x-pack/plugins/spaces/public/legacy_urls/components/embeddable_legacy_url_conflict.tsx create mode 100644 x-pack/plugins/spaces/public/legacy_urls/components/embeddable_legacy_url_conflict_internal.tsx rename x-pack/plugins/spaces/public/{lib => legacy_urls/components}/index.ts (63%) rename x-pack/plugins/spaces/public/{share_saved_objects_to_space => legacy_urls}/components/legacy_url_conflict.tsx (100%) rename x-pack/plugins/spaces/public/{share_saved_objects_to_space => legacy_urls}/components/legacy_url_conflict_internal.test.tsx (100%) create mode 100644 x-pack/plugins/spaces/public/legacy_urls/components/legacy_url_conflict_internal.tsx rename x-pack/plugins/spaces/public/{share_saved_objects_to_space/utils => legacy_urls}/index.ts (64%) rename x-pack/plugins/spaces/public/{share_saved_objects_to_space/utils => legacy_urls}/redirect_legacy_url.test.ts (100%) rename x-pack/plugins/spaces/public/{share_saved_objects_to_space/utils => legacy_urls}/redirect_legacy_url.ts (77%) create mode 100644 x-pack/plugins/spaces/public/legacy_urls/types.ts delete mode 100644 x-pack/plugins/spaces/public/lib/documentation_links.test.ts delete mode 100644 x-pack/plugins/spaces/public/lib/documentation_links.ts delete mode 100644 x-pack/plugins/spaces/public/share_saved_objects_to_space/components/constants.ts delete mode 100644 x-pack/plugins/spaces/public/share_saved_objects_to_space/components/get_saved_object_conflict_message.tsx delete mode 100644 x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.tsx delete mode 100644 x-pack/plugins/spaces/public/share_saved_objects_to_space/components/saved_object_conflict_message.tsx diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index ab0f2d0ee5a1..c8be6a439f35 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -213,6 +213,10 @@ readonly links: { mappingRolesFieldRules: string; runAsPrivilege: string; }>; + readonly spaces: Readonly<{ + kibanaLegacyUrlAliases: string; + kibanaDisableLegacyUrlAliasesApi: string; + }>; readonly watcher: Record; readonly ccs: Record; readonly plugins: Record; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index f0fe058c403e..04c2495cf3f1 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly apm: {
readonly kibanaSettings: string;
readonly supportedServiceMaps: string;
readonly customLinks: string;
readonly droppedTransactionSpans: string;
readonly upgrading: string;
readonly metaData: string;
};
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly libbeat: {
readonly getStarted: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Readonly<{
guide: string;
infrastructureThreshold: string;
logsThreshold: string;
metricsThreshold: string;
monitorStatus: string;
monitorUptime: string;
tlsCertificate: string;
uptimeDurationAnomaly: string;
}>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
}>;
readonly ecs: {
readonly guide: string;
};
readonly clients: {
readonly guide: string;
readonly goOverview: string;
readonly javaIndex: string;
readonly jsIntro: string;
readonly netGuide: string;
readonly perlGuide: string;
readonly phpGuide: string;
readonly pythonGuide: string;
readonly rubyOverview: string;
readonly rustGuide: string;
};
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly apm: {
readonly kibanaSettings: string;
readonly supportedServiceMaps: string;
readonly customLinks: string;
readonly droppedTransactionSpans: string;
readonly upgrading: string;
readonly metaData: string;
};
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly libbeat: {
readonly getStarted: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Readonly<{
guide: string;
infrastructureThreshold: string;
logsThreshold: string;
metricsThreshold: string;
monitorStatus: string;
monitorUptime: string;
tlsCertificate: string;
uptimeDurationAnomaly: string;
}>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly spaces: Readonly<{
kibanaLegacyUrlAliases: string;
kibanaDisableLegacyUrlAliasesApi: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
}>;
readonly ecs: {
readonly guide: string;
};
readonly clients: {
readonly guide: string;
readonly goOverview: string;
readonly javaIndex: string;
readonly jsIntro: string;
readonly netGuide: string;
readonly perlGuide: string;
readonly phpGuide: string;
readonly pythonGuide: string;
readonly rubyOverview: string;
readonly rustGuide: string;
};
} | | diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 9a9b7aabc4de..221b586ac154 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -356,6 +356,10 @@ export class DocLinksService { mappingRolesFieldRules: `${ELASTICSEARCH_DOCS}role-mapping-resources.html#mapping-roles-rule-field`, runAsPrivilege: `${ELASTICSEARCH_DOCS}security-privileges.html#_run_as_privilege`, }, + spaces: { + kibanaLegacyUrlAliases: `${KIBANA_DOCS}legacy-url-aliases.html`, + kibanaDisableLegacyUrlAliasesApi: `${KIBANA_DOCS}spaces-api-disable-legacy-url-aliases.html`, + }, watcher: { jiraAction: `${ELASTICSEARCH_DOCS}actions-jira.html`, pagerDutyAction: `${ELASTICSEARCH_DOCS}actions-pagerduty.html`, @@ -702,6 +706,10 @@ export interface DocLinksStart { mappingRolesFieldRules: string; runAsPrivilege: string; }>; + readonly spaces: Readonly<{ + kibanaLegacyUrlAliases: string; + kibanaDisableLegacyUrlAliasesApi: string; + }>; readonly watcher: Record; readonly ccs: Record; readonly plugins: Record; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 787155857407..b3474a669e70 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -682,6 +682,10 @@ export interface DocLinksStart { mappingRolesFieldRules: string; runAsPrivilege: string; }>; + readonly spaces: Readonly<{ + kibanaLegacyUrlAliases: string; + kibanaDisableLegacyUrlAliasesApi: string; + }>; readonly watcher: Record; readonly ccs: Record; readonly plugins: Record; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx index eea54ed525f6..4c247c031eac 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.test.tsx @@ -236,7 +236,7 @@ describe('embeddable', () => { ...savedVis, sharingSavedObjectProps: { outcome: 'conflict', - errorJSON: '{targetType: "lens", sourceId: "1", targetSpace: "space"}', + sourceId: '1', aliasTargetId: '2', }, } as ResolvedLensSavedObjectAttributes); diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 2e0ab2401c70..7faf873cf0b0 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -286,8 +286,9 @@ export class Embeddable defaultMessage: `You've encountered a URL conflict`, }), longMessage: ( - ), }; diff --git a/x-pack/plugins/lens/public/lens_attribute_service.ts b/x-pack/plugins/lens/public/lens_attribute_service.ts index fb4ef4fee72e..30369b0fd586 100644 --- a/x-pack/plugins/lens/public/lens_attribute_service.ts +++ b/x-pack/plugins/lens/public/lens_attribute_service.ts @@ -48,7 +48,7 @@ export function getLensAttributeService( outcome, alias_target_id: aliasTargetId, } = await savedObjectStore.load(savedObjectId); - const { attributes, references, type, id } = savedObject; + const { attributes, references, id } = savedObject; const document = { ...attributes, references, @@ -57,14 +57,7 @@ export function getLensAttributeService( const sharingSavedObjectProps = { aliasTargetId, outcome, - errorJSON: - outcome === 'conflict' - ? JSON.stringify({ - targetType: type, - sourceId: id, - targetSpace: (await startDependencies.spaces.getActiveSpace()).id, - }) - : undefined, + sourceId: id, }; return { diff --git a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts index 314434a16af8..5c571c750a4a 100644 --- a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts +++ b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts @@ -27,7 +27,7 @@ export const getPersisted = async ({ lensServices: LensAppServices; history?: History; }): Promise< - { doc: Document; sharingSavedObjectProps: Omit } | undefined + { doc: Document; sharingSavedObjectProps: Omit } | undefined > => { const { notifications, spaces, attributeService } = lensServices; let doc: Document; diff --git a/x-pack/plugins/lens/public/state_management/types.ts b/x-pack/plugins/lens/public/state_management/types.ts index c15b29d0040d..e46c685925b2 100644 --- a/x-pack/plugins/lens/public/state_management/types.ts +++ b/x-pack/plugins/lens/public/state_management/types.ts @@ -43,7 +43,7 @@ export interface LensAppState extends EditorFrameState { savedQuery?: SavedQuery; searchSessionId: string; resolvedDateRange: DateRange; - sharingSavedObjectProps?: Omit; + sharingSavedObjectProps?: Omit; } export type DispatchSetState = (state: Partial) => { diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 2e7876f83fc4..87e2762149ac 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -834,5 +834,5 @@ export interface ILensInterpreterRenderHandlers extends IInterpreterRenderHandle export interface SharingSavedObjectProps { outcome?: 'aliasMatch' | 'exactMatch' | 'conflict'; aliasTargetId?: string; - errorJSON?: string; + sourceId?: string; } diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index b0daace7afa9..d15a3efc5375 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -364,8 +364,9 @@ export class MapEmbeddable iconType="alert" iconColor="danger" data-test-subj="embeddable-maps-failure" - body={spaces.ui.components.getSavedObjectConflictMessage({ - json: sharingSavedObjectProps.errorJSON!, + body={spaces.ui.components.getEmbeddableLegacyUrlConflict({ + targetType: MAP_SAVED_OBJECT_TYPE, + sourceId: sharingSavedObjectProps.sourceId!, })} />
diff --git a/x-pack/plugins/maps/public/map_attribute_service.ts b/x-pack/plugins/maps/public/map_attribute_service.ts index ab380ca5a6b6..85d4d73da82c 100644 --- a/x-pack/plugins/maps/public/map_attribute_service.ts +++ b/x-pack/plugins/maps/public/map_attribute_service.ts @@ -14,12 +14,11 @@ import { checkForDuplicateTitle, OnSaveProps } from '../../../../src/plugins/sav import { getCoreOverlays, getEmbeddableService, getSavedObjectsClient } from './kibana_services'; import { extractReferences, injectReferences } from '../common/migrations/references'; import { MapByValueInput, MapByReferenceInput } from './embeddable/types'; -import { getSpacesApi } from './kibana_services'; export interface SharingSavedObjectProps { outcome?: 'aliasMatch' | 'exactMatch' | 'conflict'; aliasTargetId?: string; - errorJSON?: string; + sourceId?: string; } type MapDoc = MapSavedObjectAttributes & { @@ -88,14 +87,7 @@ export function getMapAttributeService(): MapAttributeService { sharingSavedObjectProps: { aliasTargetId, outcome, - errorJSON: - outcome === 'conflict' && getSpacesApi() - ? JSON.stringify({ - targetType: MAP_SAVED_OBJECT_TYPE, - sourceId: savedObjectId, - targetSpace: (await getSpacesApi()!.getActiveSpace()).id, - }) - : undefined, + sourceId: savedObjectId, }, }; }, diff --git a/x-pack/plugins/spaces/public/constants.ts b/x-pack/plugins/spaces/public/constants.ts index 64803ff9d4bc..b513a8affacf 100644 --- a/x-pack/plugins/spaces/public/constants.ts +++ b/x-pack/plugins/spaces/public/constants.ts @@ -19,3 +19,7 @@ export const getSpacesFeatureDescription = () => { return spacesFeatureDescription; }; + +export const DEFAULT_OBJECT_NOUN = i18n.translate('xpack.spaces.shareToSpace.objectNoun', { + defaultMessage: 'object', +}); diff --git a/x-pack/plugins/spaces/public/index.ts b/x-pack/plugins/spaces/public/index.ts index d13f8f48e671..fe04358e3048 100644 --- a/x-pack/plugins/spaces/public/index.ts +++ b/x-pack/plugins/spaces/public/index.ts @@ -20,8 +20,9 @@ export type { CopyToSpaceSavedObjectTarget, } from './copy_saved_objects_to_space'; +export type { LegacyUrlConflictProps, EmbeddableLegacyUrlConflictProps } from './legacy_urls'; + export type { - LegacyUrlConflictProps, ShareToSpaceFlyoutProps, ShareToSpaceSavedObjectTarget, } from './share_saved_objects_to_space'; diff --git a/x-pack/plugins/spaces/public/legacy_urls/components/embeddable_legacy_url_conflict.tsx b/x-pack/plugins/spaces/public/legacy_urls/components/embeddable_legacy_url_conflict.tsx new file mode 100644 index 000000000000..24f36723f978 --- /dev/null +++ b/x-pack/plugins/spaces/public/legacy_urls/components/embeddable_legacy_url_conflict.tsx @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import type { EmbeddableLegacyUrlConflictProps } from '../types'; +import type { InternalProps } from './embeddable_legacy_url_conflict_internal'; + +export const getEmbeddableLegacyUrlConflict = async ( + internalProps: InternalProps +): Promise> => { + const { EmbeddableLegacyUrlConflictInternal } = await import( + './embeddable_legacy_url_conflict_internal' + ); + return (props: EmbeddableLegacyUrlConflictProps) => { + return ; + }; +}; diff --git a/x-pack/plugins/spaces/public/legacy_urls/components/embeddable_legacy_url_conflict_internal.tsx b/x-pack/plugins/spaces/public/legacy_urls/components/embeddable_legacy_url_conflict_internal.tsx new file mode 100644 index 000000000000..8f86c2658cc3 --- /dev/null +++ b/x-pack/plugins/spaces/public/legacy_urls/components/embeddable_legacy_url_conflict_internal.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiButtonEmpty, + EuiCallOut, + EuiCodeBlock, + EuiLink, + EuiSpacer, + EuiTextAlign, +} from '@elastic/eui'; +import React, { useState } from 'react'; +import useAsync from 'react-use/lib/useAsync'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import type { StartServicesAccessor } from 'src/core/public'; + +import type { PluginsStart } from '../../plugin'; +import type { SpacesManager } from '../../spaces_manager'; +import type { EmbeddableLegacyUrlConflictProps } from '../types'; + +export interface InternalProps { + spacesManager: SpacesManager; + getStartServices: StartServicesAccessor; +} + +export const EmbeddableLegacyUrlConflictInternal = ( + props: InternalProps & EmbeddableLegacyUrlConflictProps +) => { + const { spacesManager, getStartServices, targetType, sourceId } = props; + + const [expandError, setExpandError] = useState(false); + + const { value: asyncParams } = useAsync(async () => { + const [{ docLinks }] = await getStartServices(); + const { id: targetSpace } = await spacesManager.getActiveSpace(); + const docLink = docLinks.links.spaces.kibanaDisableLegacyUrlAliasesApi; + const aliasJsonString = JSON.stringify({ targetSpace, targetType, sourceId }, null, 2); + return { docLink, aliasJsonString }; + }, [getStartServices, spacesManager]); + const { docLink, aliasJsonString } = asyncParams ?? {}; + + if (!aliasJsonString || !docLink) { + return null; + } + + return ( + <> + + + {expandError ? ( + + + {'_disable_legacy_url_aliases API'} + + ), + }} + /> + } + color="danger" + iconType="alert" + > + + {aliasJsonString} + + + + ) : ( + setExpandError(true)}> + {i18n.translate('xpack.spaces.embeddableLegacyUrlConflict.detailsButton', { + defaultMessage: `View details`, + })} + + )} + + ); +}; diff --git a/x-pack/plugins/spaces/public/lib/index.ts b/x-pack/plugins/spaces/public/legacy_urls/components/index.ts similarity index 63% rename from x-pack/plugins/spaces/public/lib/index.ts rename to x-pack/plugins/spaces/public/legacy_urls/components/index.ts index 8ba38e2ecefd..c23749da2e89 100644 --- a/x-pack/plugins/spaces/public/lib/index.ts +++ b/x-pack/plugins/spaces/public/legacy_urls/components/index.ts @@ -5,4 +5,5 @@ * 2.0. */ -export { DocumentationLinksService } from './documentation_links'; +export { getEmbeddableLegacyUrlConflict } from './embeddable_legacy_url_conflict'; +export { getLegacyUrlConflict } from './legacy_url_conflict'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict.tsx b/x-pack/plugins/spaces/public/legacy_urls/components/legacy_url_conflict.tsx similarity index 100% rename from x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict.tsx rename to x-pack/plugins/spaces/public/legacy_urls/components/legacy_url_conflict.tsx diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.test.tsx b/x-pack/plugins/spaces/public/legacy_urls/components/legacy_url_conflict_internal.test.tsx similarity index 100% rename from x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.test.tsx rename to x-pack/plugins/spaces/public/legacy_urls/components/legacy_url_conflict_internal.test.tsx diff --git a/x-pack/plugins/spaces/public/legacy_urls/components/legacy_url_conflict_internal.tsx b/x-pack/plugins/spaces/public/legacy_urls/components/legacy_url_conflict_internal.tsx new file mode 100644 index 000000000000..a108e44fefe6 --- /dev/null +++ b/x-pack/plugins/spaces/public/legacy_urls/components/legacy_url_conflict_internal.tsx @@ -0,0 +1,135 @@ +/* + * 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 { + EuiButton, + EuiButtonEmpty, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiSpacer, + EuiToolTip, +} from '@elastic/eui'; +import React, { useState } from 'react'; +import useAsync from 'react-use/lib/useAsync'; +import { first } from 'rxjs/operators'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import type { StartServicesAccessor } from 'src/core/public'; + +import { DEFAULT_OBJECT_NOUN } from '../../constants'; +import type { PluginsStart } from '../../plugin'; +import type { LegacyUrlConflictProps } from '../types'; + +export interface InternalProps { + getStartServices: StartServicesAccessor; +} + +export const LegacyUrlConflictInternal = (props: InternalProps & LegacyUrlConflictProps) => { + const { + getStartServices, + objectNoun = DEFAULT_OBJECT_NOUN, + currentObjectId, + otherObjectId, + otherObjectPath, + } = props; + + const [isDismissed, setIsDismissed] = useState(false); + + const { value: asyncParams } = useAsync(async () => { + const [{ application: applicationStart, docLinks }] = await getStartServices(); + const appId = await applicationStart.currentAppId$.pipe(first()).toPromise(); // retrieve the most recent value from the BehaviorSubject + const docLink = docLinks.links.spaces.kibanaLegacyUrlAliases; + return { applicationStart, appId, docLink }; + }, [getStartServices]); + const { docLink, applicationStart, appId } = asyncParams ?? {}; + + if (!applicationStart || !appId || !docLink || isDismissed) { + return null; + } + + function clickLinkButton() { + applicationStart!.navigateToApp(appId!, { path: otherObjectPath }); + } + + function clickDismissButton() { + setIsDismissed(true); + } + + return ( + + } + > + + {i18n.translate('xpack.spaces.legacyUrlConflict.documentationLinkText', { + defaultMessage: 'Learn more', + })} + + ), + }} + /> + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/index.ts b/x-pack/plugins/spaces/public/legacy_urls/index.ts similarity index 64% rename from x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/index.ts rename to x-pack/plugins/spaces/public/legacy_urls/index.ts index a40bc87cd4dc..b79f65075ce5 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/index.ts +++ b/x-pack/plugins/spaces/public/legacy_urls/index.ts @@ -5,4 +5,7 @@ * 2.0. */ +export { getEmbeddableLegacyUrlConflict, getLegacyUrlConflict } from './components'; export { createRedirectLegacyUrl } from './redirect_legacy_url'; + +export type { EmbeddableLegacyUrlConflictProps, LegacyUrlConflictProps } from './types'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.test.ts b/x-pack/plugins/spaces/public/legacy_urls/redirect_legacy_url.test.ts similarity index 100% rename from x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.test.ts rename to x-pack/plugins/spaces/public/legacy_urls/redirect_legacy_url.test.ts diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.ts b/x-pack/plugins/spaces/public/legacy_urls/redirect_legacy_url.ts similarity index 77% rename from x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.ts rename to x-pack/plugins/spaces/public/legacy_urls/redirect_legacy_url.ts index d427b1bc0524..dbc3d68a4dde 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/utils/redirect_legacy_url.ts +++ b/x-pack/plugins/spaces/public/legacy_urls/redirect_legacy_url.ts @@ -10,9 +10,9 @@ import { first } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; import type { StartServicesAccessor } from 'src/core/public'; -import type { PluginsStart } from '../../plugin'; -import type { SpacesApiUi } from '../../ui_api'; -import { DEFAULT_OBJECT_NOUN } from '../components/constants'; +import { DEFAULT_OBJECT_NOUN } from '../constants'; +import type { PluginsStart } from '../plugin'; +import type { SpacesApiUi } from '../ui_api'; export function createRedirectLegacyUrl( getStartServices: StartServicesAccessor @@ -22,10 +22,10 @@ export function createRedirectLegacyUrl( const { currentAppId$, navigateToApp } = application; const appId = await currentAppId$.pipe(first()).toPromise(); // retrieve the most recent value from the BehaviorSubject - const title = i18n.translate('xpack.spaces.shareToSpace.redirectLegacyUrlToast.title', { + const title = i18n.translate('xpack.spaces.redirectLegacyUrlToast.title', { defaultMessage: `We redirected you to a new URL`, }); - const text = i18n.translate('xpack.spaces.shareToSpace.redirectLegacyUrlToast.text', { + const text = i18n.translate('xpack.spaces.redirectLegacyUrlToast.text', { defaultMessage: `The {objectNoun} you're looking for has a new location. Use this URL from now on.`, values: { objectNoun }, }); diff --git a/x-pack/plugins/spaces/public/legacy_urls/types.ts b/x-pack/plugins/spaces/public/legacy_urls/types.ts new file mode 100644 index 000000000000..b3a80627b5b4 --- /dev/null +++ b/x-pack/plugins/spaces/public/legacy_urls/types.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Properties for the LegacyUrlConflict component. + */ +export interface LegacyUrlConflictProps { + /** + * The string that is used to describe the object in the callout, e.g., _There is a legacy URL for this page that points to a different + * **object**_. + * + * Default value is 'object'. + */ + objectNoun?: string; + /** + * The ID of the object that is currently shown on the page. + */ + currentObjectId: string; + /** + * The ID of the other object that the legacy URL alias points to. + */ + otherObjectId: string; + /** + * The path within your application to use for the new URL, optionally including `search` and/or `hash` URL components. Do not include + * `/app/my-app` or the current base path. + */ + otherObjectPath: string; +} + +/** + * Properties for the EmbeddableLegacyUrlConflict component. + */ +export interface EmbeddableLegacyUrlConflictProps { + /** + * The target type of the legacy URL alias. + */ + targetType: string; + /** + * The source ID of the legacy URL alias. + */ + sourceId: string; +} diff --git a/x-pack/plugins/spaces/public/lib/documentation_links.test.ts b/x-pack/plugins/spaces/public/lib/documentation_links.test.ts deleted file mode 100644 index 5ebaf0ebadaf..000000000000 --- a/x-pack/plugins/spaces/public/lib/documentation_links.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { docLinksServiceMock } from 'src/core/public/mocks'; - -import { DocumentationLinksService } from './documentation_links'; - -describe('DocumentationLinksService', () => { - const setup = () => { - const docLinks = docLinksServiceMock.createStartContract(); - const service = new DocumentationLinksService(docLinks); - return { docLinks, service }; - }; - - describe('#getKibanaPrivilegesDocUrl', () => { - it('returns expected value', () => { - const { service } = setup(); - expect(service.getKibanaPrivilegesDocUrl()).toMatchInlineSnapshot( - `"https://www.elastic.co/guide/en/kibana/mocked-test-branch/kibana-privileges.html"` - ); - }); - }); -}); diff --git a/x-pack/plugins/spaces/public/lib/documentation_links.ts b/x-pack/plugins/spaces/public/lib/documentation_links.ts deleted file mode 100644 index 108be17fe84b..000000000000 --- a/x-pack/plugins/spaces/public/lib/documentation_links.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { DocLinksStart } from 'src/core/public'; - -export class DocumentationLinksService { - private readonly kbnPrivileges: string; - - constructor(docLinks: DocLinksStart) { - this.kbnPrivileges = `${docLinks.links.security.kibanaPrivileges}`; - } - - public getKibanaPrivilegesDocUrl() { - return `${this.kbnPrivileges}`; - } -} diff --git a/x-pack/plugins/spaces/public/mocks.ts b/x-pack/plugins/spaces/public/mocks.ts index 76cafd4c7f5a..9146a0aa2c99 100644 --- a/x-pack/plugins/spaces/public/mocks.ts +++ b/x-pack/plugins/spaces/public/mocks.ts @@ -41,7 +41,7 @@ const createApiUiComponentsMock = () => { getSpaceList: jest.fn(), getLegacyUrlConflict: jest.fn(), getSpaceAvatar: jest.fn(), - getSavedObjectConflictMessage: jest.fn(), + getEmbeddableLegacyUrlConflict: jest.fn(), }; return mock; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/constants.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/constants.ts deleted file mode 100644 index ef3248e1cd60..000000000000 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/constants.ts +++ /dev/null @@ -1,12 +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 { i18n } from '@kbn/i18n'; - -export const DEFAULT_OBJECT_NOUN = i18n.translate('xpack.spaces.shareToSpace.objectNoun', { - defaultMessage: 'object', -}); diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/get_saved_object_conflict_message.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/get_saved_object_conflict_message.tsx deleted file mode 100644 index 66b2a5652057..000000000000 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/get_saved_object_conflict_message.tsx +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import type { SavedObjectConflictMessageProps } from '../types'; - -export const getSavedObjectConflictMessage = async (): Promise< - React.FC -> => { - const { SavedObjectConflictMessage } = await import('./saved_object_conflict_message'); - return (props: SavedObjectConflictMessageProps) => { - return ; - }; -}; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/index.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/index.ts index fa641d03fd71..f69271578968 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/index.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/index.ts @@ -6,5 +6,3 @@ */ export { getShareToSpaceFlyoutComponent } from './share_to_space_flyout'; -export { getSavedObjectConflictMessage } from './get_saved_object_conflict_message'; -export { getLegacyUrlConflict } from './legacy_url_conflict'; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.tsx deleted file mode 100644 index 95bf7b404db3..000000000000 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/legacy_url_conflict_internal.tsx +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - EuiButton, - EuiButtonEmpty, - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, -} from '@elastic/eui'; -import React, { useEffect, useState } from 'react'; -import { first } from 'rxjs/operators'; - -import { FormattedMessage } from '@kbn/i18n/react'; -import type { ApplicationStart, StartServicesAccessor } from 'src/core/public'; - -import type { PluginsStart } from '../../plugin'; -import type { LegacyUrlConflictProps } from '../types'; -import { DEFAULT_OBJECT_NOUN } from './constants'; - -export interface InternalProps { - getStartServices: StartServicesAccessor; -} - -export const LegacyUrlConflictInternal = (props: InternalProps & LegacyUrlConflictProps) => { - const { - getStartServices, - objectNoun = DEFAULT_OBJECT_NOUN, - currentObjectId, - otherObjectId, - otherObjectPath, - } = props; - - const [applicationStart, setApplicationStart] = useState(); - const [isDismissed, setIsDismissed] = useState(false); - const [appId, setAppId] = useState(); - - useEffect(() => { - async function setup() { - const [{ application }] = await getStartServices(); - const appIdValue = await application.currentAppId$.pipe(first()).toPromise(); // retrieve the most recent value from the BehaviorSubject - setApplicationStart(application); - setAppId(appIdValue); - } - setup(); - }, [getStartServices]); - - if (!applicationStart || !appId || isDismissed) { - return null; - } - - function clickLinkButton() { - applicationStart!.navigateToApp(appId!, { path: otherObjectPath }); - } - - function clickDismissButton() { - setIsDismissed(true); - } - - return ( - - } - > - - - - - - - - - - - - - - - - - - - ); -}; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/saved_object_conflict_message.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/saved_object_conflict_message.tsx deleted file mode 100644 index 22a1ad7cd20a..000000000000 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/saved_object_conflict_message.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiButtonEmpty, EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; -import React, { useState } from 'react'; - -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import type { SavedObjectConflictMessageProps } from '../types'; - -export const SavedObjectConflictMessage = ({ json }: SavedObjectConflictMessageProps) => { - const [expandError, setExpandError] = useState(false); - return ( - <> - - {i18n.translate('xpack.spaces.legacyURLConflict.documentationLinkText', { - defaultMessage: 'legacy URL alias', - })} - - ), - }} - /> - - {expandError ? ( - - ) : ( - setExpandError(true)}> - {i18n.translate('xpack.spaces.legacyURLConflict.expandError', { - defaultMessage: `Show more`, - })} - - )} - - ); -}; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx index 3b7569d7c36d..9ba2e41098fd 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx @@ -26,7 +26,6 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../common'; import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../../common/constants'; -import { DocumentationLinksService } from '../../lib'; import { getSpaceAvatarComponent } from '../../space_avatar'; import { useSpaces } from '../../spaces_context'; import type { SpacesDataEntry } from '../../types'; @@ -135,9 +134,7 @@ export const SelectableSpacesControl = (props: Props) => { return null; } - const kibanaPrivilegesUrl = new DocumentationLinksService( - docLinks! - ).getKibanaPrivilegesDocUrl(); + const docLink = docLinks?.links.security.kibanaPrivileges; return ( @@ -146,7 +143,7 @@ export const SelectableSpacesControl = (props: Props) => { defaultMessage="To view hidden spaces, you need {additionalPrivilegesLink}." values={{ additionalPrivilegesLink: ( - + { return null; } - const kibanaPrivilegesUrl = new DocumentationLinksService( - docLinks! - ).getKibanaPrivilegesDocUrl(); - + const docLink = docLinks?.links.security.kibanaPrivileges; return ( <> { values={{ objectNoun, readAndWritePrivilegesLink: ( - + + getEmbeddableLegacyUrlConflict({ spacesManager, getStartServices }) + ), getLegacyUrlConflict: wrapLazy(() => getLegacyUrlConflict({ getStartServices })), getSpaceAvatar: wrapLazy(getSpaceAvatarComponent), - getSavedObjectConflictMessage: wrapLazy(() => getSavedObjectConflictMessage()), }; }; diff --git a/x-pack/plugins/spaces/public/ui_api/index.ts b/x-pack/plugins/spaces/public/ui_api/index.ts index e0749b04de13..a8c77dce6fdf 100644 --- a/x-pack/plugins/spaces/public/ui_api/index.ts +++ b/x-pack/plugins/spaces/public/ui_api/index.ts @@ -7,8 +7,8 @@ import type { StartServicesAccessor } from 'src/core/public'; +import { createRedirectLegacyUrl } from '../legacy_urls'; import type { PluginsStart } from '../plugin'; -import { createRedirectLegacyUrl } from '../share_saved_objects_to_space'; import { useSpaces } from '../spaces_context'; import type { SpacesManager } from '../spaces_manager'; import { getComponents } from './components'; diff --git a/x-pack/plugins/spaces/public/ui_api/types.ts b/x-pack/plugins/spaces/public/ui_api/types.ts index 67e43f0cd31a..eb2aefd5dd53 100644 --- a/x-pack/plugins/spaces/public/ui_api/types.ts +++ b/x-pack/plugins/spaces/public/ui_api/types.ts @@ -10,11 +10,8 @@ import type { ReactElement } from 'react'; import type { CoreStart } from 'src/core/public'; import type { CopyToSpaceFlyoutProps } from '../copy_saved_objects_to_space'; -import type { - LegacyUrlConflictProps, - SavedObjectConflictMessageProps, - ShareToSpaceFlyoutProps, -} from '../share_saved_objects_to_space'; +import type { EmbeddableLegacyUrlConflictProps, LegacyUrlConflictProps } from '../legacy_urls'; +import type { ShareToSpaceFlyoutProps } from '../share_saved_objects_to_space'; import type { SpaceAvatarProps } from '../space_avatar'; import type { SpaceListProps } from '../space_list'; import type { SpacesContextProps, SpacesReactContextValue } from '../spaces_context'; @@ -88,6 +85,12 @@ export interface SpacesApiUiComponent { * Note: must be rendered inside of a SpacesContext. */ getSpaceList: LazyComponentFn; + /** + * Displays a callout that needs to be used if an embeddable component call to `SavedObjectsClient.resolve()` results in an `"conflict"` + * outcome, which indicates that the user has loaded an embeddable which is associated directly with one object (A), *and* with a legacy + * URL that points to a different object (B). + */ + getEmbeddableLegacyUrlConflict: LazyComponentFn; /** * Displays a callout that needs to be used if a call to `SavedObjectsClient.resolve()` results in an `"conflict"` outcome, which * indicates that the user has loaded the page which is associated directly with one object (A), *and* with a legacy URL that points to a @@ -110,8 +113,4 @@ export interface SpacesApiUiComponent { * Displays an avatar for the given space. */ getSpaceAvatar: LazyComponentFn; - /** - * Displays a saved object conflict message that directs user to disable legacy URL alias - */ - getSavedObjectConflictMessage: LazyComponentFn; } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 476ba3485659..9bcedd1ffb98 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -24209,10 +24209,6 @@ "xpack.spaces.shareToSpace.currentSpaceBadge": "現在", "xpack.spaces.shareToSpace.featureIsDisabledTooltip": "この機能はこのスペースでは無効です。", "xpack.spaces.shareToSpace.flyoutTitle": "{objectNoun}をスペースに割り当てる", - "xpack.spaces.shareToSpace.legacyUrlConflictBody": "現在、{objectNoun} [id={currentObjectId}]を表示しています。このページのレガシーURLは別の{objectNoun} [id={otherObjectId}]を示しています。", - "xpack.spaces.shareToSpace.legacyUrlConflictDismissButton": "閉じる", - "xpack.spaces.shareToSpace.legacyUrlConflictLinkButton": "他の{objectNoun}に移動", - "xpack.spaces.shareToSpace.legacyUrlConflictTitle": "2つのオブジェクトがこのURLに関連付けられています", "xpack.spaces.shareToSpace.noAvailableSpaces.canCreateNewSpace.linkText": "新しいスペースを作成", "xpack.spaces.shareToSpace.noAvailableSpaces.canCreateNewSpace.text": "オブジェクトを共有するには、{createANewSpaceLink}できます。", "xpack.spaces.shareToSpace.objectNoun": "オブジェクト", @@ -24221,8 +24217,6 @@ "xpack.spaces.shareToSpace.privilegeWarningBody": "この{objectNoun}のスペースを編集するには、すべてのスペースで{readAndWritePrivilegesLink}が必要です。", "xpack.spaces.shareToSpace.privilegeWarningLink": "読み書き権限", "xpack.spaces.shareToSpace.privilegeWarningTitle": "追加の権限が必要です", - "xpack.spaces.shareToSpace.redirectLegacyUrlToast.text": "検索している{objectNoun}は新しい場所にあります。今後はこのURLを使用してください。", - "xpack.spaces.shareToSpace.redirectLegacyUrlToast.title": "新しいURLに移動しました", "xpack.spaces.shareToSpace.saveButton": "保存して閉じる", "xpack.spaces.shareToSpace.shareErrorTitle": "{objectNoun}の更新エラー", "xpack.spaces.shareToSpace.shareModeControl.buttonGroupLegend": "この共有方法を選択", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 88f3004c0bc0..d9eab3863c2c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -24614,10 +24614,6 @@ "xpack.spaces.shareToSpace.currentSpaceBadge": "当前", "xpack.spaces.shareToSpace.featureIsDisabledTooltip": "此功能在此工作区中已禁用。", "xpack.spaces.shareToSpace.flyoutTitle": "将 {objectNoun} 分配给工作区", - "xpack.spaces.shareToSpace.legacyUrlConflictBody": "当前您正在查看 {objectNoun} [id={currentObjectId}]。此页面的旧 URL 显示不同的 {objectNoun} [id={otherObjectId}]。", - "xpack.spaces.shareToSpace.legacyUrlConflictDismissButton": "关闭", - "xpack.spaces.shareToSpace.legacyUrlConflictLinkButton": "前往其他 {objectNoun}", - "xpack.spaces.shareToSpace.legacyUrlConflictTitle": "2 个对象与此 URL 关联", "xpack.spaces.shareToSpace.noAvailableSpaces.canCreateNewSpace.linkText": "创建新工作区", "xpack.spaces.shareToSpace.noAvailableSpaces.canCreateNewSpace.text": "您可以{createANewSpaceLink},用于共享您的对象。", "xpack.spaces.shareToSpace.objectNoun": "对象", @@ -24626,8 +24622,6 @@ "xpack.spaces.shareToSpace.privilegeWarningBody": "要编辑此 {objectNoun} 的工作区,您在所有工作区中都需要{readAndWritePrivilegesLink}。", "xpack.spaces.shareToSpace.privilegeWarningLink": "读写权限", "xpack.spaces.shareToSpace.privilegeWarningTitle": "需要其他权限", - "xpack.spaces.shareToSpace.redirectLegacyUrlToast.text": "您正在寻找的{objectNoun}具有新的位置。从现在开始使用此 URL。", - "xpack.spaces.shareToSpace.redirectLegacyUrlToast.title": "我们已将您重定向到新 URL", "xpack.spaces.shareToSpace.relativesControl.description": "{relativesCount} 个相关{relativesCount, plural, other {对象}}也将更改。", "xpack.spaces.shareToSpace.saveButton": "保存并关闭", "xpack.spaces.shareToSpace.shareErrorTitle": "更新 {objectNoun} 时出错", From 02822a66fa7041b65d10910eb545bdde371b8c68 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Thu, 7 Oct 2021 19:11:20 -0500 Subject: [PATCH 14/74] Relocate internal APM API endpoints to /internal (#114196) All endpoints except annotations, source maps, and agent configuration are now at /internal/apm instead of /api/apm. None of the UX endpoints have been updated, only APM. If you search for "/api/apm" in the codebase, you should only see the above endpoints. Fixes #113383. --- x-pack/plugins/apm/dev_docs/local_setup.md | 2 +- .../integration/power_user/no_data_screen.ts | 2 +- .../integration/read_only_user/home.spec.ts | 4 +- .../service_overview/header_filters.spec.ts | 22 ++++++----- .../service_overview/instances_table.spec.ts | 6 +-- .../service_overview/service_overview.spec.ts | 2 +- .../service_overview/time_comparison.spec.ts | 14 ++++--- .../error_count_alert_trigger/index.tsx | 3 +- .../index.tsx | 3 +- .../index.tsx | 3 +- .../app/Settings/ApmIndices/index.tsx | 7 ++-- .../anomaly_detection/add_environments.tsx | 4 +- .../Settings/anomaly_detection/create_jobs.ts | 2 +- .../app/Settings/anomaly_detection/index.tsx | 4 +- .../DeleteButton.tsx | 2 +- .../link_preview.tsx | 2 +- .../saveCustomLink.ts | 4 +- .../customize_ui/custom_link/index.tsx | 2 +- .../components/app/Settings/schema/index.tsx | 9 +++-- .../public/components/app/TraceLink/index.tsx | 2 +- .../backend_detail_dependencies_table.tsx | 2 +- .../backend_error_rate_chart.tsx | 2 +- .../backend_latency_chart.tsx | 2 +- .../backend_throughput_chart.tsx | 2 +- .../index.tsx | 2 +- .../Distribution/index.tsx | 2 +- .../error_group_details/detail_view/index.tsx | 2 +- .../app/error_group_details/index.tsx | 2 +- .../app/error_group_overview/List/index.tsx | 2 +- .../app/error_group_overview/index.tsx | 2 +- .../service_dependencies_breakdown_chart.tsx | 3 +- .../app/service_inventory/index.tsx | 4 +- .../__fixtures__/service_api_mock_data.ts | 2 +- .../service_inventory/service_list/index.tsx | 4 +- .../service_list/service_list.test.tsx | 2 +- .../components/app/service_logs/index.tsx | 4 +- .../service_map/Popover/backend_contents.tsx | 2 +- .../service_map/Popover/service_contents.tsx | 2 +- .../components/app/service_map/index.tsx | 2 +- .../app/service_node_metrics/index.tsx | 2 +- .../app/service_node_overview/index.tsx | 2 +- .../service_overview.test.tsx | 37 +++++++++--------- .../index.tsx | 2 +- .../get_columns.tsx | 4 +- .../service_overview_errors_table/index.tsx | 8 ++-- ...ice_overview_instances_chart_and_table.tsx | 8 ++-- .../get_columns.tsx | 4 +- .../index.tsx | 4 +- .../instance_actions_menu/menu_sections.ts | 2 +- .../instance_details.test.tsx | 2 +- .../intance_details.tsx | 2 +- .../use_instance_details_fetcher.tsx | 2 +- .../service_overview_throughput_chart.tsx | 2 +- .../app/service_profiling/index.tsx | 4 +- .../service_profiling_flamegraph.tsx | 3 +- .../components/app/trace_overview/index.tsx | 4 +- .../app/trace_overview/trace_list.tsx | 2 +- .../use_waterfall_fetcher.ts | 2 +- .../waterfall_helpers/waterfall_helpers.ts | 2 +- .../waterfallContainer.stories.data.ts | 2 +- .../components/app/transaction_link/index.tsx | 2 +- .../routing/templates/apm_main_template.tsx | 2 +- .../MetadataTable/ErrorMetadata/index.tsx | 2 +- .../MetadataTable/SpanMetadata/index.tsx | 2 +- .../TransactionMetadata/index.tsx | 2 +- .../anomaly_detection_setup_link.tsx | 2 +- .../helper/get_alert_annotations.test.tsx | 2 +- .../charts/helper/get_alert_annotations.tsx | 2 +- .../custom_tooltip.stories.tsx | 2 +- .../custom_tooltip.tsx | 2 +- .../index.tsx | 2 +- .../latency_chart/latency_chart.stories.tsx | 6 +-- .../use_transaction_breakdown.ts | 2 +- .../transaction_error_rate_chart/index.tsx | 4 +- .../shared/service_icons/cloud_details.tsx | 2 +- .../service_icons/container_details.tsx | 2 +- .../shared/service_icons/index.test.tsx | 8 ++-- .../components/shared/service_icons/index.tsx | 4 +- .../shared/service_icons/service_details.tsx | 2 +- .../custom_link_menu_section/index.tsx | 2 +- .../shared/transactions_table/get_columns.tsx | 4 +- .../shared/transactions_table/index.tsx | 6 +-- .../anomaly_detection_jobs_context.tsx | 4 +- .../apm_backend/apm_backend_context.tsx | 4 +- .../apm_service/apm_service_context.tsx | 2 +- .../apm_service/use_service_agent_fetcher.ts | 2 +- .../use_service_alerts_fetcher.tsx | 2 +- .../use_service_transaction_types_fetcher.tsx | 3 +- .../public/hooks/use_dynamic_index_pattern.ts | 2 +- .../public/hooks/use_environments_fetcher.tsx | 2 +- .../use_error_group_distribution_fetcher.tsx | 3 +- .../use_fallback_to_transactions_fetcher.tsx | 2 +- .../use_service_metric_charts_fetcher.ts | 2 +- .../use_transaction_latency_chart_fetcher.ts | 2 +- .../use_transaction_trace_samples_fetcher.ts | 2 +- .../selectors/latency_chart_selectors.ts | 2 +- .../apm/public/services/callApi.test.ts | 13 ++++--- .../apm/public/services/callApmApi.test.ts | 12 +++--- .../apm_observability_overview_fetchers.ts | 4 +- .../apm/public/services/rest/callApi.ts | 2 +- .../apm/public/services/rest/index_pattern.ts | 2 +- .../config_agent/config_agent.stories.tsx | 2 +- .../config_agent/get_policy_options.test.ts | 2 +- .../public/tutorial/config_agent/index.tsx | 4 +- .../tutorial/tutorial_apm_fleet_check.ts | 2 +- .../tutorial_fleet_instructions/index.tsx | 4 +- .../apm/server/routes/alerts/chart_preview.ts | 6 +-- x-pack/plugins/apm/server/routes/backends.ts | 12 +++--- .../plugins/apm/server/routes/environments.ts | 2 +- x-pack/plugins/apm/server/routes/errors.ts | 6 +-- .../apm/server/routes/event_metadata.ts | 2 +- .../server/routes/fallback_to_transactions.ts | 2 +- x-pack/plugins/apm/server/routes/fleet.ts | 12 +++--- .../server/routes/historical_data/index.ts | 2 +- .../apm/server/routes/index_pattern.ts | 4 +- x-pack/plugins/apm/server/routes/metrics.ts | 2 +- .../server/routes/observability_overview.ts | 4 +- .../plugins/apm/server/routes/service_map.ts | 6 +-- .../apm/server/routes/service_nodes.ts | 2 +- x-pack/plugins/apm/server/routes/services.ts | 39 ++++++++++--------- .../routes/settings/anomaly_detection.ts | 6 +-- .../apm/server/routes/settings/apm_indices.ts | 6 +-- .../apm/server/routes/settings/custom_link.ts | 10 ++--- x-pack/plugins/apm/server/routes/traces.ts | 8 ++-- .../plugins/apm/server/routes/transactions.ts | 15 ++++--- .../server/scripts/get_alerts_index.sh | 2 +- .../tests/alerts/chart_preview.ts | 12 +++--- .../tests/feature_controls.ts | 34 ++++++++-------- .../tests/historical_data/has_data.ts | 4 +- .../test/apm_api_integration/tests/index.ts | 2 +- .../tests/inspect/inspect.ts | 6 +-- .../tests/metadata/event_metadata.ts | 6 +-- .../tests/metrics_charts/metrics_charts.ts | 6 +-- .../tests/observability_overview/has_data.ts | 6 +-- .../observability_overview.ts | 4 +- .../__snapshots__/service_maps.snap | 4 +- .../tests/service_maps/service_maps.ts | 32 +++++++-------- .../service_overview/dependencies/index.ts | 10 ++--- .../service_overview/get_service_node_ids.ts | 2 +- .../service_overview/instance_details.ts | 9 +++-- .../instances_detailed_statistics.ts | 8 ++-- .../instances_main_statistics.ts | 14 +++---- .../tests/services/agent.ts | 4 +- .../error_groups_detailed_statistics.ts | 12 +++--- .../services/error_groups_main_statistics.ts | 6 +-- .../tests/services/get_error_group_ids.ts | 2 +- .../tests/services/service_details.ts | 6 +-- .../tests/services/service_icons.ts | 6 +-- .../services/services_detailed_statistics.ts | 15 +++---- .../tests/services/throughput.ts | 12 +++--- .../tests/services/top_services.ts | 20 +++++----- .../tests/services/transaction_types.ts | 4 +- .../tests/settings/anomaly_detection/basic.ts | 4 +- .../anomaly_detection/no_access_user.ts | 4 +- .../settings/anomaly_detection/read_user.ts | 4 +- .../settings/anomaly_detection/write_user.ts | 6 ++- .../tests/settings/custom_link.ts | 10 ++--- .../tests/traces/top_traces.ts | 4 +- .../tests/traces/trace_by_id.tsx | 6 +-- .../tests/transactions/breakdown.ts | 8 ++-- .../tests/transactions/error_rate.ts | 10 ++--- .../tests/transactions/latency.ts | 24 ++++++------ .../tests/transactions/trace_samples.ts | 2 +- ...transactions_groups_detailed_statistics.ts | 12 +++--- .../transactions_groups_main_statistics.ts | 8 ++-- 165 files changed, 457 insertions(+), 428 deletions(-) diff --git a/x-pack/plugins/apm/dev_docs/local_setup.md b/x-pack/plugins/apm/dev_docs/local_setup.md index fd350d81a35a..eaa99560400e 100644 --- a/x-pack/plugins/apm/dev_docs/local_setup.md +++ b/x-pack/plugins/apm/dev_docs/local_setup.md @@ -46,4 +46,4 @@ This will create: All APM api endpoints accept `_inspect=true` as a query param that will output all Elasticsearch queries performed in that request. It will be available in the browser response and on localhost it is also available in the Kibana Node.js process output. Example: -`/api/apm/services/my_service?_inspect=true` +`/internal/apm/services/my_service?_inspect=true` diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/no_data_screen.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/no_data_screen.ts index 2d5c2a6f1622..1e954d998229 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/no_data_screen.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/power_user/no_data_screen.ts @@ -6,7 +6,7 @@ */ /* eslint-disable @typescript-eslint/naming-convention */ -const apmIndicesSaveURL = '/api/apm/settings/apm-indices/save'; +const apmIndicesSaveURL = '/internal/apm/settings/apm-indices/save'; describe('No data screen', () => { describe('bypass no data screen on settings pages', () => { diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/home.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/home.spec.ts index 679e2934f9c3..8457282f5c25 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/home.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/home.spec.ts @@ -17,11 +17,11 @@ const serviceInventoryHref = url.format({ const apisToIntercept = [ { - endpoint: '/api/apm/service?*', + endpoint: '/internal/apm/service?*', name: 'servicesMainStatistics', }, { - endpoint: '/api/apm/services/detailed_statistics?*', + endpoint: '/internal/apm/services/detailed_statistics?*', name: 'servicesDetailedStatistics', }, ]; diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/header_filters.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/header_filters.spec.ts index 720e9b4b67e0..6950a0bbadb9 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/header_filters.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/header_filters.spec.ts @@ -16,43 +16,47 @@ const serviceOverviewHref = url.format({ const apisToIntercept = [ { - endpoint: '/api/apm/services/opbeans-node/transactions/charts/latency?*', + endpoint: + '/internal/apm/services/opbeans-node/transactions/charts/latency?*', name: 'latencyChartRequest', }, { - endpoint: '/api/apm/services/opbeans-node/throughput?*', + endpoint: '/internal/apm/services/opbeans-node/throughput?*', name: 'throughputChartRequest', }, { - endpoint: '/api/apm/services/opbeans-node/transactions/charts/error_rate?*', + endpoint: + '/internal/apm/services/opbeans-node/transactions/charts/error_rate?*', name: 'errorRateChartRequest', }, { endpoint: - '/api/apm/services/opbeans-node/transactions/groups/detailed_statistics?*', + '/internal/apm/services/opbeans-node/transactions/groups/detailed_statistics?*', name: 'transactionGroupsDetailedRequest', }, { endpoint: - '/api/apm/services/opbeans-node/service_overview_instances/detailed_statistics?*', + '/internal/apm/services/opbeans-node/service_overview_instances/detailed_statistics?*', name: 'instancesDetailedRequest', }, { endpoint: - '/api/apm/services/opbeans-node/service_overview_instances/main_statistics?*', + '/internal/apm/services/opbeans-node/service_overview_instances/main_statistics?*', name: 'instancesMainStatisticsRequest', }, { - endpoint: '/api/apm/services/opbeans-node/error_groups/main_statistics?*', + endpoint: + '/internal/apm/services/opbeans-node/error_groups/main_statistics?*', name: 'errorGroupsMainStatisticsRequest', }, { - endpoint: '/api/apm/services/opbeans-node/transaction/charts/breakdown?*', + endpoint: + '/internal/apm/services/opbeans-node/transaction/charts/breakdown?*', name: 'transactonBreakdownRequest', }, { endpoint: - '/api/apm/services/opbeans-node/transactions/groups/main_statistics?*', + '/internal/apm/services/opbeans-node/transactions/groups/main_statistics?*', name: 'transactionsGroupsMainStatisticsRequest', }, ]; diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/instances_table.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/instances_table.spec.ts index 7bfa4689db4f..c7d9fa4e3210 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/instances_table.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/instances_table.spec.ts @@ -18,17 +18,17 @@ const serviceOverviewHref = url.format({ const apisToIntercept = [ { endpoint: - '/api/apm/services/opbeans-java/service_overview_instances/main_statistics?*', + '/internal/apm/services/opbeans-java/service_overview_instances/main_statistics?*', name: 'instancesMainRequest', }, { endpoint: - '/api/apm/services/opbeans-java/service_overview_instances/detailed_statistics?*', + '/internal/apm/services/opbeans-java/service_overview_instances/detailed_statistics?*', name: 'instancesDetailsRequest', }, { endpoint: - '/api/apm/services/opbeans-java/service_overview_instances/details/31651f3c624b81c55dd4633df0b5b9f9ab06b151121b0404ae796632cd1f87ad?*', + '/internal/apm/services/opbeans-java/service_overview_instances/details/31651f3c624b81c55dd4633df0b5b9f9ab06b151121b0404ae796632cd1f87ad?*', name: 'instanceDetailsRequest', }, ]; diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts index a0dc7082ca33..b0cf424b8067 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/service_overview.spec.ts @@ -59,7 +59,7 @@ describe('Service Overview', () => { }); it('hides dependency tab when RUM service', () => { - cy.intercept('GET', '/api/apm/services/opbeans-rum/agent?*').as( + cy.intercept('GET', '/internal/apm/services/opbeans-rum/agent?*').as( 'agentRequest' ); cy.visit( diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/time_comparison.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/time_comparison.spec.ts index 2e05a2c062aa..65a82a8ab6bd 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/time_comparison.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/time_comparison.spec.ts @@ -18,30 +18,32 @@ const serviceOverviewHref = url.format({ const apisToIntercept = [ { - endpoint: '/api/apm/services/opbeans-java/transactions/charts/latency?*', + endpoint: + '/internal/apm/services/opbeans-java/transactions/charts/latency?*', name: 'latencyChartRequest', }, { - endpoint: '/api/apm/services/opbeans-java/throughput?*', + endpoint: '/internal/apm/services/opbeans-java/throughput?*', name: 'throughputChartRequest', }, { - endpoint: '/api/apm/services/opbeans-java/transactions/charts/error_rate?*', + endpoint: + '/internal/apm/services/opbeans-java/transactions/charts/error_rate?*', name: 'errorRateChartRequest', }, { endpoint: - '/api/apm/services/opbeans-java/transactions/groups/detailed_statistics?*', + '/internal/apm/services/opbeans-java/transactions/groups/detailed_statistics?*', name: 'transactionGroupsDetailedRequest', }, { endpoint: - '/api/apm/services/opbeans-java/error_groups/detailed_statistics?*', + '/internal/apm/services/opbeans-java/error_groups/detailed_statistics?*', name: 'errorGroupsDetailedRequest', }, { endpoint: - '/api/apm/services/opbeans-java/service_overview_instances/detailed_statistics?*', + '/internal/apm/services/opbeans-java/service_overview_instances/detailed_statistics?*', name: 'instancesDetailedRequest', }, ]; diff --git a/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx index cb7b367fb390..eac128a7e88c 100644 --- a/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx @@ -61,7 +61,8 @@ export function ErrorCountAlertTrigger(props: Props) { }); if (interval && start && end) { return callApmApi({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_count', + endpoint: + 'GET /internal/apm/alerts/chart_preview/transaction_error_count', params: { query: { environment: params.environment, diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx index 5327eb561bfc..8957dfc823e4 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx @@ -99,7 +99,8 @@ export function TransactionDurationAlertTrigger(props: Props) { }); if (interval && start && end) { return callApmApi({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_duration', + endpoint: + 'GET /internal/apm/alerts/chart_preview/transaction_duration', params: { query: { aggregationType: params.aggregationType, diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx index 3bad7ae15f65..ddddc4bbecba 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx @@ -68,7 +68,8 @@ export function TransactionErrorRateAlertTrigger(props: Props) { }); if (interval && start && end) { return callApmApi({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_rate', + endpoint: + 'GET /internal/apm/alerts/chart_preview/transaction_error_rate', params: { query: { environment: params.environment, diff --git a/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx index 2d74187f9d83..6685dddd87d7 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx @@ -76,7 +76,7 @@ async function saveApmIndices({ apmIndices: Record; }) { await callApmApi({ - endpoint: 'POST /api/apm/settings/apm-indices/save', + endpoint: 'POST /internal/apm/settings/apm-indices/save', signal: null, params: { body: apmIndices, @@ -86,7 +86,8 @@ async function saveApmIndices({ clearCache(); } -type ApiResponse = APIReturnType<`GET /api/apm/settings/apm-index-settings`>; +type ApiResponse = + APIReturnType<`GET /internal/apm/settings/apm-index-settings`>; // avoid infinite loop by initializing the state outside the component const INITIAL_STATE: ApiResponse = { apmIndexSettings: [] }; @@ -103,7 +104,7 @@ export function ApmIndices() { (_callApmApi) => { if (canSave) { return _callApmApi({ - endpoint: `GET /api/apm/settings/apm-index-settings`, + endpoint: `GET /internal/apm/settings/apm-index-settings`, }); } }, diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/add_environments.tsx b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/add_environments.tsx index a60e685eacbd..b130c727cfcf 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/add_environments.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/add_environments.tsx @@ -35,7 +35,7 @@ interface Props { } type ApiResponse = - APIReturnType<'GET /api/apm/settings/anomaly-detection/environments'>; + APIReturnType<'GET /internal/apm/settings/anomaly-detection/environments'>; const INITIAL_DATA: ApiResponse = { environments: [] }; export function AddEnvironments({ @@ -50,7 +50,7 @@ export function AddEnvironments({ const { data = INITIAL_DATA, status } = useFetcher( (callApmApi) => callApmApi({ - endpoint: `GET /api/apm/settings/anomaly-detection/environments`, + endpoint: `GET /internal/apm/settings/anomaly-detection/environments`, }), [], { preservePreviousData: false } diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/create_jobs.ts b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/create_jobs.ts index a5ab0a3002bb..3e3493d60f83 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/create_jobs.ts +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/create_jobs.ts @@ -28,7 +28,7 @@ export async function createJobs({ }) { try { await callApmApi({ - endpoint: 'POST /api/apm/settings/anomaly-detection/jobs', + endpoint: 'POST /internal/apm/settings/anomaly-detection/jobs', signal: null, params: { body: { environments }, diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx index 0fe7f9360de0..8e1064a71647 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/index.tsx @@ -17,7 +17,7 @@ import { useLicenseContext } from '../../../../context/license/use_license_conte import { APIReturnType } from '../../../../services/rest/createCallApmApi'; export type AnomalyDetectionApiResponse = - APIReturnType<'GET /api/apm/settings/anomaly-detection/jobs'>; + APIReturnType<'GET /internal/apm/settings/anomaly-detection/jobs'>; const DEFAULT_VALUE: AnomalyDetectionApiResponse = { jobs: [], @@ -40,7 +40,7 @@ export function AnomalyDetection() { (callApmApi) => { if (canGetJobs) { return callApmApi({ - endpoint: `GET /api/apm/settings/anomaly-detection/jobs`, + endpoint: `GET /internal/apm/settings/anomaly-detection/jobs`, }); } }, diff --git a/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/create_edit_custom_link_flyout/DeleteButton.tsx b/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/create_edit_custom_link_flyout/DeleteButton.tsx index c6547aaff067..f425979df71e 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/create_edit_custom_link_flyout/DeleteButton.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/create_edit_custom_link_flyout/DeleteButton.tsx @@ -49,7 +49,7 @@ async function deleteConfig( ) { try { await callApmApi({ - endpoint: 'DELETE /api/apm/settings/custom_links/{id}', + endpoint: 'DELETE /internal/apm/settings/custom_links/{id}', signal: null, params: { path: { id: customLinkId }, diff --git a/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/create_edit_custom_link_flyout/link_preview.tsx b/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/create_edit_custom_link_flyout/link_preview.tsx index 726d4ba0d65e..7ffeafedab4b 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/create_edit_custom_link_flyout/link_preview.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/create_edit_custom_link_flyout/link_preview.tsx @@ -34,7 +34,7 @@ const fetchTransaction = debounce( async (filters: Filter[], callback: (transaction: Transaction) => void) => { const transaction = await callApmApi({ signal: null, - endpoint: 'GET /api/apm/settings/custom_links/transaction', + endpoint: 'GET /internal/apm/settings/custom_links/transaction', params: { query: convertFiltersToQuery(filters) }, }); callback(transaction); diff --git a/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/create_edit_custom_link_flyout/saveCustomLink.ts b/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/create_edit_custom_link_flyout/saveCustomLink.ts index aa20cf74d151..6dcabc4adeda 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/create_edit_custom_link_flyout/saveCustomLink.ts +++ b/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/create_edit_custom_link_flyout/saveCustomLink.ts @@ -35,7 +35,7 @@ export async function saveCustomLink({ if (id) { await callApmApi({ - endpoint: 'PUT /api/apm/settings/custom_links/{id}', + endpoint: 'PUT /internal/apm/settings/custom_links/{id}', signal: null, params: { path: { id }, @@ -44,7 +44,7 @@ export async function saveCustomLink({ }); } else { await callApmApi({ - endpoint: 'POST /api/apm/settings/custom_links', + endpoint: 'POST /internal/apm/settings/custom_links', signal: null, params: { body: customLink, diff --git a/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/index.tsx index beea1d827684..295cc4d008f8 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/customize_ui/custom_link/index.tsx @@ -38,7 +38,7 @@ export function CustomLinkOverview() { async (callApmApi) => { if (hasValidLicense) { return callApmApi({ - endpoint: 'GET /api/apm/settings/custom_links', + endpoint: 'GET /internal/apm/settings/custom_links', }); } }, diff --git a/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx index 6b7538e61c13..ac32e22fa3de 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx @@ -20,7 +20,7 @@ import { import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; type FleetMigrationCheckResponse = - APIReturnType<'GET /api/apm/fleet/migration_check'>; + APIReturnType<'GET /internal/apm/fleet/migration_check'>; const APM_DATA_STREAMS_MIGRATION_STATUS_LS = { value: '', @@ -46,7 +46,8 @@ export function Schema() { data = {} as FleetMigrationCheckResponse, status, } = useFetcher( - (callApi) => callApi({ endpoint: 'GET /api/apm/fleet/migration_check' }), + (callApi) => + callApi({ endpoint: 'GET /internal/apm/fleet/migration_check' }), [], { preservePreviousData: false } ); @@ -118,7 +119,7 @@ async function getUnsupportedApmServerConfigs( ) { try { const { unsupported } = await callApmApi({ - endpoint: 'GET /api/apm/fleet/apm_server_schema/unsupported', + endpoint: 'GET /internal/apm/fleet/apm_server_schema/unsupported', signal: null, }); return unsupported; @@ -142,7 +143,7 @@ async function createCloudApmPackagePolicy( updateLocalStorage(FETCH_STATUS.LOADING); try { const { cloudApmPackagePolicy } = await callApmApi({ - endpoint: 'POST /api/apm/fleet/cloud_apm_package_policy', + endpoint: 'POST /internal/apm/fleet/cloud_apm_package_policy', signal: null, }); updateLocalStorage(FETCH_STATUS.SUCCESS); diff --git a/x-pack/plugins/apm/public/components/app/TraceLink/index.tsx b/x-pack/plugins/apm/public/components/app/TraceLink/index.tsx index 2733ee0ddbdb..1da022f3d933 100644 --- a/x-pack/plugins/apm/public/components/app/TraceLink/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TraceLink/index.tsx @@ -29,7 +29,7 @@ export function TraceLink() { (callApmApi) => { if (traceId) { return callApmApi({ - endpoint: 'GET /api/apm/traces/{traceId}/root_transaction', + endpoint: 'GET /internal/apm/traces/{traceId}/root_transaction', params: { path: { traceId, diff --git a/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_detail_dependencies_table.tsx b/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_detail_dependencies_table.tsx index f98358e3a9c2..be493f8a98b1 100644 --- a/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_detail_dependencies_table.tsx +++ b/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_detail_dependencies_table.tsx @@ -44,7 +44,7 @@ export function BackendDetailDependenciesTable() { } return callApmApi({ - endpoint: 'GET /api/apm/backends/{backendName}/upstream_services', + endpoint: 'GET /internal/apm/backends/{backendName}/upstream_services', params: { path: { backendName, diff --git a/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_error_rate_chart.tsx b/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_error_rate_chart.tsx index d48178a8522b..cf14145dba82 100644 --- a/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_error_rate_chart.tsx +++ b/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_error_rate_chart.tsx @@ -44,7 +44,7 @@ export function BackendFailedTransactionRateChart({ } return callApmApi({ - endpoint: 'GET /api/apm/backends/{backendName}/charts/error_rate', + endpoint: 'GET /internal/apm/backends/{backendName}/charts/error_rate', params: { path: { backendName, diff --git a/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_latency_chart.tsx b/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_latency_chart.tsx index 759d15398887..3f5a56d55d82 100644 --- a/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_latency_chart.tsx +++ b/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_latency_chart.tsx @@ -40,7 +40,7 @@ export function BackendLatencyChart({ height }: { height: number }) { } return callApmApi({ - endpoint: 'GET /api/apm/backends/{backendName}/charts/latency', + endpoint: 'GET /internal/apm/backends/{backendName}/charts/latency', params: { path: { backendName, diff --git a/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_throughput_chart.tsx b/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_throughput_chart.tsx index 2cfc7ea31762..f5d9cb7a7a55 100644 --- a/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_throughput_chart.tsx +++ b/x-pack/plugins/apm/public/components/app/backend_detail_overview/backend_throughput_chart.tsx @@ -36,7 +36,7 @@ export function BackendThroughputChart({ height }: { height: number }) { } return callApmApi({ - endpoint: 'GET /api/apm/backends/{backendName}/charts/throughput', + endpoint: 'GET /internal/apm/backends/{backendName}/charts/throughput', params: { path: { backendName, diff --git a/x-pack/plugins/apm/public/components/app/backend_inventory/backend_inventory_dependencies_table/index.tsx b/x-pack/plugins/apm/public/components/app/backend_inventory/backend_inventory_dependencies_table/index.tsx index ea135104982e..05eb9892fc10 100644 --- a/x-pack/plugins/apm/public/components/app/backend_inventory/backend_inventory_dependencies_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/backend_inventory/backend_inventory_dependencies_table/index.tsx @@ -45,7 +45,7 @@ export function BackendInventoryDependenciesTable() { } return callApmApi({ - endpoint: 'GET /api/apm/backends/top_backends', + endpoint: 'GET /internal/apm/backends/top_backends', params: { query: { start, end, environment, numBuckets: 20, offset, kuery }, }, diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/Distribution/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/Distribution/index.tsx index b6fc0d4fcf65..3d1d0ee564ba 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_details/Distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_details/Distribution/index.tsx @@ -35,7 +35,7 @@ const ALERT_RULE_TYPE_ID: typeof ALERT_RULE_TYPE_ID_TYPED = ALERT_RULE_TYPE_ID_NON_TYPED; type ErrorDistributionAPIResponse = - APIReturnType<'GET /api/apm/services/{serviceName}/errors/distribution'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/errors/distribution'>; interface FormattedBucket { x0: number; diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/index.tsx index 6e6f323a5525..d4ffd8ece9d4 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/index.tsx @@ -54,7 +54,7 @@ const TransactionLinkName = euiStyled.div` `; interface Props { - errorGroup: APIReturnType<'GET /api/apm/services/{serviceName}/errors/{groupId}'>; + errorGroup: APIReturnType<'GET /internal/apm/services/{serviceName}/errors/{groupId}'>; urlParams: ApmUrlParams; kuery: string; } diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx index 9145e019c37e..011434889298 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx @@ -127,7 +127,7 @@ export function ErrorGroupDetails() { (callApmApi) => { if (start && end) { return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/errors/{groupId}', + endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}', params: { path: { serviceName, diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/List/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_overview/List/index.tsx index d42c77c06b0a..d7c5b1f4bc35 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_overview/List/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_overview/List/index.tsx @@ -48,7 +48,7 @@ const Culprit = euiStyled.div` `; type ErrorGroupItem = - APIReturnType<'GET /api/apm/services/{serviceName}/errors'>['errorGroups'][0]; + APIReturnType<'GET /internal/apm/services/{serviceName}/errors'>['errorGroups'][0]; interface Props { items: ErrorGroupItem[]; diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx index 97a3c38b6598..a445b0d8522c 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx @@ -44,7 +44,7 @@ export function ErrorGroupOverview() { if (start && end) { return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/errors', + endpoint: 'GET /internal/apm/services/{serviceName}/errors', params: { path: { serviceName, diff --git a/x-pack/plugins/apm/public/components/app/service_dependencies/service_dependencies_breakdown_chart.tsx b/x-pack/plugins/apm/public/components/app/service_dependencies/service_dependencies_breakdown_chart.tsx index 426328a8ce9f..979a2404dfdf 100644 --- a/x-pack/plugins/apm/public/components/app/service_dependencies/service_dependencies_breakdown_chart.tsx +++ b/x-pack/plugins/apm/public/components/app/service_dependencies/service_dependencies_breakdown_chart.tsx @@ -29,7 +29,8 @@ export function ServiceDependenciesBreakdownChart({ const { data, status } = useFetcher( (callApmApi) => { return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/dependencies/breakdown', + endpoint: + 'GET /internal/apm/services/{serviceName}/dependencies/breakdown', params: { path: { serviceName, diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx index 7cd9c7c8e199..aea7c1faab60 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx @@ -64,7 +64,7 @@ function useServicesFetcher() { (callApmApi) => { if (start && end) { return callApmApi({ - endpoint: 'GET /api/apm/services', + endpoint: 'GET /internal/apm/services', params: { query: { environment, @@ -90,7 +90,7 @@ function useServicesFetcher() { (callApmApi) => { if (start && end && mainStatisticsData.items.length) { return callApmApi({ - endpoint: 'GET /api/apm/services/detailed_statistics', + endpoint: 'GET /internal/apm/services/detailed_statistics', params: { query: { environment, diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/__fixtures__/service_api_mock_data.ts b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/__fixtures__/service_api_mock_data.ts index 0f5edb5a4c9c..85e7eeadc748 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/__fixtures__/service_api_mock_data.ts +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/__fixtures__/service_api_mock_data.ts @@ -7,7 +7,7 @@ import { APIReturnType } from '../../../../../services/rest/createCallApmApi'; -type ServiceListAPIResponse = APIReturnType<'GET /api/apm/services'>; +type ServiceListAPIResponse = APIReturnType<'GET /internal/apm/services'>; export const items: ServiceListAPIResponse['items'] = [ { diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx index 9c1893b76f7d..ea65c837a417 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx @@ -43,10 +43,10 @@ import { ServiceLink } from '../../../shared/service_link'; import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; import { HealthBadge } from './HealthBadge'; -type ServiceListAPIResponse = APIReturnType<'GET /api/apm/services'>; +type ServiceListAPIResponse = APIReturnType<'GET /internal/apm/services'>; type Items = ServiceListAPIResponse['items']; type ServicesDetailedStatisticsAPIResponse = - APIReturnType<'GET /api/apm/services/detailed_statistics'>; + APIReturnType<'GET /internal/apm/services/detailed_statistics'>; type ServiceListItem = ValuesType; diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/service_list.test.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/service_list.test.tsx index 75aad2283de0..69ec1e6b1eb9 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/service_list.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/service_list.test.tsx @@ -33,7 +33,7 @@ describe('ServiceList', () => { const callApmApiSpy = getCallApmApiSpy().mockImplementation( ({ endpoint }) => { - if (endpoint === 'GET /api/apm/fallback_to_transactions') { + if (endpoint === 'GET /internal/apm/fallback_to_transactions') { return Promise.resolve({ fallbackToTransactions: false }); } return Promise.reject(`Response for ${endpoint} is not defined`); diff --git a/x-pack/plugins/apm/public/components/app/service_logs/index.tsx b/x-pack/plugins/apm/public/components/app/service_logs/index.tsx index 79818473d26b..bb32919196f8 100644 --- a/x-pack/plugins/apm/public/components/app/service_logs/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_logs/index.tsx @@ -35,7 +35,7 @@ export function ServiceLogs() { (callApmApi) => { if (start && end) { return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/infrastructure', + endpoint: 'GET /internal/apm/services/{serviceName}/infrastructure', params: { path: { serviceName }, query: { @@ -92,7 +92,7 @@ export function ServiceLogs() { } export const getInfrastructureKQLFilter = ( - data?: APIReturnType<'GET /api/apm/services/{serviceName}/infrastructure'> + data?: APIReturnType<'GET /internal/apm/services/{serviceName}/infrastructure'> ) => { const containerIds = data?.serviceInfrastructure?.containerIds ?? []; const hostNames = data?.serviceInfrastructure?.hostNames ?? []; diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx index c01cf4579fdb..c04619338f80 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/backend_contents.tsx @@ -38,7 +38,7 @@ export function BackendContents({ (callApmApi) => { if (backendName) { return callApmApi({ - endpoint: 'GET /api/apm/service-map/backend/{backendName}', + endpoint: 'GET /internal/apm/service-map/backend/{backendName}', params: { path: { backendName }, query: { diff --git a/x-pack/plugins/apm/public/components/app/service_map/Popover/service_contents.tsx b/x-pack/plugins/apm/public/components/app/service_map/Popover/service_contents.tsx index 5eef580793d1..f320123ce072 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/Popover/service_contents.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/Popover/service_contents.tsx @@ -47,7 +47,7 @@ export function ServiceContents({ (callApmApi) => { if (serviceName && start && end) { return callApmApi({ - endpoint: 'GET /api/apm/service-map/service/{serviceName}', + endpoint: 'GET /internal/apm/service-map/service/{serviceName}', params: { path: { serviceName }, query: { environment, start, end }, diff --git a/x-pack/plugins/apm/public/components/app/service_map/index.tsx b/x-pack/plugins/apm/public/components/app/service_map/index.tsx index 7499eb9cd658..75d5837f738c 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/index.tsx @@ -124,7 +124,7 @@ export function ServiceMap({ return callApmApi({ isCachable: false, - endpoint: 'GET /api/apm/service-map', + endpoint: 'GET /internal/apm/service-map', params: { query: { start, diff --git a/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx b/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx index 1f83ad317e96..19527cd08498 100644 --- a/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx @@ -85,7 +85,7 @@ export function ServiceNodeMetrics() { if (start && end) { return callApmApi({ endpoint: - 'GET /api/apm/services/{serviceName}/node/{serviceNodeName}/metadata', + 'GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata', params: { path: { serviceName, serviceNodeName }, query: { diff --git a/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx index bef87891d741..04bb578b0c43 100644 --- a/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx @@ -48,7 +48,7 @@ function ServiceNodeOverview() { return undefined; } return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/serviceNodes', + endpoint: 'GET /internal/apm/services/{serviceName}/serviceNodes', params: { path: { serviceName, diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx index 62cefa5de47f..42a0c68535ef 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx @@ -99,21 +99,21 @@ describe('ServiceOverview', () => { /* eslint-disable @typescript-eslint/naming-convention */ const calls = { - 'GET /api/apm/services/{serviceName}/error_groups/main_statistics': { + 'GET /internal/apm/services/{serviceName}/error_groups/main_statistics': { error_groups: [] as any[], }, - 'GET /api/apm/services/{serviceName}/transactions/groups/main_statistics': + 'GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics': { transactionGroups: [] as any[], totalTransactionGroups: 0, isAggregationAccurate: true, }, - 'GET /api/apm/services/{serviceName}/dependencies': { + 'GET /internal/apm/services/{serviceName}/dependencies': { serviceDependencies: [], }, - 'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics': + 'GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics': [], - 'GET /api/apm/services/{serviceName}/transactions/charts/latency': { + 'GET /internal/apm/services/{serviceName}/transactions/charts/latency': { currentPeriod: { overallAvgDuration: null, latencyTimeseries: [], @@ -123,26 +123,27 @@ describe('ServiceOverview', () => { latencyTimeseries: [], }, }, - 'GET /api/apm/services/{serviceName}/throughput': { + 'GET /internal/apm/services/{serviceName}/throughput': { currentPeriod: [], previousPeriod: [], }, - 'GET /api/apm/services/{serviceName}/transactions/charts/error_rate': { - currentPeriod: { - transactionErrorRate: [], - noHits: true, - average: null, - }, - previousPeriod: { - transactionErrorRate: [], - noHits: true, - average: null, + 'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate': + { + currentPeriod: { + transactionErrorRate: [], + noHits: true, + average: null, + }, + previousPeriod: { + transactionErrorRate: [], + noHits: true, + average: null, + }, }, - }, 'GET /api/apm/services/{serviceName}/annotation/search': { annotations: [], }, - 'GET /api/apm/fallback_to_transactions': { + 'GET /internal/apm/fallback_to_transactions': { fallbackToTransactions: false, }, }; diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx index b035d626c371..b29daa1dd558 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx @@ -59,7 +59,7 @@ export function ServiceOverviewDependenciesTable({ } return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/dependencies', + endpoint: 'GET /internal/apm/services/{serviceName}/dependencies', params: { path: { serviceName }, query: { start, end, environment, numBuckets: 20, offset }, diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_columns.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_columns.tsx index 86f1907365bf..14a8b59cd782 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_columns.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/get_columns.tsx @@ -16,9 +16,9 @@ import { TimestampTooltip } from '../../../shared/TimestampTooltip'; import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; type ErrorGroupMainStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/error_groups/main_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/error_groups/main_statistics'>; type ErrorGroupDetailedStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/error_groups/detailed_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics'>; export function getColumns({ serviceName, diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx index ebf634fd6b18..d71167637803 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx @@ -30,9 +30,9 @@ interface Props { serviceName: string; } type ErrorGroupMainStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/error_groups/main_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/error_groups/main_statistics'>; type ErrorGroupDetailedStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/error_groups/detailed_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics'>; type SortDirection = 'asc' | 'desc'; type SortField = 'name' | 'lastSeen' | 'occurrences'; @@ -97,7 +97,7 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { } return callApmApi({ endpoint: - 'GET /api/apm/services/{serviceName}/error_groups/main_statistics', + 'GET /internal/apm/services/{serviceName}/error_groups/main_statistics', params: { path: { serviceName }, query: { @@ -150,7 +150,7 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { if (requestId && items.length && start && end && transactionType) { return callApmApi({ endpoint: - 'GET /api/apm/services/{serviceName}/error_groups/detailed_statistics', + 'GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics', params: { path: { serviceName }, query: { diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx index ab42d9b80634..74a9a60afd91 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx @@ -28,9 +28,9 @@ interface ServiceOverviewInstancesChartAndTableProps { } type ApiResponseMainStats = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics'>; type ApiResponseDetailedStats = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>; const INITIAL_STATE_MAIN_STATS = { currentPeriodItems: [] as ApiResponseMainStats['currentPeriod'], @@ -100,7 +100,7 @@ export function ServiceOverviewInstancesChartAndTable({ return callApmApi({ endpoint: - 'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics', + 'GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics', params: { path: { serviceName, @@ -181,7 +181,7 @@ export function ServiceOverviewInstancesChartAndTable({ return callApmApi({ endpoint: - 'GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics', + 'GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics', params: { path: { serviceName, diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx index 3a80e0b07532..853ea37112ad 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/get_columns.tsx @@ -33,11 +33,11 @@ import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; import { InstanceActionsMenu } from './instance_actions_menu'; type ServiceInstanceMainStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics'>; type MainStatsServiceInstanceItem = ServiceInstanceMainStatistics['currentPeriod'][0]; type ServiceInstanceDetailedStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>; export function getColumns({ serviceName, diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx index a8a93e8d4473..9084ffdda59f 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/index.tsx @@ -29,11 +29,11 @@ import { useApmParams } from '../../../../hooks/use_apm_params'; import { useBreakpoints } from '../../../../hooks/use_breakpoints'; type ServiceInstanceMainStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics'>; type MainStatsServiceInstanceItem = ServiceInstanceMainStatistics['currentPeriod'][0]; type ServiceInstanceDetailedStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>; export interface TableOptions { pageIndex: number; diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/instance_actions_menu/menu_sections.ts b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/instance_actions_menu/menu_sections.ts index cd7e64ae3966..7e7f30065c95 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/instance_actions_menu/menu_sections.ts +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/instance_actions_menu/menu_sections.ts @@ -17,7 +17,7 @@ import { } from '../../../../shared/transaction_action_menu/sections_helper'; type InstaceDetails = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}'>; function getInfraMetricsQuery(timestamp?: string) { if (!timestamp) { diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/instance_details.test.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/instance_details.test.tsx index 3a0ed21ca980..b8e18eec43e6 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/instance_details.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/instance_details.test.tsx @@ -16,7 +16,7 @@ import { InstanceDetails } from './intance_details'; import * as useInstanceDetailsFetcher from './use_instance_details_fetcher'; type ServiceInstanceDetails = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}'>; describe('InstanceDetails', () => { it('renders loading spinner when data is being fetched', () => { diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/intance_details.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/intance_details.tsx index 7a194039e0d4..fad5628b2192 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/intance_details.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/intance_details.tsx @@ -34,7 +34,7 @@ import { getCloudIcon, getContainerIcon } from '../../../shared/service_icons'; import { useInstanceDetailsFetcher } from './use_instance_details_fetcher'; type ServiceInstanceDetails = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}'>; interface Props { serviceName: string; diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/use_instance_details_fetcher.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/use_instance_details_fetcher.tsx index c1018d6d742f..de833b9b3be1 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/use_instance_details_fetcher.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/use_instance_details_fetcher.tsx @@ -28,7 +28,7 @@ export function useInstanceDetailsFetcher({ } return callApmApi({ endpoint: - 'GET /api/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}', + 'GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}', params: { path: { serviceName, diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx index c3d17b9f18a9..0780c2d27271 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx @@ -69,7 +69,7 @@ export function ServiceOverviewThroughputChart({ (callApmApi) => { if (serviceName && transactionType && start && end) { return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/throughput', + endpoint: 'GET /internal/apm/services/{serviceName}/throughput', params: { path: { serviceName, diff --git a/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx b/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx index 15caa833764b..5342ae8e0db2 100644 --- a/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx @@ -19,7 +19,7 @@ import { ServiceProfilingFlamegraph } from './service_profiling_flamegraph'; import { ServiceProfilingTimeline } from './service_profiling_timeline'; type ApiResponse = - APIReturnType<'GET /api/apm/services/{serviceName}/profiling/timeline'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/profiling/timeline'>; const DEFAULT_DATA: ApiResponse = { profilingTimeline: [] }; export function ServiceProfiling() { @@ -38,7 +38,7 @@ export function ServiceProfiling() { } return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/profiling/timeline', + endpoint: 'GET /internal/apm/services/{serviceName}/profiling/timeline', params: { path: { serviceName }, query: { diff --git a/x-pack/plugins/apm/public/components/app/service_profiling/service_profiling_flamegraph.tsx b/x-pack/plugins/apm/public/components/app/service_profiling/service_profiling_flamegraph.tsx index a2ce4dcf7e83..8626c4a3b061 100644 --- a/x-pack/plugins/apm/public/components/app/service_profiling/service_profiling_flamegraph.tsx +++ b/x-pack/plugins/apm/public/components/app/service_profiling/service_profiling_flamegraph.tsx @@ -147,7 +147,8 @@ export function ServiceProfilingFlamegraph({ } return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/profiling/statistics', + endpoint: + 'GET /internal/apm/services/{serviceName}/profiling/statistics', params: { path: { serviceName, diff --git a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx index 63cd27cba41e..9725df9809ea 100644 --- a/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/trace_overview/index.tsx @@ -16,7 +16,7 @@ import { useFallbackToTransactionsFetcher } from '../../../hooks/use_fallback_to import { AggregatedTransactionsBadge } from '../../shared/aggregated_transactions_badge'; import { useTimeRange } from '../../../hooks/use_time_range'; -type TracesAPIResponse = APIReturnType<'GET /api/apm/traces'>; +type TracesAPIResponse = APIReturnType<'GET /internal/apm/traces'>; const DEFAULT_RESPONSE: TracesAPIResponse = { items: [], }; @@ -35,7 +35,7 @@ export function TraceOverview() { (callApmApi) => { if (start && end) { return callApmApi({ - endpoint: 'GET /api/apm/traces', + endpoint: 'GET /internal/apm/traces', params: { query: { environment, diff --git a/x-pack/plugins/apm/public/components/app/trace_overview/trace_list.tsx b/x-pack/plugins/apm/public/components/app/trace_overview/trace_list.tsx index 7c4e0ffca1f5..0fc25b28b60e 100644 --- a/x-pack/plugins/apm/public/components/app/trace_overview/trace_list.tsx +++ b/x-pack/plugins/apm/public/components/app/trace_overview/trace_list.tsx @@ -20,7 +20,7 @@ import { ImpactBar } from '../../shared/ImpactBar'; import { TransactionDetailLink } from '../../shared/Links/apm/transaction_detail_link'; import { ITableColumn, ManagedTable } from '../../shared/managed_table'; -type TraceGroup = APIReturnType<'GET /api/apm/traces'>['items'][0]; +type TraceGroup = APIReturnType<'GET /internal/apm/traces'>['items'][0]; const StyledTransactionLink = euiStyled(TransactionDetailLink)` font-size: ${({ theme }) => theme.eui.euiFontSizeS}; diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/use_waterfall_fetcher.ts b/x-pack/plugins/apm/public/components/app/transaction_details/use_waterfall_fetcher.ts index 12bb8dbe12e4..e7fbc315522e 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/use_waterfall_fetcher.ts +++ b/x-pack/plugins/apm/public/components/app/transaction_details/use_waterfall_fetcher.ts @@ -36,7 +36,7 @@ export function useWaterfallFetcher() { (callApmApi) => { if (traceId && start && end) { return callApmApi({ - endpoint: 'GET /api/apm/traces/{traceId}', + endpoint: 'GET /internal/apm/traces/{traceId}', params: { path: { traceId }, query: { diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_helpers/waterfall_helpers.ts b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_helpers/waterfall_helpers.ts index 9501ad1839d4..661ba2556d84 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_helpers/waterfall_helpers.ts +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_helpers/waterfall_helpers.ts @@ -12,7 +12,7 @@ import { APMError } from '../../../../../../../../typings/es_schemas/ui/apm_erro import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction'; -type TraceAPIResponse = APIReturnType<'GET /api/apm/traces/{traceId}'>; +type TraceAPIResponse = APIReturnType<'GET /internal/apm/traces/{traceId}'>; interface IWaterfallGroup { [key: string]: IWaterfallSpanOrTransaction[]; diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfallContainer.stories.data.ts b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfallContainer.stories.data.ts index 87150cdfc83a..60285c835bbf 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfallContainer.stories.data.ts +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfallContainer.stories.data.ts @@ -16,7 +16,7 @@ export const location = { hash: '', } as Location; -type TraceAPIResponse = APIReturnType<'GET /api/apm/traces/{traceId}'>; +type TraceAPIResponse = APIReturnType<'GET /internal/apm/traces/{traceId}'>; export const urlParams = { start: '2020-03-22T15:16:38.742Z', diff --git a/x-pack/plugins/apm/public/components/app/transaction_link/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_link/index.tsx index 468a90f6b17d..251daf3d0d03 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_link/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_link/index.tsx @@ -28,7 +28,7 @@ export function TransactionLink() { (callApmApi) => { if (transactionId) { return callApmApi({ - endpoint: 'GET /api/apm/transactions/{transactionId}', + endpoint: 'GET /internal/apm/transactions/{transactionId}', params: { path: { transactionId, diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx index ac0b8a1517b2..14bf7de789c9 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx @@ -49,7 +49,7 @@ export function ApmMainTemplate({ services.observability.navigation.PageTemplate; const { data } = useFetcher((callApmApi) => { - return callApmApi({ endpoint: 'GET /api/apm/has_data' }); + return callApmApi({ endpoint: 'GET /internal/apm/has_data' }); }, []); const noDataConfig = getNoDataConfig({ diff --git a/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx index f6ffc34ecee0..a0cf2a1aec48 100644 --- a/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx @@ -20,7 +20,7 @@ export function ErrorMetadata({ error }: Props) { const { data: errorEvent, status } = useFetcher( (callApmApi) => { return callApmApi({ - endpoint: 'GET /api/apm/event_metadata/{processorEvent}/{id}', + endpoint: 'GET /internal/apm/event_metadata/{processorEvent}/{id}', params: { path: { processorEvent: ProcessorEvent.error, diff --git a/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx index bf5702b4acf3..166440d0975b 100644 --- a/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx @@ -20,7 +20,7 @@ export function SpanMetadata({ span }: Props) { const { data: spanEvent, status } = useFetcher( (callApmApi) => { return callApmApi({ - endpoint: 'GET /api/apm/event_metadata/{processorEvent}/{id}', + endpoint: 'GET /internal/apm/event_metadata/{processorEvent}/{id}', params: { path: { processorEvent: ProcessorEvent.span, diff --git a/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx index 32c0101c73b4..b437173c4b63 100644 --- a/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx @@ -20,7 +20,7 @@ export function TransactionMetadata({ transaction }: Props) { const { data: transactionEvent, status } = useFetcher( (callApmApi) => { return callApmApi({ - endpoint: 'GET /api/apm/event_metadata/{processorEvent}/{id}', + endpoint: 'GET /internal/apm/event_metadata/{processorEvent}/{id}', params: { path: { processorEvent: ProcessorEvent.transaction, diff --git a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/anomaly_detection_setup_link.tsx b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/anomaly_detection_setup_link.tsx index c84cbc9a4b10..970cc886d30b 100644 --- a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/anomaly_detection_setup_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/anomaly_detection_setup_link.tsx @@ -27,7 +27,7 @@ import { APIReturnType } from '../../../services/rest/createCallApmApi'; import { getAPMHref } from '../Links/apm/APMLink'; export type AnomalyDetectionApiResponse = - APIReturnType<'GET /api/apm/settings/anomaly-detection/jobs'>; + APIReturnType<'GET /internal/apm/settings/anomaly-detection/jobs'>; const DEFAULT_DATA = { jobs: [], hasLegacyJobs: false }; diff --git a/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.test.tsx index e63f7fbc79ab..4573e8e5a174 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.test.tsx @@ -30,7 +30,7 @@ import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { getAlertAnnotations } from './get_alert_annotations'; type Alert = ValuesType< - APIReturnType<'GET /api/apm/services/{serviceName}/alerts'>['alerts'] + APIReturnType<'GET /internal/apm/services/{serviceName}/alerts'>['alerts'] >; const euiColorDanger = 'red'; diff --git a/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.tsx b/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.tsx index 39e1ce59de4b..f4c47a9247e7 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/helper/get_alert_annotations.tsx @@ -46,7 +46,7 @@ const ALERT_RULE_TYPE_ID: typeof ALERT_RULE_TYPE_ID_TYPED = const ALERT_RULE_NAME: typeof ALERT_RULE_NAME_TYPED = ALERT_RULE_NAME_NON_TYPED; type Alert = ValuesType< - APIReturnType<'GET /api/apm/services/{serviceName}/alerts'>['alerts'] + APIReturnType<'GET /internal/apm/services/{serviceName}/alerts'>['alerts'] >; function getAlertColor({ diff --git a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.stories.tsx b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.stories.tsx index d39770b68d5b..56138a676a29 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.stories.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.stories.tsx @@ -12,7 +12,7 @@ import { getDurationFormatter } from '../../../../../common/utils/formatters'; import { CustomTooltip } from './custom_tooltip'; type ServiceInstanceMainStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics'>; type MainStatsServiceInstanceItem = ServiceInstanceMainStatistics['currentPeriod'][0]; diff --git a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.tsx b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.tsx index 7289ffddd0ea..2974f909615f 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/custom_tooltip.tsx @@ -18,7 +18,7 @@ import { import { useTheme } from '../../../../hooks/use_theme'; type ServiceInstanceMainStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics'>; type MainStatsServiceInstanceItem = ServiceInstanceMainStatistics['currentPeriod'][0]; diff --git a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/index.tsx index 5c974664eb1d..30bf5908ef37 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/index.tsx @@ -37,7 +37,7 @@ import { getResponseTimeTickFormatter } from '../transaction_charts/helper'; import { CustomTooltip } from './custom_tooltip'; type ApiResponseMainStats = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics'>; export interface InstancesLatencyDistributionChartProps { height: number; diff --git a/x-pack/plugins/apm/public/components/shared/charts/latency_chart/latency_chart.stories.tsx b/x-pack/plugins/apm/public/components/shared/charts/latency_chart/latency_chart.stories.tsx index 511b605e3c22..ad51e66f1959 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/latency_chart/latency_chart.stories.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/latency_chart/latency_chart.stories.tsx @@ -42,8 +42,8 @@ import { import { LatencyChart } from './'; interface Args { - alertsResponse: APIReturnType<'GET /api/apm/services/{serviceName}/alerts'>; - latencyChartResponse: APIReturnType<'GET /api/apm/services/{serviceName}/transactions/charts/latency'>; + alertsResponse: APIReturnType<'GET /internal/apm/services/{serviceName}/alerts'>; + latencyChartResponse: APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/latency'>; } export default { @@ -70,7 +70,7 @@ export default { basePath: { prepend: () => {} }, get: (endpoint: string) => { switch (endpoint) { - case `/api/apm/services/${serviceName}/transactions/charts/latency`: + case `/internal/apm/services/${serviceName}/transactions/charts/latency`: return latencyChartResponse; default: return {}; diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts index e814459fbf51..079b8455de7a 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/use_transaction_breakdown.ts @@ -39,7 +39,7 @@ export function useTransactionBreakdown({ if (serviceName && start && end && transactionType) { return callApmApi({ endpoint: - 'GET /api/apm/services/{serviceName}/transaction/charts/breakdown', + 'GET /internal/apm/services/{serviceName}/transaction/charts/breakdown', params: { path: { serviceName }, query: { diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx index 88788c7c3d39..95b73a5276b8 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx @@ -36,7 +36,7 @@ interface Props { } type ErrorRate = - APIReturnType<'GET /api/apm/services/{serviceName}/transactions/charts/error_rate'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate'>; const INITIAL_STATE: ErrorRate = { currentPeriod: { @@ -82,7 +82,7 @@ export function TransactionErrorRateChart({ if (transactionType && serviceName && start && end) { return callApmApi({ endpoint: - 'GET /api/apm/services/{serviceName}/transactions/charts/error_rate', + 'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate', params: { path: { serviceName, diff --git a/x-pack/plugins/apm/public/components/shared/service_icons/cloud_details.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/cloud_details.tsx index 2c706fa863a9..eded3ff9fd1c 100644 --- a/x-pack/plugins/apm/public/components/shared/service_icons/cloud_details.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/cloud_details.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { APIReturnType } from '../../../services/rest/createCallApmApi'; type ServiceDetailsReturnType = - APIReturnType<'GET /api/apm/services/{serviceName}/metadata/details'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/metadata/details'>; interface Props { cloud: ServiceDetailsReturnType['cloud']; diff --git a/x-pack/plugins/apm/public/components/shared/service_icons/container_details.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/container_details.tsx index 75f7c23a7808..cd2e3a518b3b 100644 --- a/x-pack/plugins/apm/public/components/shared/service_icons/container_details.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/container_details.tsx @@ -13,7 +13,7 @@ import { asInteger } from '../../../../common/utils/formatters'; import { APIReturnType } from '../../../services/rest/createCallApmApi'; type ServiceDetailsReturnType = - APIReturnType<'GET /api/apm/services/{serviceName}/metadata/details'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/metadata/details'>; interface Props { container: ServiceDetailsReturnType['container']; diff --git a/x-pack/plugins/apm/public/components/shared/service_icons/index.test.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/index.test.tsx index bd90cae0277a..b04fe26e1a8b 100644 --- a/x-pack/plugins/apm/public/components/shared/service_icons/index.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/index.test.tsx @@ -186,7 +186,7 @@ describe('ServiceIcons', () => { }; it('Shows loading spinner while fetching data', () => { const apisMockData = { - 'GET /api/apm/services/{serviceName}/metadata/icons': { + 'GET /internal/apm/services/{serviceName}/metadata/icons': { data: { agentName: 'java', containerType: 'Kubernetes', @@ -195,7 +195,7 @@ describe('ServiceIcons', () => { status: fetcherHook.FETCH_STATUS.SUCCESS, refetch: jest.fn(), }, - 'GET /api/apm/services/{serviceName}/metadata/details': { + 'GET /internal/apm/services/{serviceName}/metadata/details': { data: undefined, status: fetcherHook.FETCH_STATUS.LOADING, refetch: jest.fn(), @@ -228,7 +228,7 @@ describe('ServiceIcons', () => { it('shows service content', () => { const apisMockData = { - 'GET /api/apm/services/{serviceName}/metadata/icons': { + 'GET /internal/apm/services/{serviceName}/metadata/icons': { data: { agentName: 'java', containerType: 'Kubernetes', @@ -237,7 +237,7 @@ describe('ServiceIcons', () => { status: fetcherHook.FETCH_STATUS.SUCCESS, refetch: jest.fn(), }, - 'GET /api/apm/services/{serviceName}/metadata/details': { + 'GET /internal/apm/services/{serviceName}/metadata/details': { data: { service: { versions: ['v1.0.0'] } }, status: fetcherHook.FETCH_STATUS.SUCCESS, refetch: jest.fn(), diff --git a/x-pack/plugins/apm/public/components/shared/service_icons/index.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/index.tsx index 780c3b0222f1..77639ea1f6d7 100644 --- a/x-pack/plugins/apm/public/components/shared/service_icons/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/index.tsx @@ -71,7 +71,7 @@ export function ServiceIcons({ start, end, serviceName }: Props) { (callApmApi) => { if (serviceName && start && end) { return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/metadata/icons', + endpoint: 'GET /internal/apm/services/{serviceName}/metadata/icons', params: { path: { serviceName }, query: { start, end }, @@ -87,7 +87,7 @@ export function ServiceIcons({ start, end, serviceName }: Props) { if (selectedIconPopover && serviceName && start && end) { return callApmApi({ isCachable: true, - endpoint: 'GET /api/apm/services/{serviceName}/metadata/details', + endpoint: 'GET /internal/apm/services/{serviceName}/metadata/details', params: { path: { serviceName }, query: { start, end }, diff --git a/x-pack/plugins/apm/public/components/shared/service_icons/service_details.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/service_details.tsx index daf253f6bc0a..9ab8b5ba8a1f 100644 --- a/x-pack/plugins/apm/public/components/shared/service_icons/service_details.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/service_details.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { APIReturnType } from '../../../services/rest/createCallApmApi'; type ServiceDetailsReturnType = - APIReturnType<'GET /api/apm/services/{serviceName}/metadata/details'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/metadata/details'>; interface Props { service: ServiceDetailsReturnType['service']; diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/index.tsx b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/index.tsx index bc234b88a08e..ea247d1907a2 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/custom_link_menu_section/index.tsx @@ -61,7 +61,7 @@ export function CustomLinkMenuSection({ (callApmApi) => callApmApi({ isCachable: false, - endpoint: 'GET /api/apm/settings/custom_links', + endpoint: 'GET /internal/apm/settings/custom_links', params: { query: convertFiltersToQuery(filters) }, }), [filters] diff --git a/x-pack/plugins/apm/public/components/shared/transactions_table/get_columns.tsx b/x-pack/plugins/apm/public/components/shared/transactions_table/get_columns.tsx index b11a2994f1ca..18e9beb2c879 100644 --- a/x-pack/plugins/apm/public/components/shared/transactions_table/get_columns.tsx +++ b/x-pack/plugins/apm/public/components/shared/transactions_table/get_columns.tsx @@ -28,13 +28,13 @@ import { TruncateWithTooltip } from '../truncate_with_tooltip'; import { getLatencyColumnLabel } from './get_latency_column_label'; type TransactionGroupMainStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/transactions/groups/main_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics'>; type ServiceTransactionGroupItem = ValuesType< TransactionGroupMainStatistics['transactionGroups'] >; type TransactionGroupDetailedStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/transactions/groups/detailed_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics'>; export function getColumns({ serviceName, diff --git a/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx b/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx index 6f91684de0ee..d6d955b8ef5a 100644 --- a/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/transactions_table/index.tsx @@ -30,7 +30,7 @@ import { ElasticDocsLink } from '../Links/ElasticDocsLink'; import { useBreakpoints } from '../../../hooks/use_breakpoints'; type ApiResponse = - APIReturnType<'GET /api/apm/services/{serviceName}/transactions/groups/main_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics'>; interface InitialState { requestId: string; @@ -116,7 +116,7 @@ export function TransactionsTable({ } return callApmApi({ endpoint: - 'GET /api/apm/services/{serviceName}/transactions/groups/main_statistics', + 'GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics', params: { path: { serviceName }, query: { @@ -189,7 +189,7 @@ export function TransactionsTable({ ) { return callApmApi({ endpoint: - 'GET /api/apm/services/{serviceName}/transactions/groups/detailed_statistics', + 'GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics', params: { path: { serviceName }, query: { diff --git a/x-pack/plugins/apm/public/context/anomaly_detection_jobs/anomaly_detection_jobs_context.tsx b/x-pack/plugins/apm/public/context/anomaly_detection_jobs/anomaly_detection_jobs_context.tsx index b8d6feda826a..bf9f2941fa2f 100644 --- a/x-pack/plugins/apm/public/context/anomaly_detection_jobs/anomaly_detection_jobs_context.tsx +++ b/x-pack/plugins/apm/public/context/anomaly_detection_jobs/anomaly_detection_jobs_context.tsx @@ -10,7 +10,7 @@ import { FETCH_STATUS, useFetcher } from '../../hooks/use_fetcher'; import { APIReturnType } from '../../services/rest/createCallApmApi'; export interface AnomalyDetectionJobsContextValue { - anomalyDetectionJobsData?: APIReturnType<'GET /api/apm/settings/anomaly-detection/jobs'>; + anomalyDetectionJobsData?: APIReturnType<'GET /internal/apm/settings/anomaly-detection/jobs'>; anomalyDetectionJobsStatus: FETCH_STATUS; anomalyDetectionJobsRefetch: () => void; } @@ -30,7 +30,7 @@ export function AnomalyDetectionJobsContextProvider({ const { data, status } = useFetcher( (callApmApi) => callApmApi({ - endpoint: `GET /api/apm/settings/anomaly-detection/jobs`, + endpoint: `GET /internal/apm/settings/anomaly-detection/jobs`, }), [fetchId], // eslint-disable-line react-hooks/exhaustive-deps { showToastOnError: false } diff --git a/x-pack/plugins/apm/public/context/apm_backend/apm_backend_context.tsx b/x-pack/plugins/apm/public/context/apm_backend/apm_backend_context.tsx index d6cc139e72b6..6093f05c2cb0 100644 --- a/x-pack/plugins/apm/public/context/apm_backend/apm_backend_context.tsx +++ b/x-pack/plugins/apm/public/context/apm_backend/apm_backend_context.tsx @@ -15,7 +15,7 @@ export const ApmBackendContext = createContext< | { backendName: string; metadata: { - data?: APIReturnType<'GET /api/apm/backends/{backendName}/metadata'>; + data?: APIReturnType<'GET /internal/apm/backends/{backendName}/metadata'>; status?: FETCH_STATUS; }; } @@ -41,7 +41,7 @@ export function ApmBackendContextProvider({ } return callApmApi({ - endpoint: 'GET /api/apm/backends/{backendName}/metadata', + endpoint: 'GET /internal/apm/backends/{backendName}/metadata', params: { path: { backendName, diff --git a/x-pack/plugins/apm/public/context/apm_service/apm_service_context.tsx b/x-pack/plugins/apm/public/context/apm_service/apm_service_context.tsx index d6b052a5dc88..9d207eee2fba 100644 --- a/x-pack/plugins/apm/public/context/apm_service/apm_service_context.tsx +++ b/x-pack/plugins/apm/public/context/apm_service/apm_service_context.tsx @@ -20,7 +20,7 @@ import { useApmParams } from '../../hooks/use_apm_params'; import { useTimeRange } from '../../hooks/use_time_range'; export type APMServiceAlert = ValuesType< - APIReturnType<'GET /api/apm/services/{serviceName}/alerts'>['alerts'] + APIReturnType<'GET /internal/apm/services/{serviceName}/alerts'>['alerts'] >; export const APMServiceContext = createContext<{ diff --git a/x-pack/plugins/apm/public/context/apm_service/use_service_agent_fetcher.ts b/x-pack/plugins/apm/public/context/apm_service/use_service_agent_fetcher.ts index e30fd962e5a9..0202ff3a1012 100644 --- a/x-pack/plugins/apm/public/context/apm_service/use_service_agent_fetcher.ts +++ b/x-pack/plugins/apm/public/context/apm_service/use_service_agent_fetcher.ts @@ -29,7 +29,7 @@ export function useServiceAgentFetcher({ (callApmApi) => { if (serviceName) { return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/agent', + endpoint: 'GET /internal/apm/services/{serviceName}/agent', params: { path: { serviceName }, query: { start, end }, diff --git a/x-pack/plugins/apm/public/context/apm_service/use_service_alerts_fetcher.tsx b/x-pack/plugins/apm/public/context/apm_service/use_service_alerts_fetcher.tsx index 8491cdfb3f5e..d0e1edc775a8 100644 --- a/x-pack/plugins/apm/public/context/apm_service/use_service_alerts_fetcher.tsx +++ b/x-pack/plugins/apm/public/context/apm_service/use_service_alerts_fetcher.tsx @@ -41,7 +41,7 @@ export function useServiceAlertsFetcher({ } return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/alerts', + endpoint: 'GET /internal/apm/services/{serviceName}/alerts', params: { path: { serviceName, diff --git a/x-pack/plugins/apm/public/context/apm_service/use_service_transaction_types_fetcher.tsx b/x-pack/plugins/apm/public/context/apm_service/use_service_transaction_types_fetcher.tsx index c2e81cb0c92a..e81a2d956bf3 100644 --- a/x-pack/plugins/apm/public/context/apm_service/use_service_transaction_types_fetcher.tsx +++ b/x-pack/plugins/apm/public/context/apm_service/use_service_transaction_types_fetcher.tsx @@ -22,7 +22,8 @@ export function useServiceTransactionTypesFetcher({ (callApmApi) => { if (serviceName && start && end) { return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/transaction_types', + endpoint: + 'GET /internal/apm/services/{serviceName}/transaction_types', params: { path: { serviceName }, query: { start, end }, diff --git a/x-pack/plugins/apm/public/hooks/use_dynamic_index_pattern.ts b/x-pack/plugins/apm/public/hooks/use_dynamic_index_pattern.ts index 9c637dc1336a..dc19af0a98ce 100644 --- a/x-pack/plugins/apm/public/hooks/use_dynamic_index_pattern.ts +++ b/x-pack/plugins/apm/public/hooks/use_dynamic_index_pattern.ts @@ -10,7 +10,7 @@ import { useFetcher } from './use_fetcher'; export function useDynamicIndexPatternFetcher() { const { data, status } = useFetcher((callApmApi) => { return callApmApi({ - endpoint: 'GET /api/apm/index_pattern/dynamic', + endpoint: 'GET /internal/apm/index_pattern/dynamic', isCachable: true, }); }, []); diff --git a/x-pack/plugins/apm/public/hooks/use_environments_fetcher.tsx b/x-pack/plugins/apm/public/hooks/use_environments_fetcher.tsx index d6352b561c64..e37677e806e8 100644 --- a/x-pack/plugins/apm/public/hooks/use_environments_fetcher.tsx +++ b/x-pack/plugins/apm/public/hooks/use_environments_fetcher.tsx @@ -38,7 +38,7 @@ export function useEnvironmentsFetcher({ (callApmApi) => { if (start && end) { return callApmApi({ - endpoint: 'GET /api/apm/environments', + endpoint: 'GET /internal/apm/environments', params: { query: { start, diff --git a/x-pack/plugins/apm/public/hooks/use_error_group_distribution_fetcher.tsx b/x-pack/plugins/apm/public/hooks/use_error_group_distribution_fetcher.tsx index 683a545cdd12..120cbba952f3 100644 --- a/x-pack/plugins/apm/public/hooks/use_error_group_distribution_fetcher.tsx +++ b/x-pack/plugins/apm/public/hooks/use_error_group_distribution_fetcher.tsx @@ -30,7 +30,8 @@ export function useErrorGroupDistributionFetcher({ (callApmApi) => { if (start && end) { return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/errors/distribution', + endpoint: + 'GET /internal/apm/services/{serviceName}/errors/distribution', params: { path: { serviceName }, query: { diff --git a/x-pack/plugins/apm/public/hooks/use_fallback_to_transactions_fetcher.tsx b/x-pack/plugins/apm/public/hooks/use_fallback_to_transactions_fetcher.tsx index 35122bfef174..3dfb02cec5fb 100644 --- a/x-pack/plugins/apm/public/hooks/use_fallback_to_transactions_fetcher.tsx +++ b/x-pack/plugins/apm/public/hooks/use_fallback_to_transactions_fetcher.tsx @@ -19,7 +19,7 @@ export function useFallbackToTransactionsFetcher({ kuery }: { kuery: string }) { const { data = { fallbackToTransactions: false } } = useFetcher( (callApmApi) => { return callApmApi({ - endpoint: 'GET /api/apm/fallback_to_transactions', + endpoint: 'GET /internal/apm/fallback_to_transactions', params: { query: { kuery, start, end }, }, diff --git a/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts index 82338d45a413..ef360698192e 100644 --- a/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_service_metric_charts_fetcher.ts @@ -40,7 +40,7 @@ export function useServiceMetricChartsFetcher({ (callApmApi) => { if (serviceName && start && end && agentName) { return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/metrics/charts', + endpoint: 'GET /internal/apm/services/{serviceName}/metrics/charts', params: { path: { serviceName }, query: { diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts index 660408700b4d..d44ec853a242 100644 --- a/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_transaction_latency_chart_fetcher.ts @@ -57,7 +57,7 @@ export function useTransactionLatencyChartsFetcher({ ) { return callApmApi({ endpoint: - 'GET /api/apm/services/{serviceName}/transactions/charts/latency', + 'GET /internal/apm/services/{serviceName}/transactions/charts/latency', params: { path: { serviceName }, query: { diff --git a/x-pack/plugins/apm/public/hooks/use_transaction_trace_samples_fetcher.ts b/x-pack/plugins/apm/public/hooks/use_transaction_trace_samples_fetcher.ts index 5279c7ce143f..48c2d555fd43 100644 --- a/x-pack/plugins/apm/public/hooks/use_transaction_trace_samples_fetcher.ts +++ b/x-pack/plugins/apm/public/hooks/use_transaction_trace_samples_fetcher.ts @@ -51,7 +51,7 @@ export function useTransactionTraceSamplesFetcher({ if (serviceName && start && end && transactionType && transactionName) { const response = await callApmApi({ endpoint: - 'GET /api/apm/services/{serviceName}/transactions/traces/samples', + 'GET /internal/apm/services/{serviceName}/transactions/traces/samples', params: { path: { serviceName, diff --git a/x-pack/plugins/apm/public/selectors/latency_chart_selectors.ts b/x-pack/plugins/apm/public/selectors/latency_chart_selectors.ts index 1fb4eb51dd77..bb6814308934 100644 --- a/x-pack/plugins/apm/public/selectors/latency_chart_selectors.ts +++ b/x-pack/plugins/apm/public/selectors/latency_chart_selectors.ts @@ -14,7 +14,7 @@ import { APMChartSpec, Coordinate } from '../../typings/timeseries'; import { APIReturnType } from '../services/rest/createCallApmApi'; export type LatencyChartsResponse = - APIReturnType<'GET /api/apm/services/{serviceName}/transactions/charts/latency'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/latency'>; export interface LatencyChartData { currentPeriod?: APMChartSpec; diff --git a/x-pack/plugins/apm/public/services/callApi.test.ts b/x-pack/plugins/apm/public/services/callApi.test.ts index 82be0bbf5dfc..85e58bc81a93 100644 --- a/x-pack/plugins/apm/public/services/callApi.test.ts +++ b/x-pack/plugins/apm/public/services/callApi.test.ts @@ -41,11 +41,14 @@ describe('callApi', () => { }); it('should add debug param for APM endpoints', async () => { - await callApi(core, { pathname: `/api/apm/status/server` }); - - expect(core.http.get).toHaveBeenCalledWith('/api/apm/status/server', { - query: { _inspect: true }, - }); + await callApi(core, { pathname: `/internal/apm/status/server` }); + + expect(core.http.get).toHaveBeenCalledWith( + '/internal/apm/status/server', + { + query: { _inspect: true }, + } + ); }); it('should not add debug param for non-APM endpoints', async () => { diff --git a/x-pack/plugins/apm/public/services/callApmApi.test.ts b/x-pack/plugins/apm/public/services/callApmApi.test.ts index 56146c49fc57..b1d31586c2ce 100644 --- a/x-pack/plugins/apm/public/services/callApmApi.test.ts +++ b/x-pack/plugins/apm/public/services/callApmApi.test.ts @@ -24,7 +24,7 @@ describe('callApmApi', () => { it('should format the pathname with the given path params', async () => { await callApmApi({ - endpoint: 'GET /api/apm/{param1}/to/{param2}', + endpoint: 'GET /internal/apm/{param1}/to/{param2}', params: { path: { param1: 'foo', @@ -36,14 +36,14 @@ describe('callApmApi', () => { expect(callApi).toHaveBeenCalledWith( {}, expect.objectContaining({ - pathname: '/api/apm/foo/to/bar', + pathname: '/internal/apm/foo/to/bar', }) ); }); it('should add the query parameters to the options object', async () => { await callApmApi({ - endpoint: 'GET /api/apm', + endpoint: 'GET /internal/apm', params: { query: { foo: 'bar', @@ -55,7 +55,7 @@ describe('callApmApi', () => { expect(callApi).toHaveBeenCalledWith( {}, expect.objectContaining({ - pathname: '/api/apm', + pathname: '/internal/apm', query: { foo: 'bar', bar: 'foo', @@ -66,7 +66,7 @@ describe('callApmApi', () => { it('should stringify the body and add it to the options object', async () => { await callApmApi({ - endpoint: 'POST /api/apm', + endpoint: 'POST /internal/apm', params: { body: { foo: 'bar', @@ -78,7 +78,7 @@ describe('callApmApi', () => { expect(callApi).toHaveBeenCalledWith( {}, expect.objectContaining({ - pathname: '/api/apm', + pathname: '/internal/apm', method: 'post', body: { foo: 'bar', diff --git a/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts b/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts index 1b95c88a5fdc..f26fd85bde96 100644 --- a/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts +++ b/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts @@ -17,7 +17,7 @@ export const fetchObservabilityOverviewPageData = async ({ bucketSize, }: FetchDataParams): Promise => { const data = await callApmApi({ - endpoint: 'GET /api/apm/observability_overview', + endpoint: 'GET /internal/apm/observability_overview', signal: null, params: { query: { @@ -52,7 +52,7 @@ export const fetchObservabilityOverviewPageData = async ({ export async function getHasData() { return await callApmApi({ - endpoint: 'GET /api/apm/observability_overview/has_data', + endpoint: 'GET /internal/apm/observability_overview/has_data', signal: null, }); } diff --git a/x-pack/plugins/apm/public/services/rest/callApi.ts b/x-pack/plugins/apm/public/services/rest/callApi.ts index 53ddbed90413..9b6d0c383e9a 100644 --- a/x-pack/plugins/apm/public/services/rest/callApi.ts +++ b/x-pack/plugins/apm/public/services/rest/callApi.ts @@ -18,7 +18,7 @@ function fetchOptionsWithDebug( ) { const debugEnabled = inspectableEsQueriesEnabled && - startsWith(fetchOptions.pathname, '/api/apm'); + startsWith(fetchOptions.pathname, '/internal/apm'); const { body, ...rest } = fetchOptions; diff --git a/x-pack/plugins/apm/public/services/rest/index_pattern.ts b/x-pack/plugins/apm/public/services/rest/index_pattern.ts index 0bb5839759f6..f250c514000b 100644 --- a/x-pack/plugins/apm/public/services/rest/index_pattern.ts +++ b/x-pack/plugins/apm/public/services/rest/index_pattern.ts @@ -9,7 +9,7 @@ import { callApmApi } from './createCallApmApi'; export const createStaticIndexPattern = async () => { return await callApmApi({ - endpoint: 'POST /api/apm/index_pattern/static', + endpoint: 'POST /internal/apm/index_pattern/static', signal: null, }); }; diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/config_agent.stories.tsx b/x-pack/plugins/apm/public/tutorial/config_agent/config_agent.stories.tsx index 166839390bc2..7bebd89dc002 100644 --- a/x-pack/plugins/apm/public/tutorial/config_agent/config_agent.stories.tsx +++ b/x-pack/plugins/apm/public/tutorial/config_agent/config_agent.stories.tsx @@ -12,7 +12,7 @@ import { POLICY_ELASTIC_AGENT_ON_CLOUD } from '../../../common/fleet'; import TutorialConfigAgent from './'; import { APIReturnType } from '../../services/rest/createCallApmApi'; -export type APIResponseType = APIReturnType<'GET /api/apm/fleet/agents'>; +export type APIResponseType = APIReturnType<'GET /internal/apm/fleet/agents'>; interface Args { apmAgent: string; diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/get_policy_options.test.ts b/x-pack/plugins/apm/public/tutorial/config_agent/get_policy_options.test.ts index c6dc7265f3d3..79eefeab85c6 100644 --- a/x-pack/plugins/apm/public/tutorial/config_agent/get_policy_options.test.ts +++ b/x-pack/plugins/apm/public/tutorial/config_agent/get_policy_options.test.ts @@ -7,7 +7,7 @@ import { getPolicyOptions } from './get_policy_options'; import { APIReturnType } from '../../services/rest/createCallApmApi'; -type APIResponseType = APIReturnType<'GET /api/apm/fleet/agents'>; +type APIResponseType = APIReturnType<'GET /internal/apm/fleet/agents'>; const policyElasticAgentOnCloudAgent = { id: 'policy-elastic-agent-on-cloud', diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/index.tsx b/x-pack/plugins/apm/public/tutorial/config_agent/index.tsx index cfb51b4cd3b6..5ff1fd7f4211 100644 --- a/x-pack/plugins/apm/public/tutorial/config_agent/index.tsx +++ b/x-pack/plugins/apm/public/tutorial/config_agent/index.tsx @@ -21,7 +21,7 @@ import { CopyCommands } from './copy_commands'; import { getPolicyOptions, PolicyOption } from './get_policy_options'; import { PolicySelector } from './policy_selector'; -export type APIResponseType = APIReturnType<'GET /api/apm/fleet/agents'>; +export type APIResponseType = APIReturnType<'GET /internal/apm/fleet/agents'>; const CentralizedContainer = styled.div` display: flex; @@ -90,7 +90,7 @@ function TutorialConfigAgent({ async function fetchData() { setIsLoading(true); try { - const response = await http.get('/api/apm/fleet/agents'); + const response = await http.get('/internal/apm/fleet/agents'); if (response) { setData(response as APIResponseType); } diff --git a/x-pack/plugins/apm/public/tutorial/tutorial_apm_fleet_check.ts b/x-pack/plugins/apm/public/tutorial/tutorial_apm_fleet_check.ts index 8db8614d606a..8502689724d0 100644 --- a/x-pack/plugins/apm/public/tutorial/tutorial_apm_fleet_check.ts +++ b/x-pack/plugins/apm/public/tutorial/tutorial_apm_fleet_check.ts @@ -9,7 +9,7 @@ import { callApmApi } from '../services/rest/createCallApmApi'; export async function hasFleetApmIntegrations() { try { const { hasData = false } = await callApmApi({ - endpoint: 'GET /api/apm/fleet/has_data', + endpoint: 'GET /internal/apm/fleet/has_data', signal: null, }); return hasData; diff --git a/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx index 175d6797bb29..f0d6dc72b72a 100644 --- a/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx +++ b/x-pack/plugins/apm/public/tutorial/tutorial_fleet_instructions/index.tsx @@ -29,7 +29,7 @@ const CentralizedContainer = styled.div` align-items: center; `; -type APIResponseType = APIReturnType<'GET /api/apm/fleet/has_data'>; +type APIResponseType = APIReturnType<'GET /internal/apm/fleet/has_data'>; function TutorialFleetInstructions({ http, basePath, isDarkTheme }: Props) { const [data, setData] = useState(); @@ -39,7 +39,7 @@ function TutorialFleetInstructions({ http, basePath, isDarkTheme }: Props) { async function fetchData() { setIsLoading(true); try { - const response = await http.get('/api/apm/fleet/has_data'); + const response = await http.get('/internal/apm/fleet/has_data'); setData(response as APIResponseType); } catch (e) { setIsLoading(false); diff --git a/x-pack/plugins/apm/server/routes/alerts/chart_preview.ts b/x-pack/plugins/apm/server/routes/alerts/chart_preview.ts index 012cf304aa3c..23a794bb7976 100644 --- a/x-pack/plugins/apm/server/routes/alerts/chart_preview.ts +++ b/x-pack/plugins/apm/server/routes/alerts/chart_preview.ts @@ -34,7 +34,7 @@ const alertParamsRt = t.intersection([ export type AlertParams = t.TypeOf; const transactionErrorRateChartPreview = createApmServerRoute({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_rate', + endpoint: 'GET /internal/apm/alerts/chart_preview/transaction_error_rate', params: t.type({ query: alertParamsRt }), options: { tags: ['access:apm'] }, handler: async (resources) => { @@ -52,7 +52,7 @@ const transactionErrorRateChartPreview = createApmServerRoute({ }); const transactionErrorCountChartPreview = createApmServerRoute({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_count', + endpoint: 'GET /internal/apm/alerts/chart_preview/transaction_error_count', params: t.type({ query: alertParamsRt }), options: { tags: ['access:apm'] }, handler: async (resources) => { @@ -71,7 +71,7 @@ const transactionErrorCountChartPreview = createApmServerRoute({ }); const transactionDurationChartPreview = createApmServerRoute({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_duration', + endpoint: 'GET /internal/apm/alerts/chart_preview/transaction_duration', params: t.type({ query: alertParamsRt }), options: { tags: ['access:apm'] }, handler: async (resources) => { diff --git a/x-pack/plugins/apm/server/routes/backends.ts b/x-pack/plugins/apm/server/routes/backends.ts index 7aabfb329913..feb4ca8bb978 100644 --- a/x-pack/plugins/apm/server/routes/backends.ts +++ b/x-pack/plugins/apm/server/routes/backends.ts @@ -19,7 +19,7 @@ import { getThroughputChartsForBackend } from '../lib/backends/get_throughput_ch import { getErrorRateChartsForBackend } from '../lib/backends/get_error_rate_charts_for_backend'; const topBackendsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/backends/top_backends', + endpoint: 'GET /internal/apm/backends/top_backends', params: t.intersection([ t.type({ query: t.intersection([ @@ -65,7 +65,7 @@ const topBackendsRoute = createApmServerRoute({ }); const upstreamServicesForBackendRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/backends/{backendName}/upstream_services', + endpoint: 'GET /internal/apm/backends/{backendName}/upstream_services', params: t.intersection([ t.type({ path: t.type({ @@ -121,7 +121,7 @@ const upstreamServicesForBackendRoute = createApmServerRoute({ }); const backendMetadataRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/backends/{backendName}/metadata', + endpoint: 'GET /internal/apm/backends/{backendName}/metadata', params: t.type({ path: t.type({ backendName: t.string, @@ -150,7 +150,7 @@ const backendMetadataRoute = createApmServerRoute({ }); const backendLatencyChartsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/backends/{backendName}/charts/latency', + endpoint: 'GET /internal/apm/backends/{backendName}/charts/latency', params: t.type({ path: t.type({ backendName: t.string, @@ -193,7 +193,7 @@ const backendLatencyChartsRoute = createApmServerRoute({ }); const backendThroughputChartsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/backends/{backendName}/charts/throughput', + endpoint: 'GET /internal/apm/backends/{backendName}/charts/throughput', params: t.type({ path: t.type({ backendName: t.string, @@ -236,7 +236,7 @@ const backendThroughputChartsRoute = createApmServerRoute({ }); const backendFailedTransactionRateChartsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/backends/{backendName}/charts/error_rate', + endpoint: 'GET /internal/apm/backends/{backendName}/charts/error_rate', params: t.type({ path: t.type({ backendName: t.string, diff --git a/x-pack/plugins/apm/server/routes/environments.ts b/x-pack/plugins/apm/server/routes/environments.ts index 4b00320009e2..59e75f6f9c34 100644 --- a/x-pack/plugins/apm/server/routes/environments.ts +++ b/x-pack/plugins/apm/server/routes/environments.ts @@ -15,7 +15,7 @@ import { createApmServerRoute } from './create_apm_server_route'; import { createApmServerRouteRepository } from './create_apm_server_route_repository'; const environmentsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/environments', + endpoint: 'GET /internal/apm/environments', params: t.type({ query: t.intersection([ t.partial({ diff --git a/x-pack/plugins/apm/server/routes/errors.ts b/x-pack/plugins/apm/server/routes/errors.ts index 9a4199e31949..0864276b67fe 100644 --- a/x-pack/plugins/apm/server/routes/errors.ts +++ b/x-pack/plugins/apm/server/routes/errors.ts @@ -15,7 +15,7 @@ import { environmentRt, kueryRt, rangeRt } from './default_api_types'; import { createApmServerRouteRepository } from './create_apm_server_route_repository'; const errorsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/errors', + endpoint: 'GET /internal/apm/services/{serviceName}/errors', params: t.type({ path: t.type({ serviceName: t.string, @@ -54,7 +54,7 @@ const errorsRoute = createApmServerRoute({ }); const errorGroupsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/errors/{groupId}', + endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}', params: t.type({ path: t.type({ serviceName: t.string, @@ -82,7 +82,7 @@ const errorGroupsRoute = createApmServerRoute({ }); const errorDistributionRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/errors/distribution', + endpoint: 'GET /internal/apm/services/{serviceName}/errors/distribution', params: t.type({ path: t.type({ serviceName: t.string, diff --git a/x-pack/plugins/apm/server/routes/event_metadata.ts b/x-pack/plugins/apm/server/routes/event_metadata.ts index 8970ab8ffdee..00241d2ef1c6 100644 --- a/x-pack/plugins/apm/server/routes/event_metadata.ts +++ b/x-pack/plugins/apm/server/routes/event_metadata.ts @@ -13,7 +13,7 @@ import { processorEventRt } from '../../common/processor_event'; import { setupRequest } from '../lib/helpers/setup_request'; const eventMetadataRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/event_metadata/{processorEvent}/{id}', + endpoint: 'GET /internal/apm/event_metadata/{processorEvent}/{id}', options: { tags: ['access:apm'] }, params: t.type({ path: t.type({ diff --git a/x-pack/plugins/apm/server/routes/fallback_to_transactions.ts b/x-pack/plugins/apm/server/routes/fallback_to_transactions.ts index 8f30b9c08982..ba74cc0b7a88 100644 --- a/x-pack/plugins/apm/server/routes/fallback_to_transactions.ts +++ b/x-pack/plugins/apm/server/routes/fallback_to_transactions.ts @@ -13,7 +13,7 @@ import { createApmServerRouteRepository } from './create_apm_server_route_reposi import { kueryRt, rangeRt } from './default_api_types'; const fallbackToTransactionsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/fallback_to_transactions', + endpoint: 'GET /internal/apm/fallback_to_transactions', params: t.partial({ query: t.intersection([kueryRt, t.partial(rangeRt.props)]), }), diff --git a/x-pack/plugins/apm/server/routes/fleet.ts b/x-pack/plugins/apm/server/routes/fleet.ts index 5bfbfef5e680..d8097228df0d 100644 --- a/x-pack/plugins/apm/server/routes/fleet.ts +++ b/x-pack/plugins/apm/server/routes/fleet.ts @@ -28,7 +28,7 @@ import { createApmServerRoute } from './create_apm_server_route'; import { createApmServerRouteRepository } from './create_apm_server_route_repository'; const hasFleetDataRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/fleet/has_data', + endpoint: 'GET /internal/apm/fleet/has_data', options: { tags: [] }, handler: async ({ core, plugins }) => { const fleetPluginStart = await plugins.fleet?.start(); @@ -44,7 +44,7 @@ const hasFleetDataRoute = createApmServerRoute({ }); const fleetAgentsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/fleet/agents', + endpoint: 'GET /internal/apm/fleet/agents', options: { tags: [] }, handler: async ({ core, plugins }) => { const cloudSetup = plugins.cloud?.setup; @@ -92,7 +92,7 @@ const fleetAgentsRoute = createApmServerRoute({ }); const saveApmServerSchemaRoute = createApmServerRoute({ - endpoint: 'POST /api/apm/fleet/apm_server_schema', + endpoint: 'POST /internal/apm/fleet/apm_server_schema', options: { tags: ['access:apm', 'access:apm_write'] }, params: t.type({ body: t.type({ @@ -113,7 +113,7 @@ const saveApmServerSchemaRoute = createApmServerRoute({ }); const getUnsupportedApmServerSchemaRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/fleet/apm_server_schema/unsupported', + endpoint: 'GET /internal/apm/fleet/apm_server_schema/unsupported', options: { tags: ['access:apm'] }, handler: async (resources) => { const { context } = resources; @@ -125,7 +125,7 @@ const getUnsupportedApmServerSchemaRoute = createApmServerRoute({ }); const getMigrationCheckRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/fleet/migration_check', + endpoint: 'GET /internal/apm/fleet/migration_check', options: { tags: ['access:apm'] }, handler: async (resources) => { const { plugins, context, config, request } = resources; @@ -154,7 +154,7 @@ const getMigrationCheckRoute = createApmServerRoute({ }); const createCloudApmPackagePolicyRoute = createApmServerRoute({ - endpoint: 'POST /api/apm/fleet/cloud_apm_package_policy', + endpoint: 'POST /internal/apm/fleet/cloud_apm_package_policy', options: { tags: ['access:apm', 'access:apm_write'] }, handler: async (resources) => { const { plugins, context, config, request, logger } = resources; diff --git a/x-pack/plugins/apm/server/routes/historical_data/index.ts b/x-pack/plugins/apm/server/routes/historical_data/index.ts index be3fd29d14b9..fb67dc4f5b64 100644 --- a/x-pack/plugins/apm/server/routes/historical_data/index.ts +++ b/x-pack/plugins/apm/server/routes/historical_data/index.ts @@ -11,7 +11,7 @@ import { createApmServerRouteRepository } from '../create_apm_server_route_repos import { hasHistoricalAgentData } from './has_historical_agent_data'; const hasDataRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/has_data', + endpoint: 'GET /internal/apm/has_data', options: { tags: ['access:apm'] }, handler: async (resources) => { const setup = await setupRequest(resources); diff --git a/x-pack/plugins/apm/server/routes/index_pattern.ts b/x-pack/plugins/apm/server/routes/index_pattern.ts index c957e828bf12..a996636ae56a 100644 --- a/x-pack/plugins/apm/server/routes/index_pattern.ts +++ b/x-pack/plugins/apm/server/routes/index_pattern.ts @@ -12,7 +12,7 @@ import { getDynamicIndexPattern } from '../lib/index_pattern/get_dynamic_index_p import { createApmServerRoute } from './create_apm_server_route'; const staticIndexPatternRoute = createApmServerRoute({ - endpoint: 'POST /api/apm/index_pattern/static', + endpoint: 'POST /internal/apm/index_pattern/static', options: { tags: ['access:apm'] }, handler: async (resources) => { const { @@ -43,7 +43,7 @@ const staticIndexPatternRoute = createApmServerRoute({ }); const dynamicIndexPatternRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/index_pattern/dynamic', + endpoint: 'GET /internal/apm/index_pattern/dynamic', options: { tags: ['access:apm'] }, handler: async ({ context, config, logger }) => { const dynamicIndexPattern = await getDynamicIndexPattern({ diff --git a/x-pack/plugins/apm/server/routes/metrics.ts b/x-pack/plugins/apm/server/routes/metrics.ts index ea4878016652..8b6b16a26f1d 100644 --- a/x-pack/plugins/apm/server/routes/metrics.ts +++ b/x-pack/plugins/apm/server/routes/metrics.ts @@ -13,7 +13,7 @@ import { createApmServerRouteRepository } from './create_apm_server_route_reposi import { environmentRt, kueryRt, rangeRt } from './default_api_types'; const metricsChartsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/metrics/charts', + endpoint: 'GET /internal/apm/services/{serviceName}/metrics/charts', params: t.type({ path: t.type({ serviceName: t.string, diff --git a/x-pack/plugins/apm/server/routes/observability_overview.ts b/x-pack/plugins/apm/server/routes/observability_overview.ts index feaa6b580dac..a99291ff32bb 100644 --- a/x-pack/plugins/apm/server/routes/observability_overview.ts +++ b/x-pack/plugins/apm/server/routes/observability_overview.ts @@ -17,7 +17,7 @@ import { createApmServerRouteRepository } from './create_apm_server_route_reposi import { createApmServerRoute } from './create_apm_server_route'; const observabilityOverviewHasDataRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/observability_overview/has_data', + endpoint: 'GET /internal/apm/observability_overview/has_data', options: { tags: ['access:apm'] }, handler: async (resources) => { const setup = await setupRequest(resources); @@ -26,7 +26,7 @@ const observabilityOverviewHasDataRoute = createApmServerRoute({ }); const observabilityOverviewRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/observability_overview', + endpoint: 'GET /internal/apm/observability_overview', params: t.type({ query: t.intersection([rangeRt, t.type({ bucketSize: t.string })]), }), diff --git a/x-pack/plugins/apm/server/routes/service_map.ts b/x-pack/plugins/apm/server/routes/service_map.ts index 8fb3abe99e36..f9062ac13e04 100644 --- a/x-pack/plugins/apm/server/routes/service_map.ts +++ b/x-pack/plugins/apm/server/routes/service_map.ts @@ -20,7 +20,7 @@ import { createApmServerRouteRepository } from './create_apm_server_route_reposi import { environmentRt, rangeRt } from './default_api_types'; const serviceMapRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/service-map', + endpoint: 'GET /internal/apm/service-map', params: t.type({ query: t.intersection([ t.partial({ @@ -70,7 +70,7 @@ const serviceMapRoute = createApmServerRoute({ }); const serviceMapServiceNodeRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/service-map/service/{serviceName}', + endpoint: 'GET /internal/apm/service-map/service/{serviceName}', params: t.type({ path: t.type({ serviceName: t.string, @@ -114,7 +114,7 @@ const serviceMapServiceNodeRoute = createApmServerRoute({ }); const serviceMapBackendNodeRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/service-map/backend/{backendName}', + endpoint: 'GET /internal/apm/service-map/backend/{backendName}', params: t.type({ path: t.type({ backendName: t.string, diff --git a/x-pack/plugins/apm/server/routes/service_nodes.ts b/x-pack/plugins/apm/server/routes/service_nodes.ts index 4bd1c9359972..2081b794f8ab 100644 --- a/x-pack/plugins/apm/server/routes/service_nodes.ts +++ b/x-pack/plugins/apm/server/routes/service_nodes.ts @@ -14,7 +14,7 @@ import { rangeRt, kueryRt } from './default_api_types'; import { environmentRt } from '../../common/environment_rt'; const serviceNodesRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/serviceNodes', + endpoint: 'GET /internal/apm/services/{serviceName}/serviceNodes', params: t.type({ path: t.type({ serviceName: t.string, diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts index a612575d16ff..d4af7315b9c2 100644 --- a/x-pack/plugins/apm/server/routes/services.ts +++ b/x-pack/plugins/apm/server/routes/services.ts @@ -48,7 +48,7 @@ import { getServiceDependenciesBreakdown } from '../lib/services/get_service_dep import { getBucketSizeForAggregatedTransactions } from '../lib/helpers/get_bucket_size_for_aggregated_transactions'; const servicesRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services', + endpoint: 'GET /internal/apm/services', params: t.type({ query: t.intersection([environmentRt, kueryRt, rangeRt]), }), @@ -75,7 +75,7 @@ const servicesRoute = createApmServerRoute({ }); const servicesDetailedStatisticsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/detailed_statistics', + endpoint: 'GET /internal/apm/services/detailed_statistics', params: t.type({ query: t.intersection([ environmentRt, @@ -116,7 +116,7 @@ const servicesDetailedStatisticsRoute = createApmServerRoute({ }); const serviceMetadataDetailsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/metadata/details', + endpoint: 'GET /internal/apm/services/{serviceName}/metadata/details', params: t.type({ path: t.type({ serviceName: t.string }), query: rangeRt, @@ -147,7 +147,7 @@ const serviceMetadataDetailsRoute = createApmServerRoute({ }); const serviceMetadataIconsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/metadata/icons', + endpoint: 'GET /internal/apm/services/{serviceName}/metadata/icons', params: t.type({ path: t.type({ serviceName: t.string }), query: rangeRt, @@ -178,7 +178,7 @@ const serviceMetadataIconsRoute = createApmServerRoute({ }); const serviceAgentRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/agent', + endpoint: 'GET /internal/apm/services/{serviceName}/agent', params: t.type({ path: t.type({ serviceName: t.string, @@ -211,7 +211,7 @@ const serviceAgentRoute = createApmServerRoute({ }); const serviceTransactionTypesRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/transaction_types', + endpoint: 'GET /internal/apm/services/{serviceName}/transaction_types', params: t.type({ path: t.type({ serviceName: t.string, @@ -243,7 +243,7 @@ const serviceTransactionTypesRoute = createApmServerRoute({ const serviceNodeMetadataRoute = createApmServerRoute({ endpoint: - 'GET /api/apm/services/{serviceName}/node/{serviceNodeName}/metadata', + 'GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata', params: t.type({ path: t.type({ serviceName: t.string, @@ -383,7 +383,8 @@ const serviceAnnotationsCreateRoute = createApmServerRoute({ }); const serviceErrorGroupsMainStatisticsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/error_groups/main_statistics', + endpoint: + 'GET /internal/apm/services/{serviceName}/error_groups/main_statistics', params: t.type({ path: t.type({ serviceName: t.string, @@ -420,7 +421,7 @@ const serviceErrorGroupsMainStatisticsRoute = createApmServerRoute({ const serviceErrorGroupsDetailedStatisticsRoute = createApmServerRoute({ endpoint: - 'GET /api/apm/services/{serviceName}/error_groups/detailed_statistics', + 'GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics', params: t.type({ path: t.type({ serviceName: t.string, @@ -474,7 +475,7 @@ const serviceErrorGroupsDetailedStatisticsRoute = createApmServerRoute({ }); const serviceThroughputRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/throughput', + endpoint: 'GET /internal/apm/services/{serviceName}/throughput', params: t.type({ path: t.type({ serviceName: t.string, @@ -556,7 +557,7 @@ const serviceThroughputRoute = createApmServerRoute({ const serviceInstancesMainStatisticsRoute = createApmServerRoute({ endpoint: - 'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics', + 'GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics', params: t.type({ path: t.type({ serviceName: t.string, @@ -630,7 +631,7 @@ const serviceInstancesMainStatisticsRoute = createApmServerRoute({ const serviceInstancesDetailedStatisticsRoute = createApmServerRoute({ endpoint: - 'GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics', + 'GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics', params: t.type({ path: t.type({ serviceName: t.string, @@ -693,7 +694,7 @@ const serviceInstancesDetailedStatisticsRoute = createApmServerRoute({ export const serviceInstancesMetadataDetails = createApmServerRoute({ endpoint: - 'GET /api/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}', + 'GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}', params: t.type({ path: t.type({ serviceName: t.string, @@ -718,7 +719,7 @@ export const serviceInstancesMetadataDetails = createApmServerRoute({ }); export const serviceDependenciesRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/dependencies', + endpoint: 'GET /internal/apm/services/{serviceName}/dependencies', params: t.type({ path: t.type({ serviceName: t.string, @@ -773,7 +774,7 @@ export const serviceDependenciesRoute = createApmServerRoute({ }); export const serviceDependenciesBreakdownRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/dependencies/breakdown', + endpoint: 'GET /internal/apm/services/{serviceName}/dependencies/breakdown', params: t.type({ path: t.type({ serviceName: t.string, @@ -805,7 +806,7 @@ export const serviceDependenciesBreakdownRoute = createApmServerRoute({ }); const serviceProfilingTimelineRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/profiling/timeline', + endpoint: 'GET /internal/apm/services/{serviceName}/profiling/timeline', params: t.type({ path: t.type({ serviceName: t.string, @@ -837,7 +838,7 @@ const serviceProfilingTimelineRoute = createApmServerRoute({ }); const serviceProfilingStatisticsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/profiling/statistics', + endpoint: 'GET /internal/apm/services/{serviceName}/profiling/statistics', params: t.type({ path: t.type({ serviceName: t.string, @@ -886,7 +887,7 @@ const serviceProfilingStatisticsRoute = createApmServerRoute({ }); const serviceAlertsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/alerts', + endpoint: 'GET /internal/apm/services/{serviceName}/alerts', params: t.type({ path: t.type({ serviceName: t.string, @@ -922,7 +923,7 @@ const serviceAlertsRoute = createApmServerRoute({ }); const serviceInfrastructureRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/infrastructure', + endpoint: 'GET /internal/apm/services/{serviceName}/infrastructure', params: t.type({ path: t.type({ serviceName: t.string, diff --git a/x-pack/plugins/apm/server/routes/settings/anomaly_detection.ts b/x-pack/plugins/apm/server/routes/settings/anomaly_detection.ts index c7e45eb8c32e..78db4e0c14b3 100644 --- a/x-pack/plugins/apm/server/routes/settings/anomaly_detection.ts +++ b/x-pack/plugins/apm/server/routes/settings/anomaly_detection.ts @@ -23,7 +23,7 @@ import { createApmServerRouteRepository } from '../create_apm_server_route_repos // get ML anomaly detection jobs for each environment const anomalyDetectionJobsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/settings/anomaly-detection/jobs', + endpoint: 'GET /internal/apm/settings/anomaly-detection/jobs', options: { tags: ['access:apm', 'access:ml:canGetJobs'], }, @@ -51,7 +51,7 @@ const anomalyDetectionJobsRoute = createApmServerRoute({ // create new ML anomaly detection jobs for each given environment const createAnomalyDetectionJobsRoute = createApmServerRoute({ - endpoint: 'POST /api/apm/settings/anomaly-detection/jobs', + endpoint: 'POST /internal/apm/settings/anomaly-detection/jobs', options: { tags: ['access:apm', 'access:apm_write', 'access:ml:canCreateJob'], }, @@ -83,7 +83,7 @@ const createAnomalyDetectionJobsRoute = createApmServerRoute({ // get all available environments to create anomaly detection jobs for const anomalyDetectionEnvironmentsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/settings/anomaly-detection/environments', + endpoint: 'GET /internal/apm/settings/anomaly-detection/environments', options: { tags: ['access:apm'] }, handler: async (resources) => { const setup = await setupRequest(resources); diff --git a/x-pack/plugins/apm/server/routes/settings/apm_indices.ts b/x-pack/plugins/apm/server/routes/settings/apm_indices.ts index 003471aa89f3..1cba5f972c27 100644 --- a/x-pack/plugins/apm/server/routes/settings/apm_indices.ts +++ b/x-pack/plugins/apm/server/routes/settings/apm_indices.ts @@ -16,7 +16,7 @@ import { saveApmIndices } from '../../lib/settings/apm_indices/save_apm_indices' // get list of apm indices and values const apmIndexSettingsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/settings/apm-index-settings', + endpoint: 'GET /internal/apm/settings/apm-index-settings', options: { tags: ['access:apm'] }, handler: async ({ config, context }) => { const apmIndexSettings = await getApmIndexSettings({ config, context }); @@ -26,7 +26,7 @@ const apmIndexSettingsRoute = createApmServerRoute({ // get apm indices configuration object const apmIndicesRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/settings/apm-indices', + endpoint: 'GET /internal/apm/settings/apm-indices', options: { tags: ['access:apm'] }, handler: async (resources) => { const { context, config } = resources; @@ -39,7 +39,7 @@ const apmIndicesRoute = createApmServerRoute({ // save ui indices const saveApmIndicesRoute = createApmServerRoute({ - endpoint: 'POST /api/apm/settings/apm-indices/save', + endpoint: 'POST /internal/apm/settings/apm-indices/save', options: { tags: ['access:apm', 'access:apm_write'], }, diff --git a/x-pack/plugins/apm/server/routes/settings/custom_link.ts b/x-pack/plugins/apm/server/routes/settings/custom_link.ts index c9c5d236c14f..af880898176b 100644 --- a/x-pack/plugins/apm/server/routes/settings/custom_link.ts +++ b/x-pack/plugins/apm/server/routes/settings/custom_link.ts @@ -25,7 +25,7 @@ import { createApmServerRoute } from '../create_apm_server_route'; import { createApmServerRouteRepository } from '../create_apm_server_route_repository'; const customLinkTransactionRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/settings/custom_links/transaction', + endpoint: 'GET /internal/apm/settings/custom_links/transaction', options: { tags: ['access:apm'] }, params: t.partial({ query: filterOptionsRt, @@ -41,7 +41,7 @@ const customLinkTransactionRoute = createApmServerRoute({ }); const listCustomLinksRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/settings/custom_links', + endpoint: 'GET /internal/apm/settings/custom_links', options: { tags: ['access:apm'] }, params: t.partial({ query: filterOptionsRt, @@ -63,7 +63,7 @@ const listCustomLinksRoute = createApmServerRoute({ }); const createCustomLinkRoute = createApmServerRoute({ - endpoint: 'POST /api/apm/settings/custom_links', + endpoint: 'POST /internal/apm/settings/custom_links', params: t.type({ body: payloadRt, }), @@ -86,7 +86,7 @@ const createCustomLinkRoute = createApmServerRoute({ }); const updateCustomLinkRoute = createApmServerRoute({ - endpoint: 'PUT /api/apm/settings/custom_links/{id}', + endpoint: 'PUT /internal/apm/settings/custom_links/{id}', params: t.type({ path: t.type({ id: t.string, @@ -116,7 +116,7 @@ const updateCustomLinkRoute = createApmServerRoute({ }); const deleteCustomLinkRoute = createApmServerRoute({ - endpoint: 'DELETE /api/apm/settings/custom_links/{id}', + endpoint: 'DELETE /internal/apm/settings/custom_links/{id}', params: t.type({ path: t.type({ id: t.string, diff --git a/x-pack/plugins/apm/server/routes/traces.ts b/x-pack/plugins/apm/server/routes/traces.ts index 52aa507bb38b..a71b7eefeed3 100644 --- a/x-pack/plugins/apm/server/routes/traces.ts +++ b/x-pack/plugins/apm/server/routes/traces.ts @@ -17,7 +17,7 @@ import { createApmServerRouteRepository } from './create_apm_server_route_reposi import { getTransaction } from '../lib/transactions/get_transaction'; const tracesRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/traces', + endpoint: 'GET /internal/apm/traces', params: t.type({ query: t.intersection([environmentRt, kueryRt, rangeRt]), }), @@ -41,7 +41,7 @@ const tracesRoute = createApmServerRoute({ }); const tracesByIdRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/traces/{traceId}', + endpoint: 'GET /internal/apm/traces/{traceId}', params: t.type({ path: t.type({ traceId: t.string, @@ -60,7 +60,7 @@ const tracesByIdRoute = createApmServerRoute({ }); const rootTransactionByTraceIdRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/traces/{traceId}/root_transaction', + endpoint: 'GET /internal/apm/traces/{traceId}/root_transaction', params: t.type({ path: t.type({ traceId: t.string, @@ -76,7 +76,7 @@ const rootTransactionByTraceIdRoute = createApmServerRoute({ }); const transactionByIdRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/transactions/{transactionId}', + endpoint: 'GET /internal/apm/transactions/{transactionId}', params: t.type({ path: t.type({ transactionId: t.string, diff --git a/x-pack/plugins/apm/server/routes/transactions.ts b/x-pack/plugins/apm/server/routes/transactions.ts index a8d5c1169909..0e24d64d8c6c 100644 --- a/x-pack/plugins/apm/server/routes/transactions.ts +++ b/x-pack/plugins/apm/server/routes/transactions.ts @@ -31,7 +31,7 @@ import { const transactionGroupsMainStatisticsRoute = createApmServerRoute({ endpoint: - 'GET /api/apm/services/{serviceName}/transactions/groups/main_statistics', + 'GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics', params: t.type({ path: t.type({ serviceName: t.string }), query: t.intersection([ @@ -85,7 +85,7 @@ const transactionGroupsMainStatisticsRoute = createApmServerRoute({ const transactionGroupsDetailedStatisticsRoute = createApmServerRoute({ endpoint: - 'GET /api/apm/services/{serviceName}/transactions/groups/detailed_statistics', + 'GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics', params: t.type({ path: t.type({ serviceName: t.string }), query: t.intersection([ @@ -150,7 +150,8 @@ const transactionGroupsDetailedStatisticsRoute = createApmServerRoute({ }); const transactionLatencyChartsRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/transactions/charts/latency', + endpoint: + 'GET /internal/apm/services/{serviceName}/transactions/charts/latency', params: t.type({ path: t.type({ serviceName: t.string, @@ -227,7 +228,8 @@ const transactionLatencyChartsRoute = createApmServerRoute({ }); const transactionTraceSamplesRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/transactions/traces/samples', + endpoint: + 'GET /internal/apm/services/{serviceName}/transactions/traces/samples', params: t.type({ path: t.type({ serviceName: t.string, @@ -284,7 +286,8 @@ const transactionTraceSamplesRoute = createApmServerRoute({ }); const transactionChartsBreakdownRoute = createApmServerRoute({ - endpoint: 'GET /api/apm/services/{serviceName}/transaction/charts/breakdown', + endpoint: + 'GET /internal/apm/services/{serviceName}/transaction/charts/breakdown', params: t.type({ path: t.type({ serviceName: t.string, @@ -321,7 +324,7 @@ const transactionChartsBreakdownRoute = createApmServerRoute({ const transactionChartsErrorRateRoute = createApmServerRoute({ endpoint: - 'GET /api/apm/services/{serviceName}/transactions/charts/error_rate', + 'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate', params: t.type({ path: t.type({ serviceName: t.string, diff --git a/x-pack/plugins/rule_registry/server/scripts/get_alerts_index.sh b/x-pack/plugins/rule_registry/server/scripts/get_alerts_index.sh index bfa74aa016f0..addd2d4ab619 100755 --- a/x-pack/plugins/rule_registry/server/scripts/get_alerts_index.sh +++ b/x-pack/plugins/rule_registry/server/scripts/get_alerts_index.sh @@ -20,4 +20,4 @@ curl -v -k \ -u $USER:changeme \ -X GET "${KIBANA_URL}${SPACE_URL}/internal/rac/alerts/index" | jq . -# -X GET "${KIBANA_URL}${SPACE_URL}/api/apm/settings/apm-alerts-as-data-indices" | jq . +# -X GET "${KIBANA_URL}${SPACE_URL}/internal/apm/settings/apm-alerts-as-data-indices" | jq . diff --git a/x-pack/test/apm_api_integration/tests/alerts/chart_preview.ts b/x-pack/test/apm_api_integration/tests/alerts/chart_preview.ts index d3e5b0823348..621ed5dcfd8d 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/chart_preview.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/chart_preview.ts @@ -33,7 +33,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('transaction_error_rate (without data)', async () => { const options = getOptions(); const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_rate', + endpoint: 'GET /internal/apm/alerts/chart_preview/transaction_error_rate', ...options, }); @@ -46,7 +46,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { options.params.query.transactionType = undefined; const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_count', + endpoint: 'GET /internal/apm/alerts/chart_preview/transaction_error_count', ...options, }); @@ -58,7 +58,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const options = getOptions(); const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_duration', + endpoint: 'GET /internal/apm/alerts/chart_preview/transaction_duration', ...options, }); @@ -71,7 +71,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('transaction_error_rate (with data)', async () => { const options = getOptions(); const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_rate', + endpoint: 'GET /internal/apm/alerts/chart_preview/transaction_error_rate', ...options, }); @@ -88,7 +88,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { options.params.query.transactionType = undefined; const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_count', + endpoint: 'GET /internal/apm/alerts/chart_preview/transaction_error_count', ...options, }); @@ -104,7 +104,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const options = getOptions(); const response = await apmApiClient.readUser({ ...options, - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_duration', + endpoint: 'GET /internal/apm/alerts/chart_preview/transaction_duration', }); expect(response.status).to.be(200); diff --git a/x-pack/test/apm_api_integration/tests/feature_controls.ts b/x-pack/test/apm_api_integration/tests/feature_controls.ts index 18fcf4fef5fe..ef3ba5d61fb5 100644 --- a/x-pack/test/apm_api_integration/tests/feature_controls.ts +++ b/x-pack/test/apm_api_integration/tests/feature_controls.ts @@ -44,87 +44,89 @@ export default function featureControlsTests({ getService }: FtrProviderContext) { // this doubles as a smoke test for the _inspect query parameter req: { - url: `/api/apm/services/foo/errors?start=${start}&end=${end}&_inspect=true&environment=ENVIRONMENT_ALL&kuery=`, + url: `/internal/apm/services/foo/errors?start=${start}&end=${end}&_inspect=true&environment=ENVIRONMENT_ALL&kuery=`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/errors/bar?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=`, + url: `/internal/apm/services/foo/errors/bar?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/errors/distribution?start=${start}&end=${end}&groupId=bar&environment=ENVIRONMENT_ALL&kuery=`, + url: `/internal/apm/services/foo/errors/distribution?start=${start}&end=${end}&groupId=bar&environment=ENVIRONMENT_ALL&kuery=`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/errors/distribution?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=`, + url: `/internal/apm/services/foo/errors/distribution?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/metrics/charts?start=${start}&end=${end}&agentName=cool-agent&environment=ENVIRONMENT_ALL&kuery=`, + url: `/internal/apm/services/foo/metrics/charts?start=${start}&end=${end}&agentName=cool-agent&environment=ENVIRONMENT_ALL&kuery=`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=`, + url: `/internal/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/agent?start=${start}&end=${end}`, + url: `/internal/apm/services/foo/agent?start=${start}&end=${end}`, }, expectForbidden: expect403, expectResponse: expect200, }, { - req: { url: `/api/apm/services/foo/transaction_types?start=${start}&end=${end}` }, + req: { url: `/internal/apm/services/foo/transaction_types?start=${start}&end=${end}` }, expectForbidden: expect403, expectResponse: expect200, }, { - req: { url: `/api/apm/traces?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` }, + req: { + url: `/internal/apm/traces?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=`, + }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/traces/foo?start=${start}&end=${end}`, + url: `/internal/apm/traces/foo?start=${start}&end=${end}`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&kuery=`, + url: `/internal/apm/services/foo/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&kuery=`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&transactionName=baz&kuery=`, + url: `/internal/apm/services/foo/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=bar&latencyAggregationType=avg&transactionName=baz&kuery=`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/transactions/traces/samples?start=${start}&end=${end}&transactionType=bar&transactionName=baz&environment=ENVIRONMENT_ALL&kuery=`, + url: `/internal/apm/services/foo/transactions/traces/samples?start=${start}&end=${end}&transactionType=bar&transactionName=baz&environment=ENVIRONMENT_ALL&kuery=`, }, expectForbidden: expect403, expectResponse: expect200, @@ -147,21 +149,21 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }, { req: { - url: `/api/apm/settings/custom_links/transaction`, + url: `/internal/apm/settings/custom_links/transaction`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/metadata/details?start=${start}&end=${end}`, + url: `/internal/apm/services/foo/metadata/details?start=${start}&end=${end}`, }, expectForbidden: expect403, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/metadata/icons?start=${start}&end=${end}`, + url: `/internal/apm/services/foo/metadata/icons?start=${start}&end=${end}`, }, expectForbidden: expect403, expectResponse: expect200, diff --git a/x-pack/test/apm_api_integration/tests/historical_data/has_data.ts b/x-pack/test/apm_api_integration/tests/historical_data/has_data.ts index ec3f0e5e7f36..70048c3d3921 100644 --- a/x-pack/test/apm_api_integration/tests/historical_data/has_data.ts +++ b/x-pack/test/apm_api_integration/tests/historical_data/has_data.ts @@ -18,7 +18,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { { config: 'basic', archives: [] }, () => { it('handles the empty state', async () => { - const response = await supertest.get(`/api/apm/has_data`); + const response = await supertest.get(`/internal/apm/has_data`); expect(response.status).to.be(200); expect(response.body.hasData).to.be(false); @@ -31,7 +31,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { { config: 'basic', archives: [archiveName] }, () => { it('returns hasData: true', async () => { - const response = await supertest.get(`/api/apm/has_data`); + const response = await supertest.get(`/internal/apm/has_data`); expect(response.status).to.be(200); expect(response.body.hasData).to.be(true); diff --git a/x-pack/test/apm_api_integration/tests/index.ts b/x-pack/test/apm_api_integration/tests/index.ts index efe159b36e3d..49f1ae91c628 100644 --- a/x-pack/test/apm_api_integration/tests/index.ts +++ b/x-pack/test/apm_api_integration/tests/index.ts @@ -150,7 +150,7 @@ export default function apmApiIntegrationTests(providerContext: FtrProviderConte describe('traces/top_traces', function () { loadTestFile(require.resolve('./traces/top_traces')); }); - describe('/api/apm/traces/{traceId}', function () { + describe('/internal/apm/traces/{traceId}', function () { loadTestFile(require.resolve('./traces/trace_by_id')); }); diff --git a/x-pack/test/apm_api_integration/tests/inspect/inspect.ts b/x-pack/test/apm_api_integration/tests/inspect/inspect.ts index 95805f4ef452..75b5a02fff80 100644 --- a/x-pack/test/apm_api_integration/tests/inspect/inspect.ts +++ b/x-pack/test/apm_api_integration/tests/inspect/inspect.ts @@ -21,7 +21,7 @@ export default function customLinksTests({ getService }: FtrProviderContext) { describe('when omitting `_inspect` query param', () => { it('returns response without `_inspect`', async () => { const { status, body } = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/environments', + endpoint: 'GET /internal/apm/environments', params: { query: { start: metadata.start, @@ -39,7 +39,7 @@ export default function customLinksTests({ getService }: FtrProviderContext) { describe('elasticsearch calls made with end-user auth are returned', () => { it('for environments', async () => { const { status, body } = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/environments', + endpoint: 'GET /internal/apm/environments', params: { query: { start: metadata.start, @@ -67,7 +67,7 @@ export default function customLinksTests({ getService }: FtrProviderContext) { describe('elasticsearch calls made with internal user are not return', () => { it('for custom links', async () => { const { status, body } = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/settings/custom_links', + endpoint: 'GET /internal/apm/settings/custom_links', params: { query: { 'service.name': 'opbeans-node', diff --git a/x-pack/test/apm_api_integration/tests/metadata/event_metadata.ts b/x-pack/test/apm_api_integration/tests/metadata/event_metadata.ts index d979f0bad1ec..fb98cc9a6abd 100644 --- a/x-pack/test/apm_api_integration/tests/metadata/event_metadata.ts +++ b/x-pack/test/apm_api_integration/tests/metadata/event_metadata.ts @@ -40,7 +40,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const id = await getLastDocId(ProcessorEvent.transaction); const { body } = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/event_metadata/{processorEvent}/{id}', + endpoint: 'GET /internal/apm/event_metadata/{processorEvent}/{id}', params: { path: { processorEvent: ProcessorEvent.transaction, @@ -70,7 +70,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const id = await getLastDocId(ProcessorEvent.error); const { body } = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/event_metadata/{processorEvent}/{id}', + endpoint: 'GET /internal/apm/event_metadata/{processorEvent}/{id}', params: { path: { processorEvent: ProcessorEvent.error, @@ -100,7 +100,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const id = await getLastDocId(ProcessorEvent.span); const { body } = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/event_metadata/{processorEvent}/{id}', + endpoint: 'GET /internal/apm/event_metadata/{processorEvent}/{id}', params: { path: { processorEvent: ProcessorEvent.span, diff --git a/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts b/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts index a3e02984a16d..8d3a18a44f02 100644 --- a/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts +++ b/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts @@ -33,7 +33,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let chartsResponse: ChartResponse; before(async () => { chartsResponse = await supertest.get( - `/api/apm/services/opbeans-node/metrics/charts?start=${start}&end=${end}&agentName=${agentName}&kuery=&environment=ENVIRONMENT_ALL` + `/internal/apm/services/opbeans-node/metrics/charts?start=${start}&end=${end}&agentName=${agentName}&kuery=&environment=ENVIRONMENT_ALL` ); }); it('contains CPU usage and System memory usage chart data', async () => { @@ -121,7 +121,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let chartsResponse: ChartResponse; before(async () => { chartsResponse = await supertest.get( - `/api/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&agentName=${agentName}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&agentName=${agentName}&environment=ENVIRONMENT_ALL&kuery=` ); }); @@ -410,7 +410,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const end = encodeURIComponent('2020-09-08T15:05:00.000Z'); const chartsResponse: ChartResponse = await supertest.get( - `/api/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&agentName=${agentName}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&agentName=${agentName}&environment=ENVIRONMENT_ALL&kuery=` ); const systemMemoryUsageChart = chartsResponse.body.charts.find( diff --git a/x-pack/test/apm_api_integration/tests/observability_overview/has_data.ts b/x-pack/test/apm_api_integration/tests/observability_overview/has_data.ts index 1b0f8fdcf873..90a01c472e13 100644 --- a/x-pack/test/apm_api_integration/tests/observability_overview/has_data.ts +++ b/x-pack/test/apm_api_integration/tests/observability_overview/has_data.ts @@ -19,7 +19,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { it('returns false when there is no data', async () => { const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/observability_overview/has_data', + endpoint: 'GET /internal/apm/observability_overview/has_data', }); expect(response.status).to.be(200); expect(response.body.hasData).to.eql(false); @@ -33,7 +33,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { it('returns false when there is only onboarding data', async () => { const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/observability_overview/has_data', + endpoint: 'GET /internal/apm/observability_overview/has_data', }); expect(response.status).to.be(200); expect(response.body.hasData).to.eql(false); @@ -47,7 +47,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { it('returns true when there is at least one document on transaction, error or metrics indices', async () => { const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/observability_overview/has_data', + endpoint: 'GET /internal/apm/observability_overview/has_data', }); expect(response.status).to.be(200); expect(response.body.hasData).to.eql(true); diff --git a/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.ts b/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.ts index 76a157d72cc6..b463db81e6c9 100644 --- a/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.ts +++ b/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.ts @@ -28,7 +28,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('when data is not loaded', () => { it('handles the empty state', async () => { const response = await supertest.get( - `/api/apm/observability_overview?start=${start}&end=${end}&bucketSize=${bucketSize}` + `/internal/apm/observability_overview?start=${start}&end=${end}&bucketSize=${bucketSize}` ); expect(response.status).to.be(200); @@ -45,7 +45,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { it('returns the service count and transaction coordinates', async () => { const response = await supertest.get( - `/api/apm/observability_overview?start=${start}&end=${end}&bucketSize=${bucketSize}` + `/internal/apm/observability_overview?start=${start}&end=${end}&bucketSize=${bucketSize}` ); expect(response.status).to.be(200); diff --git a/x-pack/test/apm_api_integration/tests/service_maps/__snapshots__/service_maps.snap b/x-pack/test/apm_api_integration/tests/service_maps/__snapshots__/service_maps.snap index c66609b8a8a9..9e32f311e8d1 100644 --- a/x-pack/test/apm_api_integration/tests/service_maps/__snapshots__/service_maps.snap +++ b/x-pack/test/apm_api_integration/tests/service_maps/__snapshots__/service_maps.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`APM API tests trial apm_8.0.0 Service Map with data /api/apm/service-map returns the correct data 3`] = ` +exports[`APM API tests trial apm_8.0.0 Service Map with data /internal/apm/service-map returns the correct data 3`] = ` Array [ Object { "data": Object { @@ -1376,7 +1376,7 @@ Array [ ] `; -exports[`APM API tests trial apm_8.0.0 Service Map with data /api/apm/service-map with ML data with the default apm user returns the correct anomaly stats 3`] = ` +exports[`APM API tests trial apm_8.0.0 Service Map with data /internal/apm/service-map with ML data with the default apm user returns the correct anomaly stats 3`] = ` Object { "elements": Array [ Object { diff --git a/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts b/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts index 816e4e26ef86..37fe340d7519 100644 --- a/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts +++ b/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts @@ -28,7 +28,7 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) registry.when('Service map with a basic license', { config: 'basic', archives: [] }, () => { it('is only be available to users with Platinum license (or higher)', async () => { const response = await supertest.get( - `/api/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` + `/internal/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` ); expect(response.status).to.be(403); @@ -40,10 +40,10 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) }); registry.when('Service map without data', { config: 'trial', archives: [] }, () => { - describe('/api/apm/service-map', () => { + describe('/internal/apm/service-map', () => { it('returns an empty list', async () => { const response = await supertest.get( - `/api/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` + `/internal/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` ); expect(response.status).to.be(200); @@ -51,14 +51,14 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) }); }); - describe('/api/apm/service-map/service/{serviceName}', () => { + describe('/internal/apm/service-map/service/{serviceName}', () => { it('returns an object with nulls', async () => { const q = querystring.stringify({ start: metadata.start, end: metadata.end, environment: 'ENVIRONMENT_ALL', }); - const response = await supertest.get(`/api/apm/service-map/service/opbeans-node?${q}`); + const response = await supertest.get(`/internal/apm/service-map/service/opbeans-node?${q}`); expect(response.status).to.be(200); @@ -76,14 +76,14 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) }); }); - describe('/api/apm/service-map/backend/{backendName}', () => { + describe('/internal/apm/service-map/backend/{backendName}', () => { it('returns an object with nulls', async () => { const q = querystring.stringify({ start: metadata.start, end: metadata.end, environment: 'ENVIRONMENT_ALL', }); - const response = await supertest.get(`/api/apm/service-map/backend/postgres?${q}`); + const response = await supertest.get(`/internal/apm/service-map/backend/postgres?${q}`); expect(response.status).to.be(200); @@ -101,12 +101,12 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) }); registry.when('Service Map with data', { config: 'trial', archives: ['apm_8.0.0'] }, () => { - describe('/api/apm/service-map', () => { + describe('/internal/apm/service-map', () => { let response: PromiseReturnType; before(async () => { response = await supertest.get( - `/api/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` + `/internal/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` ); }); @@ -159,7 +159,7 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) describe('with the default apm user', () => { before(async () => { response = await supertest.get( - `/api/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` + `/internal/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` ); }); @@ -246,7 +246,7 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) describe('with a user that does not have access to ML', () => { before(async () => { response = await supertestAsApmReadUserWithoutMlAccess.get( - `/api/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` + `/internal/apm/service-map?start=${start}&end=${end}&environment=ENVIRONMENT_ALL` ); }); @@ -267,7 +267,7 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) it('returns service map elements', async () => { response = await supertest.get( url.format({ - pathname: '/api/apm/service-map', + pathname: '/internal/apm/service-map', query: { environment: 'ENVIRONMENT_ALL', start: metadata.start, @@ -284,14 +284,14 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) }); }); - describe('/api/apm/service-map/service/{serviceName}', () => { + describe('/internal/apm/service-map/service/{serviceName}', () => { it('returns an object with data', async () => { const q = querystring.stringify({ start: metadata.start, end: metadata.end, environment: 'ENVIRONMENT_ALL', }); - const response = await supertest.get(`/api/apm/service-map/service/opbeans-node?${q}`); + const response = await supertest.get(`/internal/apm/service-map/service/opbeans-node?${q}`); expect(response.status).to.be(200); @@ -309,14 +309,14 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) }); }); - describe('/api/apm/service-map/backend/{backendName}', () => { + describe('/internal/apm/service-map/backend/{backendName}', () => { it('returns an object with data', async () => { const q = querystring.stringify({ start: metadata.start, end: metadata.end, environment: 'ENVIRONMENT_ALL', }); - const response = await supertest.get(`/api/apm/service-map/backend/postgresql?${q}`); + const response = await supertest.get(`/internal/apm/service-map/backend/postgresql?${q}`); expect(response.status).to.be(200); diff --git a/x-pack/test/apm_api_integration/tests/service_overview/dependencies/index.ts b/x-pack/test/apm_api_integration/tests/service_overview/dependencies/index.ts index 4bd9785b3142..1e8acccbb192 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/dependencies/index.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/dependencies/index.ts @@ -37,7 +37,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { it('handles the empty state', async () => { const response = await apmApiClient.readUser({ - endpoint: `GET /api/apm/services/{serviceName}/dependencies`, + endpoint: `GET /internal/apm/services/{serviceName}/dependencies`, params: { path: { serviceName: 'opbeans-java' }, query: { @@ -61,7 +61,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { let response: { status: number; - body: APIReturnType<'GET /api/apm/services/{serviceName}/dependencies'>; + body: APIReturnType<'GET /internal/apm/services/{serviceName}/dependencies'>; }; const indices = { @@ -212,7 +212,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); response = await apmApiClient.readUser({ - endpoint: `GET /api/apm/services/{serviceName}/dependencies`, + endpoint: `GET /internal/apm/services/{serviceName}/dependencies`, params: { path: { serviceName: 'opbeans-java' }, query: { @@ -309,12 +309,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { let response: { status: number; - body: APIReturnType<'GET /api/apm/services/{serviceName}/dependencies'>; + body: APIReturnType<'GET /internal/apm/services/{serviceName}/dependencies'>; }; before(async () => { response = await apmApiClient.readUser({ - endpoint: `GET /api/apm/services/{serviceName}/dependencies`, + endpoint: `GET /internal/apm/services/{serviceName}/dependencies`, params: { path: { serviceName: 'opbeans-python' }, query: { diff --git a/x-pack/test/apm_api_integration/tests/service_overview/get_service_node_ids.ts b/x-pack/test/apm_api_integration/tests/service_overview/get_service_node_ids.ts index 1472c1f8c1a0..019c41f9292b 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/get_service_node_ids.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/get_service_node_ids.ts @@ -22,7 +22,7 @@ export async function getServiceNodeIds({ count?: number; }) { const { body } = await apmApiSupertest({ - endpoint: `GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics`, + endpoint: `GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics`, params: { path: { serviceName }, query: { diff --git a/x-pack/test/apm_api_integration/tests/service_overview/instance_details.ts b/x-pack/test/apm_api_integration/tests/service_overview/instance_details.ts index d2b6136fafeb..f3de8823199a 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/instance_details.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/instance_details.ts @@ -14,7 +14,7 @@ import { getServiceNodeIds } from './get_service_node_ids'; import { createApmApiClient } from '../../common/apm_api_supertest'; type ServiceOverviewInstanceDetails = - APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}'>; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('legacySupertestAsApmReadUser'); @@ -31,7 +31,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles empty state', async () => { const response = await supertest.get( url.format({ - pathname: '/api/apm/services/opbeans-java/service_overview_instances/details/foo', + pathname: + '/internal/apm/services/opbeans-java/service_overview_instances/details/foo', query: { start, end, @@ -62,7 +63,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { serviceNodeIds = await getServiceNodeIds({ apmApiSupertest, start, end }); response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/service_overview_instances/details/${serviceNodeIds[0]}`, + pathname: `/internal/apm/services/opbeans-java/service_overview_instances/details/${serviceNodeIds[0]}`, query: { start, end, @@ -89,7 +90,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles empty state when instance id not found', async () => { const response = await supertest.get( url.format({ - pathname: '/api/apm/services/opbeans-java/service_overview_instances/details/foo', + pathname: '/internal/apm/services/opbeans-java/service_overview_instances/details/foo', query: { start, end, diff --git a/x-pack/test/apm_api_integration/tests/service_overview/instances_detailed_statistics.ts b/x-pack/test/apm_api_integration/tests/service_overview/instances_detailed_statistics.ts index 1ad272bafaa8..9d9fb6a221cf 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/instances_detailed_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/instances_detailed_statistics.ts @@ -26,7 +26,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { interface Response { status: number; - body: APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>; + body: APIReturnType<'GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>; } registry.when( @@ -37,7 +37,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles the empty state', async () => { const response: Response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/service_overview_instances/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/service_overview_instances/detailed_statistics`, query: { latencyAggregationType: 'avg', start, @@ -75,7 +75,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { beforeEach(async () => { response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/service_overview_instances/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/service_overview_instances/detailed_statistics`, query: { latencyAggregationType: 'avg', start, @@ -129,7 +129,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { beforeEach(async () => { response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/service_overview_instances/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/service_overview_instances/detailed_statistics`, query: { latencyAggregationType: 'avg', numBuckets: 20, diff --git a/x-pack/test/apm_api_integration/tests/service_overview/instances_main_statistics.ts b/x-pack/test/apm_api_integration/tests/service_overview/instances_main_statistics.ts index 355778757af3..2d165f4ceb90 100644 --- a/x-pack/test/apm_api_integration/tests/service_overview/instances_main_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/instances_main_statistics.ts @@ -29,7 +29,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('when data is not loaded', () => { it('handles the empty state', async () => { const response = await apmApiClient.readUser({ - endpoint: `GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics`, + endpoint: `GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics`, params: { path: { serviceName: 'opbeans-java' }, query: { @@ -58,12 +58,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { describe('fetching java data', () => { let response: { - body: APIReturnType<`GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics`>; + body: APIReturnType<`GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics`>; }; beforeEach(async () => { response = await apmApiClient.readUser({ - endpoint: `GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics`, + endpoint: `GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics`, params: { path: { serviceName: 'opbeans-java' }, query: { @@ -129,12 +129,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('fetching non-java data', () => { let response: { - body: APIReturnType<`GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics`>; + body: APIReturnType<`GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics`>; }; beforeEach(async () => { response = await apmApiClient.readUser({ - endpoint: `GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics`, + endpoint: `GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics`, params: { path: { serviceName: 'opbeans-ruby' }, query: { @@ -197,12 +197,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { describe('fetching java data', () => { let response: { - body: APIReturnType<`GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics`>; + body: APIReturnType<`GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics`>; }; beforeEach(async () => { response = await apmApiClient.readUser({ - endpoint: `GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics`, + endpoint: `GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics`, params: { path: { serviceName: 'opbeans-java' }, query: { diff --git a/x-pack/test/apm_api_integration/tests/services/agent.ts b/x-pack/test/apm_api_integration/tests/services/agent.ts index 3e44dbe685cd..fcd21f5a1a07 100644 --- a/x-pack/test/apm_api_integration/tests/services/agent.ts +++ b/x-pack/test/apm_api_integration/tests/services/agent.ts @@ -21,7 +21,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { registry.when('Agent name when data is not loaded', { config: 'basic', archives: [] }, () => { it('handles the empty state', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/agent?start=${start}&end=${end}` + `/internal/apm/services/opbeans-node/agent?start=${start}&end=${end}` ); expect(response.status).to.be(200); @@ -35,7 +35,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { it('returns the agent name', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/agent?start=${start}&end=${end}` + `/internal/apm/services/opbeans-node/agent?start=${start}&end=${end}` ); expect(response.status).to.be(200); diff --git a/x-pack/test/apm_api_integration/tests/services/error_groups_detailed_statistics.ts b/x-pack/test/apm_api_integration/tests/services/error_groups_detailed_statistics.ts index 7c7a5b5e9c51..3587a3e96c0d 100644 --- a/x-pack/test/apm_api_integration/tests/services/error_groups_detailed_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/services/error_groups_detailed_statistics.ts @@ -16,7 +16,7 @@ import { createApmApiClient } from '../../common/apm_api_supertest'; import { getErrorGroupIds } from './get_error_group_ids'; type ErrorGroupsDetailedStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/error_groups/detailed_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics'>; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('legacySupertestAsApmReadUser'); @@ -35,7 +35,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/error_groups/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/error_groups/detailed_statistics`, query: { start, end, @@ -63,7 +63,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/error_groups/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/error_groups/detailed_statistics`, query: { start, end, @@ -98,7 +98,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns an empty state when requested groupIds are not available in the given time range', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/error_groups/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/error_groups/detailed_statistics`, query: { start, end, @@ -133,7 +133,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/error_groups/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/error_groups/detailed_statistics`, query: { numBuckets: 20, transactionType: 'request', @@ -179,7 +179,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns an empty state when requested groupIds are not available in the given time range', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/error_groups/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/error_groups/detailed_statistics`, query: { numBuckets: 20, transactionType: 'request', diff --git a/x-pack/test/apm_api_integration/tests/services/error_groups_main_statistics.ts b/x-pack/test/apm_api_integration/tests/services/error_groups_main_statistics.ts index 98c906fe6119..b6fb0696f3f7 100644 --- a/x-pack/test/apm_api_integration/tests/services/error_groups_main_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/services/error_groups_main_statistics.ts @@ -13,7 +13,7 @@ import { registry } from '../../common/registry'; import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; type ErrorGroupsMainStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/error_groups/main_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/error_groups/main_statistics'>; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('legacySupertestAsApmReadUser'); @@ -29,7 +29,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles empty state', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/error_groups/main_statistics`, + pathname: `/internal/apm/services/opbeans-java/error_groups/main_statistics`, query: { start, end, @@ -56,7 +56,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns the correct data', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/error_groups/main_statistics`, + pathname: `/internal/apm/services/opbeans-java/error_groups/main_statistics`, query: { start, end, diff --git a/x-pack/test/apm_api_integration/tests/services/get_error_group_ids.ts b/x-pack/test/apm_api_integration/tests/services/get_error_group_ids.ts index b2253750ae7f..9fa7232240db 100644 --- a/x-pack/test/apm_api_integration/tests/services/get_error_group_ids.ts +++ b/x-pack/test/apm_api_integration/tests/services/get_error_group_ids.ts @@ -21,7 +21,7 @@ export async function getErrorGroupIds({ count?: number; }) { const { body } = await apmApiSupertest({ - endpoint: `GET /api/apm/services/{serviceName}/error_groups/main_statistics`, + endpoint: `GET /internal/apm/services/{serviceName}/error_groups/main_statistics`, params: { path: { serviceName }, query: { diff --git a/x-pack/test/apm_api_integration/tests/services/service_details.ts b/x-pack/test/apm_api_integration/tests/services/service_details.ts index 263aaa504946..28a3ee3ef82f 100644 --- a/x-pack/test/apm_api_integration/tests/services/service_details.ts +++ b/x-pack/test/apm_api_integration/tests/services/service_details.ts @@ -24,7 +24,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles the empty state', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/metadata/details`, + pathname: `/internal/apm/services/opbeans-java/metadata/details`, query: { start, end }, }) ); @@ -42,7 +42,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns java service details', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/metadata/details`, + pathname: `/internal/apm/services/opbeans-java/metadata/details`, query: { start, end }, }) ); @@ -88,7 +88,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns python service details', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-python/metadata/details`, + pathname: `/internal/apm/services/opbeans-python/metadata/details`, query: { start, end }, }) ); diff --git a/x-pack/test/apm_api_integration/tests/services/service_icons.ts b/x-pack/test/apm_api_integration/tests/services/service_icons.ts index 619603efc412..e948c6981d6c 100644 --- a/x-pack/test/apm_api_integration/tests/services/service_icons.ts +++ b/x-pack/test/apm_api_integration/tests/services/service_icons.ts @@ -21,7 +21,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles the empty state', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/metadata/icons`, + pathname: `/internal/apm/services/opbeans-java/metadata/icons`, query: { start, end }, }) ); @@ -38,7 +38,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns java service icons', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/metadata/icons`, + pathname: `/internal/apm/services/opbeans-java/metadata/icons`, query: { start, end }, }) ); @@ -57,7 +57,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns python service icons', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-python/metadata/icons`, + pathname: `/internal/apm/services/opbeans-python/metadata/icons`, query: { start, end }, }) ); diff --git a/x-pack/test/apm_api_integration/tests/services/services_detailed_statistics.ts b/x-pack/test/apm_api_integration/tests/services/services_detailed_statistics.ts index c5c1032a794f..d605c5f31382 100644 --- a/x-pack/test/apm_api_integration/tests/services/services_detailed_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/services/services_detailed_statistics.ts @@ -13,7 +13,8 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; import { isFiniteNumber } from '../../../../plugins/apm/common/utils/is_finite_number'; -type ServicesDetailedStatisticsReturn = APIReturnType<'GET /api/apm/services/detailed_statistics'>; +type ServicesDetailedStatisticsReturn = + APIReturnType<'GET /internal/apm/services/detailed_statistics'>; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('legacySupertestAsApmReadUser'); @@ -30,7 +31,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles the empty state', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/detailed_statistics`, + pathname: `/internal/apm/services/detailed_statistics`, query: { start, end, @@ -56,7 +57,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/detailed_statistics`, + pathname: `/internal/apm/services/detailed_statistics`, query: { start, end, @@ -107,7 +108,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns empty when empty service names is passed', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/detailed_statistics`, + pathname: `/internal/apm/services/detailed_statistics`, query: { start, end, @@ -124,7 +125,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('filters by environment', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/detailed_statistics`, + pathname: `/internal/apm/services/detailed_statistics`, query: { start, end, @@ -141,7 +142,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('filters by kuery', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/detailed_statistics`, + pathname: `/internal/apm/services/detailed_statistics`, query: { start, end, @@ -165,7 +166,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/detailed_statistics`, + pathname: `/internal/apm/services/detailed_statistics`, query: { start: moment(end).subtract(15, 'minutes').toISOString(), end, diff --git a/x-pack/test/apm_api_integration/tests/services/throughput.ts b/x-pack/test/apm_api_integration/tests/services/throughput.ts index 53c7efe28a6b..d960cccc2877 100644 --- a/x-pack/test/apm_api_integration/tests/services/throughput.ts +++ b/x-pack/test/apm_api_integration/tests/services/throughput.ts @@ -17,7 +17,7 @@ import archives_metadata from '../../common/fixtures/es_archiver/archives_metada import { FtrProviderContext } from '../../common/ftr_provider_context'; import { registry } from '../../common/registry'; -type ThroughputReturn = APIReturnType<'GET /api/apm/services/{serviceName}/throughput'>; +type ThroughputReturn = APIReturnType<'GET /internal/apm/services/{serviceName}/throughput'>; export default function ApiTest({ getService }: FtrProviderContext) { const apmApiClient = getService('apmApiClient'); @@ -92,7 +92,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { kuery?: string; }) { const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/services/{serviceName}/throughput', + endpoint: 'GET /internal/apm/services/{serviceName}/throughput', params: { path: { serviceName: 'synth-go', @@ -166,7 +166,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { registry.when('Throughput when data is not loaded', { config: 'basic', archives: [] }, () => { it('handles the empty state', async () => { const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/services/{serviceName}/throughput', + endpoint: 'GET /internal/apm/services/{serviceName}/throughput', params: { path: { serviceName: 'opbeans-java', @@ -195,7 +195,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('when querying without kql filter', () => { before(async () => { const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/services/{serviceName}/throughput', + endpoint: 'GET /internal/apm/services/{serviceName}/throughput', params: { path: { serviceName: 'opbeans-java', @@ -249,7 +249,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('with kql filter to force transaction-based UI', () => { before(async () => { const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/services/{serviceName}/throughput', + endpoint: 'GET /internal/apm/services/{serviceName}/throughput', params: { path: { serviceName: 'opbeans-java', @@ -285,7 +285,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { before(async () => { const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/services/{serviceName}/throughput', + endpoint: 'GET /internal/apm/services/{serviceName}/throughput', params: { path: { serviceName: 'opbeans-java', diff --git a/x-pack/test/apm_api_integration/tests/services/top_services.ts b/x-pack/test/apm_api_integration/tests/services/top_services.ts index 8e79d6cda58e..18700fb04125 100644 --- a/x-pack/test/apm_api_integration/tests/services/top_services.ts +++ b/x-pack/test/apm_api_integration/tests/services/top_services.ts @@ -33,7 +33,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { it('handles the empty state', async () => { const response = await supertest.get( - `/api/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` ); expect(response.status).to.be(200); @@ -49,14 +49,14 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { let response: { status: number; - body: APIReturnType<'GET /api/apm/services'>; + body: APIReturnType<'GET /internal/apm/services'>; }; let sortedItems: typeof response.body.items; before(async () => { response = await supertest.get( - `/api/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` ); sortedItems = sortBy(response.body.items, 'serviceName'); }); @@ -192,15 +192,15 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('includes services that only report metric data', async () => { interface Response { status: number; - body: APIReturnType<'GET /api/apm/services'>; + body: APIReturnType<'GET /internal/apm/services'>; } const [unfilteredResponse, filteredResponse] = await Promise.all([ supertest.get( - `/api/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` ) as Promise, supertest.get( - `/api/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=${encodeURIComponent( + `/internal/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=${encodeURIComponent( 'not (processor.event:transaction)' )}` ) as Promise, @@ -231,12 +231,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('and fetching a list of services', () => { let response: { status: number; - body: APIReturnType<'GET /api/apm/services'>; + body: APIReturnType<'GET /internal/apm/services'>; }; before(async () => { response = await supertest.get( - `/api/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` ); }); @@ -282,7 +282,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let response: PromiseReturnType; before(async () => { response = await supertestAsApmReadUserWithoutMlAccess.get( - `/api/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/services?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` ); }); @@ -307,7 +307,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let response: PromiseReturnType; before(async () => { response = await supertest.get( - `/api/apm/services?environment=ENVIRONMENT_ALL&start=${start}&end=${end}&kuery=${encodeURIComponent( + `/internal/apm/services?environment=ENVIRONMENT_ALL&start=${start}&end=${end}&kuery=${encodeURIComponent( 'service.name:opbeans-java' )}` ); diff --git a/x-pack/test/apm_api_integration/tests/services/transaction_types.ts b/x-pack/test/apm_api_integration/tests/services/transaction_types.ts index 6f574b5c8e99..4b752971e82f 100644 --- a/x-pack/test/apm_api_integration/tests/services/transaction_types.ts +++ b/x-pack/test/apm_api_integration/tests/services/transaction_types.ts @@ -26,7 +26,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { it('handles empty state', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction_types?start=${start}&end=${end}` + `/internal/apm/services/opbeans-node/transaction_types?start=${start}&end=${end}` ); expect(response.status).to.be(200); @@ -42,7 +42,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { it('handles empty state', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction_types?start=${start}&end=${end}` + `/internal/apm/services/opbeans-node/transaction_types?start=${start}&end=${end}` ); expect(response.status).to.be(200); diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/basic.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/basic.ts index 40708adb754a..7e05bd3faabf 100644 --- a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/basic.ts +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/basic.ts @@ -17,12 +17,12 @@ export default function apiTest({ getService }: FtrProviderContext) { type SupertestAsUser = typeof noAccessUser | typeof readUser | typeof writeUser; function getJobs(user: SupertestAsUser) { - return user.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); + return user.get(`/internal/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); } function createJobs(user: SupertestAsUser, environments: string[]) { return user - .post(`/api/apm/settings/anomaly-detection/jobs`) + .post(`/internal/apm/settings/anomaly-detection/jobs`) .send({ environments }) .set('kbn-xsrf', 'foo'); } diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/no_access_user.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/no_access_user.ts index 135038755dc6..0b73b67c8e4c 100644 --- a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/no_access_user.ts +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/no_access_user.ts @@ -13,12 +13,12 @@ export default function apiTest({ getService }: FtrProviderContext) { const noAccessUser = getService('legacySupertestAsNoAccessUser'); function getJobs() { - return noAccessUser.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); + return noAccessUser.get(`/internal/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); } function createJobs(environments: string[]) { return noAccessUser - .post(`/api/apm/settings/anomaly-detection/jobs`) + .post(`/internal/apm/settings/anomaly-detection/jobs`) .send({ environments }) .set('kbn-xsrf', 'foo'); } diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/read_user.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/read_user.ts index 3beebb434b31..982a47001f82 100644 --- a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/read_user.ts +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/read_user.ts @@ -13,12 +13,12 @@ export default function apiTest({ getService }: FtrProviderContext) { const apmReadUser = getService('legacySupertestAsApmReadUser'); function getJobs() { - return apmReadUser.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); + return apmReadUser.get(`/internal/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); } function createJobs(environments: string[]) { return apmReadUser - .post(`/api/apm/settings/anomaly-detection/jobs`) + .post(`/internal/apm/settings/anomaly-detection/jobs`) .send({ environments }) .set('kbn-xsrf', 'foo'); } diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.ts index 7c13533a1429..3d671c8fb825 100644 --- a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.ts +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.ts @@ -15,12 +15,14 @@ export default function apiTest({ getService }: FtrProviderContext) { const legacyWriteUserClient = getService('legacySupertestAsApmWriteUser'); function getJobs() { - return apmApiClient.writeUser({ endpoint: `GET /api/apm/settings/anomaly-detection/jobs` }); + return apmApiClient.writeUser({ + endpoint: `GET /internal/apm/settings/anomaly-detection/jobs`, + }); } function createJobs(environments: string[]) { return apmApiClient.writeUser({ - endpoint: `POST /api/apm/settings/anomaly-detection/jobs`, + endpoint: `POST /internal/apm/settings/anomaly-detection/jobs`, params: { body: { environments }, }, diff --git a/x-pack/test/apm_api_integration/tests/settings/custom_link.ts b/x-pack/test/apm_api_integration/tests/settings/custom_link.ts index 03b2ad4aa321..86f60be3fdc4 100644 --- a/x-pack/test/apm_api_integration/tests/settings/custom_link.ts +++ b/x-pack/test/apm_api_integration/tests/settings/custom_link.ts @@ -126,7 +126,7 @@ export default function customLinksTests({ getService }: FtrProviderContext) { it('fetches a transaction sample', async () => { const response = await apmApiClient.readUser({ - endpoint: 'GET /api/apm/settings/custom_links/transaction', + endpoint: 'GET /internal/apm/settings/custom_links/transaction', params: { query: { 'service.name': 'opbeans-java', @@ -141,7 +141,7 @@ export default function customLinksTests({ getService }: FtrProviderContext) { function searchCustomLinks(filters?: any) { return apmApiClient.readUser({ - endpoint: 'GET /api/apm/settings/custom_links', + endpoint: 'GET /internal/apm/settings/custom_links', params: { query: filters, }, @@ -152,7 +152,7 @@ export default function customLinksTests({ getService }: FtrProviderContext) { log.debug('creating configuration', customLink); return apmApiClient.writeUser({ - endpoint: 'POST /api/apm/settings/custom_links', + endpoint: 'POST /internal/apm/settings/custom_links', params: { body: customLink, }, @@ -163,7 +163,7 @@ export default function customLinksTests({ getService }: FtrProviderContext) { log.debug('updating configuration', id, customLink); return apmApiClient.writeUser({ - endpoint: 'PUT /api/apm/settings/custom_links/{id}', + endpoint: 'PUT /internal/apm/settings/custom_links/{id}', params: { path: { id }, body: customLink, @@ -175,7 +175,7 @@ export default function customLinksTests({ getService }: FtrProviderContext) { log.debug('deleting configuration', id); return apmApiClient.writeUser({ - endpoint: 'DELETE /api/apm/settings/custom_links/{id}', + endpoint: 'DELETE /internal/apm/settings/custom_links/{id}', params: { path: { id } }, }); } diff --git a/x-pack/test/apm_api_integration/tests/traces/top_traces.ts b/x-pack/test/apm_api_integration/tests/traces/top_traces.ts index 705c3d9ff4a1..4968732f8220 100644 --- a/x-pack/test/apm_api_integration/tests/traces/top_traces.ts +++ b/x-pack/test/apm_api_integration/tests/traces/top_traces.ts @@ -24,7 +24,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { registry.when('Top traces when data is not loaded', { config: 'basic', archives: [] }, () => { it('handles empty state', async () => { const response = await supertest.get( - `/api/apm/traces?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/traces?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` ); expect(response.status).to.be(200); @@ -39,7 +39,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { let response: any; before(async () => { response = await supertest.get( - `/api/apm/traces?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/traces?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=` ); }); diff --git a/x-pack/test/apm_api_integration/tests/traces/trace_by_id.tsx b/x-pack/test/apm_api_integration/tests/traces/trace_by_id.tsx index 5b4ab5f45da4..d99a2b95a792 100644 --- a/x-pack/test/apm_api_integration/tests/traces/trace_by_id.tsx +++ b/x-pack/test/apm_api_integration/tests/traces/trace_by_id.tsx @@ -22,7 +22,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { registry.when('Trace does not exist', { config: 'basic', archives: [] }, () => { it('handles empty state', async () => { const response = await apmApiSupertest({ - endpoint: `GET /api/apm/traces/{traceId}`, + endpoint: `GET /internal/apm/traces/{traceId}`, params: { path: { traceId: 'foo' }, query: { start, end }, @@ -35,10 +35,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); registry.when('Trace exists', { config: 'basic', archives: [archiveName] }, () => { - let response: SupertestReturnType<`GET /api/apm/traces/{traceId}`>; + let response: SupertestReturnType<`GET /internal/apm/traces/{traceId}`>; before(async () => { response = await apmApiSupertest({ - endpoint: `GET /api/apm/traces/{traceId}`, + endpoint: `GET /internal/apm/traces/{traceId}`, params: { path: { traceId: '64d0014f7530df24e549dd17cc0a8895' }, query: { start, end }, diff --git a/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts b/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts index de23b8fea536..bc2e2f534a0b 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts @@ -24,7 +24,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { registry.when('Breakdown when data is not loaded', { config: 'basic', archives: [] }, () => { it('handles the empty state', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&transactionType=${transactionType}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&transactionType=${transactionType}&environment=ENVIRONMENT_ALL&kuery=` ); expect(response.status).to.be(200); expect(response.body).to.eql({ timeseries: [] }); @@ -37,7 +37,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { () => { it('returns the transaction breakdown for a service', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&transactionType=${transactionType}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&transactionType=${transactionType}&environment=ENVIRONMENT_ALL&kuery=` ); expect(response.status).to.be(200); @@ -45,7 +45,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); it('returns the transaction breakdown for a transaction group', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&transactionType=${transactionType}&transactionName=${transactionName}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&transactionType=${transactionType}&transactionName=${transactionName}&environment=ENVIRONMENT_ALL&kuery=` ); expect(response.status).to.be(200); @@ -104,7 +104,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); it('returns the transaction breakdown sorted by name', async () => { const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&transactionType=${transactionType}&environment=ENVIRONMENT_ALL&kuery=` + `/internal/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&transactionType=${transactionType}&environment=ENVIRONMENT_ALL&kuery=` ); expect(response.status).to.be(200); diff --git a/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts b/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts index d06eebd2cb98..2ddaa4677394 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts @@ -15,7 +15,7 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; import { registry } from '../../common/registry'; type ErrorRate = - APIReturnType<'GET /api/apm/services/{serviceName}/transactions/charts/error_rate'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate'>; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('legacySupertestAsApmReadUser'); @@ -30,7 +30,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles the empty state', async () => { const response = await supertest.get( format({ - pathname: '/api/apm/services/opbeans-java/transactions/charts/error_rate', + pathname: '/internal/apm/services/opbeans-java/transactions/charts/error_rate', query: { start, end, transactionType, environment: 'ENVIRONMENT_ALL', kuery: '' }, }) ); @@ -46,7 +46,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles the empty state with comparison data', async () => { const response = await supertest.get( format({ - pathname: '/api/apm/services/opbeans-java/transactions/charts/error_rate', + pathname: '/internal/apm/services/opbeans-java/transactions/charts/error_rate', query: { transactionType, start: moment(end).subtract(15, 'minutes').toISOString(), @@ -78,7 +78,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const response = await supertest.get( format({ - pathname: '/api/apm/services/opbeans-java/transactions/charts/error_rate', + pathname: '/internal/apm/services/opbeans-java/transactions/charts/error_rate', query: { start, end, transactionType, environment: 'ENVIRONMENT_ALL', kuery: '' }, }) ); @@ -138,7 +138,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const response = await supertest.get( format({ - pathname: '/api/apm/services/opbeans-java/transactions/charts/error_rate', + pathname: '/internal/apm/services/opbeans-java/transactions/charts/error_rate', query: { transactionType, start: moment(end).subtract(15, 'minutes').toISOString(), diff --git a/x-pack/test/apm_api_integration/tests/transactions/latency.ts b/x-pack/test/apm_api_integration/tests/transactions/latency.ts index df6daa897380..beaff7647868 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/latency.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/latency.ts @@ -15,7 +15,7 @@ import archives_metadata from '../../common/fixtures/es_archiver/archives_metada import { registry } from '../../common/registry'; type LatencyChartReturnType = - APIReturnType<'GET /api/apm/services/{serviceName}/transactions/charts/latency'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/latency'>; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('legacySupertestAsApmReadUser'); @@ -31,7 +31,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns 400 when latencyAggregationType is not informed', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`, + pathname: `/internal/apm/services/opbeans-node/transactions/charts/latency`, query: { start, end, @@ -47,7 +47,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns 400 when transactionType is not informed', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`, + pathname: `/internal/apm/services/opbeans-node/transactions/charts/latency`, query: { start, end, @@ -63,7 +63,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles the empty state', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`, + pathname: `/internal/apm/services/opbeans-node/transactions/charts/latency`, query: { start, end, @@ -96,7 +96,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`, + pathname: `/internal/apm/services/opbeans-node/transactions/charts/latency`, query: { start, end, @@ -121,7 +121,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`, + pathname: `/internal/apm/services/opbeans-node/transactions/charts/latency`, query: { start, end, @@ -146,7 +146,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`, + pathname: `/internal/apm/services/opbeans-node/transactions/charts/latency`, query: { start, end, @@ -176,7 +176,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`, + pathname: `/internal/apm/services/opbeans-node/transactions/charts/latency`, query: { latencyAggregationType: 'avg', transactionType: 'request', @@ -217,7 +217,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`, + pathname: `/internal/apm/services/opbeans-node/transactions/charts/latency`, query: { start, end, @@ -257,7 +257,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/transactions/charts/latency`, + pathname: `/internal/apm/services/opbeans-java/transactions/charts/latency`, query: { start, end, @@ -279,7 +279,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-python/transactions/charts/latency`, + pathname: `/internal/apm/services/opbeans-python/transactions/charts/latency`, query: { start, end, @@ -319,7 +319,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/transactions/charts/latency`, + pathname: `/internal/apm/services/opbeans-java/transactions/charts/latency`, query: { start, end, diff --git a/x-pack/test/apm_api_integration/tests/transactions/trace_samples.ts b/x-pack/test/apm_api_integration/tests/transactions/trace_samples.ts index fca9222e69bd..19e75ff08fec 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/trace_samples.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/trace_samples.ts @@ -17,7 +17,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const archiveName = 'apm_8.0.0'; const metadata = archives_metadata[archiveName]; - const url = `/api/apm/services/opbeans-java/transactions/traces/samples?${qs.stringify({ + const url = `/internal/apm/services/opbeans-java/transactions/traces/samples?${qs.stringify({ environment: 'ENVIRONMENT_ALL', kuery: '', start: metadata.start, diff --git a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_detailed_statistics.ts b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_detailed_statistics.ts index 84d7a2986ecb..add954f490db 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_detailed_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_detailed_statistics.ts @@ -16,7 +16,7 @@ import { registry } from '../../common/registry'; import { removeEmptyCoordinates, roundNumber } from '../../utils'; type TransactionsGroupsDetailedStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/transactions/groups/detailed_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics'>; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('legacySupertestAsApmReadUser'); @@ -32,7 +32,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles the empty state', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/transactions/groups/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/transactions/groups/detailed_statistics`, query: { start, end, @@ -59,7 +59,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns the correct data', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/transactions/groups/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/transactions/groups/detailed_statistics`, query: { start, end, @@ -113,7 +113,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns the correct data for latency aggregation 99th percentile', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/transactions/groups/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/transactions/groups/detailed_statistics`, query: { start, end, @@ -161,7 +161,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns empty when transaction name is not found', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/transactions/groups/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/transactions/groups/detailed_statistics`, query: { start, end, @@ -185,7 +185,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/transactions/groups/detailed_statistics`, + pathname: `/internal/apm/services/opbeans-java/transactions/groups/detailed_statistics`, query: { numBuckets: 20, transactionType: 'request', diff --git a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_main_statistics.ts b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_main_statistics.ts index ffdbf35992d7..7664d28271e1 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_main_statistics.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_main_statistics.ts @@ -14,7 +14,7 @@ import archives from '../../common/fixtures/es_archiver/archives_metadata'; import { registry } from '../../common/registry'; type TransactionsGroupsPrimaryStatistics = - APIReturnType<'GET /api/apm/services/{serviceName}/transactions/groups/main_statistics'>; + APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics'>; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('legacySupertestAsApmReadUser'); @@ -29,7 +29,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('handles the empty state', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/transactions/groups/main_statistics`, + pathname: `/internal/apm/services/opbeans-java/transactions/groups/main_statistics`, query: { start, end, @@ -57,7 +57,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns the correct data', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/transactions/groups/main_statistics`, + pathname: `/internal/apm/services/opbeans-java/transactions/groups/main_statistics`, query: { start, end, @@ -132,7 +132,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns the correct data for latency aggregation 99th percentile', async () => { const response = await supertest.get( url.format({ - pathname: `/api/apm/services/opbeans-java/transactions/groups/main_statistics`, + pathname: `/internal/apm/services/opbeans-java/transactions/groups/main_statistics`, query: { start, end, From 15c7bd019249654d0778ba29ac51f0e7e902d683 Mon Sep 17 00:00:00 2001 From: Garrett Spong Date: Thu, 7 Oct 2021 20:35:11 -0600 Subject: [PATCH 15/74] [Security Solution][Detections] Updates Indexing/Query Time columns in Rule Monitoring table to be SUM instead of MAX (#114023) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Updates the `Indexing Time` & `Query Time` columns in the `Rule Monitoring` table to be `SUM` instead of `MAX`, thus showing the total duration of indexing/querying phases within a Rule's execution rather than just the phase that took the longest. Also adds tooltips to columns for better understanding these metrics. ~Note: Wanted to add a link to documentation for `Last Gap` column, but cannot add links within `EuiToolTip` and didn't want to mis-align design of other column tooltips by introducing a popover. @elastic/security-design please advise on desired action or copy changes here -- thanks!~ 🙂 Update: As guided by design, changed `Last Gap` tooltip to be `EuiPopover` and added link to docs. ##### Indexing Time:

##### Query Time:

##### Last Gap:

### Checklist Delete any items that are not applicable to this PR. - [X] 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/master/packages/kbn-i18n/README.md) --- ...-plugin-core-public.doclinksstart.links.md | 1 + .../public/doc_links/doc_links_service.ts | 17 +++--- src/core/public/public.api.md | 1 + .../detection_engine/rules/all/columns.tsx | 60 +++++++++++++++---- .../rules/all/popover_tooltip.tsx | 47 +++++++++++++++ .../rules/all/rules_tables.tsx | 5 +- .../detection_engine/rules/translations.ts | 19 ++++++ 7 files changed, 129 insertions(+), 21 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/popover_tooltip.tsx diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index c8be6a439f35..fc7d2e976b57 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -143,6 +143,7 @@ readonly links: { readonly ruleChangeLog: string; readonly detectionsReq: string; readonly networkMap: string; + readonly troubleshootGaps: string; }; readonly query: { readonly eql: string; diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 221b586ac154..6c34693b6052 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -25,6 +25,7 @@ export class DocLinksService { const FLEET_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/fleet/${DOC_LINK_VERSION}/`; const PLUGIN_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/plugins/${DOC_LINK_VERSION}/`; const APM_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/apm/`; + const SECURITY_SOLUTION_DOCS = `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/`; return deepFreeze({ DOC_LINK_VERSION, @@ -223,13 +224,14 @@ export class DocLinksService { typesRemoval: `${ELASTICSEARCH_DOCS}removal-of-types.html`, }, siem: { - guide: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`, - gettingStarted: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`, - privileges: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/sec-requirements.html`, - ml: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/machine-learning.html`, - ruleChangeLog: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/prebuilt-rules-changelog.html`, - detectionsReq: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/detections-permissions-section.html`, - networkMap: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/conf-map-ui.html`, + guide: `${SECURITY_SOLUTION_DOCS}index.html`, + gettingStarted: `${SECURITY_SOLUTION_DOCS}index.html`, + privileges: `${SECURITY_SOLUTION_DOCS}sec-requirements.html`, + ml: `${SECURITY_SOLUTION_DOCS}machine-learning.html`, + ruleChangeLog: `${SECURITY_SOLUTION_DOCS}prebuilt-rules-changelog.html`, + detectionsReq: `${SECURITY_SOLUTION_DOCS}detections-permissions-section.html`, + networkMap: `${SECURITY_SOLUTION_DOCS}conf-map-ui.html`, + troubleshootGaps: `${SECURITY_SOLUTION_DOCS}alerts-ui-monitor.html#troubleshoot-gaps`, }, query: { eql: `${ELASTICSEARCH_DOCS}eql.html`, @@ -636,6 +638,7 @@ export interface DocLinksStart { readonly ruleChangeLog: string; readonly detectionsReq: string; readonly networkMap: string; + readonly troubleshootGaps: string; }; readonly query: { readonly eql: string; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index b3474a669e70..7170c43f36e7 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -612,6 +612,7 @@ export interface DocLinksStart { readonly ruleChangeLog: string; readonly detectionsReq: string; readonly networkMap: string; + readonly troubleshootGaps: string; }; readonly query: { readonly eql: string; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx index ad5a85a03464..6b05ee6403db 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx @@ -11,9 +11,12 @@ import { EuiText, EuiHealth, EuiToolTip, + EuiIcon, + EuiLink, } from '@elastic/eui'; -import { FormattedRelative } from '@kbn/i18n/react'; +import { FormattedMessage, FormattedRelative } from '@kbn/i18n/react'; import * as H from 'history'; +import { sum } from 'lodash'; import React, { Dispatch } from 'react'; import { isMlRule } from '../../../../../../common/machine_learning/helpers'; @@ -36,10 +39,11 @@ import { RulesTableAction } from '../../../../containers/detection_engine/rules/ import { LocalizedDateTooltip } from '../../../../../common/components/localized_date_tooltip'; import { LinkAnchor } from '../../../../../common/components/links'; import { getToolTipContent, canEditRuleWithActions } from '../../../../../common/utils/privileges'; +import { PopoverTooltip } from './popover_tooltip'; import { TagsDisplay } from './tag_display'; import { getRuleStatusText } from '../../../../../../common/detection_engine/utils'; import { APP_ID, SecurityPageName } from '../../../../../../common/constants'; -import { NavigateToAppOptions } from '../../../../../../../../../src/core/public'; +import { DocLinksStart, NavigateToAppOptions } from '../../../../../../../../../src/core/public'; export const getActions = ( dispatch: React.Dispatch, @@ -312,7 +316,8 @@ export const getColumns = ({ export const getMonitoringColumns = ( navigateToApp: (appId: string, options?: NavigateToAppOptions | undefined) => Promise, - formatUrl: FormatUrl + formatUrl: FormatUrl, + docLinks: DocLinksStart ): RulesStatusesColumns[] => { const cols: RulesStatusesColumns[] = [ { @@ -344,12 +349,17 @@ export const getMonitoringColumns = ( }, { field: 'current_status.bulk_create_time_durations', - name: i18n.COLUMN_INDEXING_TIMES, + name: ( + <> + {i18n.COLUMN_INDEXING_TIMES}{' '} + + + + + ), render: (value: RuleStatus['current_status']['bulk_create_time_durations']) => ( - {value != null && value.length > 0 - ? Math.max(...value?.map((item) => Number.parseFloat(item))) - : getEmptyTagValue()} + {value?.length ? sum(value.map(Number)).toFixed() : getEmptyTagValue()} ), truncateText: true, @@ -357,12 +367,17 @@ export const getMonitoringColumns = ( }, { field: 'current_status.search_after_time_durations', - name: i18n.COLUMN_QUERY_TIMES, + name: ( + <> + {i18n.COLUMN_QUERY_TIMES}{' '} + + + + + ), render: (value: RuleStatus['current_status']['search_after_time_durations']) => ( - {value != null && value.length > 0 - ? Math.max(...value?.map((item) => Number.parseFloat(item))) - : getEmptyTagValue()} + {value?.length ? sum(value.map(Number)).toFixed() : getEmptyTagValue()} ), truncateText: true, @@ -370,7 +385,28 @@ export const getMonitoringColumns = ( }, { field: 'current_status.gap', - name: i18n.COLUMN_GAP, + name: ( + <> + {i18n.COLUMN_GAP} + + +

+ + {'see documentation'} + + ), + }} + /> +

+
+
+ + ), render: (value: RuleStatus['current_status']['gap']) => ( {value ?? getEmptyTagValue()} diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/popover_tooltip.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/popover_tooltip.tsx new file mode 100644 index 000000000000..5cf0a0c0b28f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/popover_tooltip.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { EuiPopover, EuiButtonIcon } from '@elastic/eui'; +import * as i18n from '../translations'; + +interface PopoverTooltipProps { + columnName: string; + children: React.ReactNode; +} + +/** + * Table column tooltip component utilizing EuiPopover for rich content like documentation links + * @param columnName string Name of column to use as aria-label of button + * @param children React.ReactNode of content to display in popover tooltip + */ +const PopoverTooltipComponent = ({ columnName, children }: PopoverTooltipProps) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + return ( + setIsPopoverOpen(false)} + button={ + setIsPopoverOpen(!isPopoverOpen)} + size="s" + color="primary" + iconType="questionInCircle" + /> + } + > + {children} + + ); +}; + +export const PopoverTooltip = React.memo(PopoverTooltipComponent); + +PopoverTooltip.displayName = 'PopoverTooltip'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx index f32321a0a03d..9d9425cdabe6 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx @@ -96,6 +96,7 @@ export const RulesTables = React.memo( setRefreshRulesData, selectedTab, }) => { + const docLinks = useKibana().services.docLinks; const [initLoading, setInitLoading] = useState(true); const { @@ -299,8 +300,8 @@ export const RulesTables = React.memo( ]); const monitoringColumns = useMemo( - () => getMonitoringColumns(navigateToApp, formatUrl), - [navigateToApp, formatUrl] + () => getMonitoringColumns(navigateToApp, formatUrl, docLinks), + [navigateToApp, formatUrl, docLinks] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts index 28ed14774acf..53efe28cba49 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts @@ -13,6 +13,11 @@ export const BACK_TO_DETECTIONS = i18n.translate( defaultMessage: 'Back to detections', } ); +export const POPOVER_TOOLTIP_ARIA_LABEL = (columnName: string) => + i18n.translate('xpack.securitySolution.detectionEngine.rules.popoverTooltip.ariaLabel', { + defaultMessage: 'Tooltip for column: {columnName}', + values: { columnName }, + }); export const IMPORT_RULE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.importRuleTitle', @@ -364,6 +369,13 @@ export const COLUMN_INDEXING_TIMES = i18n.translate( } ); +export const COLUMN_INDEXING_TIMES_TOOLTIP = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.columns.indexingTimesTooltip', + { + defaultMessage: 'Total time spent indexing alerts during last Rule execution', + } +); + export const COLUMN_QUERY_TIMES = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.columns.queryTimes', { @@ -371,6 +383,13 @@ export const COLUMN_QUERY_TIMES = i18n.translate( } ); +export const COLUMN_QUERY_TIMES_TOOLTIP = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.columns.queryTimesTooltip', + { + defaultMessage: 'Total time spent querying source indices during last Rule execution', + } +); + export const COLUMN_GAP = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.columns.gap', { From 2498b6212cf0b34196d3e254c4e9f6a4c3a3d050 Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Fri, 8 Oct 2021 08:42:44 +0300 Subject: [PATCH 16/74] [MetricVis] Move the expression function and renderer to a separate plugin. (#113427) * Added expressionMetricVis plugin. * splitted metric vis_type and expression. * Fixed check errors. * Fixed plugin docs. * updated snapshots. * Added code owner to expressionMetricVis. * updated chart docs. * Changed the `Metric` type export. * Added storybook. * Added bucket story. * Added without label story. * Added "with custom font size" story. * Added other stories. * Added DatatableColumn accessors. * Replaced Metric with MetricOptions. * Fixed MetricOptions.any * updated the check of min/max existence. * Updated function/renderer names. metricRenderer -> metricVisRenderer metricFunction -> metricVisFunction * updated snapshot. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .github/CODEOWNERS | 1 + .i18nrc.json | 1 + api_docs/charts.json | 92 +++---- docs/developer/plugin-list.asciidoc | 4 + packages/kbn-optimizer/limits.yml | 3 +- .../__tests__/enumerate_patterns.test.js | 6 +- .../__tests__/transforms.test.js | 12 +- src/dev/storybook/aliases.ts | 1 + .../expression_metric/.i18nrc.json | 6 + .../expression_metric/.storybook/main.js | 23 ++ .../expression_metric/README.md | 9 + .../expression_metric/common/constants.ts | 9 + .../metric_vis_function.test.ts.snap} | 2 +- .../common/expression_functions/index.ts | 9 + .../metric_vis_function.test.ts} | 13 +- .../metric_vis_function.ts} | 82 ++---- .../expression_metric/common/index.ts | 25 ++ .../common/types/expression_functions.ts | 49 ++++ .../common/types/expression_renderers.ts | 46 ++++ .../expression_metric/common/types/index.ts | 10 + .../expression_metric/jest.config.js | 19 ++ .../expression_metric/kibana.json | 15 ++ .../public/__mocks__/format_service.ts | 13 + .../__stories__/metric_renderer.stories.tsx | 250 ++++++++++++++++++ .../metric_component.test.tsx.snap} | 0 .../public/components/metric.scss} | 0 .../components/metric_component.test.tsx} | 4 +- .../public/components/metric_component.tsx} | 48 ++-- .../public/components/metric_value.test.tsx} | 2 +- .../public/components/metric_value.tsx} | 6 +- .../public/expression_renderers/index.ts | 9 + .../metric_vis_renderer.tsx | 13 +- .../public/format_service.ts} | 9 +- .../expression_metric/public/index.ts | 13 + .../expression_metric/public/plugin.ts | 36 +++ .../expression_metric/server/index.ts | 13 + .../expression_metric/server/plugin.ts | 34 +++ .../expression_metric/tsconfig.json | 23 ++ src/plugins/charts/common/index.ts | 34 ++- .../static/color_maps/color_maps.ts | 0 .../static/color_maps/heatmap_color.test.ts | 0 .../static/color_maps/heatmap_color.ts | 0 .../static/color_maps/index.ts | 0 .../static/color_maps/mock.ts | 0 .../static/color_maps/truncated_color_maps.ts | 0 .../static/components/collections.ts | 0 .../charts/common/static/components/index.ts | 9 + src/plugins/charts/common/static/index.ts | 21 ++ .../static/components => common}/types.ts | 3 +- src/plugins/charts/kibana.json | 1 + src/plugins/charts/public/index.ts | 15 ++ src/plugins/charts/public/mocks.ts | 2 +- .../charts/public/static/components/index.ts | 2 - src/plugins/charts/public/static/index.ts | 1 - src/plugins/vis_types/metric/kibana.json | 2 +- .../metric/public/components/index.ts | 9 + .../metric/public/metric_vis_type.ts | 2 +- src/plugins/vis_types/metric/public/plugin.ts | 25 +- src/plugins/vis_types/metric/public/to_ast.ts | 4 +- src/plugins/vis_types/metric/public/types.ts | 9 - src/plugins/vis_types/metric/tsconfig.json | 4 +- .../screenshots/baseline/metric_all_data.png | Bin 29776 -> 29812 bytes .../baseline/metric_empty_data.png | Bin 5163 -> 5152 bytes .../baseline/metric_multi_metric_data.png | Bin 42478 -> 43524 bytes .../baseline/metric_percentage_mode.png | Bin 22032 -> 30182 bytes .../baseline/metric_single_metric_data.png | Bin 20209 -> 27840 bytes .../snapshots/baseline/combined_test3.json | 2 +- .../snapshots/baseline/final_output_test.json | 2 +- .../snapshots/baseline/metric_all_data.json | 2 +- .../snapshots/baseline/metric_empty_data.json | 2 +- .../baseline/metric_multi_metric_data.json | 2 +- .../baseline/metric_percentage_mode.json | 2 +- .../baseline/metric_single_metric_data.json | 2 +- .../snapshots/baseline/partial_test_2.json | 2 +- .../snapshots/baseline/step_output_test3.json | 2 +- .../snapshots/session/combined_test3.json | 2 +- .../snapshots/session/final_output_test.json | 2 +- .../snapshots/session/metric_all_data.json | 2 +- .../snapshots/session/metric_empty_data.json | 2 +- .../session/metric_multi_metric_data.json | 2 +- .../session/metric_percentage_mode.json | 2 +- .../session/metric_single_metric_data.json | 2 +- .../snapshots/session/partial_test_2.json | 2 +- .../snapshots/session/step_output_test3.json | 2 +- .../translations/translations/ja-JP.json | 30 +-- .../translations/translations/zh-CN.json | 30 +-- 86 files changed, 884 insertions(+), 265 deletions(-) create mode 100755 src/plugins/chart_expressions/expression_metric/.i18nrc.json create mode 100644 src/plugins/chart_expressions/expression_metric/.storybook/main.js create mode 100755 src/plugins/chart_expressions/expression_metric/README.md create mode 100644 src/plugins/chart_expressions/expression_metric/common/constants.ts rename src/plugins/{vis_types/metric/public/__snapshots__/metric_vis_fn.test.ts.snap => chart_expressions/expression_metric/common/expression_functions/__snapshots__/metric_vis_function.test.ts.snap} (98%) create mode 100644 src/plugins/chart_expressions/expression_metric/common/expression_functions/index.ts rename src/plugins/{vis_types/metric/public/metric_vis_fn.test.ts => chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts} (81%) rename src/plugins/{vis_types/metric/public/metric_vis_fn.ts => chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts} (66%) create mode 100755 src/plugins/chart_expressions/expression_metric/common/index.ts create mode 100644 src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts create mode 100644 src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts create mode 100644 src/plugins/chart_expressions/expression_metric/common/types/index.ts create mode 100644 src/plugins/chart_expressions/expression_metric/jest.config.js create mode 100755 src/plugins/chart_expressions/expression_metric/kibana.json create mode 100644 src/plugins/chart_expressions/expression_metric/public/__mocks__/format_service.ts create mode 100644 src/plugins/chart_expressions/expression_metric/public/__stories__/metric_renderer.stories.tsx rename src/plugins/{vis_types/metric/public/components/__snapshots__/metric_vis_component.test.tsx.snap => chart_expressions/expression_metric/public/components/__snapshots__/metric_component.test.tsx.snap} (100%) rename src/plugins/{vis_types/metric/public/components/metric_vis.scss => chart_expressions/expression_metric/public/components/metric.scss} (100%) rename src/plugins/{vis_types/metric/public/components/metric_vis_component.test.tsx => chart_expressions/expression_metric/public/components/metric_component.test.tsx} (97%) rename src/plugins/{vis_types/metric/public/components/metric_vis_component.tsx => chart_expressions/expression_metric/public/components/metric_component.tsx} (81%) rename src/plugins/{vis_types/metric/public/components/metric_vis_value.test.tsx => chart_expressions/expression_metric/public/components/metric_value.test.tsx} (97%) rename src/plugins/{vis_types/metric/public/components/metric_vis_value.tsx => chart_expressions/expression_metric/public/components/metric_value.tsx} (94%) create mode 100644 src/plugins/chart_expressions/expression_metric/public/expression_renderers/index.ts rename src/plugins/{vis_types/metric/public => chart_expressions/expression_metric/public/expression_renderers}/metric_vis_renderer.tsx (73%) rename src/plugins/{vis_types/metric/public/services.ts => chart_expressions/expression_metric/public/format_service.ts} (57%) create mode 100644 src/plugins/chart_expressions/expression_metric/public/index.ts create mode 100644 src/plugins/chart_expressions/expression_metric/public/plugin.ts create mode 100644 src/plugins/chart_expressions/expression_metric/server/index.ts create mode 100644 src/plugins/chart_expressions/expression_metric/server/plugin.ts create mode 100644 src/plugins/chart_expressions/expression_metric/tsconfig.json rename src/plugins/charts/{public => common}/static/color_maps/color_maps.ts (100%) rename src/plugins/charts/{public => common}/static/color_maps/heatmap_color.test.ts (100%) rename src/plugins/charts/{public => common}/static/color_maps/heatmap_color.ts (100%) rename src/plugins/charts/{public => common}/static/color_maps/index.ts (100%) rename src/plugins/charts/{public => common}/static/color_maps/mock.ts (100%) rename src/plugins/charts/{public => common}/static/color_maps/truncated_color_maps.ts (100%) rename src/plugins/charts/{public => common}/static/components/collections.ts (100%) create mode 100644 src/plugins/charts/common/static/components/index.ts create mode 100644 src/plugins/charts/common/static/index.ts rename src/plugins/charts/{public/static/components => common}/types.ts (88%) create mode 100644 src/plugins/vis_types/metric/public/components/index.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 59c238ec058a..17150e3c98ce 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -39,6 +39,7 @@ /src/plugins/visualize/ @elastic/kibana-vis-editors /src/plugins/visualizations/ @elastic/kibana-vis-editors /src/plugins/chart_expressions/expression_tagcloud/ @elastic/kibana-vis-editors +/src/plugins/chart_expressions/expression_metric/ @elastic/kibana-vis-editors /src/plugins/url_forwarding/ @elastic/kibana-vis-editors /packages/kbn-tinymath/ @elastic/kibana-vis-editors /x-pack/test/functional/apps/lens @elastic/kibana-vis-editors diff --git a/.i18nrc.json b/.i18nrc.json index 707c1cef2594..63e4cf6d2fbb 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -30,6 +30,7 @@ "expressionRevealImage": "src/plugins/expression_reveal_image", "expressionShape": "src/plugins/expression_shape", "expressionTagcloud": "src/plugins/chart_expressions/expression_tagcloud", + "expressionMetricVis": "src/plugins/chart_expressions/expression_metric", "inputControl": "src/plugins/input_control_vis", "inspector": "src/plugins/inspector", "inspectorViews": "src/legacy/core_plugins/inspector_views", diff --git a/api_docs/charts.json b/api_docs/charts.json index 642ae89e581e..9f6d07287eba 100644 --- a/api_docs/charts.json +++ b/api_docs/charts.json @@ -478,7 +478,7 @@ "signature": [ "(value: any, colorSchemaName: string) => string" ], - "path": "src/plugins/charts/public/static/color_maps/heatmap_color.ts", + "path": "src/plugins/charts/common/static/color_maps/heatmap_color.ts", "deprecated": false, "children": [ { @@ -491,7 +491,7 @@ "signature": [ "any" ], - "path": "src/plugins/charts/public/static/color_maps/heatmap_color.ts", + "path": "src/plugins/charts/common/static/color_maps/heatmap_color.ts", "deprecated": false, "isRequired": true }, @@ -505,7 +505,7 @@ "signature": [ "string" ], - "path": "src/plugins/charts/public/static/color_maps/heatmap_color.ts", + "path": "src/plugins/charts/common/static/color_maps/heatmap_color.ts", "deprecated": false, "isRequired": true } @@ -936,7 +936,7 @@ "tags": [], "label": "ColorMap", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "children": [ { @@ -949,7 +949,7 @@ "signature": [ "any" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false } ], @@ -962,7 +962,7 @@ "tags": [], "label": "ColorSchema", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "children": [ { @@ -981,7 +981,7 @@ "text": "ColorSchemas" } ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -991,7 +991,7 @@ "tags": [], "label": "text", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false } ], @@ -1817,7 +1817,7 @@ "tags": [], "label": "RawColorSchema", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "children": [ { @@ -1836,7 +1836,7 @@ "text": "ColorSchemas" } ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -1846,7 +1846,7 @@ "tags": [], "label": "label", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -1859,7 +1859,7 @@ "signature": [ "[number, number[]][]" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false } ], @@ -2011,7 +2011,7 @@ "tags": [], "label": "ColorSchemas", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "initialIsOpen": false } @@ -2027,7 +2027,7 @@ "signature": [ "\"Background\" | \"Labels\" | \"None\"" ], - "path": "src/plugins/charts/public/static/components/collections.ts", + "path": "src/plugins/charts/common/static/components/collections.ts", "deprecated": false, "initialIsOpen": false }, @@ -2048,7 +2048,7 @@ }, "[]" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "initialIsOpen": false }, @@ -2059,7 +2059,7 @@ "tags": [], "label": "defaultCountLabel", "description": [], - "path": "src/plugins/charts/public/static/components/collections.ts", + "path": "src/plugins/charts/common/static/components/collections.ts", "deprecated": false, "initialIsOpen": false }, @@ -2073,7 +2073,7 @@ "signature": [ "number" ], - "path": "src/plugins/charts/public/static/components/collections.ts", + "path": "src/plugins/charts/common/static/components/collections.ts", "deprecated": false, "initialIsOpen": false }, @@ -2124,7 +2124,7 @@ }, "[]" ], - "path": "src/plugins/charts/public/static/color_maps/truncated_color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/truncated_color_maps.ts", "deprecated": false, "initialIsOpen": false } @@ -2140,7 +2140,7 @@ "signature": [ "{ readonly Background: \"Background\"; readonly Labels: \"Labels\"; readonly None: \"None\"; }" ], - "path": "src/plugins/charts/public/static/components/collections.ts", + "path": "src/plugins/charts/common/static/components/collections.ts", "deprecated": false, "initialIsOpen": false }, @@ -2154,7 +2154,7 @@ "signature": [ "{ readonly Horizontal: number; readonly Vertical: number; readonly Angled: number; }" ], - "path": "src/plugins/charts/public/static/components/collections.ts", + "path": "src/plugins/charts/common/static/components/collections.ts", "deprecated": false, "initialIsOpen": false }, @@ -2165,7 +2165,7 @@ "tags": [], "label": "truncatedColorMaps", "description": [], - "path": "src/plugins/charts/public/static/color_maps/truncated_color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/truncated_color_maps.ts", "deprecated": false, "children": [], "initialIsOpen": false @@ -2177,7 +2177,7 @@ "tags": [], "label": "vislibColorMaps", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "children": [ { @@ -2189,7 +2189,7 @@ "description": [ "// Sequential" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "children": [ { @@ -2209,7 +2209,7 @@ }, ".Blues" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -2219,7 +2219,7 @@ "tags": [], "label": "label", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -2232,7 +2232,7 @@ "signature": [ "[number, number[]][]" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false } ] @@ -2244,7 +2244,7 @@ "tags": [], "label": "[ColorSchemas.Greens]", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "children": [ { @@ -2264,7 +2264,7 @@ }, ".Greens" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -2274,7 +2274,7 @@ "tags": [], "label": "label", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -2287,7 +2287,7 @@ "signature": [ "[number, number[]][]" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false } ] @@ -2299,7 +2299,7 @@ "tags": [], "label": "[ColorSchemas.Greys]", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "children": [ { @@ -2319,7 +2319,7 @@ }, ".Greys" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -2329,7 +2329,7 @@ "tags": [], "label": "label", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -2342,7 +2342,7 @@ "signature": [ "[number, number[]][]" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false } ] @@ -2354,7 +2354,7 @@ "tags": [], "label": "[ColorSchemas.Reds]", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "children": [ { @@ -2374,7 +2374,7 @@ }, ".Reds" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -2384,7 +2384,7 @@ "tags": [], "label": "label", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -2397,7 +2397,7 @@ "signature": [ "[number, number[]][]" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false } ] @@ -2409,7 +2409,7 @@ "tags": [], "label": "[ColorSchemas.YellowToRed]", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "children": [ { @@ -2429,7 +2429,7 @@ }, ".YellowToRed" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -2439,7 +2439,7 @@ "tags": [], "label": "label", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -2452,7 +2452,7 @@ "signature": [ "[number, number[]][]" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false } ] @@ -2464,7 +2464,7 @@ "tags": [], "label": "[ColorSchemas.GreenToRed]", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "children": [ { @@ -2484,7 +2484,7 @@ }, ".GreenToRed" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -2494,7 +2494,7 @@ "tags": [], "label": "label", "description": [], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false }, { @@ -2507,7 +2507,7 @@ "signature": [ "[number, number[]][]" ], - "path": "src/plugins/charts/public/static/color_maps/color_maps.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false } ] diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index f9cbd35d7286..0e728a4dada2 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -94,6 +94,10 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a |Expression Metric plugin adds a metric renderer and function to the expression plugin. +|{kib-repo}blob/{branch}/src/plugins/chart_expressions/expression_metric/README.md[expressionMetricVis] +|Expression MetricVis plugin adds a metric renderer and function to the expression plugin. The renderer will display the metric chart. + + |{kib-repo}blob/{branch}/src/plugins/expression_repeat_image/README.md[expressionRepeatImage] |Expression Repeat Image plugin adds a repeatImage function to the expression plugin and an associated renderer. The renderer will display the given image in mutliple instances. diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 80cd01e05b87..acef661d93bb 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -80,7 +80,6 @@ pageLoadAssetSize: usageCollection: 39762 visDefaultEditor: 50178 visTypeMarkdown: 30896 - visTypeMetric: 42790 visTypeTable: 94934 visTypeTagcloud: 37575 visTypeTimelion: 68883 @@ -115,4 +114,6 @@ pageLoadAssetSize: expressions: 239290 securitySolution: 231753 customIntegrations: 28810 + expressionMetricVis: 23121 + visTypeMetric: 23332 dataViews: 42000 diff --git a/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js b/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js index fa82641e142d..05af7c2a154a 100644 --- a/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js +++ b/src/dev/code_coverage/ingest_coverage/__tests__/enumerate_patterns.test.js @@ -26,13 +26,13 @@ describe(`enumeratePatterns`, () => { ) ).toBe(true); }); - it(`should resolve src/plugins/charts/public/static/color_maps/color_maps.ts to kibana-app`, () => { + it(`should resolve src/plugins/charts/common/static/color_maps/color_maps.ts to kibana-app`, () => { const actual = enumeratePatterns(REPO_ROOT)(log)( - new Map([['src/plugins/charts/public/static/color_maps', ['kibana-app']]]) + new Map([['src/plugins/charts/common/static/color_maps', ['kibana-app']]]) ); expect(actual[0][0]).toBe( - 'src/plugins/charts/public/static/color_maps/color_maps.ts kibana-app' + 'src/plugins/charts/common/static/color_maps/color_maps.ts kibana-app' ); }); it(`should resolve x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/translations.ts to kibana-security`, () => { diff --git a/src/dev/code_coverage/ingest_coverage/__tests__/transforms.test.js b/src/dev/code_coverage/ingest_coverage/__tests__/transforms.test.js index 60a68253d2c4..5e33f2ba548a 100644 --- a/src/dev/code_coverage/ingest_coverage/__tests__/transforms.test.js +++ b/src/dev/code_coverage/ingest_coverage/__tests__/transforms.test.js @@ -98,7 +98,7 @@ describe(`Transform fns`, () => { }); }); - describe(`with a coveredFilePath of src/plugins/charts/public/static/color_maps/color_maps.ts`, () => { + describe(`with a coveredFilePath of src/plugins/charts/common/static/color_maps/color_maps.ts`, () => { const expected = 'kibana-reporting'; it(`should resolve to ${expected}`, async () => { const actual = await teamAssignment(teamAssignmentsPathMOCK)(log)(obj); @@ -110,25 +110,25 @@ describe(`Transform fns`, () => { describe(`last fn`, () => { describe(`applied to n results`, () => { it(`should pick the last one`, () => { - const nteams = `src/plugins/charts/public/static/color_maps/color_maps.ts kibana-app -src/plugins/charts/public/static/color_maps/color_maps.ts kibana-app-arch`; + const nteams = `src/plugins/charts/common/static/color_maps/color_maps.ts kibana-app +src/plugins/charts/common/static/color_maps/color_maps.ts kibana-app-arch`; const actual = last(nteams); expect(actual).toBe( - 'src/plugins/charts/public/static/color_maps/color_maps.ts kibana-app-arch' + 'src/plugins/charts/common/static/color_maps/color_maps.ts kibana-app-arch' ); }); }); describe(`applied to 1 result`, () => { it(`should pick that 1 result`, () => { const nteams = - 'src/plugins/charts/public/static/color_maps/color_maps.ts kibana-app-arch'; + 'src/plugins/charts/common/static/color_maps/color_maps.ts kibana-app-arch'; const actual = last(nteams); expect(actual).toBe( - 'src/plugins/charts/public/static/color_maps/color_maps.ts kibana-app-arch' + 'src/plugins/charts/common/static/color_maps/color_maps.ts kibana-app-arch' ); }); }); diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index c04f0d4f9320..ee09c94477af 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -25,6 +25,7 @@ export const storybookAliases = { expression_reveal_image: 'src/plugins/expression_reveal_image/.storybook', expression_shape: 'src/plugins/expression_shape/.storybook', expression_tagcloud: 'src/plugins/chart_expressions/expression_tagcloud/.storybook', + expression_metric_vis: 'src/plugins/chart_expressions/expression_metric/.storybook', fleet: 'x-pack/plugins/fleet/storybook', infra: 'x-pack/plugins/infra/.storybook', security_solution: 'x-pack/plugins/security_solution/.storybook', diff --git a/src/plugins/chart_expressions/expression_metric/.i18nrc.json b/src/plugins/chart_expressions/expression_metric/.i18nrc.json new file mode 100755 index 000000000000..a107bad97f27 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/.i18nrc.json @@ -0,0 +1,6 @@ +{ + "prefix": "expressionMetricVis", + "paths": { + "expressionMetricVis": "." + } +} diff --git a/src/plugins/chart_expressions/expression_metric/.storybook/main.js b/src/plugins/chart_expressions/expression_metric/.storybook/main.js new file mode 100644 index 000000000000..cb483d539428 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/.storybook/main.js @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { defaultConfig } from '@kbn/storybook'; +import webpackMerge from 'webpack-merge'; +import { resolve } from 'path'; + +const mockConfig = { + resolve: { + alias: { + '../format_service': resolve(__dirname, '../public/__mocks__/format_service.ts'), + }, + }, +}; + +module.exports = { + ...defaultConfig, + webpackFinal: (config) => webpackMerge(config, mockConfig), +}; diff --git a/src/plugins/chart_expressions/expression_metric/README.md b/src/plugins/chart_expressions/expression_metric/README.md new file mode 100755 index 000000000000..ec2541ebf74d --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/README.md @@ -0,0 +1,9 @@ +# expressionMetricVis + +Expression MetricVis plugin adds a `metric` renderer and function to the expression plugin. The renderer will display the `metric` chart. + +--- + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment. diff --git a/src/plugins/chart_expressions/expression_metric/common/constants.ts b/src/plugins/chart_expressions/expression_metric/common/constants.ts new file mode 100644 index 000000000000..b39902f61ac4 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/common/constants.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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const EXPRESSION_METRIC_NAME = 'metricVis'; diff --git a/src/plugins/vis_types/metric/public/__snapshots__/metric_vis_fn.test.ts.snap b/src/plugins/chart_expressions/expression_metric/common/expression_functions/__snapshots__/metric_vis_function.test.ts.snap similarity index 98% rename from src/plugins/vis_types/metric/public/__snapshots__/metric_vis_fn.test.ts.snap rename to src/plugins/chart_expressions/expression_metric/common/expression_functions/__snapshots__/metric_vis_function.test.ts.snap index a3be4d10d9bf..03055764cc4a 100644 --- a/src/plugins/vis_types/metric/public/__snapshots__/metric_vis_fn.test.ts.snap +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/__snapshots__/metric_vis_function.test.ts.snap @@ -22,7 +22,7 @@ Object { exports[`interpreter/functions#metric returns an object with the correct structure 1`] = ` Object { - "as": "metric_vis", + "as": "metricVis", "type": "render", "value": Object { "visConfig": Object { diff --git a/src/plugins/chart_expressions/expression_metric/common/expression_functions/index.ts b/src/plugins/chart_expressions/expression_metric/common/expression_functions/index.ts new file mode 100644 index 000000000000..5eccaa62fe46 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { metricVisFunction } from './metric_vis_function'; diff --git a/src/plugins/vis_types/metric/public/metric_vis_fn.test.ts b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts similarity index 81% rename from src/plugins/vis_types/metric/public/metric_vis_fn.test.ts rename to src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts index 28124a653629..1f90322e703b 100644 --- a/src/plugins/vis_types/metric/public/metric_vis_fn.test.ts +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.test.ts @@ -6,14 +6,13 @@ * Side Public License, v 1. */ -import { createMetricVisFn } from './metric_vis_fn'; -import { functionWrapper } from '../../../expressions/common/expression_functions/specs/tests/utils'; -import { Datatable } from '../../../expressions/common/expression_types/specs'; - -type Arguments = Parameters['fn']>[1]; +import { metricVisFunction } from './metric_vis_function'; +import type { MetricArguments } from '../../common'; +import { functionWrapper } from '../../../../expressions/common/expression_functions/specs/tests/utils'; +import { Datatable } from '../../../../expressions/common/expression_types/specs'; describe('interpreter/functions#metric', () => { - const fn = functionWrapper(createMetricVisFn()); + const fn = functionWrapper(metricVisFunction()); const context = { type: 'datatable', rows: [{ 'col-0-1': 0 }], @@ -52,7 +51,7 @@ describe('interpreter/functions#metric', () => { aggType: 'count', }, ], - } as unknown as Arguments; + } as unknown as MetricArguments; it('returns an object with the correct structure', () => { const actual = fn(context, args, undefined); diff --git a/src/plugins/vis_types/metric/public/metric_vis_fn.ts b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts similarity index 66% rename from src/plugins/vis_types/metric/public/metric_vis_fn.ts rename to src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts index 210552732bc0..31f5b8421b3a 100644 --- a/src/plugins/vis_types/metric/public/metric_vis_fn.ts +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts @@ -8,60 +8,24 @@ import { i18n } from '@kbn/i18n'; -import { - ExpressionFunctionDefinition, - Datatable, - Range, - Render, - Style, -} from '../../../expressions/public'; -import { visType, VisParams } from './types'; -import { prepareLogTable, Dimension } from '../../../visualizations/public'; -import { ColorSchemas, vislibColorMaps, ColorMode } from '../../../charts/public'; -import { ExpressionValueVisDimension } from '../../../visualizations/public'; +import { visType } from '../types'; +import { prepareLogTable, Dimension } from '../../../../visualizations/common/prepare_log_table'; +import { vislibColorMaps, ColorMode } from '../../../../charts/common'; +import { MetricVisExpressionFunctionDefinition } from '../types'; +import { EXPRESSION_METRIC_NAME } from '../constants'; -export type Input = Datatable; - -interface Arguments { - percentageMode: boolean; - colorSchema: ColorSchemas; - colorMode: ColorMode; - useRanges: boolean; - invertColors: boolean; - showLabels: boolean; - bgFill: string; - subText: string; - colorRange: Range[]; - font: Style; - metric: ExpressionValueVisDimension[]; - bucket: ExpressionValueVisDimension; -} - -export interface MetricVisRenderValue { - visType: typeof visType; - visData: Input; - visConfig: Pick; -} - -export type MetricVisExpressionFunctionDefinition = ExpressionFunctionDefinition< - 'metricVis', - Input, - Arguments, - Render ->; - -export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ - name: 'metricVis', +export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({ + name: EXPRESSION_METRIC_NAME, type: 'render', inputTypes: ['datatable'], - help: i18n.translate('visTypeMetric.function.help', { + help: i18n.translate('expressionMetricVis.function.help', { defaultMessage: 'Metric visualization', }), args: { percentageMode: { types: ['boolean'], default: false, - help: i18n.translate('visTypeMetric.function.percentageMode.help', { + help: i18n.translate('expressionMetricVis.function.percentageMode.help', { defaultMessage: 'Shows metric in percentage mode. Requires colorRange to be set.', }), }, @@ -69,7 +33,7 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ types: ['string'], default: '"Green to Red"', options: Object.values(vislibColorMaps).map((value: any) => value.id), - help: i18n.translate('visTypeMetric.function.colorSchema.help', { + help: i18n.translate('expressionMetricVis.function.colorSchema.help', { defaultMessage: 'Color schema to use', }), }, @@ -77,7 +41,7 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ types: ['string'], default: '"None"', options: [ColorMode.None, ColorMode.Labels, ColorMode.Background], - help: i18n.translate('visTypeMetric.function.colorMode.help', { + help: i18n.translate('expressionMetricVis.function.colorMode.help', { defaultMessage: 'Which part of metric to color', }), }, @@ -85,7 +49,7 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ types: ['range'], multi: true, default: '{range from=0 to=10000}', - help: i18n.translate('visTypeMetric.function.colorRange.help', { + help: i18n.translate('expressionMetricVis.function.colorRange.help', { defaultMessage: 'A range object specifying groups of values to which different colors should be applied.', }), @@ -93,21 +57,21 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ useRanges: { types: ['boolean'], default: false, - help: i18n.translate('visTypeMetric.function.useRanges.help', { + help: i18n.translate('expressionMetricVis.function.useRanges.help', { defaultMessage: 'Enabled color ranges.', }), }, invertColors: { types: ['boolean'], default: false, - help: i18n.translate('visTypeMetric.function.invertColors.help', { + help: i18n.translate('expressionMetricVis.function.invertColors.help', { defaultMessage: 'Inverts the color ranges', }), }, showLabels: { types: ['boolean'], default: true, - help: i18n.translate('visTypeMetric.function.showLabels.help', { + help: i18n.translate('expressionMetricVis.function.showLabels.help', { defaultMessage: 'Shows labels under the metric values.', }), }, @@ -115,14 +79,14 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ types: ['string'], default: '"#000"', aliases: ['backgroundFill', 'bgColor', 'backgroundColor'], - help: i18n.translate('visTypeMetric.function.bgFill.help', { + help: i18n.translate('expressionMetricVis.function.bgFill.help', { defaultMessage: 'Color as html hex code (#123456), html color (red, blue) or rgba value (rgba(255,255,255,1)).', }), }, font: { types: ['style'], - help: i18n.translate('visTypeMetric.function.font.help', { + help: i18n.translate('expressionMetricVis.function.font.help', { defaultMessage: 'Font settings.', }), default: '{font size=60}', @@ -131,13 +95,13 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ types: ['string'], aliases: ['label', 'text', 'description'], default: '""', - help: i18n.translate('visTypeMetric.function.subText.help', { + help: i18n.translate('expressionMetricVis.function.subText.help', { defaultMessage: 'Custom text to show under the metric', }), }, metric: { types: ['vis_dimension'], - help: i18n.translate('visTypeMetric.function.metric.help', { + help: i18n.translate('expressionMetricVis.function.metric.help', { defaultMessage: 'metric dimension configuration', }), required: true, @@ -145,7 +109,7 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ }, bucket: { types: ['vis_dimension'], - help: i18n.translate('visTypeMetric.function.bucket.help', { + help: i18n.translate('expressionMetricVis.function.bucket.help', { defaultMessage: 'bucket dimension configuration', }), }, @@ -161,7 +125,7 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ const argsTable: Dimension[] = [ [ args.metric, - i18n.translate('visTypeMetric.function.dimension.metric', { + i18n.translate('expressionMetricVis.function.dimension.metric', { defaultMessage: 'Metric', }), ], @@ -169,7 +133,7 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ if (args.bucket) { argsTable.push([ [args.bucket], - i18n.translate('visTypeMetric.function.adimension.splitGroup', { + i18n.translate('expressionMetricVis.function.dimension.splitGroup', { defaultMessage: 'Split group', }), ]); @@ -180,7 +144,7 @@ export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ return { type: 'render', - as: 'metric_vis', + as: EXPRESSION_METRIC_NAME, value: { visData: input, visType, diff --git a/src/plugins/chart_expressions/expression_metric/common/index.ts b/src/plugins/chart_expressions/expression_metric/common/index.ts new file mode 100755 index 000000000000..ee023dca2f4f --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/common/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const PLUGIN_ID = 'expressionMetricVis'; +export const PLUGIN_NAME = 'expressionMetricVis'; + +export type { + MetricArguments, + MetricInput, + MetricVisRenderConfig, + MetricVisExpressionFunctionDefinition, + DimensionsVisParam, + MetricVisParam, + VisParams, + MetricOptions, +} from './types'; + +export { metricVisFunction } from './expression_functions'; + +export { EXPRESSION_METRIC_NAME } from './constants'; diff --git a/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts b/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts new file mode 100644 index 000000000000..5e8b01ec9300 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { + Datatable, + ExpressionFunctionDefinition, + Range, + ExpressionValueRender, + Style, +} from '../../../../expressions'; +import { ExpressionValueVisDimension } from '../../../../visualizations/common'; +import { ColorSchemas, ColorMode } from '../../../../charts/common'; +import { VisParams, visType } from './expression_renderers'; +import { EXPRESSION_METRIC_NAME } from '../constants'; + +export interface MetricArguments { + percentageMode: boolean; + colorSchema: ColorSchemas; + colorMode: ColorMode; + useRanges: boolean; + invertColors: boolean; + showLabels: boolean; + bgFill: string; + subText: string; + colorRange: Range[]; + font: Style; + metric: ExpressionValueVisDimension[]; + bucket: ExpressionValueVisDimension; +} + +export type MetricInput = Datatable; + +export interface MetricVisRenderConfig { + visType: typeof visType; + visData: MetricInput; + visConfig: Pick; +} + +export type MetricVisExpressionFunctionDefinition = ExpressionFunctionDefinition< + typeof EXPRESSION_METRIC_NAME, + MetricInput, + MetricArguments, + ExpressionValueRender +>; diff --git a/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts b/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts new file mode 100644 index 000000000000..2cc7ce853f8b --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { Range } from '../../../../expressions/common'; +import { ExpressionValueVisDimension } from '../../../../visualizations/common'; +import { ColorMode, Labels, Style, ColorSchemas } from '../../../../charts/common'; + +export const visType = 'metric'; + +export interface DimensionsVisParam { + metrics: ExpressionValueVisDimension[]; + bucket?: ExpressionValueVisDimension; +} + +export interface MetricVisParam { + percentageMode: boolean; + percentageFormatPattern?: string; + useRanges: boolean; + colorSchema: ColorSchemas; + metricColorMode: ColorMode; + colorsRange: Range[]; + labels: Labels; + invertColors: boolean; + style: Style; +} + +export interface VisParams { + addTooltip: boolean; + addLegend: boolean; + dimensions: DimensionsVisParam; + metric: MetricVisParam; + type: typeof visType; +} + +export interface MetricOptions { + value: string; + label: string; + color?: string; + bgColor?: string; + lightText: boolean; + rowIndex: number; +} diff --git a/src/plugins/chart_expressions/expression_metric/common/types/index.ts b/src/plugins/chart_expressions/expression_metric/common/types/index.ts new file mode 100644 index 000000000000..9c50bfab1305 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/common/types/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './expression_functions'; +export * from './expression_renderers'; diff --git a/src/plugins/chart_expressions/expression_metric/jest.config.js b/src/plugins/chart_expressions/expression_metric/jest.config.js new file mode 100644 index 000000000000..d737b45f34e6 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/jest.config.js @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../', + roots: ['/src/plugins/chart_expressions/expression_metric'], + coverageDirectory: + '/target/kibana-coverage/jest/src/plugins/chart_expressions/expression_metric', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/src/plugins/chart_expressions/expression_metric/{common,public,server}/**/*.{ts,tsx}', + ], +}; diff --git a/src/plugins/chart_expressions/expression_metric/kibana.json b/src/plugins/chart_expressions/expression_metric/kibana.json new file mode 100755 index 000000000000..c662dc131032 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/kibana.json @@ -0,0 +1,15 @@ +{ + "id": "expressionMetricVis", + "version": "1.0.0", + "kibanaVersion": "kibana", + "owner": { + "name": "Vis Editors", + "githubTeam": "kibana-vis-editors" + }, + "description": "Expression MetricVis plugin adds a `metric` renderer and function to the expression plugin. The renderer will display the `metric` chart.", + "server": true, + "ui": true, + "requiredPlugins": ["expressions", "fieldFormats", "charts", "visualizations", "presentationUtil"], + "requiredBundles": ["kibanaUtils"], + "optionalPlugins": [] +} diff --git a/src/plugins/chart_expressions/expression_metric/public/__mocks__/format_service.ts b/src/plugins/chart_expressions/expression_metric/public/__mocks__/format_service.ts new file mode 100644 index 000000000000..77f6d8eb0bf3 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/public/__mocks__/format_service.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const getFormatService = () => ({ + deserialize: (target: any) => ({ + convert: (text: string, format: string) => text, + }), +}); diff --git a/src/plugins/chart_expressions/expression_metric/public/__stories__/metric_renderer.stories.tsx b/src/plugins/chart_expressions/expression_metric/public/__stories__/metric_renderer.stories.tsx new file mode 100644 index 000000000000..b22616af01c9 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/public/__stories__/metric_renderer.stories.tsx @@ -0,0 +1,250 @@ +/* + * 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 { storiesOf } from '@storybook/react'; +import { ExpressionValueVisDimension } from '../../../../visualizations/common'; +import { DatatableColumn, Range } from '../../../../expressions'; +import { Render } from '../../../../presentation_util/public/__stories__'; +import { ColorMode, ColorSchemas } from '../../../../charts/common'; +import { metricVisRenderer } from '../expression_renderers'; +import { MetricVisRenderConfig, visType } from '../../common/types'; + +const config: MetricVisRenderConfig = { + visType, + visData: { + type: 'datatable', + rows: [{ 'col-0-1': 85, 'col-0-2': 30 }], + columns: [ + { + id: 'col-0-1', + name: 'Max products count', + meta: { type: 'number', params: {} }, + }, + { + id: 'col-0-2', + name: 'Median products count', + meta: { type: 'number', params: {} }, + }, + ], + }, + visConfig: { + metric: { + percentageMode: false, + useRanges: false, + colorSchema: ColorSchemas.GreenToRed, + metricColorMode: ColorMode.None, + colorsRange: [], + labels: { show: true }, + invertColors: false, + style: { + bgColor: false, + bgFill: '#000', + fontSize: 60, + labelColor: false, + subText: '', + }, + }, + dimensions: { + metrics: [ + { + accessor: 0, + format: { + id: 'number', + params: {}, + }, + type: 'vis_dimension', + }, + { + accessor: { + id: 'col-0-2', + name: 'Median products count', + meta: { type: 'number' }, + }, + format: { + id: 'number', + params: {}, + }, + type: 'vis_dimension', + }, + ], + }, + }, +}; + +const dayColumn: DatatableColumn = { + id: 'col-0-3', + name: 'Day of the week', + meta: { type: 'string', params: {} }, +}; + +const dayAccessor: ExpressionValueVisDimension = { + accessor: { + id: 'col-0-3', + name: 'Day of the week', + meta: { type: 'string' }, + }, + format: { + id: 'string', + params: {}, + }, + type: 'vis_dimension', +}; + +const dataWithBuckets = [ + { 'col-0-1': 85, 'col-0-2': 30, 'col-0-3': 'Monday' }, + { 'col-0-1': 55, 'col-0-2': 32, 'col-0-3': 'Tuesday' }, + { 'col-0-1': 56, 'col-0-2': 52, 'col-0-3': 'Wednesday' }, +]; + +const colorsRange: Range[] = [ + { type: 'range', from: 0, to: 50 }, + { type: 'range', from: 51, to: 150 }, +]; + +const containerSize = { + width: '700px', + height: '700px', +}; + +storiesOf('renderers/visMetric', module) + .add('Default', () => { + return ; + }) + .add('Without labels', () => { + return ( + + ); + }) + .add('With custom font size', () => { + return ( + + ); + }) + .add('With color ranges, background color mode', () => { + return ( + + ); + }) + .add('With color ranges, labels color mode', () => { + return ( + + ); + }) + .add('With color ranges, labels color mode, reverse mode', () => { + return ( + + ); + }) + .add('With bucket', () => { + return ( + + ); + }) + .add('With empty results', () => { + return ( + + ); + }); diff --git a/src/plugins/vis_types/metric/public/components/__snapshots__/metric_vis_component.test.tsx.snap b/src/plugins/chart_expressions/expression_metric/public/components/__snapshots__/metric_component.test.tsx.snap similarity index 100% rename from src/plugins/vis_types/metric/public/components/__snapshots__/metric_vis_component.test.tsx.snap rename to src/plugins/chart_expressions/expression_metric/public/components/__snapshots__/metric_component.test.tsx.snap diff --git a/src/plugins/vis_types/metric/public/components/metric_vis.scss b/src/plugins/chart_expressions/expression_metric/public/components/metric.scss similarity index 100% rename from src/plugins/vis_types/metric/public/components/metric_vis.scss rename to src/plugins/chart_expressions/expression_metric/public/components/metric.scss diff --git a/src/plugins/vis_types/metric/public/components/metric_vis_component.test.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_component.test.tsx similarity index 97% rename from src/plugins/vis_types/metric/public/components/metric_vis_component.test.tsx rename to src/plugins/chart_expressions/expression_metric/public/components/metric_component.test.tsx index c66704c44ea0..ec3b9aee8583 100644 --- a/src/plugins/vis_types/metric/public/components/metric_vis_component.test.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_component.test.tsx @@ -9,9 +9,9 @@ import React from 'react'; import { shallow } from 'enzyme'; -import MetricVisComponent, { MetricVisComponentProps } from './metric_vis_component'; +import MetricVisComponent, { MetricVisComponentProps } from './metric_component'; -jest.mock('../services', () => ({ +jest.mock('../format_service', () => ({ getFormatService: () => ({ deserialize: () => { return { diff --git a/src/plugins/vis_types/metric/public/components/metric_vis_component.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_component.tsx similarity index 81% rename from src/plugins/vis_types/metric/public/components/metric_vis_component.tsx rename to src/plugins/chart_expressions/expression_metric/public/components/metric_component.tsx index 837ec5ff60dc..4efdefc7d28e 100644 --- a/src/plugins/vis_types/metric/public/components/metric_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_component.tsx @@ -9,21 +9,19 @@ import { last, findIndex, isNaN } from 'lodash'; import React, { Component } from 'react'; import { isColorDark } from '@elastic/eui'; -import { MetricVisValue } from './metric_vis_value'; -import { Input } from '../metric_vis_fn'; +import { MetricVisValue } from './metric_value'; +import { MetricInput, VisParams, MetricOptions } from '../../common/types'; import type { FieldFormatsContentType, IFieldFormat } from '../../../../field_formats/common'; import { Datatable } from '../../../../expressions/public'; import { getHeatmapColors } from '../../../../charts/public'; -import { VisParams, MetricVisMetric } from '../types'; -import { getFormatService } from '../services'; +import { getFormatService } from '../format_service'; import { ExpressionValueVisDimension } from '../../../../visualizations/public'; -import { Range } from '../../../../expressions/public'; -import './metric_vis.scss'; +import './metric.scss'; export interface MetricVisComponentProps { visParams: Pick; - visData: Input; + visData: MetricInput; fireEvent: (event: any) => void; renderComplete: () => void; } @@ -33,7 +31,7 @@ class MetricVisComponent extends Component { const config = this.props.visParams.metric; const isPercentageMode = config.percentageMode; const colorsRange = config.colorsRange; - const max = (last(colorsRange) as Range).to; + const max = last(colorsRange)?.to ?? 1; const labels: string[] = []; colorsRange.forEach((range: any) => { @@ -66,7 +64,7 @@ class MetricVisComponent extends Component { }); if (bucket === -1) { - if (val < config.colorsRange[0].from) bucket = 0; + if (config.colorsRange?.[0] && val < config.colorsRange?.[0].from) bucket = 0; else bucket = config.colorsRange.length - 1; } @@ -109,14 +107,13 @@ class MetricVisComponent extends Component { } private processTableGroups(table: Datatable) { - const config = this.props.visParams.metric; - const dimensions = this.props.visParams.dimensions; - const isPercentageMode = config.percentageMode; - const min = config.colorsRange[0].from; - const max = (last(config.colorsRange) as Range).to; + const { metric: metricConfig, dimensions } = this.props.visParams; + const { percentageMode: isPercentageMode, colorsRange, style } = metricConfig; + const min = colorsRange?.[0]?.from; + const max = last(colorsRange)?.to; const colors = this.getColors(); const labels = this.getLabels(); - const metrics: MetricVisMetric[] = []; + const metrics: MetricOptions[] = []; let bucketColumnId: string; let bucketFormatter: IFieldFormat; @@ -131,27 +128,26 @@ class MetricVisComponent extends Component { const formatter = getFormatService().deserialize(metric.format); table.rows.forEach((row, rowIndex) => { let title = column.name; - let value: any = row[column.id]; + let value: number = row[column.id]; const color = this.getColor(value, labels, colors); - if (isPercentageMode) { + if (isPercentageMode && colorsRange?.length && max !== undefined && min !== undefined) { value = (value - min) / (max - min); } - value = this.getFormattedValue(formatter, value, 'html'); - + const formattedValue = this.getFormattedValue(formatter, value, 'html'); if (bucketColumnId) { const bucketValue = this.getFormattedValue(bucketFormatter, row[bucketColumnId]); title = `${bucketValue} - ${title}`; } - const shouldColor = config.colorsRange.length > 1; + const shouldColor = colorsRange && colorsRange.length > 1; metrics.push({ label: title, - value, - color: shouldColor && config.style.labelColor ? color : undefined, - bgColor: shouldColor && config.style.bgColor ? color : undefined, - lightText: shouldColor && config.style.bgColor && this.needsLightText(color), + value: formattedValue, + color: shouldColor && style.labelColor ? color : undefined, + bgColor: shouldColor && style.bgColor ? color : undefined, + lightText: shouldColor && style.bgColor && this.needsLightText(color), rowIndex, }); }); @@ -160,7 +156,7 @@ class MetricVisComponent extends Component { return metrics; } - private filterBucket = (metric: MetricVisMetric) => { + private filterBucket = (metric: MetricOptions) => { const dimensions = this.props.visParams.dimensions; if (!dimensions.bucket) { return; @@ -180,7 +176,7 @@ class MetricVisComponent extends Component { }); }; - private renderMetric = (metric: MetricVisMetric, index: number) => { + private renderMetric = (metric: MetricOptions, index: number) => { return ( void; + onFilter?: (metric: MetricOptions) => void; showLabel?: boolean; } diff --git a/src/plugins/chart_expressions/expression_metric/public/expression_renderers/index.ts b/src/plugins/chart_expressions/expression_metric/public/expression_renderers/index.ts new file mode 100644 index 000000000000..b4fb6cea84aa --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/public/expression_renderers/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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { metricVisRenderer } from './metric_vis_renderer'; diff --git a/src/plugins/vis_types/metric/public/metric_vis_renderer.tsx b/src/plugins/chart_expressions/expression_metric/public/expression_renderers/metric_vis_renderer.tsx similarity index 73% rename from src/plugins/vis_types/metric/public/metric_vis_renderer.tsx rename to src/plugins/chart_expressions/expression_metric/public/expression_renderers/metric_vis_renderer.tsx index 0bd2efbfe2ef..6c3c7696fca4 100644 --- a/src/plugins/vis_types/metric/public/metric_vis_renderer.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/expression_renderers/metric_vis_renderer.tsx @@ -9,14 +9,15 @@ import React, { lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { VisualizationContainer } from '../../../visualizations/public'; -import { ExpressionRenderDefinition } from '../../../expressions/common/expression_renderers'; -import { MetricVisRenderValue } from './metric_vis_fn'; +import { VisualizationContainer } from '../../../../visualizations/public'; +import { ExpressionRenderDefinition } from '../../../../expressions/common/expression_renderers'; +import { EXPRESSION_METRIC_NAME, MetricVisRenderConfig } from '../../common'; + // @ts-ignore -const MetricVisComponent = lazy(() => import('./components/metric_vis_component')); +const MetricVisComponent = lazy(() => import('../components/metric_component')); -export const metricVisRenderer: () => ExpressionRenderDefinition = () => ({ - name: 'metric_vis', +export const metricVisRenderer: () => ExpressionRenderDefinition = () => ({ + name: EXPRESSION_METRIC_NAME, displayName: 'metric visualization', reuseDomNode: true, render: async (domNode, { visData, visConfig }, handlers) => { diff --git a/src/plugins/vis_types/metric/public/services.ts b/src/plugins/chart_expressions/expression_metric/public/format_service.ts similarity index 57% rename from src/plugins/vis_types/metric/public/services.ts rename to src/plugins/chart_expressions/expression_metric/public/format_service.ts index e705513675e7..19d2f9a5568c 100644 --- a/src/plugins/vis_types/metric/public/services.ts +++ b/src/plugins/chart_expressions/expression_metric/public/format_service.ts @@ -6,9 +6,8 @@ * Side Public License, v 1. */ -import { createGetterSetter } from '../../../kibana_utils/common'; -import { DataPublicPluginStart } from '../../../data/public'; +import { createGetterSetter } from '../../../kibana_utils/public'; +import { FieldFormatsStart } from '../../../field_formats/public'; -export const [getFormatService, setFormatService] = createGetterSetter< - DataPublicPluginStart['fieldFormats'] ->('metric data.fieldFormats'); +export const [getFormatService, setFormatService] = + createGetterSetter('fieldFormats'); diff --git a/src/plugins/chart_expressions/expression_metric/public/index.ts b/src/plugins/chart_expressions/expression_metric/public/index.ts new file mode 100644 index 000000000000..dfb442514d5f --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/public/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { ExpressionMetricPlugin } from './plugin'; + +export function plugin() { + return new ExpressionMetricPlugin(); +} diff --git a/src/plugins/chart_expressions/expression_metric/public/plugin.ts b/src/plugins/chart_expressions/expression_metric/public/plugin.ts new file mode 100644 index 000000000000..3ac338380a39 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/public/plugin.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { CoreSetup, CoreStart, Plugin } from '../../../../core/public'; +import { Plugin as ExpressionsPublicPlugin } from '../../../expressions/public'; +import { metricVisFunction } from '../common'; +import { setFormatService } from './format_service'; +import { metricVisRenderer } from './expression_renderers'; +import { FieldFormatsStart } from '../../../field_formats/public'; + +/** @internal */ +export interface ExpressionMetricPluginSetup { + expressions: ReturnType; +} + +/** @internal */ +export interface ExpressionMetricPluginStart { + fieldFormats: FieldFormatsStart; +} + +/** @internal */ +export class ExpressionMetricPlugin implements Plugin { + public setup(core: CoreSetup, { expressions }: ExpressionMetricPluginSetup) { + expressions.registerFunction(metricVisFunction); + expressions.registerRenderer(metricVisRenderer); + } + + public start(core: CoreStart, { fieldFormats }: ExpressionMetricPluginStart) { + setFormatService(fieldFormats); + } +} diff --git a/src/plugins/chart_expressions/expression_metric/server/index.ts b/src/plugins/chart_expressions/expression_metric/server/index.ts new file mode 100644 index 000000000000..dfb442514d5f --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/server/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { ExpressionMetricPlugin } from './plugin'; + +export function plugin() { + return new ExpressionMetricPlugin(); +} diff --git a/src/plugins/chart_expressions/expression_metric/server/plugin.ts b/src/plugins/chart_expressions/expression_metric/server/plugin.ts new file mode 100644 index 000000000000..1a04d4702361 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/server/plugin.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 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 { CoreSetup, CoreStart, Plugin } from '../../../../core/public'; +import { ExpressionsServerStart, ExpressionsServerSetup } from '../../../expressions/server'; +import { metricVisFunction } from '../common'; + +interface SetupDeps { + expressions: ExpressionsServerSetup; +} + +interface StartDeps { + expression: ExpressionsServerStart; +} + +export type ExpressionMetricPluginSetup = void; +export type ExpressionMetricPluginStart = void; + +export class ExpressionMetricPlugin + implements Plugin +{ + public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionMetricPluginSetup { + expressions.registerFunction(metricVisFunction); + } + + public start(core: CoreStart): ExpressionMetricPluginStart {} + + public stop() {} +} diff --git a/src/plugins/chart_expressions/expression_metric/tsconfig.json b/src/plugins/chart_expressions/expression_metric/tsconfig.json new file mode 100644 index 000000000000..ff5089c7f4d2 --- /dev/null +++ b/src/plugins/chart_expressions/expression_metric/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "isolatedModules": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + ], + "references": [ + { "path": "../../../core/tsconfig.json" }, + { "path": "../../expressions/tsconfig.json" }, + { "path": "../../presentation_util/tsconfig.json" }, + { "path": "../../field_formats/tsconfig.json" }, + { "path": "../../charts/tsconfig.json" }, + { "path": "../../visualizations/tsconfig.json" }, + ] +} diff --git a/src/plugins/charts/common/index.ts b/src/plugins/charts/common/index.ts index ad3d2d11bbdf..618466212f5b 100644 --- a/src/plugins/charts/common/index.ts +++ b/src/plugins/charts/common/index.ts @@ -6,9 +6,33 @@ * Side Public License, v 1. */ -// TODO: https://github.com/elastic/kibana/issues/110891 -/* eslint-disable @kbn/eslint/no_export_all */ - export const COLOR_MAPPING_SETTING = 'visualization:colorMapping'; -export * from './palette'; -export * from './constants'; + +export { + CustomPaletteArguments, + CustomPaletteState, + SystemPaletteArguments, + PaletteOutput, + defaultCustomColors, + palette, + systemPalette, +} from './palette'; + +export { paletteIds } from './constants'; + +export { + ColorSchemas, + ColorSchema, + RawColorSchema, + ColorMap, + vislibColorMaps, + colorSchemas, + getHeatmapColors, + truncatedColorMaps, + truncatedColorSchemas, + ColorMode, + LabelRotation, + defaultCountLabel, +} from './static'; + +export { ColorSchemaParams, Labels, Style } from './types'; diff --git a/src/plugins/charts/public/static/color_maps/color_maps.ts b/src/plugins/charts/common/static/color_maps/color_maps.ts similarity index 100% rename from src/plugins/charts/public/static/color_maps/color_maps.ts rename to src/plugins/charts/common/static/color_maps/color_maps.ts diff --git a/src/plugins/charts/public/static/color_maps/heatmap_color.test.ts b/src/plugins/charts/common/static/color_maps/heatmap_color.test.ts similarity index 100% rename from src/plugins/charts/public/static/color_maps/heatmap_color.test.ts rename to src/plugins/charts/common/static/color_maps/heatmap_color.test.ts diff --git a/src/plugins/charts/public/static/color_maps/heatmap_color.ts b/src/plugins/charts/common/static/color_maps/heatmap_color.ts similarity index 100% rename from src/plugins/charts/public/static/color_maps/heatmap_color.ts rename to src/plugins/charts/common/static/color_maps/heatmap_color.ts diff --git a/src/plugins/charts/public/static/color_maps/index.ts b/src/plugins/charts/common/static/color_maps/index.ts similarity index 100% rename from src/plugins/charts/public/static/color_maps/index.ts rename to src/plugins/charts/common/static/color_maps/index.ts diff --git a/src/plugins/charts/public/static/color_maps/mock.ts b/src/plugins/charts/common/static/color_maps/mock.ts similarity index 100% rename from src/plugins/charts/public/static/color_maps/mock.ts rename to src/plugins/charts/common/static/color_maps/mock.ts diff --git a/src/plugins/charts/public/static/color_maps/truncated_color_maps.ts b/src/plugins/charts/common/static/color_maps/truncated_color_maps.ts similarity index 100% rename from src/plugins/charts/public/static/color_maps/truncated_color_maps.ts rename to src/plugins/charts/common/static/color_maps/truncated_color_maps.ts diff --git a/src/plugins/charts/public/static/components/collections.ts b/src/plugins/charts/common/static/components/collections.ts similarity index 100% rename from src/plugins/charts/public/static/components/collections.ts rename to src/plugins/charts/common/static/components/collections.ts diff --git a/src/plugins/charts/common/static/components/index.ts b/src/plugins/charts/common/static/components/index.ts new file mode 100644 index 000000000000..9b2384d23771 --- /dev/null +++ b/src/plugins/charts/common/static/components/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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { ColorMode, LabelRotation, defaultCountLabel } from './collections'; diff --git a/src/plugins/charts/common/static/index.ts b/src/plugins/charts/common/static/index.ts new file mode 100644 index 000000000000..9cde3bafe59e --- /dev/null +++ b/src/plugins/charts/common/static/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { + ColorSchemas, + ColorSchema, + RawColorSchema, + ColorMap, + vislibColorMaps, + colorSchemas, + getHeatmapColors, + truncatedColorMaps, + truncatedColorSchemas, +} from './color_maps'; + +export { ColorMode, LabelRotation, defaultCountLabel } from './components'; diff --git a/src/plugins/charts/public/static/components/types.ts b/src/plugins/charts/common/types.ts similarity index 88% rename from src/plugins/charts/public/static/components/types.ts rename to src/plugins/charts/common/types.ts index a92dba593f0c..841494c2edb8 100644 --- a/src/plugins/charts/public/static/components/types.ts +++ b/src/plugins/charts/common/types.ts @@ -6,8 +6,7 @@ * Side Public License, v 1. */ -import { ColorSchemas } from '../color_maps'; -import { LabelRotation } from './collections'; +import { ColorSchemas, LabelRotation } from './static'; export interface ColorSchemaParams { colorSchema: ColorSchemas; diff --git a/src/plugins/charts/kibana.json b/src/plugins/charts/kibana.json index 86971d1018e0..a3e0da41056d 100644 --- a/src/plugins/charts/kibana.json +++ b/src/plugins/charts/kibana.json @@ -3,6 +3,7 @@ "version": "kibana", "server": true, "ui": true, + "extraPublicDirs": ["common"], "requiredPlugins": ["expressions"], "owner": { "name": "Vis Editors", diff --git a/src/plugins/charts/public/index.ts b/src/plugins/charts/public/index.ts index 6674b98fce91..e3d38b797c57 100644 --- a/src/plugins/charts/public/index.ts +++ b/src/plugins/charts/public/index.ts @@ -26,4 +26,19 @@ export { CustomPaletteState, SystemPaletteArguments, paletteIds, + ColorSchemas, + ColorSchema, + RawColorSchema, + ColorMap, + vislibColorMaps, + colorSchemas, + getHeatmapColors, + truncatedColorMaps, + truncatedColorSchemas, + ColorMode, + LabelRotation, + defaultCountLabel, + ColorSchemaParams, + Labels, + Style, } from '../common'; diff --git a/src/plugins/charts/public/mocks.ts b/src/plugins/charts/public/mocks.ts index 7460962c3e1f..a481f8ca138c 100644 --- a/src/plugins/charts/public/mocks.ts +++ b/src/plugins/charts/public/mocks.ts @@ -28,7 +28,7 @@ const createStartContract = (): Start => ({ palettes: paletteServiceMock.setup({} as any), }); -export { colorMapsMock } from './static/color_maps/mock'; +export { colorMapsMock } from '../common/static/color_maps/mock'; export const chartPluginMock = { createSetupContract, diff --git a/src/plugins/charts/public/static/components/index.ts b/src/plugins/charts/public/static/components/index.ts index 549218cf992a..7f3af50a01aa 100644 --- a/src/plugins/charts/public/static/components/index.ts +++ b/src/plugins/charts/public/static/components/index.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -export { ColorMode, LabelRotation, defaultCountLabel } from './collections'; -export { ColorSchemaParams, Labels, Style } from './types'; export { LegendToggle } from './legend_toggle'; export { ColorPicker } from './color_picker'; export { CurrentTime } from './current_time'; diff --git a/src/plugins/charts/public/static/index.ts b/src/plugins/charts/public/static/index.ts index 068ac8289e00..6f5c87ce0df4 100644 --- a/src/plugins/charts/public/static/index.ts +++ b/src/plugins/charts/public/static/index.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -export * from './color_maps'; export * from './colors'; export * from './components'; export * from './utils'; diff --git a/src/plugins/vis_types/metric/kibana.json b/src/plugins/vis_types/metric/kibana.json index 950abea1c5a9..ab69f7843033 100644 --- a/src/plugins/vis_types/metric/kibana.json +++ b/src/plugins/vis_types/metric/kibana.json @@ -5,7 +5,7 @@ "server": true, "ui": true, "requiredPlugins": ["data", "visualizations", "charts", "expressions"], - "requiredBundles": ["kibanaUtils", "visDefaultEditor"], + "requiredBundles": ["visDefaultEditor"], "owner": { "name": "Vis Editors", "githubTeam": "kibana-vis-editors" diff --git a/src/plugins/vis_types/metric/public/components/index.ts b/src/plugins/vis_types/metric/public/components/index.ts new file mode 100644 index 000000000000..41c9bf10e738 --- /dev/null +++ b/src/plugins/vis_types/metric/public/components/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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { MetricVisOptions } from './metric_vis_options'; diff --git a/src/plugins/vis_types/metric/public/metric_vis_type.ts b/src/plugins/vis_types/metric/public/metric_vis_type.ts index 9fc3856ba0ed..ccacd4eae6bf 100644 --- a/src/plugins/vis_types/metric/public/metric_vis_type.ts +++ b/src/plugins/vis_types/metric/public/metric_vis_type.ts @@ -7,7 +7,7 @@ */ import { i18n } from '@kbn/i18n'; -import { MetricVisOptions } from './components/metric_vis_options'; +import { MetricVisOptions } from './components'; import { ColorSchemas, ColorMode } from '../../../charts/public'; import { VisTypeDefinition } from '../../../visualizations/public'; import { AggGroupNames } from '../../../data/public'; diff --git a/src/plugins/vis_types/metric/public/plugin.ts b/src/plugins/vis_types/metric/public/plugin.ts index 205c02d8e9c3..a88bbc8e5f2e 100644 --- a/src/plugins/vis_types/metric/public/plugin.ts +++ b/src/plugins/vis_types/metric/public/plugin.ts @@ -7,27 +7,13 @@ */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public'; -import { Plugin as ExpressionsPublicPlugin } from '../../../expressions/public'; import { VisualizationsSetup } from '../../../visualizations/public'; - -import { createMetricVisFn } from './metric_vis_fn'; import { createMetricVisTypeDefinition } from './metric_vis_type'; -import { ChartsPluginSetup } from '../../../charts/public'; -import { DataPublicPluginStart } from '../../../data/public'; -import { setFormatService } from './services'; import { ConfigSchema } from '../config'; -import { metricVisRenderer } from './metric_vis_renderer'; /** @internal */ export interface MetricVisPluginSetupDependencies { - expressions: ReturnType; visualizations: VisualizationsSetup; - charts: ChartsPluginSetup; -} - -/** @internal */ -export interface MetricVisPluginStartDependencies { - data: DataPublicPluginStart; } /** @internal */ @@ -38,16 +24,9 @@ export class MetricVisPlugin implements Plugin { this.initializerContext = initializerContext; } - public setup( - core: CoreSetup, - { expressions, visualizations, charts }: MetricVisPluginSetupDependencies - ) { - expressions.registerFunction(createMetricVisFn); - expressions.registerRenderer(metricVisRenderer); + public setup(core: CoreSetup, { visualizations }: MetricVisPluginSetupDependencies) { visualizations.createBaseVisualization(createMetricVisTypeDefinition()); } - public start(core: CoreStart, { data }: MetricVisPluginStartDependencies) { - setFormatService(data.fieldFormats); - } + public start(core: CoreStart) {} } diff --git a/src/plugins/vis_types/metric/public/to_ast.ts b/src/plugins/vis_types/metric/public/to_ast.ts index 10c782c9a50f..1e23a10dd760 100644 --- a/src/plugins/vis_types/metric/public/to_ast.ts +++ b/src/plugins/vis_types/metric/public/to_ast.ts @@ -9,7 +9,6 @@ import { get } from 'lodash'; import { getVisSchemas, SchemaConfig, VisToExpressionAst } from '../../../visualizations/public'; import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; -import { MetricVisExpressionFunctionDefinition } from './metric_vis_fn'; import { EsaggsExpressionFunctionDefinition, IndexPatternLoadExpressionFunctionDefinition, @@ -63,8 +62,7 @@ export const toExpressionAst: VisToExpressionAst = (vis, params) => { }); } - // @ts-expect-error - const metricVis = buildExpressionFunction('metricVis', { + const metricVis = buildExpressionFunction('metricVis', { percentageMode, colorSchema, colorMode: metricColorMode, diff --git a/src/plugins/vis_types/metric/public/types.ts b/src/plugins/vis_types/metric/public/types.ts index 8e86c0217bba..98065348fa91 100644 --- a/src/plugins/vis_types/metric/public/types.ts +++ b/src/plugins/vis_types/metric/public/types.ts @@ -36,12 +36,3 @@ export interface VisParams { metric: MetricVisParam; type: typeof visType; } - -export interface MetricVisMetric { - value: any; - label: string; - color?: string; - bgColor?: string; - lightText: boolean; - rowIndex: number; -} diff --git a/src/plugins/vis_types/metric/tsconfig.json b/src/plugins/vis_types/metric/tsconfig.json index e8c878425ff7..e8e2bb057301 100644 --- a/src/plugins/vis_types/metric/tsconfig.json +++ b/src/plugins/vis_types/metric/tsconfig.json @@ -13,8 +13,6 @@ { "path": "../../visualizations/tsconfig.json" }, { "path": "../../charts/tsconfig.json" }, { "path": "../../expressions/tsconfig.json" }, - { "path": "../../kibana_utils/tsconfig.json" }, - { "path": "../../vis_default_editor/tsconfig.json" }, - { "path": "../../field_formats/tsconfig.json" } + { "path": "../../vis_default_editor/tsconfig.json" } ] } diff --git a/test/interpreter_functional/screenshots/baseline/metric_all_data.png b/test/interpreter_functional/screenshots/baseline/metric_all_data.png index 66357a371a5be96013f5a3cc4f33400f78f4392d..18dca6c2c39c2a17d51aeb6892edcc943363b867 100644 GIT binary patch literal 29812 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%0A(q zKLzg1i~u~jhPT$G39}8%u#@AnR8!z(Oqb}`e1W4xCnIO4frPoPuDb|A5<7=e^9Sy% z*egS8KuJXlOeRD&4#%r{pI+z+e6@F4`xD{3K2m7bC8p4kd_qgjb{Z##ukcA}vkhfa z7-6hI%0+J2`zO6kkoVy&ucJzkAx-1ZSCQTJ@=Vgt$p((&Dzu>;_ebuJOjFN-_Q7gk_$T z2A%bhVg?$Ti)}Y(MMeEUvp}PByQvK)W%dKrc>e0H`nPzoS-HyoAG$h3L`3=D@4=87 zW&#mX)26a=N)QSVs0y=~ zk&vT#O*pNsCg>x_qkg-h``Jq;Fomww^1tR4jgg(y3+oF>K3?W>@EsumDAJ;U!(_ateEhN^LMg(?uP@?nr;H_^+^iA{dOax(R&o3u=bW2(<6)l)LvQmkz=dS3E-XgOekj`rslN*<;tM(bu zEpQy>(06kl_n8zxtG|4CB~1ob2HFaLzZ2{ghFouv7G)?gV0s3@dBn&lgm_?09IqEk zK>=5A5<3uzcpi`2U`?Ig8z1rR&e$$R5>GLzMt#lyoTqdoO zIV0cWdzU6_@M+Bwp}M&3hR0@GJPy2RG(v|@s}he<4q?lxSr#w=uyA(LG>t<;L&N5| ze-#=^&!WK%oY@c9y^iZNmAf(iYpVxcjtT{R7sKYAER9P6ZCe~zw~C>4`La7Wp>pc+ zXG6DsP;tM1XWRC4kjHsVHm7wDnPI#C-ZwZY7y7CRntI+TIoz-QM7P7iVFb8ZiDe#n zdjnRJs`|B?OUOz$C1dbdi2%2B@;L7SROP&fiRjXa=YL5`3RGh{^7DHd(6Fp4? z#+Z@`)X=&w{Q5L3+(i1Tkiudwj`1+QUykiMuiuepqYMxfY?wv6@%Q+znlE&OciK2> zW;S6=LN+sEeOr2WL2hqt*e`d~*%tbdB-u`@F>J2p9I^Xrk4#=oh=inwdgO!PP)xd? zL0b&R5B-Sw!W2=0g0|W1tpy$V2i^Z3UnQU*u2=W_G52Sh?rXZqvq%{6aNP+(lJU4- zf!$4dikY+)6fFO!Ia;#Vo93a}P{aG`{co_(I+hE2(R;mmEGj{Gq&owNd4B-6SU%KG z8~piw2BN@wq9XMj-^NC_$o`yTr*Iz3(}F&>{6Qso>VLd8Q56JYkwqJF)onY#tnBR9 zHwY4l2TS1ikx#mRspDwmqJr_o^*UMes(oQzBo-(gW+H=s+?FziMAF1!Jm=~+4bO%k zYR<)I%}JPWoRx=7$IMV9T9@Iq-H@1jK}WB{yJQ0?f1j#&UE2qV-0R4fMW7BgqTd^Sh>E-zw(N7200vYk`?sSrhCVH;lGEv6iEEQrNVA`h&6F1OtGBBgK|&9sIgo(WfU6 z;E~V*rj5>kmdnkGb<=D5P7E;V+t}Jwa}Ubx#48x=Vm;RDf|dNz#p)mvA@ z*J|pvv_|U}?Cf&N!h{DuCY#<>$2xSpf7H;_bPpO{FkcM71jC(rpchri-StM2L09hV z^IK{i9g(h$l~dS2zv5|4brm_c>jiclzQxzKDRd}^0qSVXVojeWZlN8NK*wp-7!ptB z>LOv*KR-=tm;RbQ@0&-Lj|ss#%<1`VPCVhd&fvU;j_lI#tQ~|AQhqoLcU@;@&4W$( zF8K;o5*T5ctG@2y0GlZXB}*KgAGvwHe;K2UbgSHTpX9Z1q%WqAgA)p1!+quNTB%|J zzFxlDs`J76Nx*3h`W??;OFT?$DCgh`LBJ7;Zn}{s*{5#7)p)@paV^<`^$MC@Co0xI^3_8&@=F^b!S8S`Ud+|O!5e#CtS!@Tk*i~ee+z3vG-p8BtXo^B_#N#h zCsr;z>hpW9t50mn8yS##)A;0n62)HD7T+!OaIkk}clAAka z7p4HYxp~~q&{sM2DnB_ctGv0SNlKcO+%~E_S)? zvxN4f%WvzO@+d|q3J8tNE-eK|`CXn*O5Qp+s2gayCimg%xz)Q5=YjNcANFDmx#OE# z+@3ls;@M1~Ne2crUiFm-2nnf_ye2>ERA3|)6wHH1Z>BpE&+A4;ZiJ8AmXVQZjbZnr zm#fLm&wmeM&|ZPjzR=;msqJJM-l^=tV^(`$?xw zKkcSSN=mYI%twxl+>!f0zw8r8@_}BiNB^D1H>q#^D%F(~=MS}WZ|7>rAMIGne%RYA zDlP_wN=`~@MlW}KqMEHHASf_3H}wW{?=|uv@W222zbcuJvj6R$ay=fB|MLRO_WnQj zL5=5};sgEZ?EGBig2D?C4GoQ<;pP1MEG#oC#S}>*PJyI(g;`U}zAD*sR?A)70{g~y z`E4d7bhJa0lOyzdCadeSsb^i$sOv()!cSs*w&a(I1B39Xjm^xImR0V2t*HqM zQ|N4DvKMEFK4H#&xi~kqwDczWN#mrhQIFKl?v9RtDmZ+2xSOycvD zl~RhxJhFGO%t;=R@$s66u(0(_yty_>3L|Q%k5a?_%Hy9}Sy6Czzt(T5 zX1p7yzT{{^sG<_)$-Ty-C*tG#?HkYXGC7Q}UBp@FkW5#h@jS0*W>g51%vCnFG=2>2 zvRNd8=8F=(e^SyQTUf;OQuijo5B;Zq9iNQ4Og%U`q?XQPm7h-JhgHKemWR_QjFDke zn{5B_R{yNx-n@%umRRmdcjMwrAn#=YTkn3);b}gLX;HAd1 z%Rz3V@6=aidEqguO=pX~tfn*>#Pa#J?Lh8iUBh)_*P|U0IId5YvCn8TpI*52{Y6#8 zu)`PZn_Kmbjqx%Fs@?t{c}Je@vd2Dsi7>fy&5Qdj3G3j_{J=i8wDFzpZZ4P$4*8)g z!4KQaVjp6}5mH%b{y>kaX3P&s4-xgS{Su{S}}>dorBl zU%&o-#3bN|!san!zeoFp^aTIzf)%SqE?s|IQ+3s7wsP7X1eIvUIkH}>)EkGGDeTeE zPZME@vG~n=IPJ3_RfRY*xxpXF+m<~9;4I#(t@UNodx+1?D<`Aab9k2!u6ZR{E1|tTXb{8Z3O? z^IMTT3o74Co%p`%hvbIi%E#s!4XoXlc$+voWq{Z_96V1G3p@3smw@>l3VyGahL&FD zTz0A5zkJTFM|kh@-UuLqV&(I%Q~9aQK3bZasaN%DP2863>J7FJA2@n){18K2_=y<$rvyRBzzG7;X$Z<<;Utw!Q}FVosO*)t|h=957S%D7S=x?K|=3xsZ_)Ejp(CrA-i+?5l90*_5^7#nMg%3OYE zG4v*qE@)UTx^|9ao5#ZMSYyvjp~GZlA*n1Vod@GKN5Wk0JWuA2D$T@XjBn(c=A$v|mSZ`^#agyc3Bexv=|l1rs~v=sRE4;`;x|tzG`{r5WGFSXzdYPHaw&~$F2og1@0I-m-i z^MG0)?9$cSa&n~00}LPU@aE9Cy1V}=b-}M5J0roQldQbQUul83kWB<{{VowuiMa;r zM+MF;l5Wi$;k-@2GB7p{Cm_1poFaN6r=*l=KmWu(!0F`>(`7-|C2VAbSC>!S*irXK z7l%eDg}c{DpBEc(K`aqbr#4Ryr9rt-%hL!je2j$&y1GPusqb19ktl!q@44SWWEeD0 zUCdHr!J~RN59RJ*)@J=g9{y)F7Hqs5b5XUY%Gqkq8<0%)0tfd4g%4jnn+!@3{WYVD z#F?J@{QQSr#Us(wUw4H(uN05Oysc z1;(b7lF^%Gcv(8)?mHlqHBd_>QiC5sT7_@=2(%9~4^=v0UB6Ds zMVe^do5OJV_^`35>5ktt2ahSc!Jb+DP=NzJJSyb4(wDL^UiZhM7#k?)>yg6bvNBhg z&Vy$fv21wAHn@!kF7a7C57E#`5F_*>6S(&_Dzo!bjn%#Te^Mu^>bCdxe$Sw`qdXnv zbW~Ks0d&I!!Wx% zt$1=^g}vTKWw?QU#p3GzKK>}F*KX04bF(QZSqKBpZ2xQv%`~k=F>c4@%kT&{Crk7@ z5Z}j=H;rKL6jOx!&Pseg{pI6+cmvtndl~3I?ZEeOSx*U~0Syg!Jm|s8tqW=_ByYRp znmTp$9j0d(?ozHTkAD+hA1Rs9{U-q*Q6CzwmLx;Qvn>RBzC+k|;`O|XrMkop;iHD?O(6agDUd(cnLWnGHgqQH2 zyz1M{Zu?v>^X!||b(~enSD6RT$DRy8S@(KTZVztOw_cGysio}C%WmPjxBy<&*FG+I zQRVCh2rUSO*`k=;&@dTuAfXWT!#OL32uQ{Zlk;o&b$|+Q-t|&SNjFDK>iG(7)&A9`S%rtW|Bqj-Wnoht9sOgA>j2 zjEQvE@uhI-$AH#uJA33+CpYJodma6WnUr`eoo|VWIUQx`+i>WYH#Nvb!lR7_tsRX9 zW%a?z`k#8E(;bF_l7v%#2X{3^rs}_W|K8P?Fs=HpY_*&Y|B9#MivWZZ7cJ-Iw3pAhHE$PO2c#CrbY1uvl;Mk_@U#g$vf1W zLf`d@d>iF83_cH11SfHk7U!~mrgok};b%V2f5m*eJ5)2@eg9g*Lvz_@yBA{EbV6!v zv2awaIj+@Hwl|tyshap}F04Gu%If-Q{$-?(K;v5}`2y5*141E5gp!F##{FQaDW1nn zi=f#5rult-dp4CE zKKsG?2M4`wWjiJyx?e2;0w|@3rMaZ~ig+bNI-+e-?Zoyr**X+*{C5uZnF zSsP-zpIZ5U4UHw|TETgE2e-<@X|bi(Y;S|-JvcOkib*0Juv8SPZ$8bts@!w_4l$Dy zWa8W5*QRS_%ouWd4R>XFaWF1df8!h2IUE+8>E&~7l1dmrVG<-Kb_kGUxg*4A#e+3sleBk}3g^O%jB}5SWw=>3G+zb?s z3+=#$H8+zxD36X19UEdroZG&;8X9A?T^~XnFW@Gms2G8%;XG!Yh4(D3A8uyuXuh}b zd^0uriR!jRaYDvZ3PS~t->HWG%KS06Pz;m67cQ=U&BxXLeRM2&6pb z$k_t%s5{`pAW*J*B^#BGl!Cl^bAL7zfLhDqH!7!56kg2dT0>|9LAE5-1-yuY~y8UnkDF z!&M&CmIF4@|0!(~T)5^Jj2?^`Zd6KPC{j@J$o5n%-SIv}`!~(uo2V?#@H>14+cRX| z0^4*Ff{XTRlP>R}76Jq+8b27L;n)y)3yz$PQBC@nOqlR)R7l~DlIrF{SGXUpbfyI% zK)GSB4%pBD062M7F8`@!2R@9Pa;rqVNby)?fdu{_Qr1?~t&h`rMbIb@f3A zvb`4Bp5ES&;HCOd(!4lf&V*9<5Qd$HqLehJT8sq+$f2WtD)^8ZUq#KB=4L@CUvw~# zx--0F*N_~}1ma?2!#a#?|Ehb{MF1P{g^ILjz%}|7udj-C7aiF7nGz!;4^>o#;X3}Y zUHX532Ks}_;x_3a#g;eLpsDmcy74GYxyWMPNIK$<%trZ_#T#VZ?c<56fn`^hZ+HR|4 z8(sA;Sr>-wC$C&}wOi~I_3uK4eX>t~2LmfH4PR65r}ck;j}RDfN(|P15DLdgcZJtR zdNR0{o>7nfK|cAW%EWj$d*Z;SXZH!*9XBPA5mx3)I@Gn@-M)6SnIGU`Lom&zrY-=% z!O-1BVvc{D6cF|d@b_k2DVb8C2JjI*5_S#_3*oS4We5pW}gEmW&<(OCME#whI-tgGA}18P@D&CD{o z;@VaR8U1xDYE0ed=zlrkN?~6&@BEDj+SLJ(6EAAX@;0=Hr`N1~e&3Q9Trg-A;7|(v zH0k_qj_q}FK!5)$r4LpII>CNwir*1$T^7ch%3?pXcIF-2tMoj(WEG%t^A3s-d~2hn zKg!R+0J*M&*m>TbOexnjaTyyQ@q1=fRDWml7pm7JhE2C|^dCz2-}gC&Q~zn{4?_h+ zQt+Q|GW-caKTWy@v5q|VGRG>T6>OhfpqAQ`cs25s5D=_1SHI$tc#Sl+I*a#1?(Ym;>yzB7~tW+;#EGvVuRc3qs7wL787CBgnn`gm9ho~JQL~i zvH0G1i
kM}=-{;{x;44Y6q=Umqbu<>>!iKSc|{T!M?!jstjqg7m<_oIdS9?I9i2w}Wlh$4MOx2EC}~5wa#iK!rdD z)UD9zj(v(b=wPODa;!U-_XOwEQp!j7H8ucqTYSB8B_$+gCUvN%-%i!(6R4+6cecY$ z)FDn05(5tC?F*pyl)bl=Nyu)s;{-~31_8~PD?0T8MBWVd{Ge*Ha^$fW1ZMMWvv}O4 zHi5#a?v7hmk68E&4(|Ap+I`CJm-T7o!jBM%8BPDNhGKnVS&EhlrUBJL+cGm_tB5lB z%CFR9l2Bu#aC+q**({v=<6Jo1@hTHfeW*6S_4s4q2Mx?$Vn63-%u*ll)4JY!mPdl`b}*MKCkAVgvM&YBwEejCZDhp$ z*Md_=R~J`anA6bmJQE3JPJ(Xxp#;Tqr^ediSQ}1uy&awKc9&hz_y-#|#tE{s%`l(( zZkp6)vGQ}Ote=y6c|1{Y^=Y{2)@04hzbW0no=e)z(e~abl!$g_0B$sJ@_n*iGFN$3 zgW+3?Hdlb&qgj;4uNJ@fKRTIr^AGh`HxHkGfz})n61Vg6<~zE(H4+{?-8645>Qbv5 zgU+C&A3a~-$Uo8;Tt!e{ zan1hloFqbXY;sis+8drveZhB&f4b_YA$4v)8)bL<0lg0ky~&5vNfoMGvDCr_U)EG1w_ zbX(A?2VSG179Y}ows|S;JYPlNiu-3`m%VW zWc0(+b&Q;=tKVn{|Bz*NgTM+_nM~+wpEMJ=@0x#4p|oScTT<@hpSy}(Dq<@|v^=iTdxqP_6uK{*){0wU)CXM!t@N3S)&Ctr%+ zfmBh{YK((yWo-pd%NY?Nyp+N!Voz#M>wD?e6m08g|E+SK39go&$%);~&CN&0AvVu` z_!zh1w#t^T&qL&1m;nuzmhJ%}$_dFP?Y>#b`0RL0xZwNpMSQY1D_Zq#&|rU=J~+P_ zH%Y0mxRbxX6gD)M-7##DF5Tx#xpQ7q)5pMu;8KBh2Kr*{iG4AtMk`Y_;ad(;yl7}> zUKz`)_Kr;GZV!0K*ua2&1w;hHIP}ufkPMe172DIryn02&*%q!qc>4n=Ky%Tc7Witb zT&8P-0=u;P2Mj);fBQBoGpnVUYrnt4Xk$y_MW-D*9{Wd$Kf2a3YCAh<+PiBP7uC{l zPPiVzTdXqVC+JN&9=m`4K9b)jLi+df=i@RnvNOxB9^ra-eRp||wbVE>%Q?WUGz*Jy zF*dh+{^jn~OI~beghj{dztKWLKJ0zZ+2TO+P21rb@5tlM@C4K_42J*v#+=*I&Qu3Y zft48O%%Z*IL$!0qj?Uzp+k-*YYGsnq}ps_J#ARRB|PaopsyrY>qcF%S~OLW7UN+yWw!IlAUhJV^gV>M)q zXLk!;9+>F<3r@-Qcg~&T>t^t}WFnnyQN6e%hDe_Tm&;gOOm~PSkW{nMt(m6xs5Vr< zn4y2V`t^m0NBhU4n?P9g>UOEecNAO8wA=KQUsaBKp{fttJEhm-0%ySm;DOq(Z$&T9 z_MWI|-KSS)PI7Tk4-5`IAWA5heCBl(+OzRHVze5*)3Qka{b#Bh*-or+<(9nseD%hf z4FOvFd@?-OjZlB|e;=&xCKx9mLLS1t>UYp2%;}|`47Sgxx%d`q&(}I`B30_<-rSMx zCTu=5h@d7l)t$>}v8GH0jbzi3)b?$3SVLRW=cQJPOXQ_E9$u-84>Tr8$o@TP!$sgvB;nHHEoS;V>RK_A#UyV%TO&)=ivlQDK5dN!gX~|W#s`POLT5; zB8M zw4?%%>>C-$iaUyHPRh*4$*H2moHrsmz2_QrWEEVR(j*7kp?DL_yd?`-<0ofhXvNII5&ecW(!s<*B)&pF4cmq5DxpUH(F zPMx(>LJs8|7S*24Kv>H4>(>{)?+#Lk;}I6#URL#mdTYG8uRLgXuk1H;jM#8Ii$_3q zwix3U-z@=17=6(6-fL!RT50I(Aj!LBZYxH^C2I21F{(z(R)8CHA{f#V9=hePyH*V) z(+#*h8uSW~RWp8~+dVS|IlP(e(;>R&IgQ&KJQ@aCWXp^%lJLYi3|8crdTcWv2pP?w6q zQ+5M5Tmysb8Ott$2tR`)f2CfMYh|II=++jeWWT;3!4&OJ7;5arbXx(f)Z6sUiJP-6 z+xq)sxUKr~BTYq8!o_b$f30|bS#)?{e7x*EFas{W(6hw);!u};Es`bzM4?Kurtzb& zi?;*c(AfxHI9p+p`M(Xuq1p3MbaYd9YN{rXZI6t9OAOx*Tb>u#{eWF8icou|UdkfI z-Ej0MoiUvq99DiN@igPpHWKI$B=}Tfi;9OL>|q25GUU`Z*7WxF2HbFiG@t;1yAmjY z+oA?Tt)%2!^s6=tti@fhsv*2Vm0XZ$NXzsvV9uMGfKmC92njR+FCZF7Ybyo^E9bzz|dMpI;tV!Sq>NJ>zcA}%mNlr~o3DZ-VK^1a=;@bgQmQzqFvL&$zlZ)wlZ&+kGjIysR zPN+aeMdAse(YVf@Ce#T0en!`#Ej7q?s`kd;8zVViyj(Vk4dbS}F0!mxY8Hm0w6O1|Tt=U9eSjUKkT;d#z>E8+DqP_;tuCjt(pGXwh4d}m+i(s@5Xqsx@n9TDa}f3VqUP5 zqrTM>?8cw$5WW@!o! z9xC0=X|~LQmi^n~@;u zf$0fcNWE<~Znt{Xrk#_40(o%s&i|NQ=$;O=siDx}7T|LAtG;0A@xH8#l?vW}H! z3%1kHTHSs4*m%E`l%E_0%8z_|`NkJgs@hjK6NoUPnP#%yy%H}RUk;#2QVs1( zvR3w%`H@rVc-2?@hdbUaD;4-Tl~uprHo9|1#E}69I3UVO7VW!uB@ofeGR5m_`_s}I z+5U`J!f%vkPM@2}dHC`;u7B#p3CK;fAb&76oTw7C7 z69`4tckfeDj4%-mQB0xtHO``~mPDvpQ1jPE?mUCt3Q4HH+%h7JSmt}oAX~!a`;o(3 zyfJ{|c7zDU9={iD$c;<~Nl*Q^dnTEu-Dc=jw>xcQ*NWj)&-3jfonaE1Rcynv zLqYxcvGFV7Dk#37WBh#rGQ#OM3Um`$rghuIt$Q1h*Aq~-9YiGoW8UYBEOx^&9X9<= z2E=D8BzvbhT9t$2Q!uJpP83aZV*@4_ja`m%-j9m{g zY@aD!wH;J^A?;Edad?!z=j&I#i;XmLB~_{284(y0cNwsM?AWj)-E`e^sYQB1I|5Qs zT~qVJdx+v#vK3S)gPYi8-9Ln;v`0wUFqE{y_Z+?6Q{Gp?j|u@P%Aii!Mvztk4>`z> zg$xqRr|-byEFTOLG8`CG4U=4L$W;zM@*G5;@{5H{Ki$7`AftW?J)BvSj=zBo2iQs1 zg;oEEEG7SiHdv=?mclLfq*r*kfW=mMeT%@=ww~r=~g29(Yj)9f^wvMYpm(j zRtw162vHnpk9)zYr#ydeGpRZkyMCLKkY=Rk38grx}xOY3hUMw#ph6@sNviOh9?CKre#8d#0`d9qDnL0>)(`%l;^n>6x2re%FoDWvTw{d>43#)% zs)O*aeM@&S$Kh@;Tiv&7heZ-(`bQeB05uBs^OLgCEwDNOi6s`%Y^C1C*`*BwE%wF$ z*+Fwk4G3o}YiEWXD-04$!=t{TZ^;q`cNd5KcFN6ttI=qTlJx8U@k1fs{@l{CnAz96 z4TS6iRUxCiaH6HN;g6ntzYL&owCtjfaSOfin#rG$pzPQoJcC(aX?jJCV11k;;nA

pn1(Y85^tpXp4$Vjq3UW&D{S^TEYrNBq1NZ2m(m25ewdGkD$FE75<0We+CF}-| zupUh<|MG>Av(VDq*d#d9s6`yPFO+r^?NNhCxf#0h&*5#fY)7Idjy@J)X~}b67Vs)6 z+S~B#-*)j{h#5Upl&~AqZyz(=-6ka{hf8=Qj!Ev2G!cS^3XqLaeaD%FeTN7bZVk<8 z=BWHc%zG(#2Kdw~v!8db?g7C!^sT04VV}g4?1`f-{HdD}bXG6GN46B)*gB$k5Lj=E zUV(dPlWep(F3{MJX|Dz1VT07rN zhESjm#;3eWG(Qg!Gb3IYEZlv_VeJ;WrwM$VfQb4>+nM2GdCom+{K;CQw>mDWkl!$SZ+L-S3}k*c-%MlZCr!cuAa_$tIh)N#;+@oNoXJPD;>r z<`#C_c{5R`>E3F>CN&aof@419F^Gnh&+}86(|a%gI(NF~1Zlb}qNBsas^c9UPB7dH z{g-^`72PYQo741JfyzJ~i1Kx)+oaE7?%TDsB(pV@WE2>;R_M4~Yqs#HMAQ`gj6~7< z=dv&1jz{jSJz$f3veB>DX!!LF*uq<@P6~>tGUJhjn$*5L-$!v(obRqOeDHEPoOu;O zG!z~9aVW%or=Wy4Zc2MYC~pYimuVfNs8cT&>@wy{5)Iwd@o(>FR#k}vLph%ZLpdwH zx3Lnc1%~DTD5$s|uu9#}9O9W*TUVtA2A8VM4)tM}x+sB|{oD?f`nLj2Y?-g8k3q^%Plj`p$1-}yV> zVgGTh^xosp4v>+>5*f#x4GkI2PuFNc2d87G!$EGvz>|G_4YGnKrqI*Qql6f%s6JL>!SO9u2Y==%XELZ8R&%1*qDoQglNd}Rz_~<0O^?fQa)PmZ-*X>~QLuX@=& z1|*s&Q4~vL8X+aW+R4R?BnKXJImMEFtm)&UlajV$te|N8a1iT2$GlM8OU|*reU2{kTrn&3Op@jdLW#;hvu%qB5!# z%S1+u)wzv7^!GOah1e>lo#%g}_mpaelHud2Q`|Lv?}O;Jj+^M1+H+XlwHlVAz&j+V4m- zgXO+TR7*$YZS8Ge0+S{okJgq}^1KTv!bKgO)n~kNa;Bg98AuRgSLAxg@Tjr`IPDcC zBO%_dk#-N^nh=t}I8U8gh=q)gG*lHtoOt)v^_^6x2L+!hLg%iQ(w3@Q^k7fc$b9G6 zED05l>6ZumBp`I+X}V>&FJ>qjsBCP6pB~irSBM)~)Xv#|U${!uXelw8n~-_~D&u>y zWz8SCsJ2eB#LAT2xj)Q`b&O@<4Q6SEB{hbXQ96&51EVTIc`*UkG>Ffx}LeTrTle_TzP;h=@|3O^vh zrH6D3+}0r~spmm~6IzF?SJ-5~uhZZyKZ;MuHz>rbtDzg#CNn3;>_2ZW$3GjpSIq(o zxQw`H&yjZbj^w@OP0uklBa;O1qO7|+Ah)xtW2S6#nO=e8?)0u~2Ba3Cn7Nn<+yL?_ zW6|L`-E&bA4fl2?G9c_QFn)Q|D^p;U*kY9V&*wS(s5y1kDEXyBSTBiCQg^k|kf7Pm zyu7BYm1aOx^$Z1$%MXFq(EnE(y2|dM!Xj$@-I-u9f9-t_xR-YCF@GE&Z%SE-lJ4tW z6JI`L*tLY|FYc_)kHiJ%J8N{zGWMH?REPpuoZcuM4rm-dO=PoDF#fx2u887EpemFtNG|2OR2EeqDQELXI%?6fN?|^k7>Gd2(uMNeogG08DIblgna2 zox>Z}eYH}C*AqbdF0K^=5|&S1-{quVj|B2Wh+w7Gq?Tcl?Qi z{qZ+i^X51iD1_FDDHK=CWE1ryc~LQyXriXI)L?I$CeE>kZEUi}m%j;nU5bU1!CwpVxbUC({Qu zUE15r4giWwy^ZA@nD_{3J2Dhlwi&tA=p=B0dL>Cy3sCu*f9j-Fs-^%G@YDaX`Yw_G z+K_aBfxKyUH2j8C2Z6bA^x*x zR&MT4?p3AGK|`W92!OK=p18hf!n&zhdY<8JmG7Aby!<*fBeys458>!fSeYE6C_9e40JGLx? z^tW~KN-fefca1)pWkp**4UoU7BgE7hZ}IW)#ErGI*R=y>Jlzk5!hgDkR`016@qG2# z{!dq%o)50B_V0g_&~gnQ#DzOeWg~~BN3wK;%AW_z)#Wbk`&Kj*DS52m*>v$b=k|ST z;oIf%p7ST34Yr&%-dV#rlfSom9S;^7xUUejGd<0mqzTW?CPU4^~m-NuLYQ{ zu~nzGr&wj_k>A$27S)PZ9J-R#+0-XSd-B&}bdAgNsMRM&`&@a`i&xGXc>c+bdhuc; zCB3-D*iKSddR_1KdT!$1rPIH7LiA(`9rW7+(9wbyt5TDPg_6#o0KRTFlZV@hrGw+&@n9iP7D zEDaf&oFrEqq8f|Cf5zb#R}(BqGgtQ9DdEXl;@{VxNi BWJv%3 literal 29776 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 D<^8a} diff --git a/test/interpreter_functional/screenshots/baseline/metric_empty_data.png b/test/interpreter_functional/screenshots/baseline/metric_empty_data.png index 06cd781415ab0569a0d7c055a3a0e7593a730f1f..db1dda5083de2d3b0bdada60bacea02c932a008d 100644 GIT binary patch literal 5152 zcmeHLYgAKL+CD8++d*I~)3N}Csfdxvh!6xZK-$j=B1ouo%pjMjKmfTKE&&3GNC!Tu z!f+7`35k_U+K&RC2?jz)qSYF?BoQdk5CU2V$o&!_1VS?V*!eSSe$Aih{ODR)**Pcs z^1jdW?B~4u6a@wPn*7e{cMt@b_?`aj90a}d1cKgje0L|f<7k`Ah9L84zt2AXBJ-_< zN4bBqNxOttUed36ei>e-JiveNJ+|?sXin!l2d>9HDX2R4#YOeh+TGsCx%)K0fz0+Ab2zAPcbe2~2gCNxidRY4H9tes^F~n>a+`N@2dGh45><&Ki zc;=aojuYT-bqoA=5V1v9c(b@Ty8G3uzxddMS7Ak)x2cL@3kYK1Kn|T3xi^dPX=#qZ z9eh4AIw~q0f-?2DA!zx!rM0r!K}49b=WX1>b?dZZR$@YeBLsOG00fzz$39rc_q>V) zKgq}5h8`N@FB4pXh1*189iNYyn3$ll2`aCdxjD(bQ!IQE4hKOSZNu+Ekc@x6@%EB( z+sx9|R+jfvEFKsdlv9Vs&R=K}Snk``1LEA=%;v=QeG9^@;w~t}oU0QPacmbYUN99RBW2z@0f=9w1{0yxxe9uP>O&7J^J8>;T3HbMx{L z5&5K#_-TKF8@!0GO-T95%1Uc`dd8GaE%V11RR}j?nLYwXuNC~N@mKIUexjRefh&IfSh&noir^qo`J>*L&LSw{k`USUETAH0DIfI8A~0)=d3 zAs}dPwGRXxPl${R9dH8y+fUPL0DEf9xHf47NK>7B2|zevqiA=AyCk@=FFv}y+sWz- zN51?=~}2Sy=83yYuMLUjeQ>nDWO%3KRm2<&d`v667w`0#Yco zUx`El8E=5ynetOXVWCSjf#A|KG}gMdF6|l>3W1V;{sjo2!6O~ZRcxRpxyy-apaPiM zho4Y@gs)zK6;;P#p}f45meL?_Yvu4S+ujw3d(WlqO_LDOOXfhvqek2ZfFukcY44tc z2M;1oocIt3hxX4#0Beae*f@+|t9}qi+HS$Yd$IH1wGMdc)Se!fv#~%cO)PDFP zzwehjFe-6y*a<*lMn>Bne`w)dj5GZKWVCbVCUAh%U@-nukA?ta2=Wzz08P8Un;95z zV{aD$uqNS@^9kq2J4b~tW8{NZRC6WGXAL(OU3*$P|3V*hcLy@$!;Bsh6ZchdHm6j1 zeGeLrr?lDgXorVleZH%FOS1#IX6FxMR5rmc?x{C2 zeq&g_uC2cK-_|U3tPMFT@j}yikXI(~Ki_t=mdTW^F?kLcjovJrT6n&x>xN`YzcCSl zVjoEiW&TvTW=&?COdnPn?r8KYVs%VUa_^JL#m?U+8Km7|ni*>&D5VaF<@ooBxtBv1 z1xvo#v2If8-6l*z&!UpFdKt-0550il_OENk1qj?m|1jYjAw`zUiD22HKo(o9*#i64y85sS~#*`XlpmjIhgR`23@ml*rn7$RxThbT0G_p_@PNY8v zT6-VE5Dg_*6~e9rzciMc_2O&a)uIxi6PRym=mTQZU^Nr3?-X{fm|&xl#^+m`_Jvf1 zCh_ge`3E_Z(J_P?suc+h=`BPqZ7Rp5 z#Wm*IsjpCM{KDzL6W^j+_-P?ik~x1xI>2$RrQOkJ+qgetLW1kc^8ax9=0E{KxoXXM zt+VHCF>Irj#spal_C16t%NzY%ib0<7MsrJ+x?@MFzQav2dyDYD zZEtC&yU_yg#vz@ng`<~~wR>V$Avz;{eLl+t_^|dTguyWtFO&9TYiOb8N2$q5GIqo! zGv&`2^P{B>W-S+L&eFt$yLtMm$;mu(Bq;-PB!cUD3y;L<#&I&`xPcYcnGiS9tow1b zb$y{HPwF+@o2KdQeer#{y1KsQnJ{;2b)`{{waLyrTPd6!O&Dplx79v5CchsU^Dpc4 zuRqLhT-90|K1QsG4v5p!RsR={F7HzE2iAEa-MPhfH7V-{UQCQwT9h-l&VL(9agBu$ zS}AT5c~F-121asoI}#g4_G_3Q7omydnCLMBj_l4dVODdC!p(UL>wI=>>`{z+?xwT0 zR-=zN;}9aqI1u{uDO*=v56k0-4H+kiPKC{MqudcemY6|bMNuy z!@J`3E2+rml*g)*P%v}%H$=hVYCmddyb6LEpGN~0R@oGaOQX|JD= zQ993<;Q?=b6D?DWt>w{k&QbKrIVah7j78o4 zIWpH9(Tn!35!=wX#l^*r*)tB7AsS8?;bp%kEqE73S|J?su(Ta7Wumzbi7W6Q)d`yEQO`Vj&V0Vq(<#cZ}9Vr8h&)!K>u2 zrPLaHZ7C*PJKo>dSLz_PL3t&L)@Y(qcm1X+D|WCtXQf40iO_gb+XO0fLtkvvaC1=r z5E|C>X_paVDi}*XWUbfUAJr;JgtZ4u$pdWe0Zw*@x@D#YiBgEgZ+uNQ^Y~<3ysMS` zLHz#UjQO8#8>OeEP0rwDvwjM0;sn);k4P->;yM6-&3_++-y2HVaz{GzP)!07DE9`b zJJi!EC((u~AaGF{Y3;6poeI!DC3m#4>J@HpsTWY9D!5LgwN2*R3O}x-~Tk{E-gNFlMVe)Htkng_8u2$>NlK`|Rs$?ShNFGP1|C zT=(g2%%3p6zP=;+Wv^rzqDA;oGDTxsUr~k?T=r|uT(H+t(kDOhQXH0vc$GIhp@Tx`P#wZ@G@BP`l%t2zam=H>8LJY(r|Tr5}s_> zm)sSj*G=VU8^;4Ueb15wGq*xA36zl|qWSL~4WkyTd1e%s8wEQD6ev`1cWW6;@`0Jt z3QDdnmw8dg-|{rax?K6sHkoy-1jA(8qLe zqqv58H4J+OoW+h?fx0ND6cpWin-{6b)c{b;f)Aq$?&yQX#+bT-r(`L?4 zt3<{C2aNsiInMoO50AgC_A8&>eG0)E}2&#(Rd>K{A6V7!Ak^5QyU^7ruDGrv=T LpEdu_#p{0uvmoSU literal 5163 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 diff --git a/test/interpreter_functional/screenshots/baseline/metric_multi_metric_data.png b/test/interpreter_functional/screenshots/baseline/metric_multi_metric_data.png index 5888fba713befbec30700999ea5f8df175b4cbc5..1e8594425015631de6fbba07ab5d65db7ccfee5f 100644 GIT binary patch literal 43524 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 literal 42478 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 g5%2S4fy`}OVpo}v%H!JCIfD{E{~aL00M2rTrm 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 diff --git a/test/interpreter_functional/screenshots/baseline/metric_percentage_mode.png b/test/interpreter_functional/screenshots/baseline/metric_percentage_mode.png index 8a5fd9d7a728564cb01d1e13722429a2b3450a21..bcf33d9171193e7176a149aeebf5dd903b718e5f 100644 GIT binary patch literal 30182 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

Q|RUTdzozWL2>&RtJ6)D-Dx znQ3WgXy}w~-PEF?Iq;c=W*_g7L+}YNn_~eD4W?V^rmT+pzMmhCdAA09-<>aQ#i~^c zi*yU(l{m%lxwwg*mtIjfzW;t#-l1VKEx~!Eu)>i*Sb@0fQhY`?(+;lHgX7ykao`>?bB_R(Me`si<) zZ2ij;|MH1FSNPZI_FJ4@ckbw8NIFVxLmoj}ZRskp%X5Ca zhPyj%Xb4$l@mKJCsA#GhE21kz4j1$~Pd*q!qjYf|$ElqIQ6UWoSUM=1n zEYcUT2M->!`TAhHb0)hE^J;5rQDEbKm*f|R=dn!DXiVp?Hv(Dq3A#5v7`(k~(1<0) zd>VN{jF(<`t59`SN0>0o?iZPq^d{|-;@DHdhYtk(!gAVn|A01wYJBne6G29lrSKr( zsq{Al&23f`YI2fES={?(QWo?1oOoekV{(rECOw~Ckq-Oa#ue~K+eJFop^ggSF3OSW}cyEKzEHt(9-Y-Nl>US|P{ zXrX)mK8kK)mC?mF=P}LeKcW$Ue|YzjgoL47V0F&t{fDXCL<#|?GM;TvOh5;dj4Lv_ zhujtO4M(Yb53JO&k+pOITltQ|zI03?%0pjC_O{zT1LqbPFT5F9(78#xtorJ*?z;VO z^)|DhT~y^8(>HE>qGtc4YZiaaEGM|)G^f_m&xed-_pgm*UwY1L)77+U zk*_z_h+L)Ox%`t9&K_9bUr<;mY%$TOEO}oeslXuDpu{4HfFTk-UiPWA^QrXAt+XEK z>F#{vb8g>tUmk86E=z^#C4%eB5WAz(wey|?MK=9h-7gNke?0q=l5+R`dq%`EmG-}$ z32=9lamO$fu6!RPS`sDl{&F=QQ%xxSY_k}nw>&wh{V7m7@4H>?;RiZr5SKeSQ(m7S z;`o#@&X+&cQIQ!)=VMZR4}Se*I5sgc_b=D5#*zE=hNo6I(M9r445a&vv@=5(X*6uQ9$8L2~NL6YOBu9=>z1PMzOt)nBI2CHuYEy6ie|^Er#CW?mRmZe4}*DVj0H z#{S(4G+c*5*<{|7u#}^z7KDXQ6e70xzGjhfG&((yjx|c4f2CP%d15`Mz2%jr@5WWj z$0B~k7^!kMl;q;VM7OZ-_cPSk$3YQS{YJEwXSa(SDrQ7bqH3Ai?21)r$1F@?ph4*g zZC`hfrDoxPp=WHlBZ@O|n>6~vieS1^j zgqJ=x&jjOpE&Nr=`mtDo&!!04FeAjfc`HDLs=}JH$|-EW@wRegZT_@P{~Mx~S_WFi zsLB&_j8UElN7AO}n=Cap*-tFD%U*nX(~ni#x4+svc3i-Suy1>_{_^pC4@>ojcIfwA zKUsRmtjNS?+aC3#YO@+MP{QIl)xf_XzkTR<7dcEuQ?{W=G81NCbA4A@@VMM%fj_Sr zHE?sIkEq0uG#($KM_+pv92}X^Y}jb{&kDxu!9$hRlr&}a3Y-I&uexh|_<$KZ7BAw+ z=a~JEL7X~fCaZ&=?J67`i4>mJDR;J}WeY}+{sk0qxxF!u@S4x)OfDy@-)*-6GZc+`B<$sP6V~nW6b4>hS#j5& ziUCgo8@r39doYUC5`>Sx_g%aZ%Wurkbo3}4R`#5@cTtybxyhF=UGE(C4h`mO;0d7R*wv}}pboRM(Lev3T6I>oq-q^#bmVYczW-Rpx3jEv^h9!j{dIdlMZ zIImhnx0)C18h|5v30Vv$y#BZ}WosUgaLjg4DFD~lZHRc}3z`2kB61Ie-Gme~0ds0@!qM%&gTAG947B7xJX3YUbK zT)p&H|%YJ|Kcw;hi6wM3a#kws5 zw}684sT5Rm+QETZXNy46F>#mKjFhR)>|~dpJz)zN9vKt%pidV}zh*xWMHOjh`gY}; z%g~CtuGMR@Z0{Ui_cvqTC46*U7~@89nw$6;R{t^oUUqw_!>No%G9}Xc>8BA5t@e}q z9*ROhJR@tCE75Se*mjucpe{|Cq7b?HW-=_jk9UHr6`q7t;McQDN`SSh0ewJJt+5O=xECb} zpSB-T(qUs?R0JOP(SKm6s;#;BS-WbC_)0C)()*fXl6!$IqpPx>elz$|&1N+~Cz)mcp(?HlJ~f(e*%7}z zyjWW;DhvBE>puOwW-?!GX4#OXuQdFsE~i>rmRD;vzV26e-oz&+ZJieYlo#9%K#X3@ zI6L^4*;!hTcTd4MVu@-2C4aq;@ZF&n%~lf&e%0JCyjgFYZz#g`#~;@{q_*yxyUeXT z!8boM$O(9sitDO=;z+%J&7#-BnBV7M{F|EJZS;5BtLlaok zX4O<*Dx4bdOHPTsIjKv)=4F&U$l&RIU2i6SpG%2GQM~OqQ9E$NuVmDfyvv?Rt# zd^C_o!yfJ1x34_<2m2;sk+M|tAG2sQxR!37J}0(0EI!-cd3L~~j6H{%TG0Rf%?kryH*(_dWWgBF&=Z)koXNC%~ zmKIi4jX6%{rrJvpTya-qY%-E&chZ`~-6!7Cyw?t-d3{9bR*!&NkG+#%$SFLgsSgDN zMbLIkC6Dk2KwN!@Y^xMeRAu|ct`OOX*SU$4ljTepmT@|PR>xXkwSyWsLCPqy(vgAAOXxGrG7{ax#%Gnr*E)soB$=DhSdE?w& z-Nu}K`-8GBpI@KNGSaMAsan814hoIXH+3-Lbo-2$qr!DAoCrZRhDj>fS2;0Gj;KcitKa3rrr)>90MZR#0wO$ya4 zJ9G5qhAun&v}KlXNU6-lw$3i#o-PTx*>%=kx?V6 zIrJ4$7KHR${^^~Nb?N!JDP4uovlmUfUi(VzdzgvjpAYvRjMjUhx;nr*fi-`MeTxXy;k zcEj;ivGHWIe}sE0ze(oLj(JhK?qO6AV%$ey4Q#|TQNYsscFy%p+~A?KxU z25^HXpbi6<*7bcBx}z6h{cCYQt`2XwhgKup2H@u{`cI@8O{h zj%PhvdGI&d8-wL8W2*GR);T^$hshX~*j$}e2laZ!CqN>Iy19BeDQOOa;tLr;@mKe# z0wKLom*7>(Irj7`!$~}afO>YY+hwi`Gjw%r{l`_ep&Mmr-+our86b}892_idQLB~{ z6RR>4t2zW7O~)EB1$wE@TSISse(=X#`T6AsRkbQ6?!cctK1+u6d9MLQssNV_X%IddeP@8lfP}728mLH*)K@=b);dJUJ3C;rPZ| zliZ+FX8ZATw!v=irMPgoTrTjr7O9-Rvn}P|1BxfDiIk0)|f4={}W=V!0Oerx5F0yV8qOr|AiArTS9h zf>UA@!orDnyGR;l%C{x6KCaw*W{JLqAj4j$@{5VUYX#&QQ0}Y~#>OHAr@y`V#C%pv zCka6UVf!IvJ~n$&wstM?j{%=mW3(MNr#3PNTJ4HZ1l33^Z5uA*z8fIb^l5t?NPe!l3dd)=gEG|0LLX(onpd= zg$&zn(xd_&kB@?dKsbK#)9Z8*zvbhBlQk}pH188&V%|nJQ>_aJArXy=_f#f{-~aYP zT&7D_-gxX7yM-M{SvL@ppHMlRppUqGax%E@rc+$dAv(U6lf{YJwU8yClIs_|n-h8$A3 ztA^Dqy*Dq&!Fr8Lh;_QhWK9P2DZy0TX66OAL~*xBqMbx_jKD3(K$wbcx->K?kT{i* zOGa&F#W&}i_POmX?G+w$>m^N&$rMoZF_49!{F2sDr}SfQpNYTR7-4yPMi{ z4emf4tnze?1tLRJPajD0z7A($NoG8Ng+rW{X_dv*zd+ITIa7SgvTAvJ_`oV?gG$x! zNA71`Zi<&uhZL)@0_4Ns6mm3A1LL*xC*rQyu1J;Xcp@UTbu`3f=8HgrTJ{nY6aoto2RMY#a-@kLiA!+Jbsd9EH+}#I38-% zubn5`1xLFJtuQ+F!}%(XmCJ5%#lXlu2tll_{zGW-6>I@*M8~Ag(gFrgsXr;FqXYD3 z-M^{n0Wc#xXfFT$nlhsjcx+eG0(G9XMbZZ`sNAbX;woRV%r> z$hO}gFNF0Cki>o0Wx2R%emTX({TMqo`)>5a-U*i1^xkr<5*+7?E5`TUK*-gvkmb$0cNTrGt=P`GQ$rnZUYw(o>bp$H*|{FYh;JWHZM_`7@4-qjwk*xmjq>Q8 z-2SWM%ca!NxCi6e>d8svOwYbtG<^{nnfiUK-c6BG=;lX7yaS28G`~dTXugfYiDx(c zcHbz92~MR>R8w5V^~g-MSbdiMQiP)%8yZ3MfjwMUYM;1*r)*`#p{F4C^Oo%`z>Nj( zf!erY^O+qi2(57=Fp|KL}@+^ut-n@SOi)_qjinr|b>koa^hke8G6tK*Z-5Oddxgq2j zBY!<(6rZ6&^}WG9)YSTtwxI3zC;9Ob(>`&_ADYU_#XD$2ggtKD1B9C;m#%N$OCiY9)g(aZ!7(~ zul?_DILC+j?mjjGho)p{`k)STs%MZH2Yff#oOuvM9|B`N3XTcF(sukFJ;LE?pN^eb z_ebo~ZTFXcZpAF&Pd7v}IGOu#X}QbYLk=qA}xM=Mb}J=6s`^s-?$$ynRd zGZ1`lqJR6T_{KIpzI#^63kA87ywB33zX_}~$*2s!a1azYv=ADT^M`3SD1XXoYT^2- zY#w@SEiW%_ReQ~5XN--VDVyraPaac8z`zJeP^zbrya?Q4V)7&q^Egol6O-KykW7JV zK#xj}wx|uC3neo~31HRMXSUWC@fgs=k*vVO2CTawe$~X%Zrz61S7Mz$jK-4Sq#)Oa zD5H4l`wdEMxP?S~^5q?Cgp@1+T(f!~j8A{L@b~~t(%#8N@kGV`jjGSjiAD+7cIOiT zkSuid8Wn|c3ljde@qvIfhWZQsk9svM;`wMe3feP2q6y}@KDT9-?} z^aX;K%%Toi2=g#>`T13Mk&(9z1YlfkJC0}h`wQ9MXw;!XJoZe1Zn_Z*YA27Bl$_=A z#77o(vVpeNO}lg?A46k94`sLHcGR@h-c!*Xldw2o>I5uM5h zgqJPPga^AkOObn52HN)-Z_AdG+xv8lQFgy3WJBI>SK|Djt3fXpAApj z%IXu(yPH8o*vNhT`Z}s4n%Gqz0(2`}^uNCheWE34yObO&!0D)8W0v~uob=|F%Am}Y zv`3>rO>CQ!y2%{b{eLZ=OtEBY2F(nGENpVC_cTbm$Ke2fs0jt z``PQqlY>z#Gp`dhJagtOm;3Dcqir1UxZq0ar?>|J{_|V!cLdpC1BVtyn#%kgrw1#7 zXd6rHB`mrI-7pMB+QOiy70jt>m`x56gS;vGU3R#HPMlXhMe~k`WS@xc1hmS-rt2qH z9Hz=s7*#S_r=C?Ov^{nLSjDd-lCgyCUV?UI{x!2mj(~cuQL%#Mfy3C710BV-R(o0A zpAdB+@uFt$c??LZH$Ie?>Z-;SyEovM=T;d#rtl{qV<%_1aIL9>#SA31u9aC&PKN)Tvw=-|o0Fa%jeY9fjUm!iUQ@azKDel+a_{v5 zNmSLAdE80JAPqgfJ=(<8DlRgR6TplSNYV8S&AUcKG65l#x3JuKhFjhHs$+Ol{PxII zR0f}^J!b6NNkJYcC&MgX$S52?$6FW?5n6v5nQD#==JCN*1QsnJEeq$ocsT5ZK80}T z*ulB&)k_UIxJGQl=`g7wIZH$s^hWZwdc%xLQ8`QJpFO&z3l`lif<%?>q=nBgrG}hd zHnomiJzj=A$MaB8Qwwb(Kso7e?>egj6xhene;^#{gz_pe4S*fC*-%K^)Z3$n7!bCQ zw7y=Z%4_+BKB9oTse<<0WHXeL{Vfhb#2gSoqbf40WV9`X9lLb6&ifGz6)l@zNvVs8 ziQyHEwp>9#+#`yOmu|m8t5>aTT#IvY%#B|tf$9o4c0EgPZ?8d-MO+iXxFV*~epd0= z7@ePoh?XUy{n30QAqZgs+IcaQge;FkdEp<4-14ed(eozH3;2f+_iBvuLOfJFfUo8n zm0ZUV2KWkx5(VXwYja}tb3g9nG(^@OUC#rG=L~X=AUl+e!1PVmKrTg8;ld2{gg>#( zYj;9*Nd~EvQ_>oW$9}Xz@jhIJxOtrgNo$&p9Hm1TzR(F)@f?b6S3}NZ$EBkIKYFFB ziky3uSJhaJ1 z#!Sofrzt#V&N2@w!7JYVF6&bzY+|XGt6O&W|9!n}3vJ_lB|e!(#qN`|6u-Ag@uH4s zx9078k3v~CH7$!B886V>j|G|W28f8%&@TH5E=YHc!a?qXA{xH#;f~K(@Qbha5#GHG z0{_?3>dRV4QdirUYTBK7Wg6G@V0+VMA1(K|8@bN~y8_Iz%@MITM8An=0yPmlp3ZaV!XaO)h?H@G82wSs z+nUJXv0Z7h#>+$O8fYuGXH0)}JDLX+Rc;tz_UzZ1Fr)dtmu-U6>22m$M<0vf>k`cC zZ0Flic*UouI1bRvqMc@BKoF|Wt4YGtwG57hBBIGF{SIfLXR*B910Khyq4kfpg@2V* zZ%82qlV7w{753@-b33Ve`=ZOzCfisjcjSVNQFBaG^DANXM%|nUiNMOs8rw-Txd}}FP+JM8U})@- zX^8MfCxut{3)|_L)@0h24k@cjak%jsGN*6ux+go}=7t=Xfi6?|0nkYs%iV=jJ!J_4 z^BR%0@j|xo4wmx~R77wYI4SnF=8$wZmIMkyY(*fwkF4M1c9Cyuxy!k$9#hU`wqGB# z+I)TQ`x}kh-;EjDiD(fsNJS>;y;-+)Q_@G0s%m&Vka+*wd4r4iVk*SQq4xokZv^(gGrH z3^tOaRO)LF`fkpUZ?$S5Nn#3QEAi6y5&!8qOI{?CX5B@Z(r`qQU;e?+yi_MMlxcLB zo}&ROV4y54eC8y^5EPcZEEM1nMY(@_E!mc)DHjddOKhG|TZ?f8l^ZN=>Al;^*7>9N zmWLcD)6R~~%o|ka_=pp1;iW*a{$_?3xu#O-$FiSnKkZ}QeeOEj$EQBiQ=(6DH~;A# zH$R>*>cRu&dXi!#zx12OM>sQ};rZmR^B@YAIzmE8w}3}8DTeK6@7VoM*Zg8ZXk~U{ zg)5J%Z6#9N)b7YsjX+cdi`ur--OjnPnUV<4$_I)b7Xi)?l*;aq{@Q8Hb`LsG5GsD! zg)J+UnKBoyugT`~hdLxyon32=MGv|iJZ$PpngyqKz(NrA7XtmD&{I9coQE_R@gcGz z%{OkO;O1BA7T5LTTul^@iQi1#g0Kohb94owu6cjR3YWtW zbhyjdEoW%gFG2|@l%@Zl0#X+As@Sw`^`dVZvrfNGQkUU)pF?kTTNBA|GkW-v8!ju? zqhxmR29_L|ucB%;@7JC=co?f`7Cg8RcRcmUH!tK||B`n+o>9P3UjvXq4Kg>m=H8VMa)77q@*Yq?bYBb;I5Q0a<=8Kz4sjTwC0$&;msJjJe~V*vgjv*!Oep3Pk&>a=yd4^Dph@c1g!#TE=>>%@>od@vo76F{3Nw*T^UN1>2Y@SCNZv2{9MEq&IKL>z-p(zqv3XLf=7?GH4& z2hd2+Of4^|wT~>78Y)YT;d`zHO@?JeXjbT3qGHAUb}D^aU;jv=$f+_<>O!Lt+En!3 z9hs3;3IQYc_|QtE{i;cNq8PiQVaZixNDw1A`%1&UBHc{E&(C#Dgi7QrpcWbVqly1? z5y31$SBHeNp~Nc>ny}l&@uO@14^c_L8|DO zAnEk#QYaho6o+!X@l3zsJ!mSzf2pOjb*^L4EhAcY1T7Cdv??B1R3($~Trx;MLw}bl z!UGo~&CW7keVYu@5h>THf6PA2Z8i(1l6vN>p6)u*nlV|n&!?C=eGO-sr0t6|s4?7Z z)`4>dU2$A{f%wbU4Z3r}X6;@JI$^WwHcMG%3mGOphd|h1a}nCVvD|j7mHz7-G$6(1 z750BW@Br|*Yud3ob?`Eg<)0~pdEc`8~(r_j;` zsB?>O)%>^wk{17a-=L0Omt#-X(a535OiyP&E!ZJ#v$**W=aH0XXlW?w=*D7dGpLEV zpNkQnFo)1SeOI~zVT6dHKG9uTTJAPS2u6{4YF>d(Zo(?1pjfjj+g(h%S$HYLPX*c> zu9G1Dx;K+Kz1+}uwk)M(do|lC%mIv(yU-!p?>3oR%2_{hf1-S+b*1YY<00B{78&0g zwlcRq+jnh7iu5yeXc3bs@PIBB<5z`3m%Zvvp{3^EU>i#os$~tSTfgc z=&1ez=%CAX@=Wdrf#W*9NUnplX8Oyw?G?=DtjcYnrwwTA0_uNWOk=F~JQW*k@b+eQ zcBX|w-HJV>v{eXtBLVRKZt47|E&cz`J^S7_U0q9P)#(6#b z_fyQI%Gt8a_&~a+I*l=WIyyjHZ0nEhr_A*Z0ad~xyz06pus%}d)OI$hwrV>bsWoW= zM1Kmu9A^@-54vt1HpfLnIamEv}9?PO6{nmC$3|zYYN@%xkFV}SH~_bU_1$% zraF+u8G!T>T;7~;7y3I7e5>8qp)vuuPC;vO?q=N~x{&i|2#-)3Mo$5Xg+tpi%^56Y ziD^7oQoYlB3Z6v_uLH`x^B_dIu4+q?L?fApr}53tpLSGWs<)tT zsQdYONoPH)mMy|C+UzXal>b&4(pL;m!W)-6BZX#X_8+aLUz4OZ$6zsV_XR&>Uf>`2 znchNmkHl-?DNqr27J}uJ>^^LHwT=vzN2(%QNyc6ngMR zm03od+UndK)HH`N=totCV%UG2V|g?@9;!RhNEab_`bV-~8??1?Bg@|#9c^ zKEg5r&|8Ea2Uf3DLfDdrL<8i%EGRETVPTF?*mH)*JB&e_JIm%g=f7_c&dO?9LG9J8 zwnvYZHgaKPQsJEH3uvu%q>dSNz$UxwYDg;8BsU(UQT#iDr zVIu$nn8pP^<4Sj>SOHUA5_G3WgFj6FGj=%>bZjD%-E$O3Ld&WL{jMYSJ_miWoc#_J z?LvZ4-Ow*T_Rd|iOf4fx3!3%@>nA&`#aBkv%frES5Ie)3cI8_9<7-|kKebI{cDJ4( zbJ-gh3$6%SLisG2LWGIdW7MUV-+8W1y@5_JTS%zw2i__`);g!Uso?=jN7~mAZC^nx zS*;_V&9D8r5o|UxEZEFp?DNwzEZ1J94Z}zi;xK=xC2i|M44COJHT}_=-0c|6Z)}jR z9IeF<^?t-v(yc5eK#F%}hi#pNYB2e4$N!yXjQ};c$lB0^w4U7g^4US9IWEbcntP|d zrdsmGV1?8hBge3WG*wdvO%vc@_y9#`t-PecVvq`{z*Q#8p<|Q_8jz45xVvS#{77z@ zEngjHhM0IhT}BR`Wmt)($^OGjn6cM8WMKiDd71iVNgA-9WXem!xpoa$sks{0_NY|Ll+Wp|q2aHQbv9>9F3aWlhZ{?FV4Jlo*hjs|*7{Im(lGSiL8%KBdI7ObR5H_jV zGYFgx=B2I6)MAGjS_~a2#b`#{6$2$4lIM#jj^Tu~@#X~dUQev)$5O?V8 z*@pI@_})6h_irMt*T&te0$;yuU6u{*0==VxL=LWpm^sYd+%mv6osKncO`CVnUPx+%b>- zqVAPkM{aR(<-k%^FJXtMtmr(x=(hX^>VsPBx;&7-(-@Gon1)$&WLnX znOS6Q*g3vYYC2Dm_ZQ`Z$JTrV)sn@CD=c1IJ^mhrl*;vO!N9QdnPDec|XCN0@LU_p>}1VGSjUHRsHb!NvmWYpZZ(9(7EWpJV7zN($?lVL*ek; zOw9FkoxKrV;^&ye+ZZvj;*PSrZev?yuFny|fy!Ih}o&Mk@YG<9)Bn_?Kds-Ums#9{)B| z*aaTJf^yd}yNK}Q8pYF`>Xct*lgcyQhCJB3j#97B719-l~@L~h(uwW>Q1Zl|UFqw;=U$?gBHXVzk zhV^4Dob$whHcQ2l*84f_uADM7*R{<3o30H&vX>@>Y=*h@gXmYa@80zfd7dD`0|vb2 z^xe>9uGh3Ah;=fhzJ97Nyiln&nSrk@UUaCFnzk~XTFotm1EX=QDr3MhEaFQ6(&B6R zT_h8J?2XFfu-_W7k((sgBRhvMtit=b$b7qXWxCtvG9C_djyC3S#N9o*~|A{wjT=n8v06=94`!Sl>KnNGt3XleWJ9F z!i)sR^sf7 z1*^BF-rB)PQBqbJA#>H6LFn)EnxB?3f5g^bo1anQGtJks#qz~Aq69w8%-h@@*TJ=V z7|5}nolff$WtlZHSL0x#I&ZajKF1qtPe9 z2)u^XTKhW!xnVO(>^Dyw+>&NeZ}ryY!PdM%s(-iG1htwI2J;RsS~Z@HlD-iny?Z0J za4B8BoY=s!H(b1+c40w=wR7#wJ@<5S%9rHpJF+Vzz5ers>n74d_6}^ncU<&JyweY5 z&}oAiAjt_Tc3V|LI(iVxhO^g=feY&`eN;iL;k%K2Ye-}UI@#cbzF|XkNijLFN0Hwg zzkZAy2M32uUruptNJ0)Wt(ZdGg{}^@Wa-qE#0ZzQ`!XDtE@kloa-qQnG1AUavsIh?eEOBwYyATR zY;KI7L2N?Q&C{+O{RD4~I5C;}y6Ba_NaQ3T?OM0tZ8_9@kGZw@uMtJT|M5h2{kK!9 zD)dM|+!vxQ;xZ@7Ma~oswI^P$WflUhR`>W*;FzygarjRL) z_s0IZAlM!hqO)kZYWrcUSMX4J*w;6$HD9T`KGxy0HuxZ9xXg^E&YT=;=EN(Sd%kUC z#H;>Ha1?)f>6 zsoHbI`mS7C&Ka3sZ@{Ll4U^6U&*cPM!j+k$(AL7;W2`#+B3JhDhWJ`+4LJ+5sLLcK zHb=ga>UK?rqq2IhmM14ME9+KmVChi!i1J^t_Ji>#S{JFvitM7&t~{6|9|nnqxO38X zL-WGqXrq6B~Kyx?7Pa|=69uV>^Ap|S*j2*e!`UyfOL z{rWIoKzA;l1fir+4@TQqndBaAPgf}rE?abETKDJ&JaaBiNx6t?Y%$^b1R%$k?pClc zJj|Y|HggR&$U1rVVMx6LBi)D$!0GP5QkIEy?Wt zS(vE8I~HWI6O`hT4?cb@+)APA2#BDwc!Z7#>~UHq8+?wx_bc5O1QvXsY^ zzSx?t5nq4*+WD!0{KS>n{=7Q?R(c#dFYhs#-nv!J?|@MHTp_MHfYp z?`W~PF+GZaI~l(@Q)+!{<2s**M_Ei|rBSW6`fZ)e(6EV~?z&~$lJPMcTQ!nDb!@it z^HC;2=Brtm1+T8n=aN7Y#^5BDzWCJx4C@V+7o(=`nx|k4R>yqN_H!mR<<7IOQH`

d!bqCVBdT3;zf(iZ~JRVlxESvw7D7?j)cbO*LR?GtJAAuAz*ja zW(faGc{*u8J$+_y8XMcfQgnCy6hcbI>31|03O5sMM*PJJgl%1G{EI!`26R=6ED=S;@8obPP8co&#dew_Bwb#+nNeh9M_Q8Vhh?PkQJKYE*pE`Fs z_YOE~)D;nJoYKs&p^AygCFE=hulC0EmCC%P*zV*QI%dC_zVK^AwimjJjxhmI!s!=X zHXN;$$qvbnE1){`XMxKz*&CF0Qk~UjGuu~Ymx-X`3IhD|8Q#44#E#ATTX%8!dPc(1 z_G+fZgR)a+a-2p3Jsq*tK#d6zG~dMIq;Um(#a_2?PoF;3MFM=cx^xEwJ=>iJt+oR_ znt}`7711JGm=YvDdnp_^D5#8veN~&l@!d(-bAoY{b`7xYS|RL9>QiiMZrD8>7QVln;l4>HoM+; zzZa@C2;az-1Q1dNRJ#K2`%4M&7$%BKrv&oHx>hD+Hcoye*-@+A2AT_3D)iJ2K(b)B@y@wU$n`S|e$hg7Fh5`#U0yw3N!b+t2ZYsrI? zb$%;S6u##3Fr>kt&DKSmzKT~g~*sR|<*4#wyTK*1S{jfnO zR-SE>FTt7`^3kmE6&|4JmIX(Nmwr%U7t0@AKQoVZ?B0aZ5oei{Nut;T-CBgTXliL) z>}TxSpB&FM+73Jp@lC|G#T$b7 zrT#D(2DELLk#TlJ#HsWXXHy`)RppOkn2-Ql`{b#5LkVu$6{g7axLpw@O%}Oio@Teb zu^e$_uIvZS>zy$c`Q;3hdkr7pF+k}OfI(NTKXXp6v=^@z0dL3X;KTR@k#ur4boDBH zsR9DvzlL3=+pr~zKl~!#df+keN`NBXs}jx69}^zcsSJycwJ@x3s6xO8i4kgv;<-^n z4T3hEe^Ck_Fp_oe&%UCiZLx+m?HPiaM~c#frf*?K*BZP3IvtDiyW?}7PV+n)!hG>w z7U#eT{L74vPW7ah0150P{SNi;z3)JisyvPjr&y2`{^Eyax(0A6pyO?Rc{+5KqU{S&qcg5gy6 zfU~wR0xaQt>3-#C8_Kkb%T%W|9jl=7T+Qwy$c~X6UR#~~-|l<=C=3`lVp?|Y>}X_P z8%kUUJ)vq>&kWJ0AEnBx*Y{?C~_=MQshcj z!X$G5xe-G*!xDzJW+H?!yb-L|x zEGRn!7H0aS(f|{S=X&Hmfb`HFtykla9^sWqMx6QHbw4I}MY-+8(j#U`=P3V~6)L%B zX@#K*m|&Ik4C#{F5UD`2Pe%S2BVX4?`DeJ}-bH!3mDLI*hYhYqI$L6_Gw<;mKi0(+kysxBZ)z_LqzjJHJO zuLb;AdR>|en`B3S)$!6NP$wu@lTHXdOXC~Q3#fLzf0*}m-k$0AyN=+t-PXId@>3PA z4$ySw`U0%H{35wj6%lx*mUsc7+aLK&!6hr-7W&92@+y1 zJ+N58^X9YJ8O;Wbx~U2|S{r{h{s*HXcR)u#A?Y*3h2;evo+bDnPNQd8h_$v*vE%MO z4}&7jxAUV4jfmtB92|W0?%JK`%t^M{j?~Ajk@oldQay3&(*@7jB$UJGTEC7xZOtk| z*xorY&&rUb24f5>I0A(BA#Ou>J@o3IKCchav!vgt@F^1lBx=Uml(3(b0s|7Bra(RzjWxQ&dAM%d9TO?F)rCe!s5gUs%$z%XW_x*&bFO6G~F z4q}C+CGObnBCvL_He&GAL9Z}if~a$+L9F10RFpQRvpn6-Tu6anUD#CsQIrS?pCA&M z-zy%bTDK{bfGiZBD6ERTyEDN1JmFUwGRyV1jxhqe0Rj?*o1YHEm*Z;)mS<+ zv2GB%7-+V1#guoR!6D=3mCQrvWGs&fP7E~E@1y7H3(gk++9O@9VHp_{YodPL2CP=3N2#N&gXYaA+NkB^-Sryh@t^vXY!=S`hR9uwZdOFt`nWq2 z#74U$C;Q0g=sda+dAb7KX;c%yU~fC4^ADDqZsfB*CnuOCp$LLgd&69HB%~r{W@hCE z!%16e;XvmgV{GR)diQ#Cc+2=H>41Jr`W=LOKs|7>`@FQb&eNwy%NhQS#lhd=o=djK zyf0pSg`R1OD+G2I92%-+{pHISas1Lt=1b-9bs#!@^fG5Ag8Y9gJM|rNQZbZO8&X(j zIVzcu1zKnn{|;Ug(!qw6+qxwpj)Nq~1-HDme#t%Z<>Gn)6U41gIR?SEc%=Bl2Jsng z_|I03u5Q3*7zuv0lPA@pR~B1krhB@FvwW+{30fRR*y;Z;6L|mT>%8L*=_b~nw^8dY zytTt6DQQ$Q{%a%^^un*jT-@B@&`?l}=H_l|OEo20HXEW&j_6ohwY-XkFP3n2j#B2~ z=kE$nt?Rtp#u4=CO}p>fo9i-X&eTW$o)>%#770N%^&A?jE5E&%oasCt`;4G1p;ls5 zP*ChK?Fw{l-+M<*j(zW4koUciZ?K>`m3?;+Tb`z1Jb;Tvy?psPm|!4$14C5E;sx~u zvSc!_EEf)D;oF_XwDk1d%UW>g$qzo!iDvn3d!p67y?vrNVG+-nFA-8seLpv0Fz9ZM zLQV;@@Lgfg&l-uF-8EN%!=RY-jo0RY_JA^xy>Qj$Wmcng=VeW5Hau?cW`ve{08vD& zOV)xgYW(!`=L5!|mKr9M zI&?pA3Oh#QON3N^r}-i~5lAlWJLb)mpE?^aG(BzlgrG9a{7U5d;|?|yDGUyE`5QcL z9~OP(iYzF+*xGlusWml-RJyhXmWoa6kRj>C;876g)1ZQD~l~e>aOfIim9SKdv9i|F@5R|JO(V80TM>_?J)Yxx)YF br(=Eim`QK;w9Ch3!LGfEPN$Io+vq=U2bsG>v5buJw4RHb(7=M?2G{#~knex}5kME?F9`T6+${h=(p{`b`z)I@(@P377A z^zW;iL)HGi*bjq`^Y_*GLI(f+@P97Me=o~_TgyNF;=i5f|3|S!NnAw4J-wjdJ;9J{ zQE|cxql7zrBND&-Exx=uv-m4lN<>5({*oE1bj&0skx)~+fA;KIB_kgnpBH33jV@Ha z78&8o7Id;11o;C}HqS2Ky?giVy9<4(wpSl$a-t_~Z1UFQ1!csS@)Lrf<5!-PxwBv7RU8GZf(OElc~ z-oAf-{i*O9(_Zd(kq_dgZMd)F-)CaFH|5eHTCW2C_hXuz76#uVWoM&;loPjXV;xYk^Wra7cVa0<2(BMWlFse z(SGCY4OTjl9pdK~AtBkG@4mY)CaAtgf(|j(OSTfxKGG>T4ay3hfZTki+M$1VEf|(PNZ<6pxEbZGsoXPH5-*gSFZAaL1Au#iPL(#u~jB+sOFGQy1A7z#eTk{ zBHc@OYD&Wn2*yo~*cSa}jo_d@@H3Fo-f)boQM$i9SyLZfN@AzIML$fFbed=-e`|`~8?E-w!UH8#iDF zde;_pM!A<=&z(n5gj;skF{Pj1vniSx$a`He&(o(yHj&B z9>LQOw$rplZhI8%ec!@;+&90OymZY@J%Y6-$_I(ugNXVUs=73CC;hwI)a^EH?=W*-YE7 z3Ld?DKxnkLDsb~=e5!er?1c*#ew(+n(C~BHQ?S&0y%~xY7UgkXziuvc97J(i9q|48 zbrF$N2Y}BrK_TQFzY`DB-zVulHgaA_OZ(z_YUrNzk8xe6tV;;kh2YT{{fH70hKqXT z@`FU(>bC)|$$iA2_ z_~UPaKktZP5>Up_pL1$LO#>BP`|cZgW&l1XVeA6oVf7Z)BrK0P@=^nIxe*^2V{ zTKbuJ;^#3GF5B3gRtL9r@BbsZHlwXGioWR9y*^w`dIttR5XtNoI8V&-xrj_KqVoau z+RR|#)nKk=oYy~!%JFx^K1x!E2_f&&TgDvnIJN^qh);kYM_E%-H`05eWgy%sT?F6N zn>e}GzBqX?6ns0Oz0}eCTpvZeH%spEHaoJA?WpCKm$)W)-zjEj`Bl)c)QBPbJJf_j zK_}?R_tA3c*98bazU@SHMtr)S6(k$vp=j@s**W*E^*3n$&zn%6Vziz^qpY!Q`O%B2>?^UfNKH{swrse*z+qo#FTAWMI6rWD|k+5(fFJ@>jb z@#S@A{!Y#HTgqLH=%m)A9R-!p2d=lvI%JfKAT*|?r{7HjBZ}ejx~m+6p4K0KgU&P~ z5iZ2e4128}EJ-rLq`>eE>Twq)HBU#~>bu{9)DlVGsa5^Jnd`}I+YA^M^7K@4$byMltJ(f$%+L5^;n zpmdA_i{oPd+(b!5<|Bx-zZ0>YHb(8GFn^{?1~;ap@_%?e)}>nP|4apNb*|InCOsvf zXM#|sXqi1L?aYAxmuEFJn|F*ihh%2DjtbeHt8v?@FFJ>j18}X?oBk;3t7%sOY2NhY zMLEQirE}f)gy(t^UPExa^1g~=;TEco`-I@`+~k+pu*ZLi63lAPZqCsWPj3mmmgD>>$j>jrD@N#E)) z4DI}k?AFKi0@k_C#O`fVJUw5CO%?MxsmawRt$g?BPTxe}naDEJQc+W7W~g#vL2qY}@QPks>b#&SH)c9pJCseT#5)rj8b?78pCnc(>oH6N} zOa4{8*=rAxBQUGUHykXk&KQ&$uYxsSN;X15<{5f>R{bG?`L=&1GXsDS5H)EDilM2J$XC9Yi=gFn(`%oEkVzWQ_rEA7W;56zPj z4E-_$1``12VM1jm#VpUB4N54Z4jk|)b9DmA%RG@7i*8po#5dg$SjB_2=y zz|F|m*w(GUs5`TC=$Jr%*Bz>?0NEtwsg$4Qh10*nsLKtvpGlfyf`!Z7j&3chRiOmX zp(gSgqN1Vz5)2p9XnN%9Ja5thp>i?!(G-0^Gph>OIbR(j7UhcC|LP#Qw1mkm*S3-_G3|>zTqXC-;uD)_H;O6&6o%~|&238+2lmf8d)aQi zpCI&kuEry2hvn}fwuWoZ-JW_$&mp%;k=dl;k)K8vb zJzLZ3`{E7Cr4bTJTl(w(&VPFsUy)QU}Za{hGL6sm&lC#>OWl@nR|>Mak(DD7Vq ze5G{v6g0B37NT#vbl7N=nfa5G50}lhGgcWiuGlF)x48jkY!AxZCKfux=r5jD>9`W4 zP0shVNl8P4H*IHQQLezSdwD@AN9CGgBw_vN5PPBF$S!#_W~Q&Fz;McA+CQN3I$!H3 zPr#*XONNU%T^l!vqwaWZ_xA;U>tU&H?j%vnd`X#qa)A#lQx+l^tC9NI&IADhzEaDH zCoUSrD1j=}N<%Y1)Th$Y915<$chi)?dJwuS`fqPEJ^rxCn9f{RectFtwlZ0IZzzQ@ zWRrL#^EVU*U6tktwG>lVixge@WgGoW$Tg_9t)2G?#7b$$m0vMLU515>yp@6;2iwj4 zyKfQxE4h^NGf9sD*1rL`Oq7v&P_5;?bl8T&aL=bFa#LIrMW_{5>1_y(dD^`4VMRr< z@81^?C~8+ZB0+CCAS6#NNl<{TL_nVo65{6jxHxnQPExao_q!buzByEL=f)R;2$kbZ zoUDNV!@a%Fq@>VFRlAn-fpw3}1F|M+bie|8{z{j3sHq7Ex?(xgz8DzBg+(}=(LJM|K)*>$8Z5DPq$)IoeAt!~@Y7#*osQpR!%Y^^@@PP-3%mnR<|}{8 z5tNNY+^@^d{1kItUN;Fs?qDg>O^A#Bmyet@4;9)cvx*`mus80x!^(P{&3;ORGtF_* zCrn9nVr9%1td&$kJ-Bv-h|^-w4;#XXliS{pLSeq{x%9YqY%>Su^bb0&+ySQAo?K9H zT~X$C3&4CYgfeepLEKM8t&Bye_~Kg9GXli&Z)>|ONxhKa9i2@oABDRzqM!l-30=B8 z2c-Du!J9(j1wK4c>{XJDgY$4rp4>p#D|Pg1ZY= zvotUxtEjl%Y}I@UW%*t?EH=bY!K2c?Xp~yWN~gT+Rp?_YG%`O24m53X&ivQC0%IA5 z-cSiFRzNn}iGVNs6qt`Zdoar_q$*tUq&?2r-g-dD_l& zj)b(O7DKJ)3D)K;J^)z(F5n7O(woI$p*IHd|1AQTbz4_g3e4ze=(F%QCe2+UT3(a| zy2SfHk;L{cN|rJWTs6`G`Kc+ZxqZCMS2KY-@{cKPM27HB_9gsO~wK^frrG zRVyUlVZn}P?TeO=KTys1w(h$RlW}VcX3_@QIy)Pih1oOkDTv-h+t1AyBcHtq*fXGOefPoft!@jt({B8kJG8X1f8Jg5^S5gI>EZUH(vhro`@b4wretLG_HBB4 zan_A8<1PTubt-_dvXpWLzBDNfF!I`Snl7LrjYltjuYFM4o9Cgq%pLYo^o0m{_lwg| zA^mLO!%~c-jSXv;X0sjU`9sZGP~`m1m-(ousn!D_Nfy?n~%Z>mVEkRo9Qw1SWlZ8pk%N!Psp{yUGuT|+7^mLzt1?tRj9vM0Dtg@xDC#C}|3 z`v@Z!I+*In_Y0i}@&7E0)mOUGqLdEW3fs?e+pD#|GUcxx?yjIktaJp8Y?sw8$qCfl zB7jA@<+yY?)uJeYC;FDqlY^cC&^f-^*Xr3I3joPe&(zcMU;?UbbRJh(m4l z`80Wq=H2w<IRc6uhJW+lK0wM5E7R}?EMTrhEO-h?wb&)7=8_3?SQ zF>b3=+Bk6quqdHVzUa@o>xvaFE)gwFq5L6PJod&2^h&j`v)^K!L{-qUz#L5m*ERQl z*KlPiW^kq{|ECvjPVKl3!Pd^+h24=8+_)?g#~}vFTsIV@7$8J4uhyE}*x$bx9qYX2 zEV(rE?Wy!dU7NGDDjoQ5vyQ6yyJsMjMqk0MJ)?_b$IbdNF3PvgWp2nT49UN~#xm60 z+|lhQWX;~Nl%LVfnZ4YoI0D z_-aKiZc2DFzFFyc?j84BmE1ehvvYHHv_3v{`t6f8zs;XKc^%DbOGWCnxsq7pzP+I# zt>t2*>bg@Mko^fnvGg})7bneup6fGkaS`)vZf>cU|1p&YM$jR!7bNQ5w9xrETS9uY zh15FlMSm{!{b1Oi=quTTZ}We`kF3#At!_fVUB6?`s`k}r5{rm5OFM!F_OyJ=iguLG zzsAk0UTKS{P&&q)lJ<%hW3$rxrq}rwFa2s(va+HuQBcE>rknlD_Q}7wH*A<`OV$~AeQX@djOUVd8(cXj;mv=t-V?8|p zDh8o|F(ddw(M@mN=>Do**j?apUfd8zY?+{r@Z?<?3mnas5eajQ2of?Gp_%UfR_f6Eb4%h0YLCS0yl!t86?Id&>-w66{lL!FptCXB?vzsSv^xBsmv<`Tl zjs<2RQ*E5yV6{F;iMF}G6D_!Nw%N&UYpakE5Vm~TxJVDh=_mP=TPKPrS=@=$lMs;d z0@+9W$pO-EA8oO4oNBUn|EEXVF@qlTRI(i((KJsT5C4Qo-*Te+nXL@izZ(IN3Ra*Q zA)#q^O|CJd3LW~{dLnU9K0}sVx?+^VeFd0t`qQUxc*2KEnpkI1RU41W(B9-w>e^CA zAjiDWyDS%bV?Fh=N;DuvG(>c)Gz`cl?dmbyyLXF#rKTvzP@J80qrMrtKZn>KBipt3 z=oUZbHZBFfv_kJ$N%Q{9xQeCjkxXSqzq%Xp8MugE3@R{_>y-MhJnc|&O0OeqK-lOQ zh#Xjo%w<@*d>+HzSx6cZ1(Gk(xSPOVYaGqsCFX|h(;L6dnIXl4$<9~d32AiIdK|LZLh3dBK^2)z z%g=OGk)n)L*S=yDA`-x8Gmzww=bZqsR@DIKUxRcV{yG#aB=4*U5YV)-lP*B5-28l9+2!!a3$V;WVWu_ z*wn~6YYWR81&{_xklo5yxMVMWmo!Bme|N~Mvx4bu4j?#KFe);#o7|B0eN2oG`SHPy z{$j-8{lN8gR%sW03iqy+4P@z~*|Bl1HAIZ8v<^TKBYm+yt81T$ap{7bAY`R$w||+8 z4*pOQot)H<-gM^2#EwiiC_Q_|nkEnEG~OlDz-)Dt1k?)!PbS7!tx((qxTPbIKXnRf zam!@WH!|51pG=&kAGsrhgFkZSm9@Oa?tqSejTTJFZ=xt6QQ)LO7aI?+DFTtRM_~Y9 zLG&!ZPdQ$yC7sS}#J9$*G^@@DRllF`JPd|<9inS$K~QVX0L$o1a2gYau=AH5cX7)E zABr>T{dwdeS)&xRzT{Mp$#eTIA7(VI6w<}pd~Y)jg3tsxUiY$*(q zhUzNDc_(9eq>QpE(bbhLN~iao6iW@{m3?av45rxKY7Y~`%EmI)Vlq zP?#bf%II=3gvN%bNo+QNi&iqz;H?E6DI^ODG~M?&I4_-<-3hn~b98%K z+l=&odPg?Q-$`%3muA#s@76LmV0g@f5m47o5p~U|6x6S1o1PcTiv#(1a!eYJgKs}t z|5)(-ximSD+JU7ORiJGU8Q)~3%f#ByLOg832Z74wM5W85oj*`Z3o#%7}@#9a5~yqza3 zOe!GHXl~Hg5MONR2UQ$I6L$oO02hlk8&1Vm?uzCUL+)9|tfxEzI_QPa!H92FYaVTo zf*SLd@=2S(}f-#rT`>oR6P>ZS%qXbUmeS|N3wvVC_tsF;88^d?`gbVFYhroBP< zTeKPZz?&@@Gqc-kxd+!3<7y9LyL7D^AcSVH?)?5z(f>t0{1o)52NdzUXF$3=iU9*J zoSxTJc=>~$NcWkH3_7G-rHdyVO(YPUrRybQG-OMNi>?jy8xtkjhm_Q^Cw^X?0_BdrpS)QeM{C-;gejr4a6X3w?I_s7tNjKp12v$?@tapl6ilmjAm063BOj_ zd|M_!elkhriA_og2>ku4{+jLJewUTBsOV2>ex@LHWV=g%(J)9tb^^A!ZW7C3CDIYu zg+JjRV)d6a69YsG(Q1Jl(?Uhy2guH~1zT}r(E$P3;?8Qfl;y$J{)}xULG>;O(Q-g$ zfCddHPM;t+=oTYAz5b8so+2RO1?Jo2DSu73@8ejNftmOe7e=x%Bq6%;PqIpcSXo{I zTgg_OHG~+<)2CC+(mZd-poPUJUNdiboKTVPMqPvI4b(DBnYiR>vxqE+)r(^NvljpP zf0G%^M|KUC+fZ|mM!a5thlo|{u&`6QMmwqGHRge;`g^!*Rs-L=7RhRIjv*1b=Gd$t^&aIfB3bGgamf24aVjn?QPD?o*{>oVG>1b zY(Cqnv521weWFn3zoeL}(PGP)*q8CY-uW*dm_HE6!f~jnxD1L6l2S4efMW*j5%hh= z_>^_zRkYIOfqdcK{8bTu{O z1SX}rl`%(%+S;-qR3cfw=~BGRyexU)^z@^ohwg*d+YQDJdXRKgndwMo0`iim`@w=q zG@`O2>TnTM%FwtV=z%~Rh9GE32%*sSaSy7is4*zMIEB=H47fo?CVjXYWw5w2p!$2q zwJ-P!4P*-H)fEObsO`mlxtURd|N2Ji`9#osSfTL5 z74fsyL&zZ8^B9K*(YJ=n>TW9L*VUeb)z+q=p<&WKAwsw^^U`2qvFS~M;H=z{8|yyD zLKO8-CW5k0m{1XjK*hi)4Ez6VEDvo7pkxyH!{p0CJ|#P@*j-G2$X={gj3P&Q)ap|s z8fE3`@M}ab=v^F6BS2Mew?AB#lG=u=@u+#fg^eIQiBYY)uWQ5;tNP>2n&F=CK{C};Y;KoIWb4Qy1*xc?xkqbrr39Wl@V=rw~9Wi+%@R4guW^s4V~%apXtF?rC&$FxOZJ zuq18!dH%NdOIY$T(CKoE&zJ*+G*@(Sh0+QLco!p8Fio*w{nL|s%eVNQ@1TM}i$^Mr z$X;Xfq3k%JCdX#@%N6bUd=3lJ-(BIOOC z84R265*~$L{0KylQ1U&Q`1aBj{|GT)P1dy~A3k#hHHS<#y;INtSR6#oewz<+=~AEdMvczNAGU8UfE-M%IXQi;}q|^lnStq3PXMOE)%X>rr|O3<)u* zm(T|X9h^roz;*r~$CCQ>V0%FV8a@dpAy+|rm|vC@vuRV3Dah#aIrvS!MMuGeh~WU3 zx7mA(^`}3JdQYx84hsMKa=^>Ucq2Lu0}~C_#wiP^fn@8{b7?IptytRfm1#$a=oAkEg$SWWAM(a z2`dT+G?$Q;J`b&>h)Dx^_J^jz_9-ahI{!Yw_FJKv8Y!UU?eJ?mYyolY&e%!Zz)GRFMbn8C=n*bs|Lf6 zUtJkr1b8ZHuGmsu*Gd>5J%4Y^rUI*4f|nlb+4{#sf&ApZQY=_rxk;lMQQi6tBU@-Z zKx`pjMD(ZO=pf(wEj09^K=8c;Xic8TZSSOhyhi9Zqkx-^D0D%*-)idOx;EWK$Ou-6 zG@JtDEV7@sa;JfU8MSoH zRGD)wL>{~^2|9-G>)5PUr6DC|Ux6N-1;sTqu@$K!L9QlR{$pn>&qt`(+j+)tIne?a zQucvFy`8m3Js(<7@Vqs&#yh(ie@1V^UhX;>S&;VDTsQplQ}#a;Ano zpBP&wg?ycRf3q`dq?h927G-jFG?&{Y=qbcJh0?Ja<_|wIjyH&64VE%SAxwTV^fS zo^9N9oBwoqS_`G_mK_i$Hd-Gcu?L4@Oc0LeX@V)Z8w7N&{o&!~1*E<)JNbb#cYgp! zPEj3saz!!ay*qc#@YG!clvZdcuBdJ-#F1c*wIklG3o*AdiO-uce_70^!fWaR;H@jV zw*@UO8=^zfi#k1D@($oGhUQquIfG*ej&Hh|-lX2~ec;F~XsVB#Wo9opvAfrrCwvIiD6({QaoZJ=9<^!b8Fa9)J zIj6N)j^#yrgg*$dN)gdwKdP(jcv7s$pFDnirj`@v6@QT)E}`XI5IyKCkKG^KZ)?b5 zsjDAz3jO>LY@5yq41fapS=<`B?1%zwRwrc4_ksk2#*OWZKyU2>=|m@jtMq|&L8Z32GvqJ_MtbZb@%1KP}vSYn|=*2_s8IM z6qUO)U)x^Zz@zY3mVoXZ`NVg)iNL8?AT2Xs9$w%>0#tpw$GLBth*F8w+Z}f9B#J6r?x!ed#jxwOJ$#BND9?cwso* zLuHQckYeGkZ&GsKQ_XiM#U!OnR7-bJc)DEO;1lpMu zLCD;210<_A)5Z00JvK+Z#+5v0z#UO?+ml@a)m--u=)AG6-Ff)%O}oSFL{%@C9zs3f zn#MeBxBG#W_>K#GIauD8cBz^3E-Dnl$%#-bM0e|)~n)wrnAU72eOyBU&BHr+nU$p1`D4Z!~-M~G!Jfm|~_AxXo8vg?6p zspV-RwRk+4pk2h4&`GoRg-b-V{QU6;JCrFd#oXD-xo^6Rg=fmR6S9`2hr7b4>B(ex zT0eHEI=_eh4JxWeQ?e01xE{P}JKv+@Tj(Ki@AJ6^@1n6Giv;tJfcc0r6$`RjLpABu>) z?c*-Rm*dA5S9)>5OT40@f{bVJ-t-g=vu;iMM@H3hyV=`O`C6@Lj&JRiqBb`2ZvrnV za+F_g5DkQcBR%vm%`YGTg31Z+KNp3oT#MOiv|M5?m~^yaO9*pta9wYY4ur!-ADoS( zWhlLB)g^wr78OVu!*yS&?KR(ojLn8#z$L;4Rtvu)LM>)uF00XdTdmhtNB1e+Jv=TK z93s?nveD;yn45it)@ZqHcqb`Wx&6G?`p(YX`c*j)Kz(qCkqz0~1ROAPmzxY1N-VGq zM`as(dvqWtB3eAm=Q|UB=Q1g#ZbKtGNFZOfBM-E{8f+gU=gqnE`1_2@lEYoWq+Ep< z4CZ;RnGpyd7&)hfUZY(~Zjrz_8Mdq5A9Kd3VYy2!HDHD6x?m5&PI2;dI%a-`85Uv< zZtQJQvu|CB+??%rXgGWi?5oKuyMLPQ7LOU3%+&44wY8o~FRN7P7=^EveK*4D8sT3v z;c}ceZmC6&IIX@9Pw*C~mjx7di_PNl>9@Hq-KX9Mq4I?WF3@tVUS@TkocAlueb(!6 zXI!7L{Rukh@>QnJ6+Wl7Z{2sx)mZaux2ZtAo)%JG+#m=ddlW)z8@#@^XEG+lrI0R7 zKU`|+5AMxL2^)IC68p&TVX#5+x1OGPhhg<`cYE4X=iQ6rUQRrqzaMd7j`~6ihpvj8 zNP?G4rIh3pj8-ogY}M6Gb!8Q^dPwWIpQxXZel;KY0E<60KOfkpo3*AWK*Dz9!bCz6 zrB*vdE>_DK@+RaQM}YraUBi5mXHb_hmmmW>73o(+QZhc=HXRdRKNm6YzWr^__Y z+J%)b3ZPbog>^hB9>hLM)1A=4?C%@!IR9boAW#jDM1_c({rcoq*4>|{jEHPKVn1pk7Oc`SrmBl62VD;AN zT9DCTbswd|62fI4!6tV)r&Yp z_iBat;NY5=l@;^xrK{;)b>kdvRJa|zMvVKOXl4}}=7yf0sF5=3KI?kDOSsMf#&XSt z`xCnOH*Y?3@OC{SYGTcNp2s8=)tTWq!u&;TpV?5?i!iWbmdf_08$$W3kN7n>%q8n; zYu2~6TI@CqE^QGpO}72GnM@`1z0whf_p5m@I)q%pVsx7)3oWNmDs7tRwrk3un&}l;0w=GD?A5^S-Mq%hBKLr?dA#A61UCo zJ0BU8KY{At6vyj<8%87fJ>OdHm-S$CgRrkls_`{Wi=(SmV{o_%!~rK5vXki#0G?nE~+W4N@)- z*#P#BS7tjlm)YeMOC)KSoK|OwoWI$jmL*#U(7uC%@21CO2T$S<`!YNoFp+9WBn zo-p3wqb!(RvK+0p^@h3@>$1=Mef5V|GT1+2ho`f9t#Sb9EUac-CJZ2Y&`vt7)3n(% zd2>9TH*nuRL%(=v@Ti3-ars7Q)8y~6n+!9E(W@cc}Jb{_n`5(#ZiU&I@K1oR|Sy^SH2Y@h7gk6wgjQYlaSX%D6+* zZjchlgucxa5fz=xjh@ zG~P?}xIp~iY3N%z)1O5od;he-|23e^tjp6es(hK6$zs3Br1lpDx82BHP+DIjkDJA3@sOT*vW+g&v09XeEZdZ#7eVqb}rxJHS^yMGe{zDyMOgA;5$ z>$+_*h#^5$32khw{P~{uh(cU3zWTI*7X*c8X_AC5ZxVEkrOPG>zwp1Yy)+WOL&|Bp z{#~CUOWpe#sZf*E9-Uw<^oy6YPWR5{+{<(KotY6p)&X=rR<~}=Aa^5fAmxumX6leL z5yUn|Mq#n8$g#oaRVp&WW#*zfIuz786kU)Ad($25%~bMRlUuCW&3Df+MKNoq>z`N5xi-D|Ge-?TtB&WaV|UW24@7FaokitK*F3`@*%v@t9-Bq;eZjG> zF~(g}nTlnckW<*<&>cVA(;AW#G&Ye0-IMDrs=Dlstz`VhT{`u{p@N@<4>`YD{fvjh zGKJ3SRFU8fSIIac*iCx>{46H*xk6|#lWA))R2l3hQRnOrr((Byt|j$;)+Pm~Z>X@J zU+9}R&C~8kolvZC{%U0jrybF%nzaoB1H?i?y0ycb*CB|T>lrHV9~${<6p}c zo%z`@qO$Sa-(GpvWI^CIM9Wg~xKKvU%y?WSIk+Ko>i!z4ezse4GL?IO#1C4oGJSK~ zWqj>UCWna**xY0d4?NBB20N-m=qL4|C-PxqI5%d zx7vw{z`HEf(kK*eq_#vA^17={pHzFSw5p>U>b>}T7Wz!pdRcS~`qI0;xSR?@&(Tvo zJ^j{@gytVN=^~|~nEUcii!srH{CqF(IIWJ(4b8tp*a;-$+!w=NdHOTe6F74V2a-SL;AVg2mbeP`KPC9|};d7npI=6NiB8!Zq!ok$n zoR%Bmh-}!(h*Ob%lL;Jg)!RoMI%(B;27~%li?L(entMP+@4`|iKES*IrfsMO5uDBD z`}6L2i4>T~O$H^uu$0z9qa{uz%~C7F#%9z0FY4YI2itn5uY~~u5|xbv8+|JhNtcQ9 zZjyfwIeB@!IMnkvsbvKf9uWMmBN@1K^A=KcsE zb`xV5`p|luN^pOkmfyHrqdr;GdJ8k{=x^Eoe#m};>d_82#Xr_QJSxezWkK3#8S`!M z_;}%a?KNHZokxl$oek4D4<=YQOKfj45=jHV#q5r(Me|y6X2>Q?uGN41C~CH7ODh-i z1QM<3L>?i){UB!-jyNta=v?f|Qm?rkNL1JLWe8{8*?2uLp4;}r*<)A5LW7I2RU&qq zf=n55N&36-Wtf4wx@0K(zxDOqIEco!IN4KGgI&4BX>c>iYFN71*tS1QGj7&DCkFHx z3Rx2ds2{ejuE~KRVWx8OV|Wd5DT<9*^j5@^zJK;0h4U`m&_JhF&4e!{)=F~;_jY>> zyXp4gQ1DZqD{OZC+@%&{tu^IEMbBgekR#NY054WXN~VAqZZB+c4CER0u4U=)Ym{*o z8+Rcqphr)k5HI&D7rlZq94ay*&B{U#)q2no4diONS1#yA1O(g@5=t5nt7`~~*}~Mg z-sAtaPwCY-Yp1VXX+Z;4`dO@N5%%hXb?s1hvA1tnRFv;*OaqJERI|jT?IoF(0K!|~ zZQs5f7QA?&W$^pcXN$vKx8YJt(U&jpQ;P>1cKotb`>?lc()K;-G9089Zwx=Oo1_wO z-JNgrabQ{?W+^o7GyYY5dKcuVyQ~GQ5JIM21`u?F3MHES7D}WO5J(uWakHCke+0Yr zZA>;vu*qlpzq)Z{q|9ZqC2Hto40mdJL_S%BTlih=+rF3pMn)(gTK@)mM@+K6XoGMQ zBXsu*)Y>?jIBtM|g5q7;i*NVmx;5L@^@X`TkM2c6)_9)oKtEh!DWR~bbA^Y@83B+^ zDi4}Ie9y%hyOB6`3~9OZkLokY;imB#EX>OlGX&_SnaxU8AGI%CU7ql5YrCO#8{Nuk z+qk6mRQTyFF3*Qt)|J518^TiPLDgtetRX1qcRP|_M)5`B{mM&u_WVvqBvU=0bc(U6 z&C&1pU$k~8y5b&9daC5ANkbti5`>9#xrAq0T31n3G^A@>LpOJOFRCRj5BqavbWK?dhXU|p^*W)d zbO?d|wlK9Dm5!g~wWI`(oCTV6-EpOC3iTqH6GQa}U+#*K<&L2A_f|ccn>l_rg$^iw zeTg5*6dO2Zl_8gJ#V_#3Wp`De-n%o7FQGS0(!g}6l3KIWQwjhJXf9X~@`6c!DZt(^ zbr4MGPc|AIC5S{^Hkl1*8MNSJ+L~ucmYR2N4JP-e`K(ENyQO3XUcr9UGS6+t;nP}p z%_ZCET%>ELP{5egyj6`W&3fbeCu-10ByxyZb>BOd)hKm-Q%1x*?N4``9cVSvSS2di zt>*-xLy8$eXv6W*1(1PmNOk-oA1d0>%vQ~5-u^ z8Yxv)%f$=e*&#Hg^5xeeeEzBPpWa^Yc=ntJ*GBe=e)!xrWYl#oBPp-hx!=V*5w$YLi$> z^h?i^{d1gg!C+jG%rY<9?7eSohkB|(R|vZOr+K-xyF*R6AswZViF0z*U=?y8XkeUXLQx*5WDw zU3CZz$Gx|<({%P*>48su$e7&RXe@`nzUY0+rUu{CB5R?=cw!}1%Nq~6hrv)Gd(2qX z-bTyZ4n$dfdi93^6T=zWwknb zVKwd`pZVUKT3qoZ*~t40S313{Jxt2r3W|vmj@$cd7E)Ru*fpEOjFL-Fn=0k?-nV$)}qgjC?v>N})J=gNJ#Z^>@CM*xRPEJnLBt_oZ?{5?KCiSto zx5!bv)Z#dD@n`ntBzlQn7?pAc<@6%us&WvIOr|>4<9}( zNKFtx8-@#2lZ>P+jkFt%>XZhY8TYmP|qcZaWoP?#M{$_KLiZLqdY* z9cXG(*?a44ZEf`*P`hNxIZDYA;cAe;BsNz)ID+zE{K4Se_ux1JgfjUQvETUD^-K`! zx3jmmwtx(%-i(iPF^6)EPQpt~V9v`}8&18M`W&2b*Q7uo0yqMS zk=|w;`v$;$>IV<5NsW}|M)TNH5VIJ6zn8}bol_AEge_wR$xD;+MK5j=GDMeKJPM9@|LeUq1e~abqFFIU{zmjw zc2g51oYla>^o%j6Fv)GM)-Y*Q$J+}f&ihj08dfC6NIZXjC)kh_IzadVT@6++!U?S@ zXvWkXtT5*SZRmG@7CW5sW@YlZ!t)Y5%}XSbG4NCUV2 zbHfcC6#L-}LST4)sk=AL4hEYES&EHkU5kU~m-(M*P2Ym0&-0%>k`q$QvoM9zZ%xHL zSDTwAdO}@82WETI7>^QQmZ9R3?QF|uQ6Lxkii?R|iUh8h36?7p+x!ri%?GEo6nzH) z$VbvJZHiC<72j89_$cH!EITujJY|7fh%1KRD?j`INO|hVR|%%bhzKPrYAHQ`b24x7 zgyBbp@uv$W*(zBg|7tgVW?u$`gZ9e`YssIdRogmbKkxkh-H)A(%a;ojGrFnWD^1zy zOWm!(ia)zI<=A~8vvJ|Z~xWys77H8hnc#(e8ekN^UZ040%v{#?aC zp(zyLMhEiUKjSr_@XnXRscdk1M=gXx z<}*zB==gZU*=oJ--?#y!_zZ`v56;Lr&#zxOn9w0p*Ljpqp!}0%q34OO= z1ifMaWaY^ptd)W|#LU7X&6-eGa3&xe1+Y4&<5Tl5Xu-ZW;mYJIqb4%R`3&R z7i%A%z0>7RRu@`C-dwC&!lTj|UH>*r*>?C zv0D>m*h{XW6y)KdqeSS(Un&64iVJJcaB!06;Lu+V{Al}28ZvNAEiK7cua@6YKYc5C ze&%rndWkZmhzYv_%pRzpMl?P>eV3Xc((0=sdV=Nkp4b5}9pdq4fq*UlN~b}I&*!tV(pSG; zesHOxfZN!p&unx1E@g}7nIEQ4)8@7^R#d90t0{Q|0(eULJQK4ftJ4)fUONRDjO&mjZHlNZV=;e<5;pt`}ZkAsq@ zO6}#Wo}E1%vsnKOrubg)x)=cE+uE4-&pc~4N}C-^cyNJIWu0@72uNZ%MnbKqp@29#ZKd+W~ZXSqZ+9ixYXH zn8Yq%>VO?zKj2G0J=-gu#W2rOi?0rT9|wB2saZ;B!!k~7^4-r(oTyrU&xeYiV9_3J z&)fxk0Bq|zy>Jm_f7XiSDW6Ts$1>Ho9}XSd5=>Ue>rC1gyJ#%@3A97f*?D=}Lfax2 ziY_*4>RsnR4sB@X|NS^>jJ0aU5BJT{4M0H|rO6+;yFF!^>(gT!14CP=o_W9%-bB7Z zY^tqN-_ZUaF8y>XL(yPq9W-zb>{ecbsFXC=T|C6OhDTn{<#k^|XXfM}``!smHYJ#A zK(v^6v#Y^W0R`q6+Mj7(?jon0#2DpFRFKg)_I@Bdmk=4d#+m13UV7%4E}mFtV;l7? z;mDc1$BtHL=%L16mI^_2zf@77m2e8y*=j!)H1m@1M>E*JdUHJ${$8@&PX}nECo3PN zLB68lO$#5L%{&3~!16(0Rt!udbT3#Mxd&4K@v1*@n%#AAc@7LlMexU7W}e5BUTr~~ zpu|(9ez$v9f>p)(}Crp!SyR{HM?$Jq6+% zUd~|ijz(>9WqOu-@IDmkDhsnB3rh`%TRWz9$SBaBpI2yYqu2xmnZn=0&Oa`*RwZ`G2T*`Q$MKW5gHhjjc>(RmdFKZ6A42M%3+%MCP$^IsMe%m7k3Q~Pbl%IlZT zGV}#AjP(t0+#+h=*AELUBH@% z8Epk2BH#+hd6uvO5#RtsI@p}No#47wg{+!12tEaV=x{bWN)SE`;)_6~Hg(+-4nv*z zfmr(aZN}Bvw_!=?vgQN0#8xJA2qdHN{vwqS4L7jP?PIu&&+P*@BTYhp^&^md0Nc~P zNaPt@{6{)DtHN)2K?yl7CAo5bVlr~-9Uh=N4IXf?56(QWhsCWKFH?kDX~BMp+O436 zJNB+Oq9gloA=5QJOt5qu^>~1Psbs?uJ77zT_V>Ow0jvkq1;NGQv%sTTq6@zS*Z%b~%NUkhP zdVOcV_d2H;FQw#4Xtr|XzHXT~4DAEzPx~C*W$ouE`Ck(92fbdTDPeMy3~z-h8!|~! zMIZY@w)FNM77$?)A#C@!Lvc0S3d|FoK;~Lu8khy7^Ambs77~*7F}4QC45a6ORunS- z)3x$3tLXRfbHioLjHS^mvCqBHH{lVWXYn8&;<>=ShEQqa)t)Rw-vODLZci0@BLxWd zl_1pM+L8E0bX_u7%C?0EF1Qmm$H!wG`hx+M<2jBV$WvazchZF!=f!7s#l^195xoQWW}C|BUY zB6xJtdL2qfLSZnn7+FemeNPPQa7KF&dapc^B_{Khyt=cG+Sk^8dir&xm7R6B&!@Z) zkMG>-WalR^^e(bAQr<{rfZoAJ>IjzBaG`(|IsKYI-k~*|<*rHcA25OMhX_T6$Vu)6M%@o~3T}>JeksYC9WkYk$@_JVyg_ zsn8mlBXLZ0HSziRvqP_Iqt`;>d?(O2L;#%sbqMk@^sU=uTW21L$)ba|+Rd+QfOFO! z)1C%M18OAqbtVG0(e>?$f$BWbVta%HB3g=01vT{%q%;ti2MePoWK*#6`oXP;pveh; z-!yJLDqvwEFo-=-I~}b%!k5CfXK7i1Jxb}@bLg=&z)1_0vUI>m^37%dV?=YCnmHMT z(;u*(Ho26elzl+f#ln31bi^%7idI+>T`?t_Bw+=Vn31LF(NS|8u^2(9daN7Gj0C+8 zQ7Yc*>~U)F+I~QKpt-9qRvNY^Tz-@=;D%%qBoy>24af@q>q_AM3*HT`a=jbPym;5J3@WfZtJX1J+*LqU~=%iehVdR?~5Z@wcTL zh``%*;OZ3TMKwb-cYGK2!HHv4AIo~LCM9`{50GVYdeCrWrA|(c{zlCm9f3t|4|=7; zjMAdcIfs8trXkZBrY4FdA>4o{#=6l92ageQ8Wp6Ay;9WW+U~Rsm!2wJO9XfsI`Y%) zJ_-fmW!%6dPw$zg$}~Cs6b^LC zNe`>=jE!yj;aHtO{ysiU9asqZJO2gS0GRwbK%IOKO1DxRUboc^yckaA?UjV2GVBiV3G$~;@1Q?te zBG-7+ZM?v9BAu#m%cMjUOKos~_?bf>zk- z52h$UW;2{u{*@x@MgN>s4i{H1k5;yP(mwaj3>fHS)a=fNh6W%6cT;W`cU3w_q9Cs3 z1qO*1A|rt?;v;mn!csk@iEud2485iYc%we_>BFnDEVkWpRN2zha)Ed(P6P?<+1JON zp85d1SK98NAH-Y0gJJ%GAu1X|I6(x=nC+Nuzzq!XE33vl9Jh zo~9XJyi4yYb%>8!{G8n3lGM9^U}9&x&I7I9XJFQ+^Kz6I_cA+0PNuf zR$gc}L=4`0jW0Xy@>Namsz9)UOrosHfb7dWKe1zE3?e*B&wxGYYPzaEj0uCtFX%xI zk8MriRFjv|HVU`FkA_|GfF&0>;S_Bf6`0HdDFE9%9UF+9E{0FOJFj@-3amllWQZbI zcaM;Ps*Nxw4X<9^HpT)lxgkh4DV=&{;*CZt3pt2h3-rq$9$HGlm-3$pF6cX!yWGcC zdcZdz;VBB38PN4sKJT}ItKnC9Rxiyc|OzI61xB<~)WK|$LZam*Eq5mMEF?-dfhrw(n zEhL_XnXw77uTjR(UT>^jU!y|P21-1H6Q=TBls#4HfTCVX(0a!Hz#>= z9g+1q5!TjFOp<32yh1ji({B%ixfAYy%mM!vIE|0XjI*D~078Y6z8664!|L$p#Y>$>Js4@stJAGZa%CU=I%F6v#O^yr-<0`7y4e zw#{v-J#se$kmy0~!hpPih#45T{Ux;G`psO&>_f{M4T7@=Xvq@y%~CcZoO_67A-D35 zAy5nbaBp>Z5n5;E^m^M7&3uq z-^&#!(xXRHXDxbz^ipNC=!Kau!vN93$#G(E)}9?m&eL_X#?bvsT}mNW+}{WRS-vUa z9^CtWjhQeFq98=g91Bi+bnJ(m`v~6vnK8o8rWpdw`ZKeOYJi)QI=>%b#qb~9mo0wL z<(q<#1(2jYX-Tgp+iV47b+^kw@zmjPSYW}-a0+}`){ttC?Mf)zB5z`?4b3Q)q`Mq zHT--%X3u{s75@JkIsg0Of9CxEdolmN$NcXx|9#Vm+sNv=5uY%dvxOS)f0TXm&2e*E Kv+|=JSN;ufq5X{j literal 20209 zcmeIaXH=8v*FG9&bR4lTNKrsRML>dpf{0RWfP(adUPfsW1f)xMR7ARz5a~*X1duL$ zMx-T354|ZwI!Fn^;{!jUEKAaCfm#l>$dG7n(_b%6U?T!BGs)|P$I2ceU z)X{r)?`Wb>`#+;l`!4?e8@zIn+b$o4QvG=E&P~kIeKY?Y_Wa^Iy1TF{YniDkKuNU| zlBW?j&;%Su=9ll1W2&}iSxO_`aFBU59lrb_bl!~V)_0QOvHI4#8UR=LhqvpWmS59{&FGyB|FNdFelv*t5WYeBwW$ z@IOM~_yyFBzZ%KpcAuSytj^9W@p3*lYXb(e%L50qJ)Q?NKFrIwRO&RD-+Z2bdZ5=4 zZJhLRP6(@1GVC48KqQeKr>3M#l!!H=9VkK@^>TAqb@j{phMs)=Bp-i0f;8--)?K+8 z)d^qWwGWw|?aqc0b)3K7&kv8#!jO6f%Stc;gKp!e(T_#N#OR5zx|&q((s&C6S2UGV zKG$yK%8G4Ea%DiNKl$YVs^Ns5Ua$TS+Bub&#muIu)|?{As&-Y^AvZtIo1UJ})9O9b zBKVnUIr-+4jDBM7g>&cbSACb;y+>U;6v0w0d()!OYVcO2hcn^9{@>k1#Kl!M8Bbws zrT;iZ$~WiM^o|ji(q3Zo(S@CwamHP)qUl0xR@30YZ(Ji&s@_rr^uf7 zDc=2CO-V%spZhxlgQl;KOio3i>A0;*{8cS@WMujC=K-11#M9J@UgbsB6T*g_<9Uad zYqxww1+T4nJJ0o~9_8$0MxF85Z?;Q7q1-I!G0yI;YZu&<{ai$dauL?u(r*z~PiHF$$C@tP~{n8GbR;?;PknI8M~0I<>o@qpu)4Ldvg zRGZ^7m-$}eK)!zV#>2TIc;W4544>+m)2Ar8Q4O&QcNnpiWkvf@4N`yof*SKL8qV$h z;5r;pQro<#nYUDhA+o86zw;5H`F+opoEU4o<3?$W@DIosylYO0YSTIV{X(^n@nUeD z+S;8V7MX{INs2*T-GNo=lkJSZ4~Z{CSfP&y8-C1akPIGEvF@s~{?hlf$gW?%(!Gk$ zMr$#(IU< z9i3UVeUQS*C2XL;#PooC_}Aa6vR4&DwTyeq?W!%BcMNU%i`#`t2F{uM_xCPZ!WzOR z$5}AeB-zu6G*Z7usp5{x`$;GW$=AkuHUZb&|nr^i2;pPiT+PAm2=4;5- zoL6nTntBt7j_E6+M{c7~^La;hMfRa89**b8;08#+iyal~CX+)R^Vs!8+L(&nF^;t8 z0y!OT-Aapi)wsKuFEJwM+_-CD8Itta3vwx`saD@-TBtOt4$B{ZoEd9&r1FdjF%pE5 zU%$TOW^nh>M^!#Pv|Q7+XBAr8Q}dsHRw*Z>b!2W0Ek5ZL4S4-~gk0?PO~>`{ckdqJ z+DLg0)?G8YN4-5hF4h#!N||pyNRAP=Id2y$u`5XDyrjEs1LwkRY5qEB?}vr0U)qoQ z5x_0+xNBNwBkN3soO_(bmk@l(m8Y4h-aQ}gty|5O(Q!jrFBU(A#FtZ(oDb4UvZy$; zprG=P0r&pUMd!2czmn}_JQzE}a|xO6vP-rvjLaW7xmNtp;){m2r03de2mqQ&jbcz+ zia=u4yF22z_mKtTu(zcJhINhA{!L!Ur7| ztdU^W22Sm4eIl0Fo%K#${BaJ6ba5Zbq2&+M868M<)JjXA_2)UcbWLzdY5Lcf_xcL# zsu|j&vip}OgIdOp`WRMbSqSxIzVm(g>hSj>t6}-7ix%wXo%`QWhJ3_ap|6spwnchnof_brNKLMMr;p78PM zf~%1Jaek8@y#pdn>x-7BEE(#yt74t)wKGJ+GbHv2t39 zmU-gSH2FbEqgF(Ele(>Lwzl(hy9Q+56bd1C;_tr_S)}Hb-Eh>YqN$Gb^5+4p%sa-T z7B*uJ=OrV0^p zAr*XJiclfJ8%wBKgLYdbgucn^HgTbO1Z%j~j|YJ`oDf;ndwkzI0JHG7xf zU6#Ky^V$DS@b(DLY)_Fv!o&dm==GVa`%uy+E})*(|Hc>`#UCZKt+_h5%4pOA)vrCp ze{^h7b$L_2;Ht3&+*HZ8s3tAA_Bps=rc0v32Wa1 zeR0E8MbZmUJQnY4mlcUiyT9JKC{23hUe%_elWURjkFro?OblPaRHyYhzd^o%o|mC~ z6eHF|(!0*3apy&|q($Y4rLaZJRv!(mUzp+GzS4`vwN>O9!qROs*YiZJH$&nbB;z6R zldYL$V=lU4J@cPywj_2#2LCjyeE)uU`3z^bfZYE5Cq*fFHhys5(hGARN;=PE9_#m{3_Vq^aBtVZU*6Lq+!%YO_u2$k%|eaX58wbeMGr+W9@k8T7|P`{-*bT_?i zB&$IYj^zJjOgaloCCLX)DHu<8-CM9GP?+hQ#p=eCrlwAvKmyrF%#`o&oRpiW#$3Mc z#s3fgiUy&gsRgSy+#XWW=(sO}Fu?vk191<=m0|>z2{hS-A2ch4rOo$}>uQy&A2XAL}k#d;{=Rf5VdxYo4T!4Y7 z30#f^eO5wuE@pH6m*}=?J2fiNKsn)LYc+wSO>EN5dsu`*C5S<)y$cYk=?PpOX456x zysKN8KskN6;Cll}D7K5jyb?TZ)&*xD=Cc_qB;Y!Bx9nv}Fi zai8h=^Ss;W0{6;=7&vmm2i=QP19_EAND7doqqnBMKob95~w z+RbyJf2UL#@rr>}1K29HN|{#ap3y~C9c*p}_05B{oT^HX-xKaZB)Uo)S5mkI*6wNM123UX7yN+p#JI^=$jukScs(_lbrD*ZWkK3x>6Ey69aMQbr0(aD z;SSmdeI(Dj7P*!BF80?3R#Ql+f#U)tknbb(wtqVoR%07cl9Ccf{U`BQqmhlV?b)$x zMQ$(MstIDPvbjQUzWEpelF{y;AGsOCb_tD95a&UmS`ILv{_L}>-r+M@HS_hERDhJ$ zG}TdFUh&|E!FJJ1KUQ>fktSF+TWsLWgW!2~5pj};-6aTNaUP41hqBvu-Xw7py(vHb zO(*Nj#Wjb#rn2#bGj25_5}Q^tYoS>6|N1F|+kBWJdCKC)f?qQ2D1E9+bNO|fs3}<2 zy-bOB@b0jz3~Wjkd-LB->EF5YHVc!PvK1L0FC@03H>WY!kW_0gDtPA1jiEpDbj`ZH z@!I2tbBZdgV5c;pL6hmENimB?qw>`m))WCP%LXGR@fx!tee54d@?;m!(upX!V$*lm zwBF~Un5d{I%gK`&12Lk`uLf8(#O_1*Hh7p*q|ciaSk8M#JMV6MLn}(3zX+oV$tzGUW-k-V~@X}VfcUjPq!M_spx1TjjJvA{-#q@ zQ-<;Eb+-;NHJvc_f^pCHoY$QuhD9k4^BN5LimlGRjQ||o<93*qb2&K)t6R}mTFIa} zAR!oO5FH&I5MQD`$03H+Z{Rj3OP?1LHYy`kuyJmtGH9-Lmo)@^F(@W?7kaL&~e}>YA(9f?E(E zGiE>vEy~JLxzDxVe_~!tQ^F^NOW5?75fFFFeO_$6YP`3v(5p6m=Ukyo=K669CqEB= zioSOs+q+V~ugWxi01r83pr_C(aDWvG377ZBAoV8?tJgHO(rO!y082Vsqj0xI;lhQC zw^2ey8U429*mYjfbN7dwrn5`3K5WnkJnj%Pr9@0-IC}fWhyU6Z(I_{sX`*$`d_5L` zWi->u!=rBf2t=y!3vDn$DCV|%5iNA%=&5tAwL;Ok&kH?PjadshT6DC$rzD=d9ze>} z8xH{iOLJ`{vYtFZ?g8M-9f!Xz zV-PEa?GWmX2VCM>A>z`c1UsjWV1%rEx^Q_RQi zLSo7RAn#TAGy&N}?BLv>2X^DHKi#xkT#9q}fn)glr#mh89J*dS+uHt-ws_~$1{S!a z;yV;dR`B|D`Mf1Ia*(lIMOwAv$cP#`Xk}I|qdk@Tjl_WaboOGgBA0-m7y>-)G@BDT z_VQGbH5GHSz|OfLXpu(A{D9Pwy(~w~!D{^o`TSYSq5Gc~ex1bmShC<3{lboerV41l zdjnyS%S6wcKXI~<>uhg<8Uht1u-V#LZnY0e4xvV503^r?@bKI|YK#*o=qs_#u8xRu zF}48sOgliNqQ%oJ_mw=?8_+5)AM zmV@XI#vaxU<$Yz&&d%X7IB+uSqv|(W*ao@t!sp=%P%EmK*e>2>rHoE zBuMQ&^hN3m{0Xj$r!W3oc+em#3(gq!39_PY{zY1JQar*x#qL; z917q7LeBRRP?eYa??d#C7CFiZaiE}pc5KJC)(6*t}oTo~B9}~xw51|2U zLTJ>DAqE!I-UmPA}pjOg?~2bGC#1%$Dj!onrd(PpW6b>ojq%~ECx`h9Ne`A`_Y zZtNu*ZSV3szR~oMe%j$?laCpD_?KzJGi9J?v7h((DiR8u=d52%zW3w8{P|9PbLDjB zcOe73LFMK#giI{!fJAi0lJOi29sdWQ_@ zlGkL!$vB)HBnsjO-49Rlfch8N^s9m$YW_gIyAdzrq5434n!*g6BH<%{#@nVf85twD zudB;W(M&8(C7!*!EUCC zZOdo12OEG-OD->oIQUU`q%`tM$*S2_kvv6lX4msDRDCCaGBFB zb5)zd>(80_naveiWo|AOUrv5CJw+vM6wJ}_tU5R-D9-CHlc`0-$xZSJZ@Q+nd> zsF6~LEr!p{Mjp%Vp;e17A?9pZs>iul6>0;Ac}!eDMf|#J)$wuG;dt0-Ip*ybVb!Yr#{Trd^OGVzt5RmknFNoyFp+C( z9cQC8vdj*-8Ql4D!n`hPz6-b3a9PxPHK8&_(k5+aq7Ag77LXX;%y)TL8`lhZsU-lP zAZG`#CSo37GQUMEMnS}(wP$SIIz*~Zg|Rcr>gr1nL-|$l``KCN}ERm(0h# zeq$|Y9e1UwBx^zlhm`xvCR|5VAh4CoAjV3V)_ajMJ1ohFJ0B2$3_KKx$Mf2u4T^)B zKJY$%Q?gf3(OC4P1q+f|_UCg^Y;Db(U|K=AZX|Rb z@wpi)yYaf|8fa&*)FBqs8IxtsOLtaRWt<7Br8}3@ZuDu#Z{Y znAPRQC}^)lvvO(QekOJ7)V1?=j|R(18QKAch$h=7|F($5qh)!;j&aMLLp^hY8tldg z3SncTfT;DKyT%-nX3rKiE8UCCx)A2X*&M$a+LUF$uQ{~RHR4JWqUddgOc)0n&~)) zu-g5EYsOfjY;pw~=*9&@9LKY*wD>NRY#cpFs`#N+kT6AgvW!ocngVe$2O0$~$01?OQ=o)L3$SqQ+qX}YXs{|v&K@jZC6n$k*B_F| zT~!Dsl$?_F#8^mgOz@P2ff_*}3F_orD0w*UB_IZ|GO57jQ&G{h=2Z9CtpjXhu8^t8 z3kCw2_it+`uB?cSJgeQK$&qe%*R4RQ0-a#^PntrvZKYXP|Lue1(*T1kf)juEJB-fs zI)V--%9CXAR()-Gl4@iz3ui5D%l6=oMlk8u@2t?n8fs75CWjGf+JL*rwHac4i_1x|`?ka8%mtM^~uMWBj zRI>_b-m$P5%Eyj3Z!0FO(YuEWEr-3MSx=t4;9A*L6wx%*mranyRzGlz7T+5D=hoMb zVio0RYUwoRVke?AlBTE$%nXT2tcl_5UksepZM`a;qMLB-Deo(UAaRD+NR9ai1s(Ca zx_UU8K9S5w=}+^KR32)b!`q4yK{@yZ^%l{mt&tYll1Nt=QWt;u^;fJqpZ67Q3#xVN zeQ5ZJ@_^bSBJQPU@#XD`f78<<$Ai&DuuZRm$G)&uMRpTtmu2)W1K&@R{`K4ude9FL zwVto~bpyAc8BJe!dHL2?v3EXd(e|K*)`e{9CLm1Y;3z+t^z0dGJ6@dl%}l#x=vU{3 zZ}QRDYz3icx_e2pLp`J)O3?MhmhLnagGkN}x%gCs{E*6=&;7jNGdik?JJ>L1j>f(8 zo2~x{>-G&444RW9Ed&~$I*9Ii2P$+U>Da7slwNYZs`|_c6S5IvDd_7(gK~D*_mg$= zjMd^D-8+#3MfS-?MrHI!JvE)2^DL^gT|HF`pdW^OKc{)!=p(3zD3mz^4JL{sfX{!rO6i;-Rc#W`ka-P7$HCq&FMR~X|2OYC7mx8%ig?z5KH|_T-@bs0w zypWuSz#0jMv5Cu-eMRYr@{A~)IR%z2ncg+u7e9lX&{inGysY!RDLx&95DN2PnUf|# zN2XbW=Q9Q$q}tu?I_1$&Z7Qq zl?=5EE4Cd!p2&!>gO15az2S={@9m;;-%uQ}iPY1s1MioYoseEeDHHVVuxOpg^tAgM z>d=1LlTE*dX*jr%Jg?(-Ew3AuIqCJg%wRzJbf_t|^;KW1Z4TCeZkqIEzzGOe0HuZ% zMD4Qe;(n)%*d<6rHdJF%r>v1V zBF!`Fo|Y_C9BI+=Kra}Y_lhx%xFviB#4*XSNB970T%E7Qsc;z zPE+OL!_)9ZF#wFqAx`2iKnf(%$_qYS?m{uRy7NK70W`k~h_@~Oc#eKF#Hv6w_58@ewOJOs9ysW6L zvX5s72h=?fzW(WpW&3A)23YNYfcI<($opTv=RNE_w;teZ9VI9MD`mD=sA6y8I^QMi zDa}1=H5}_PFW2zU{0KRVLODF@P4Wji^X_?Tn)6}t*I9yg&~DJ*-DY5ogm-=VA_$uT z)tL2V?uiyHWClWl8*u9LVqk}5(>Y1xY-momfV5z zs2#J3H~To`R%B@DYO*^wyf&AA($PY5t%^l0L?Zpr|1#DD6l-kmO5CCr$Qy_b^kfMd zEes6kwC8^gf81-=m2`r#ZzA-TxV5aXniB)s`;Y^WZAANKO@VX-?+2&xgC%oDYfmJ0 zmx)vG++9A^x8Aq-UJzk_g*0A9MmF)?WzLf+xok~LTlMwKb&r}hVPxTFkEg5O?u4zk zwRY`fe91w9YK+$kJJfz??z~*iZ&Q8FV2o zhSxp>5u#&t`my_FeAr?!fupOt|MoD(m=l24Z`D*aA(Ocbm`1@9`buoFjb%(=f1Aku zh9^4n;w7CQr?-6fe6MCIt7Yr#lN|^z5T9%2n;n89pT73=uH7=n8Hk;Mj5V_ZURNFj zAp-s1Adyc^QIAw^H{a#m`f#(9zTjtysoZdU?h`9JYg&3AGCl&U?T`t#VZx(y$HLaJEKd4-80| zzu1ngEimgZ(nXO@v#YdrRh)jw;~XTo)&%wR^z_%L-2u_!Iv~W(;d(3l-JRz5o`*;< zsrJJ-*vy_z>NL};Eu?=%qw#nxO}WZoyAm4uNYabesJ69nezcp9$ek$EI?tKS>is|G zn$`|MhXncaj2j5j-k&w5|G&I+Mh(1=^%*Cnq2Z5b)h+-xK$hT#G|TdCUfU-ZhF0)e zUp}0;;xKlJwUoxjs-PIc)j_e5qC6;>gnmQ-uJhLRM#X!Afy9Xse;`1#<&hVuRHC9p z<=VF;GzpSEb(AhVwWef%z+glZ{PEe9Sgiv+2XdxG&tZ=14sQbn90D1L@L}+?(mS2D zDI8Ry)hEAXV3Uq#s~>dyvVXRxO+ z?7AE3Dx{>?*0bl6e8*fhgN8-rL*TSLu;^oLOzs9T^GQKIpBf3NIpWqJ66k z>?^iO2Vm5pud5Iy%{MM9idsQ3R}}pzz8N=B?5XpAepMM`|YwBj&?%$hEJ@-=CNd+lC#L|E9_`r^})j(mC&i}l<-4Q`X+hR3+1 zQCOF1ZmHS{K#sLn9jM#J=R(Z-JyY%Le_E=fD=xRE^l~56w9+ef z)SEsoBO_-|%Q|4X9bu$owx`s<#~c8-64-#Al9p;U-8EYVd)s>`=+nZ`uIhlsptB5g zx7DFPu&wv(LB^WQO^3$jW^GtVm>^=(wzGZES>}YuPENK#i6HogFkIDo0Gt`_9}m|2 zvXV`n{QIBVlAdE3IpYu@eR+;0I~QdJ1qeXW9wBQw&;oMC8`@0!%IBXUKgk3#Qi|YJ zgmBPj`igYmnMN)C%>I1^uGKrL$kJe9MDKRkm`qP}o*VFSQu%qKWC8em99-?j=oEHO zpFu3~8{-r|6rv9&tRKyM3Rhd#uk^VeKbI*$Qf^K zsh)sle5oOzONc%u)QAsgizB-Xp+TDe$`Xb^qYytZ_8P{hf;1!y6Mk9R$x#kd?WHfu zoM%&b6hkvr%5{*IQFyrObc{fRECvuAg+|5;W-)2&Tn~1W(}!s`hePmC&?v4<7w8%- zeI5<9nzQVniF$rMnXO-`eI0U$5i|V*;okwm4nPTelKpWm_SCf^3NnBIxy!nz6@b%+gaL8e znY+lp+s7smG1(AO9KIv=*NqJMr8)<87+KQJHYUcwX>*VnNL3B`)P;?~e;j(@{@m$a zTF3@VJ%J`7f~Ze1K%OHtZrN87; ztbV;iD|D+_WjJyK^VBR7n%?h$jSC`9VqI1_qzJB$-Mf?Fiw5r*>(n|EZF*TmYA!{F zg}uj#;{?)$X&bnZy(e#y_KxPfq)DT~>f`B&RR`c7qT@Xi+T_+s5vVvV!fR zgko?Y`Z&0qt;j)c-%y)mm>o9ngMcKM$P$10s1RNqK+tYwTO|?ExvW8C)~>0`j_in1 znKnPU&tcDmEz(Bv9clO9P%JjJ>9caW*S$8s=#;GvX*y(TRr^C*$4TW55H)sIA;OP_ z3&>1~@@N!tIz9pjJXz2-+#y5pN1Et>!6Y4)-}V)HU_sG>A*D2Hr00RCUCN;ezZQDc zs~UA=EAu1M81Q+MKag{LiG9=-ey#M{%vEH3_*JlhKsquZa_N_`zl(Pxmw zsYg6+PBVLViX-D9IY!1|6bKXy4=KliW68S^fpmyhfkt9wHQ+!4oICda;J8Au5^YJm z_73(vHXBbqod@ue9`|@!d+J;5Fg4?zAmU7MKQ}cw%2=P_P9NV}!Jj)AThyAx03P`7 zfY$%;+o?{wBzQCkRm)mdX?vKsW<$r${w@E#^w{Qm9%B1al^l$v_bSm3Y-#0R{cVf?~k z!?IMd$fL!0@8Aw}+^ei=b>-_oC}h@i`LDROKi!8)fr7u|r?Qd_H{}2m;F{2In;Lvz zmw6LoRQeJ;8{|Zwe&Sz~1rVD!=+OuDk8)pE7%X#X7ncuULFO-%mw&WP^?JNRd=B@L ziUs|v2xw60#o$ft+pmIK>o}+waJtf_jf>hyCL(HQQ-7@1O~k-pBr@oP%!mm;VA9Yp zb1IFwYLfzXKrX+uDdsd$HP?jz5ZmMs1DX#BA0(;RE3;j=wkC^g&l-%_ndD}AG|=0m zJBUl_rm$9e?W2Z~eM(9jSYi1=ARtPVfXc+6gFr$d-|z)S63$ zIJ!R2@-|Z%rI8m73`&7cM_MooD0j%435mPgfqm8OD!2WPMAkmc&>5DX|2zDKWFuBK zrc_{SWqki)n!SDXNX=U9s&lY=xA5rpWq$=GFWBsEEGK0Vexk{X4B-V->U zLNHe@M3>EAg)!*3O82f4GZciDDI&9&oBC1kRAhJwBa4Eg48`>n2*(&E{>1v*m%he#~I6iysi|?vGm?}@<%?5 zog{HQaK>g~?q%y1-xXFKt)jkpz*Cr~AdWD2_;(aQ{zhIHc8Do@oNJ8~IY3+PAS;!= zl)qy&*-pdt6^4QP(U6-GwRRy%R`waevOeY!UPQ1+j$Yw;RqhbZ)Dt+&p5rN0NNA7N zg#a4F#UQ$M#62kE76Xci4w91zB3t))-uNhfWZoJig4T?-Q9R^pq-cX~2vZ!a)C^zm z_0)JW-s8@wY+S~Umq(T}(PFPCAkUr;KADEhJR`2m3pfeV^XcpE_d}%(wU2U^XujBR zhBl~OW2)*lA0j~`z@g*)AO%5CNElI*um=&33`?$GhG<1*){vnv#N^WofRhC`>zqLy zAvZL%?&26#|IW%`-VXIy+o!L1=~C?hrfYv%=yVj1o-$C2Ic!^s*V8P;E9YdITckx9 z#AH(6#-x5vNI7mL{5C3mLUOjFDIy>wiDr8)xcSVgcjBgJqQ@(_kALot+t1B!yNg#X z$pVyDG`0)3t)7-;zy8l-cAt$YPMKcUHz?FD1>lxRQ@o5;*C%;p^Fmwdt*%J_QH;9! znPdezPBcGXHY%(uKic7WFGJDe1y4PZ5Ox7mpP_xMY;5EP!%9PKzZDy^rudQ@T^V6D z=BcUTTK2ylWbDq=S{E&TyrF(tvJg+-36RfsU%5-L2b75aPR(RyLG!=OUR#@Dps$ac zko(GP%7dOi|0P?u>;^kg%)DZ0?wp%6mmchtK1cs3j5Rc5rO{5m2_v}PNLCDfLo1(U zWAE)XFh6=q+Av*}M(N3)yzM;Es@?TXe#wklno|tB4G0Qq{ck6Frw;Qa1eh;MIc?%4 zWZD~mU3@AX5bPgD9rsU=Oe|#kV#a&EGcwP0URKXqG^(*|J?*!$BS9O)wl)C87xalckZaDS5mx0$#EkTq|HGwa*KKboiYqQF=Pj^{aST0~d zdNJqa=eMfYF`;B=RK^66NTKE_MUT_s{wxk^j7X>4RiTro`ceyuJ@Zw@nYm4jkO>eF zr2O)8DvQHO2{K#}^zBujC?-zMWGu0(;hOi>%ioU8@FY*)j>j`Mb)_;KJ{&=xb(L#4 zxxMwfp7Mzn0=7~g%f2+McfMv?MN{>XLEUp-WwYgp1Yto(x&v*0fjP-IHnq!2h=q-f zFIfps78RA%6%@>t7t=d*NZfp8uw1QcgLFji*MrM#zS0BqeR`lZYu~*2R}g+aH!3XV z8@_0{a=onmzUR8U-mv%Ti=eia&^JX+;3-rh7Jqp+US70f=sYaMitDJiHY>93SE{QE zHo)lWUf_Wp=$-B>3A^k%^oyRAP)wuJ-3$TK`5~|T-;QzRXr(WjKki+AFX{A1aC)0H z(2PD4COrBhEE)Y}pjV6)aiQ|L#}}txm33QF`1{Q&%m2&^3XUy#1b1@mwB*InWM|Gj zN4swKr86}kkeF0a@vEqI?ibDJLN>%u8mUU=?CjhmytvKA#naMj1wnK@c8=9=7uf4V z6I)+DcwvUk7|{Tm2@IKeZ4NSuD#*#*>T0iD!Vc6JalfG!DO_Cyhs}^qc zdu7hkN!xC29AdW5>w;p>jLu%qB^p<9I>{Q8c!L_qnR%tO)WeLIm#S%%-<5;Wk5$FB zlY(JB=Dd)Bw25|ob7;oL7TLp7J1*JIm&;tHGvlRPGtaLMITCi(y800p7Cp|)Y?ckT zoM)QQL6dZv_`q@XqefTHLc|r9t@FNp`E)IDD+k!;|9m-t)VkzIqTIVcXO-Fj z7_Yl6>fpGVVHA7C7dOz8E@9b%wF-}5c9z8wwcDy=^G5k$VDuhCk@<8wu6>NlX1Mrf zcAtxO`7y@E*TKPUh%dPgJNw-l9QqihZB7bvHi__H$qJj-j&BiX6`UfN9BZ8D$_`u^ zr&%?9JcO7fsARk%$DMB5>MFOThuBOnUe@T{jSg*eBUT4bSeVXDUnUnuPi>m1@$&QH zdc<0YxBZGmHMJ_rPI;`%;PJ8LihHJ>qL$yR^pMSgJ$J+wj5nSXclZdSnwf*89!_VY z^`;}Ei&Wp|ciFW5fU7^x&!2UEb7i)h77!2+PTx-SiL4*h$k2y59VM;O#iD$$Lc@>s zU(xp}-6b+yTk7fGXWZN!SESjK6^`ljUllT!S5zc^REd>qJGyIMsT2XbfM3H%Qi>>Y zNp0_Q(u}aMK^C#<{kEPj>EeB-)@wyieHBLB!ZBGaM0K7-4-HNUJB@ptuxK*7r5Io)HDT|F-=?um0bPaQ%9-lG5jSz#*p#M9sF} zhpF*4ei`Jc8E2e$C@(MHxbbNFx7u2)M#ih7Q`4R~$|}^|w$1`BF%%dO@w!k%NGCmR%k2^2^c=*0R#ntSCE)7c?Ovi7dBzU9~dt z)Vs_M3*5w;bTg9vdIGMlTU~FaX94drH;~#Q*MG78P{h}zgJwscPTdckX1|#e*gU-UVJjc>jfs(RyS~w=hGaC}SXw}YlDZ01AvozJ%%=fnw z!WTwEeXxUN&X_of>v{?huI6DeF-b>biziCMYRtSgtkemn*g6j9nO48~ZeLu*9919A%+p%Rmx`sqcj z6XS{#nn+F3z+h57e7Izkb5P2)H8L|-pc87FGq3V}5-$(r!S>~uu$%;$cJuphuVF_` z5=h8^gQVM{j*+8^yf3bAnTxkGNaz3MHEpf*4;R@lvDp|_E3gVGQ11GWN_G~z6V06F zoT}6Ad#S`;wF#>q4YQ}H92K{mD@QWQ#_Du7#eVDaA@Z88L?9I3<)LsI13f*rs*R?L&r>)pRmhBb zXBGlps5IXtU-yky&@A}0#64M`8%Q)S6|UrOtoq!lEt+5cKzdN4*qEV_T=uD^#{7z! zwTSiCPpP8rPHNr0W8;ZYE!?ePJc{O59;=%uXlR6yj>U6D;S+uI;Zc5}r|@A5H`9u3 z`*Yg2940;-fvd9rG19_-O;&Jbr|4Kb8qxAHlNNjYK_P^L_>E818(SZ8y1RDcd;dJO z!rX(s+r%e6=;KE;lB!6K;{jt!2cbF1Pi~PDf~?7-7(#y3^R6l=De0VEsEdzgm&CBQ z2>jsFr(3D6)8|YV!ddg{LSoM&8Fr!d#cANg?`q&fF*24_&1`LJ-z)@I(94(wfHVw6j^o1dl6aBPfzoqc zANaIfhlX_P-i{}NYVp@G?(1l4$j||hjB6o(;S#!xwRd;a8aOULeiLL@wQbz>!ibyD z!u`g6Zs2;b=bXGzLwt!ul=xP38-w`6o-;zag?CtX~(wcoK?HhbeCP{+Y|f~UwW6E zc&!n;A~&xeyB-Fn37V7H1dB8vx_m%+uDq09Ac6Fzt?=Frq#nnl7ZHk{eAQ%!@SeW4 zxTHpbG}sW9EXr{;LnJu^@}8e6Bjw*~LvEm9-N7#^w?W+s`CX6XhqaH`eRen5w`sX@ znIG*%)Y>LAc+8}?mr<4FPx68SSd-1aL~rJJZ&(q8kvq=)4!_O&ac5)NtPt002wyQtL9iU4x|kw-h+X%G_W&WMs<8tDcdhqG)b7)5OC8_#KI$I1$$d z_x$-YcP{k<=58;MdEh{P4qxTWFmsZ3Vw$HsbbVV%hkd zlsSMARibFu1-spSU*ayyr!K7j%D~EPa+_UnI09elUJ4sR_SDmJQoMB4mVIGCJhCe- z;?`020TU#XgSWt6?fR8j7A`Jf)2<+$PM^O!Vq`~;>doisrUbU7IV*$t{q>6`&rkO< z-4=h)j=|T0f3dK$XC4TQdIWi2<>c;jJ~b~Do%|*$1zZd9#M-*LjK)e=%A+kmg{cmo z*SEeTmSeOxc#Jwz^f-NEHpt4NP$aBs(jLPA<-PY^mgW-=Nx)9x%To|LOzr4Ou>1QT zni^cPr@Ma>@a>DKR8^=uXpN*znKc_l@sD{4N}}fNPmgk5R)Q1M2}y`!GNk4oT}*!P zg40~4HM24*w@KZ-#LrCS?G;FR`xFf~IJXaR{f@K>QefEIx9_0xAWoJ48{&} zpo$LEN>`m+o*c$x>&KC&X5@C>JA{R8$izwFb4|Rjr*vX+mGT|OZQUvx9pWyrx;-C$z<4DUde(X17OW-(ugLNWWVqz1?sNi|%6yiL$8MT6S{w8DaZrHZ#fzQrDMLdK&)f6p; zHmR!cW&{i%aPHhKV1-zF=pa)6`t=|_GBP842DAw(QKy5dW0LxpfWs+8 z0uG*pCF)~Y(N8hjAbq-m1cNywK57nf0A^Fw2;sbVTYzx#ckX1k7iFR6W_k-GpNzZ< z4?oUI9%sA&2?`4D_K%v-EQ68|KnK`?_qY9=(Q+nWB7ALgd^wHIN3s8e8Gs2^=b1J8 z)U>BqL%;~1K7DeMwQP=SYmwvDeE9G=IqSm*btofnC)XVnJXUs8inO%Gu7w0pz6=uA zV#G1RqMEe`FL@!iOX_5S>$3An01Ai!`fZfuvo)I77avQM?51|ndP?Nf+B7r*{7V%O zWRg5smSr+*{0r(XkVC^ra1teEh#?IPR!q$6*S}M<4HQ%TfxH65Kob#1(8|;#p|Js2 zJ-81+NlAS|vY}auw@6!?MnFi2rj@2vC0gb2{h)`Y4>2)_%aI-FRHX&JX@@Uy;syq? zy=gf{l9cgsd9AeOH*Li6_x}_ cturi|WXKy|A|K)sK;C;#UiD7Stv{dtAAjfd>;M1& diff --git a/test/interpreter_functional/snapshots/baseline/combined_test3.json b/test/interpreter_functional/snapshots/baseline/combined_test3.json index 64b1052552c8..107d3fcbc5c5 100644 --- a/test/interpreter_functional/snapshots/baseline/combined_test3.json +++ b/test/interpreter_functional/snapshots/baseline/combined_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/final_output_test.json b/test/interpreter_functional/snapshots/baseline/final_output_test.json index 64b1052552c8..107d3fcbc5c5 100644 --- a/test/interpreter_functional/snapshots/baseline/final_output_test.json +++ b/test/interpreter_functional/snapshots/baseline/final_output_test.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_all_data.json b/test/interpreter_functional/snapshots/baseline/metric_all_data.json index 0e1e5a723373..9c10b53ce860 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_all_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_all_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_empty_data.json b/test/interpreter_functional/snapshots/baseline/metric_empty_data.json index c318121535c8..6fa08239f422 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_empty_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_empty_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json index fc8622a818de..4410447d2bb2 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json index 95c011f9259b..2abb3070c3d0 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json +++ b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json index f4a8cd1f14e1..cce892a2f8c6 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/partial_test_2.json b/test/interpreter_functional/snapshots/baseline/partial_test_2.json index 64b1052552c8..107d3fcbc5c5 100644 --- a/test/interpreter_functional/snapshots/baseline/partial_test_2.json +++ b/test/interpreter_functional/snapshots/baseline/partial_test_2.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/step_output_test3.json b/test/interpreter_functional/snapshots/baseline/step_output_test3.json index 64b1052552c8..107d3fcbc5c5 100644 --- a/test/interpreter_functional/snapshots/baseline/step_output_test3.json +++ b/test/interpreter_functional/snapshots/baseline/step_output_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/combined_test3.json b/test/interpreter_functional/snapshots/session/combined_test3.json index 64b1052552c8..107d3fcbc5c5 100644 --- a/test/interpreter_functional/snapshots/session/combined_test3.json +++ b/test/interpreter_functional/snapshots/session/combined_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/final_output_test.json b/test/interpreter_functional/snapshots/session/final_output_test.json index 64b1052552c8..107d3fcbc5c5 100644 --- a/test/interpreter_functional/snapshots/session/final_output_test.json +++ b/test/interpreter_functional/snapshots/session/final_output_test.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_all_data.json b/test/interpreter_functional/snapshots/session/metric_all_data.json index 0e1e5a723373..9c10b53ce860 100644 --- a/test/interpreter_functional/snapshots/session/metric_all_data.json +++ b/test/interpreter_functional/snapshots/session/metric_all_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_empty_data.json b/test/interpreter_functional/snapshots/session/metric_empty_data.json index c318121535c8..6fa08239f422 100644 --- a/test/interpreter_functional/snapshots/session/metric_empty_data.json +++ b/test/interpreter_functional/snapshots/session/metric_empty_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json b/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json index fc8622a818de..4410447d2bb2 100644 --- a/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json +++ b/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_percentage_mode.json b/test/interpreter_functional/snapshots/session/metric_percentage_mode.json index 95c011f9259b..2abb3070c3d0 100644 --- a/test/interpreter_functional/snapshots/session/metric_percentage_mode.json +++ b/test/interpreter_functional/snapshots/session/metric_percentage_mode.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_single_metric_data.json b/test/interpreter_functional/snapshots/session/metric_single_metric_data.json index f4a8cd1f14e1..cce892a2f8c6 100644 --- a/test/interpreter_functional/snapshots/session/metric_single_metric_data.json +++ b/test/interpreter_functional/snapshots/session/metric_single_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/partial_test_2.json b/test/interpreter_functional/snapshots/session/partial_test_2.json index 64b1052552c8..107d3fcbc5c5 100644 --- a/test/interpreter_functional/snapshots/session/partial_test_2.json +++ b/test/interpreter_functional/snapshots/session/partial_test_2.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/step_output_test3.json b/test/interpreter_functional/snapshots/session/step_output_test3.json index 64b1052552c8..107d3fcbc5c5 100644 --- a/test/interpreter_functional/snapshots/session/step_output_test3.json +++ b/test/interpreter_functional/snapshots/session/step_output_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9bcedd1ffb98..f36f3abe66a4 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4913,21 +4913,21 @@ "visTypeMetric.colorModes.backgroundOptionLabel": "背景", "visTypeMetric.colorModes.labelsOptionLabel": "ラベル", "visTypeMetric.colorModes.noneOptionLabel": "なし", - "visTypeMetric.function.adimension.splitGroup": "グループを分割", - "visTypeMetric.function.bgFill.help": "html 16 進数コード(#123456)、html 色(red、blue)、または rgba 値(rgba(255,255,255,1))。", - "visTypeMetric.function.bucket.help": "バケットディメンションの構成です。", - "visTypeMetric.function.colorMode.help": "色を変更するメトリックの部分", - "visTypeMetric.function.colorRange.help": "別の色が適用される値のグループを指定する範囲オブジェクト。", - "visTypeMetric.function.colorSchema.help": "使用する配色", - "visTypeMetric.function.dimension.metric": "メトリック", - "visTypeMetric.function.font.help": "フォント設定です。", - "visTypeMetric.function.help": "メトリックビジュアライゼーション", - "visTypeMetric.function.invertColors.help": "色範囲を反転します", - "visTypeMetric.function.metric.help": "メトリックディメンションの構成です。", - "visTypeMetric.function.percentageMode.help": "百分率モードでメトリックを表示します。colorRange を設定する必要があります。", - "visTypeMetric.function.showLabels.help": "メトリック値の下にラベルを表示します。", - "visTypeMetric.function.subText.help": "メトリックの下に表示するカスタムテキスト", - "visTypeMetric.function.useRanges.help": "有効な色範囲です。", + "expressionMetricVis.function.dimension.splitGroup": "グループを分割", + "expressionMetricVis.function.bgFill.help": "html 16 進数コード(#123456)、html 色(red、blue)、または rgba 値(rgba(255,255,255,1))。", + "expressionMetricVis.function.bucket.help": "バケットディメンションの構成です。", + "expressionMetricVis.function.colorMode.help": "色を変更するメトリックの部分", + "expressionMetricVis.function.colorRange.help": "別の色が適用される値のグループを指定する範囲オブジェクト。", + "expressionMetricVis.function.colorSchema.help": "使用する配色", + "expressionMetricVis.function.dimension.metric": "メトリック", + "expressionMetricVis.function.font.help": "フォント設定です。", + "expressionMetricVis.function.help": "メトリックビジュアライゼーション", + "expressionMetricVis.function.invertColors.help": "色範囲を反転します", + "expressionMetricVis.function.metric.help": "メトリックディメンションの構成です。", + "expressionMetricVis.function.percentageMode.help": "百分率モードでメトリックを表示します。colorRange を設定する必要があります。", + "expressionMetricVis.function.showLabels.help": "メトリック値の下にラベルを表示します。", + "expressionMetricVis.function.subText.help": "メトリックの下に表示するカスタムテキスト", + "expressionMetricVis.function.useRanges.help": "有効な色範囲です。", "visTypeMetric.metricDescription": "計算結果を単独の数字として表示します。", "visTypeMetric.metricTitle": "メトリック", "visTypeMetric.params.color.useForLabel": "使用する色", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index d9eab3863c2c..11b951b97ae0 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4958,21 +4958,21 @@ "visTypeMetric.colorModes.backgroundOptionLabel": "背景", "visTypeMetric.colorModes.labelsOptionLabel": "标签", "visTypeMetric.colorModes.noneOptionLabel": "无", - "visTypeMetric.function.adimension.splitGroup": "拆分组", - "visTypeMetric.function.bgFill.help": "将颜色表示为 html 十六进制代码 (#123456)、html 颜色(red、blue)或 rgba 值 (rgba(255,255,255,1))。", - "visTypeMetric.function.bucket.help": "存储桶维度配置", - "visTypeMetric.function.colorMode.help": "指标的哪部分要上色", - "visTypeMetric.function.colorRange.help": "指定应将不同颜色应用到的值组的范围对象。", - "visTypeMetric.function.colorSchema.help": "要使用的颜色方案", - "visTypeMetric.function.dimension.metric": "指标", - "visTypeMetric.function.font.help": "字体设置。", - "visTypeMetric.function.help": "指标可视化", - "visTypeMetric.function.invertColors.help": "反转颜色范围", - "visTypeMetric.function.metric.help": "指标维度配置", - "visTypeMetric.function.percentageMode.help": "以百分比模式显示指标。需要设置 colorRange。", - "visTypeMetric.function.showLabels.help": "在指标值下显示标签。", - "visTypeMetric.function.subText.help": "要在指标下显示的定制文本", - "visTypeMetric.function.useRanges.help": "已启用颜色范围。", + "expressionMetricVis.function.dimension.splitGroup": "拆分组", + "expressionMetricVis.function.bgFill.help": "将颜色表示为 html 十六进制代码 (#123456)、html 颜色(red、blue)或 rgba 值 (rgba(255,255,255,1))。", + "expressionMetricVis.function.bucket.help": "存储桶维度配置", + "expressionMetricVis.function.colorMode.help": "指标的哪部分要上色", + "expressionMetricVis.function.colorRange.help": "指定应将不同颜色应用到的值组的范围对象。", + "expressionMetricVis.function.colorSchema.help": "要使用的颜色方案", + "expressionMetricVis.function.dimension.metric": "指标", + "expressionMetricVis.function.font.help": "字体设置。", + "expressionMetricVis.function.help": "指标可视化", + "expressionMetricVis.function.invertColors.help": "反转颜色范围", + "expressionMetricVis.function.metric.help": "指标维度配置", + "expressionMetricVis.function.percentageMode.help": "以百分比模式显示指标。需要设置 colorRange。", + "expressionMetricVis.function.showLabels.help": "在指标值下显示标签。", + "expressionMetricVis.function.subText.help": "要在指标下显示的定制文本", + "expressionMetricVis.function.useRanges.help": "已启用颜色范围。", "visTypeMetric.metricDescription": "将计算结果显示为单个数字。", "visTypeMetric.metricTitle": "指标", "visTypeMetric.params.color.useForLabel": "将颜色用于", From b6f1682635ef6d12af5074536a129b62bab11ef8 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Fri, 8 Oct 2021 10:59:27 +0200 Subject: [PATCH 17/74] Fix dev doc pages (#114297) * fix doc page * fix doc page bis --- dev_docs/key_concepts/navigation.mdx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dev_docs/key_concepts/navigation.mdx b/dev_docs/key_concepts/navigation.mdx index 85b0fe8429a5..27ba3db11141 100644 --- a/dev_docs/key_concepts/navigation.mdx +++ b/dev_docs/key_concepts/navigation.mdx @@ -50,7 +50,7 @@ console.log(discoverUrl); // http://localhost:5601/bpr/s/space/app/discover const discoverUrlWithSomeState = core.http.basePath.prepend(`/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'2020-09-10T11:39:50.203Z',to:'2020-09-10T11:40:20.249Z'))&_a=(columns:!(_source),filters:!(),index:'90943e30-9a47-11e8-b64d-95841ca0b247',interval:auto,query:(language:kuery,query:''),sort:!())`); ``` -Instead, each app should expose {kib-repo}tree/{branch}/src/plugins/share/common/url_service/locators/README.md[a locator]. +Instead, each app should expose [a locator](https://github.com/elastic/kibana/blob/master/src/plugins/share/common/url_service/locators/README.md). Other apps should use those locators for navigation or URL creation. ```tsx @@ -114,8 +114,7 @@ const MySPALink = () => As it would be too much boilerplate to do this for each link in your app, there is a handy wrapper that helps with it: [RedirectAppLinks](https://github.com/elastic/kibana/blob/master/src/plugins/kibana_react/public/app_links/redirect_app_link.tsx#L49). -[source,typescript jsx] ----- +```jsx const MyApp = () => {/*...*/} @@ -123,7 +122,7 @@ const MyApp = () => Go to Dashboard {/*...*/} ----- +``` ## Setting up internal app routing @@ -167,7 +166,7 @@ Common use-case for using `core`'s `ScopedHistory` directly: ## Syncing state with URL Historically Kibana apps store _a lot_ of application state in the URL. -The most common pattern that {kib} apps follow today is storing state in `_a` and `_g` query params in [rison](https://github.com/w33ble/rison-node#readme) format. +The most common pattern that Kibana apps follow today is storing state in `_a` and `_g` query params in [rison](https://github.com/w33ble/rison-node#readme) format. Those query params follow the convention: From d32520af6af92e6a06c56aada43ffe82be1593d1 Mon Sep 17 00:00:00 2001 From: Kevin Lacabane Date: Fri, 8 Oct 2021 13:26:28 +0200 Subject: [PATCH 18/74] [Stack Monitoring] sync timepicker with url (#114290) * sync timepicker hook with url * name function * fix linter errors --- .../application/hooks/use_monitoring_time.ts | 14 +++++++++++++- .../public/components/shared/toolbar.tsx | 8 +------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts b/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts index e512f90d76e6..3054714ec3aa 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts +++ b/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useCallback, useState, useContext } from 'react'; +import { useCallback, useState, useContext, useEffect } from 'react'; import createContainer from 'constate'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { Legacy } from '../../legacy_shims'; @@ -53,6 +53,18 @@ export const useMonitoringTime = () => { [currentTimerange, setTimeRange, state] ); + useEffect(() => { + const sub = Legacy.shims.timefilter.getTimeUpdate$().subscribe(function onTimeUpdate() { + const updatedTime = Legacy.shims.timefilter.getTime(); + setTimeRange({ ...currentTimerange, ...updatedTime }); + state.time = { ...updatedTime }; + state.save?.(); + }); + + return () => sub.unsubscribe(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return { currentTimerange, setTimeRange, diff --git a/x-pack/plugins/monitoring/public/components/shared/toolbar.tsx b/x-pack/plugins/monitoring/public/components/shared/toolbar.tsx index 7a7130007dad..32bbdd6ecbed 100644 --- a/x-pack/plugins/monitoring/public/components/shared/toolbar.tsx +++ b/x-pack/plugins/monitoring/public/components/shared/toolbar.tsx @@ -40,14 +40,8 @@ export const MonitoringToolbar: React.FC = ({ pageTitle, return; } handleTimeChange(selectedTime.start, selectedTime.end); - state.time = { - from: selectedTime.start, - to: selectedTime.end, - }; - Legacy.shims.timefilter.setTime(state.time); - state.save?.(); }, - [handleTimeChange, state] + [handleTimeChange] ); const onRefreshChange = useCallback( From 581714ed40d698a3df3d21744825833e354a5fec Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Fri, 8 Oct 2021 10:50:03 -0500 Subject: [PATCH 19/74] skip flaky test. #114261 --- x-pack/test/functional/apps/uptime/certificates.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/uptime/certificates.ts b/x-pack/test/functional/apps/uptime/certificates.ts index 610f07c18378..49298ec3a6f9 100644 --- a/x-pack/test/functional/apps/uptime/certificates.ts +++ b/x-pack/test/functional/apps/uptime/certificates.ts @@ -18,7 +18,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const es = getService('es'); - describe('certificates', function () { + // FLAKY https://github.com/elastic/kibana/issues/114261 + describe.skip('certificates', function () { describe('empty certificates', function () { before(async () => { await esArchiver.load(BLANK_INDEX_PATH); From 485aebc73d5ab7bfc7507e689fcec5c209a80ac3 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Fri, 8 Oct 2021 10:58:52 -0500 Subject: [PATCH 20/74] skip failing es promotion suites. #111240 --- .../api_integration/apis/uptime/rest/telemetry_collectors.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts b/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts index 8e80208b3d80..de65fe6deb98 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts @@ -14,7 +14,8 @@ export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const es = getService('es'); - describe('telemetry collectors heartbeat', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/111240 + describe.skip('telemetry collectors heartbeat', () => { before('generating data', async () => { await getService('esArchiver').load('x-pack/test/functional/es_archives/uptime/blank'); From 4893b27590a9506d4ab5f31ff6a638b94c7928f3 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Fri, 8 Oct 2021 11:18:54 -0500 Subject: [PATCH 21/74] skip flaky test. #112922 --- .../components/transform_list/expanded_row.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx index 8c96aae2e0da..bccd3aff72c5 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx @@ -20,7 +20,8 @@ jest.mock('../../../../../app/app_dependencies'); import { MlSharedContext } from '../../../../../app/__mocks__/shared_context'; import { getMlSharedImports } from '../../../../../shared_imports'; -describe('Transform: Transform List ', () => { +// FLAKY https://github.com/elastic/kibana/issues/112922 +describe.skip('Transform: Transform List ', () => { // Set timezone to US/Eastern for consistent test results. beforeEach(() => { moment.tz.setDefault('US/Eastern'); From b06e6db2f5fda9c75a4473b1efedc553ea5210af Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 8 Oct 2021 15:27:33 -0500 Subject: [PATCH 22/74] [kbn/optimizer] log about high-level optimizer progress (#103354) * [kbn/optimizer] log about high-level optimizer progress * restore logOptimizerProgress helper to fix tests * fix lint error Co-authored-by: spalger Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../kbn-cli-dev-mode/src/optimizer.test.ts | 4 +- packages/kbn-cli-dev-mode/src/optimizer.ts | 9 ++- packages/kbn-optimizer/src/cli.ts | 15 ++++- packages/kbn-optimizer/src/index.ts | 1 + .../src/log_optimizer_progress.ts | 62 +++++++++++++++++++ .../kbn-optimizer/src/log_optimizer_state.ts | 7 +-- 6 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 packages/kbn-optimizer/src/log_optimizer_progress.ts diff --git a/packages/kbn-cli-dev-mode/src/optimizer.test.ts b/packages/kbn-cli-dev-mode/src/optimizer.test.ts index ee8ea5f38ae8..e1763ab4c475 100644 --- a/packages/kbn-cli-dev-mode/src/optimizer.test.ts +++ b/packages/kbn-cli-dev-mode/src/optimizer.test.ts @@ -18,9 +18,11 @@ import { Optimizer, Options } from './optimizer'; jest.mock('@kbn/optimizer'); const realOptimizer = jest.requireActual('@kbn/optimizer'); -const { runOptimizer, OptimizerConfig, logOptimizerState } = jest.requireMock('@kbn/optimizer'); +const { runOptimizer, OptimizerConfig, logOptimizerState, logOptimizerProgress } = + jest.requireMock('@kbn/optimizer'); logOptimizerState.mockImplementation(realOptimizer.logOptimizerState); +logOptimizerProgress.mockImplementation(realOptimizer.logOptimizerProgress); class MockOptimizerConfig {} diff --git a/packages/kbn-cli-dev-mode/src/optimizer.ts b/packages/kbn-cli-dev-mode/src/optimizer.ts index fab566829f7a..3f7a6edc2231 100644 --- a/packages/kbn-cli-dev-mode/src/optimizer.ts +++ b/packages/kbn-cli-dev-mode/src/optimizer.ts @@ -18,7 +18,13 @@ import { } from '@kbn/dev-utils'; import * as Rx from 'rxjs'; import { ignoreElements } from 'rxjs/operators'; -import { runOptimizer, OptimizerConfig, logOptimizerState, OptimizerUpdate } from '@kbn/optimizer'; +import { + runOptimizer, + OptimizerConfig, + logOptimizerState, + logOptimizerProgress, + OptimizerUpdate, +} from '@kbn/optimizer'; export interface Options { enabled: boolean; @@ -111,6 +117,7 @@ export class Optimizer { subscriber.add( runOptimizer(config) .pipe( + logOptimizerProgress(log), logOptimizerState(log, config), tap(({ state }) => { this.phase$.next(state.phase); diff --git a/packages/kbn-optimizer/src/cli.ts b/packages/kbn-optimizer/src/cli.ts index d5b9996dfb2c..7f0c39ccd0e5 100644 --- a/packages/kbn-optimizer/src/cli.ts +++ b/packages/kbn-optimizer/src/cli.ts @@ -13,6 +13,7 @@ import { lastValueFrom } from '@kbn/std'; import { run, createFlagError, Flags } from '@kbn/dev-utils'; import { logOptimizerState } from './log_optimizer_state'; +import { logOptimizerProgress } from './log_optimizer_progress'; import { OptimizerConfig } from './optimizer'; import { runOptimizer } from './run_optimizer'; import { validateLimitsForAllBundles, updateBundleLimits } from './limits'; @@ -97,6 +98,11 @@ export function runKbnOptimizerCli(options: { defaultLimitsPath: string }) { throw createFlagError('expected --report-stats to have no value'); } + const logProgress = flags.progress ?? false; + if (typeof logProgress !== 'boolean') { + throw createFlagError('expected --progress to have no value'); + } + const filter = typeof flags.filter === 'string' ? [flags.filter] : flags.filter; if (!Array.isArray(filter) || !filter.every((f) => typeof f === 'string')) { throw createFlagError('expected --filter to be one or more strings'); @@ -144,7 +150,11 @@ export function runKbnOptimizerCli(options: { defaultLimitsPath: string }) { const update$ = runOptimizer(config); await lastValueFrom( - update$.pipe(logOptimizerState(log, config), reportOptimizerTimings(log, config)) + update$.pipe( + logProgress ? logOptimizerProgress(log) : (x) => x, + logOptimizerState(log, config), + reportOptimizerTimings(log, config) + ) ); if (updateLimits) { @@ -169,6 +179,7 @@ export function runKbnOptimizerCli(options: { defaultLimitsPath: string }) { 'inspect-workers', 'validate-limits', 'update-limits', + 'progress', ], string: ['workers', 'scan-dir', 'filter', 'limits'], default: { @@ -176,12 +187,14 @@ export function runKbnOptimizerCli(options: { defaultLimitsPath: string }) { examples: true, cache: true, 'inspect-workers': true, + progress: true, filter: [], focus: [], }, help: ` --watch run the optimizer in watch mode --workers max number of workers to use + --no-progress disable logging of progress information --oss only build oss plugins --profile profile the webpack builds and write stats.json files to build outputs --no-core disable generating the core bundle diff --git a/packages/kbn-optimizer/src/index.ts b/packages/kbn-optimizer/src/index.ts index a5838a8a0fac..d5e810d584d2 100644 --- a/packages/kbn-optimizer/src/index.ts +++ b/packages/kbn-optimizer/src/index.ts @@ -9,6 +9,7 @@ export { OptimizerConfig } from './optimizer'; export * from './run_optimizer'; export * from './log_optimizer_state'; +export * from './log_optimizer_progress'; export * from './node'; export * from './limits'; export * from './cli'; diff --git a/packages/kbn-optimizer/src/log_optimizer_progress.ts b/packages/kbn-optimizer/src/log_optimizer_progress.ts new file mode 100644 index 000000000000..d07c9dc6eff3 --- /dev/null +++ b/packages/kbn-optimizer/src/log_optimizer_progress.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ToolingLog } from '@kbn/dev-utils'; +import * as Rx from 'rxjs'; +import { tap } from 'rxjs/operators'; + +import { OptimizerUpdate } from './run_optimizer'; + +const PROGRESS_REPORT_INTERVAL = 10_000; + +export function logOptimizerProgress( + log: ToolingLog +): Rx.MonoTypeOperatorFunction { + return (update$) => + new Rx.Observable((subscriber) => { + const allBundleIds = new Set(); + const completeBundles = new Set(); + let loggedCompletion = new Set(); + + // catalog bundle ids and which have completed at least once, forward + // updates to next subscriber + subscriber.add( + update$ + .pipe( + tap(({ state }) => { + for (const { bundleId, type } of state.compilerStates) { + allBundleIds.add(bundleId); + if (type !== 'running') { + completeBundles.add(bundleId); + } + } + }), + tap(subscriber) + ) + .subscribe() + ); + + // on interval check to see if at least 3 new bundles have completed at + // least one build and log about our progress if so + subscriber.add( + Rx.interval(PROGRESS_REPORT_INTERVAL).subscribe( + () => { + if (completeBundles.size - loggedCompletion.size < 3) { + return; + } + + log.info( + `[${completeBundles.size}/${allBundleIds.size}] initial bundle builds complete` + ); + loggedCompletion = new Set(completeBundles); + }, + (error) => subscriber.error(error) + ) + ); + }); +} diff --git a/packages/kbn-optimizer/src/log_optimizer_state.ts b/packages/kbn-optimizer/src/log_optimizer_state.ts index 61f6406255a8..517e3bbfa513 100644 --- a/packages/kbn-optimizer/src/log_optimizer_state.ts +++ b/packages/kbn-optimizer/src/log_optimizer_state.ts @@ -82,14 +82,11 @@ export function logOptimizerState(log: ToolingLog, config: OptimizerConfig) { continue; } + bundleStates.set(id, type); + if (type === 'running') { bundlesThatWereBuilt.add(id); } - - bundleStates.set(id, type); - log.debug( - `[${id}] state = "${type}"${type !== 'running' ? ` after ${state.durSec} sec` : ''}` - ); } if (state.phase === 'running' || state.phase === 'initializing') { From 032473ba29849f40e459a3f5c7410fb12457ff3d Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 8 Oct 2021 17:52:44 -0500 Subject: [PATCH 23/74] [ci-stats-reporter] ensure HTTP adapter is used (#114367) * [ci-stats-reporter] ensure HTTP adapter is used * update kbn/pm dist Co-authored-by: spalger --- .../src/ci_stats_reporter/ci_stats_reporter.ts | 3 +++ packages/kbn-pm/dist/index.js | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts index 45d31c1eefad..fe48ce99e685 100644 --- a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts +++ b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts @@ -13,6 +13,8 @@ import Path from 'path'; import crypto from 'crypto'; import execa from 'execa'; import Axios from 'axios'; +// @ts-expect-error not "public", but necessary to prevent Jest shimming from breaking things +import httpAdapter from 'axios/lib/adapters/http'; import { ToolingLog } from '../tooling_log'; import { parseConfig, Config } from './ci_stats_config'; @@ -225,6 +227,7 @@ export class CiStatsReporter { baseURL: BASE_URL, headers, data: body, + adapter: httpAdapter, }); return true; diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index cab1f6d916f0..f39563637914 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -8965,6 +8965,8 @@ var _execa = _interopRequireDefault(__webpack_require__(134)); var _axios = _interopRequireDefault(__webpack_require__(177)); +var _http = _interopRequireDefault(__webpack_require__(199)); + var _ci_stats_config = __webpack_require__(218); /* @@ -8974,6 +8976,7 @@ var _ci_stats_config = __webpack_require__(218); * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +// @ts-expect-error not "public", but necessary to prevent Jest shimming from breaking things const BASE_URL = 'https://ci-stats.kibana.dev'; class CiStatsReporter { @@ -9173,7 +9176,8 @@ class CiStatsReporter { url: path, baseURL: BASE_URL, headers, - data: body + data: body, + adapter: _http.default }); return true; } catch (error) { From cbc4f5235c0a38e2f076ce97e7af2564036f9a5b Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Sat, 9 Oct 2021 02:48:23 -0400 Subject: [PATCH 24/74] [Fleet] Add installed integration callouts (#113893) Co-authored-by: Clint Andrew Hall Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...-plugin-core-public.doclinksstart.links.md | 1 + .../public/doc_links/doc_links_service.ts | 2 + src/core/public/public.api.md | 1 + .../epm/components/package_list_grid.tsx | 3 ++ .../sections/epm/screens/home/index.tsx | 40 ++++++++++++++++++- .../fleet/storybook/context/doc_links.ts | 21 ++++++++++ .../plugins/fleet/storybook/context/index.tsx | 4 +- .../plugins/fleet/storybook/context/stubs.tsx | 2 - 8 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/fleet/storybook/context/doc_links.ts diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index fc7d2e976b57..c8ccdfeedb83 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -235,6 +235,7 @@ readonly links: { datastreamsNamingScheme: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; + learnMoreBlog: string; }>; readonly ecs: { readonly guide: string; diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 6c34693b6052..01108298adc9 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -476,6 +476,7 @@ export class DocLinksService { datastreamsNamingScheme: `${FLEET_DOCS}data-streams.html#data-streams-naming-scheme`, upgradeElasticAgent: `${FLEET_DOCS}upgrade-elastic-agent.html`, upgradeElasticAgent712lower: `${FLEET_DOCS}upgrade-elastic-agent.html#upgrade-7.12-lower`, + learnMoreBlog: `${ELASTIC_WEBSITE_URL}blog/elastic-agent-and-fleet-make-it-easier-to-integrate-your-systems-with-elastic`, }, ecs: { guide: `${ELASTIC_WEBSITE_URL}guide/en/ecs/current/index.html`, @@ -730,6 +731,7 @@ export interface DocLinksStart { datastreamsNamingScheme: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; + learnMoreBlog: string; }>; readonly ecs: { readonly guide: string; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 7170c43f36e7..45b7e3bdc02b 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -704,6 +704,7 @@ export interface DocLinksStart { datastreamsNamingScheme: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; + learnMoreBlog: string; }>; readonly ecs: { readonly guide: string; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx index ac83cc83b684..109f7500f160 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx @@ -38,6 +38,7 @@ export interface ListProps { setSelectedCategory: (category: string) => void; onSearchChange: (search: string) => void; showMissingIntegrationMessage?: boolean; + callout?: JSX.Element | null; } export function PackageListGrid({ @@ -49,6 +50,7 @@ export function PackageListGrid({ onSearchChange, setSelectedCategory, showMissingIntegrationMessage = false, + callout, }: ListProps) { const [searchTerm, setSearchTerm] = useState(initialSearch || ''); const localSearchRef = useLocalSearch(list); @@ -105,6 +107,7 @@ export function PackageListGrid({ }} onChange={onQueryChange} /> + {callout ? callout : null} {gridContent} {showMissingIntegrationMessage && ( diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx index 62225d14d385..06cf85699bf6 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx @@ -5,10 +5,13 @@ * 2.0. */ -import React, { memo, useMemo } from 'react'; +import React, { memo, useMemo, Fragment } from 'react'; import { Switch, Route, useLocation, useHistory, useParams } from 'react-router-dom'; import semverLt from 'semver/functions/lt'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; import { installationStatuses } from '../../../../../../../common/constants'; import type { DynamicPage, DynamicPagePathValues, StaticPage } from '../../../../constants'; @@ -24,6 +27,7 @@ import { useGetAppendCustomIntegrations, useGetReplacementCustomIntegrations, useLink, + useStartServices, } from '../../../../hooks'; import { doesPackageHaveIntegrations } from '../../../../services'; import { DefaultLayout } from '../../../../layouts'; @@ -143,6 +147,7 @@ const InstalledPackages: React.FC = memo(() => { experimental: true, }); const { getHref, getAbsolutePath } = useLink(); + const { docLinks } = useStartServices(); const { selectedCategory, searchParam } = getParams( useParams(), @@ -225,6 +230,38 @@ const InstalledPackages: React.FC = memo(() => { return mapToCard(getAbsolutePath, getHref, item); }); + const link = ( + + {i18n.translate('xpack.fleet.epmList.availableCalloutBlogText', { + defaultMessage: 'announcement blog post', + })} + + ); + const calloutMessage = ( + + ); + + const callout = + selectedCategory === 'updates_available' ? null : ( + + + +

{calloutMessage}

+ + + ); + return ( { initialSearch={searchParam} title={title} list={cards} + callout={callout} /> ); }); diff --git a/x-pack/plugins/fleet/storybook/context/doc_links.ts b/x-pack/plugins/fleet/storybook/context/doc_links.ts new file mode 100644 index 000000000000..56287dd9116a --- /dev/null +++ b/x-pack/plugins/fleet/storybook/context/doc_links.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DocLinksStart } from 'kibana/public'; + +export const getDocLinks = () => { + const docLinks: DocLinksStart = { + links: { + fleet: { + learnMoreBlog: + 'https://www.elastic.co/blog/elastic-agent-and-fleet-make-it-easier-to-integrate-your-systems-with-elastic', + }, + }, + } as unknown as DocLinksStart; + + return docLinks; +}; diff --git a/x-pack/plugins/fleet/storybook/context/index.tsx b/x-pack/plugins/fleet/storybook/context/index.tsx index e5a360c28385..6d563346e917 100644 --- a/x-pack/plugins/fleet/storybook/context/index.tsx +++ b/x-pack/plugins/fleet/storybook/context/index.tsx @@ -27,6 +27,7 @@ import { getHttp } from './http'; import { getUiSettings } from './ui_settings'; import { getNotifications } from './notifications'; import { stubbedStartServices } from './stubs'; +import { getDocLinks } from './doc_links'; // TODO: clintandrewhall - this is not ideal, or complete. The root context of Fleet applications // requires full start contracts of its dependencies. As a result, we have to mock all of those contracts @@ -42,8 +43,10 @@ export const StorybookContext: React.FC<{ storyContext?: StoryContext }> = ({ const history = new ScopedHistory(browserHistory, basepath); const startServices: FleetStartServices = { + ...stubbedStartServices, application: getApplication(), chrome: getChrome(), + docLinks: getDocLinks(), http: getHttp(), notifications: getNotifications(), uiSettings: getUiSettings(), @@ -58,7 +61,6 @@ export const StorybookContext: React.FC<{ storyContext?: StoryContext }> = ({ customIntegrations: { ContextProvider: getStorybookContextProvider(), }, - ...stubbedStartServices, }; setHttpClient(startServices.http); diff --git a/x-pack/plugins/fleet/storybook/context/stubs.tsx b/x-pack/plugins/fleet/storybook/context/stubs.tsx index 54ae18b083a2..a7db4bd8f68c 100644 --- a/x-pack/plugins/fleet/storybook/context/stubs.tsx +++ b/x-pack/plugins/fleet/storybook/context/stubs.tsx @@ -9,7 +9,6 @@ import type { FleetStartServices } from '../../public/plugin'; type Stubs = | 'storage' - | 'docLinks' | 'data' | 'deprecations' | 'fatalErrors' @@ -22,7 +21,6 @@ type StubbedStartServices = Pick; export const stubbedStartServices: StubbedStartServices = { storage: {} as FleetStartServices['storage'], - docLinks: {} as FleetStartServices['docLinks'], data: {} as FleetStartServices['data'], deprecations: {} as FleetStartServices['deprecations'], fatalErrors: {} as FleetStartServices['fatalErrors'], From 961fe752c5a25c75eb4d02d3e8019b55a5661af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Sat, 9 Oct 2021 14:53:55 +0100 Subject: [PATCH 25/74] [Watcher] Use fixed_interval instead of interval (#113527) --- .../forms/hook_form_lib/hooks/use_field.ts | 1 - .../helpers/app_context.mock.tsx | 4 +- .../client_integration/helpers/index.ts | 2 +- .../helpers/setup_environment.ts | 1 + .../helpers/watch_create_threshold.helpers.ts | 2 +- .../helpers/watch_list.helpers.ts | 16 +- .../helpers/watch_status.helpers.ts | 5 +- .../watch_create_json.test.ts | 22 ++- .../watch_create_threshold.test.tsx | 148 ++++++++++++------ .../client_integration/watch_edit.test.ts | 22 +-- .../client_integration/watch_status.test.ts | 16 +- x-pack/plugins/watcher/public/plugin.ts | 6 +- .../get_interval_type.test.ts | 36 +++++ .../get_interval_type/get_interval_type.ts | 21 +++ .../watch/lib/get_interval_type/index.ts | 8 + .../threshold_watch/build_visualize_query.js | 36 +++-- .../watch/threshold_watch/threshold_watch.js | 4 +- x-pack/plugins/watcher/server/plugin.ts | 9 +- .../api/watch/register_visualize_route.ts | 3 +- x-pack/plugins/watcher/server/types.ts | 2 + 20 files changed, 232 insertions(+), 132 deletions(-) create mode 100644 x-pack/plugins/watcher/server/models/watch/lib/get_interval_type/get_interval_type.test.ts create mode 100644 x-pack/plugins/watcher/server/models/watch/lib/get_interval_type/get_interval_type.ts create mode 100644 x-pack/plugins/watcher/server/models/watch/lib/get_interval_type/index.ts diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts index dedc390c4771..c01295f6ee42 100644 --- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts +++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_field.ts @@ -515,7 +515,6 @@ export const useField = ( if (resetValue) { hasBeenReset.current = true; const newValue = deserializeValue(updatedDefaultValue ?? defaultValue); - // updateStateIfMounted('value', newValue); setValue(newValue); return newValue; } diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/app_context.mock.tsx b/x-pack/plugins/watcher/__jest__/client_integration/helpers/app_context.mock.tsx index 01c715583274..8176d3fcbbca 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/app_context.mock.tsx +++ b/x-pack/plugins/watcher/__jest__/client_integration/helpers/app_context.mock.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { of } from 'rxjs'; import { ComponentType } from 'enzyme'; import { LocationDescriptorObject } from 'history'; + import { docLinksServiceMock, uiSettingsServiceMock, @@ -17,6 +18,7 @@ import { scopedHistoryMock, } from '../../../../../../src/core/public/mocks'; import { AppContextProvider } from '../../../public/application/app_context'; +import { AppDeps } from '../../../public/application/app'; import { LicenseStatus } from '../../../common/types/license_status'; class MockTimeBuckets { @@ -35,7 +37,7 @@ history.createHref.mockImplementation((location: LocationDescriptorObject) => { return `${location.pathname}${location.search ? '?' + location.search : ''}`; }); -export const mockContextValue = { +export const mockContextValue: AppDeps = { licenseStatus$: of({ valid: true }), docLinks: docLinksServiceMock.createStartContract(), setBreadcrumbs: jest.fn(), diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/watcher/__jest__/client_integration/helpers/index.ts index 961e2a458dc0..09a841ff147a 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/helpers/index.ts @@ -11,7 +11,7 @@ import { setup as watchCreateJsonSetup } from './watch_create_json.helpers'; import { setup as watchCreateThresholdSetup } from './watch_create_threshold.helpers'; import { setup as watchEditSetup } from './watch_edit.helpers'; -export { nextTick, getRandomString, findTestSubject, TestBed } from '@kbn/test/jest'; +export { getRandomString, findTestSubject, TestBed } from '@kbn/test/jest'; export { wrapBodyResponse, unwrapBodyResponse } from './body_response'; export { setupEnvironment } from './setup_environment'; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.ts b/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.ts index 05b325ee946b..5ba0387d21ba 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/helpers/setup_environment.ts @@ -7,6 +7,7 @@ import axios from 'axios'; import axiosXhrAdapter from 'axios/lib/adapters/xhr'; + import { init as initHttpRequests } from './http_requests'; import { setHttpClient, setSavedObjectsClient } from '../../../public/application/lib/api'; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_threshold.helpers.ts b/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_threshold.helpers.ts index c70684b80a6d..caddf1df93d4 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_threshold.helpers.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_create_threshold.helpers.ts @@ -93,7 +93,7 @@ export type TestSubjects = | 'toEmailAddressInput' | 'triggerIntervalSizeInput' | 'watchActionAccordion' - | 'watchActionAccordion.mockComboBox' + | 'watchActionAccordion.toEmailAddressInput' | 'watchActionsPanel' | 'watchThresholdButton' | 'watchThresholdInput' diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_list.helpers.ts b/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_list.helpers.ts index ad171f9e40ca..c0643e70dded 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_list.helpers.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_list.helpers.ts @@ -7,7 +7,7 @@ import { act } from 'react-dom/test-utils'; -import { registerTestBed, findTestSubject, TestBed, TestBedConfig, nextTick } from '@kbn/test/jest'; +import { registerTestBed, findTestSubject, TestBed, TestBedConfig } from '@kbn/test/jest'; import { WatchList } from '../../../public/application/sections/watch_list/components/watch_list'; import { ROUTES, REFRESH_INTERVALS } from '../../../common/constants'; import { withAppContext } from './app_context.mock'; @@ -24,7 +24,6 @@ const initTestBed = registerTestBed(withAppContext(WatchList), testBedConfig); export interface WatchListTestBed extends TestBed { actions: { selectWatchAt: (index: number) => void; - clickWatchAt: (index: number) => void; clickWatchActionAt: (index: number, action: 'delete' | 'edit') => void; searchWatches: (term: string) => void; advanceTimeToTableRefresh: () => Promise; @@ -45,18 +44,6 @@ export const setup = async (): Promise => { checkBox.simulate('change', { target: { checked: true } }); }; - const clickWatchAt = async (index: number) => { - const { rows } = testBed.table.getMetaData('watchesTable'); - const watchesLink = findTestSubject(rows[index].reactWrapper, 'watchesLink'); - - await act(async () => { - const { href } = watchesLink.props(); - testBed.router.navigateTo(href!); - await nextTick(); - testBed.component.update(); - }); - }; - const clickWatchActionAt = async (index: number, action: 'delete' | 'edit') => { const { component, table } = testBed; const { rows } = table.getMetaData('watchesTable'); @@ -95,7 +82,6 @@ export const setup = async (): Promise => { ...testBed, actions: { selectWatchAt, - clickWatchAt, clickWatchActionAt, searchWatches, advanceTimeToTableRefresh, diff --git a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_status.helpers.ts b/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_status.helpers.ts index a1c7e8b40499..02b6908fc1d4 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_status.helpers.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/helpers/watch_status.helpers.ts @@ -7,7 +7,7 @@ import { act } from 'react-dom/test-utils'; -import { registerTestBed, findTestSubject, TestBed, TestBedConfig, delay } from '@kbn/test/jest'; +import { registerTestBed, findTestSubject, TestBed, TestBedConfig } from '@kbn/test/jest'; import { WatchStatus } from '../../../public/application/sections/watch_status/components/watch_status'; import { ROUTES } from '../../../common/constants'; import { WATCH_ID } from './jest_constants'; @@ -89,9 +89,8 @@ export const setup = async (): Promise => { await act(async () => { button.simulate('click'); - await delay(100); - component.update(); }); + component.update(); }; return { diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts b/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts index 4a632d9752ca..f9ea51a80ae7 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts @@ -9,7 +9,7 @@ import { act } from 'react-dom/test-utils'; import { getExecuteDetails } from '../../__fixtures__'; import { defaultWatch } from '../../public/application/models/watch'; -import { setupEnvironment, pageHelpers, nextTick, wrapBodyResponse } from './helpers'; +import { setupEnvironment, pageHelpers, wrapBodyResponse } from './helpers'; import { WatchCreateJsonTestBed } from './helpers/watch_create_json.helpers'; import { WATCH } from './helpers/jest_constants'; @@ -19,19 +19,19 @@ describe(' create route', () => { const { server, httpRequestsMockHelpers } = setupEnvironment(); let testBed: WatchCreateJsonTestBed; + beforeAll(() => { + jest.useFakeTimers(); + }); + afterAll(() => { + jest.useRealTimers(); server.restore(); }); describe('on component mount', () => { beforeEach(async () => { testBed = await setup(); - - await act(async () => { - const { component } = testBed; - await nextTick(); - component.update(); - }); + testBed.component.update(); }); test('should set the correct page title', () => { @@ -92,7 +92,6 @@ describe(' create route', () => { await act(async () => { actions.clickSubmitButton(); - await nextTick(); }); const latestRequest = server.requests[server.requests.length - 1]; @@ -141,9 +140,8 @@ describe(' create route', () => { await act(async () => { actions.clickSubmitButton(); - await nextTick(); - component.update(); }); + component.update(); expect(exists('sectionError')).toBe(true); expect(find('sectionError').text()).toContain(error.message); @@ -169,7 +167,6 @@ describe(' create route', () => { await act(async () => { actions.clickSimulateButton(); - await nextTick(); }); const latestRequest = server.requests[server.requests.length - 1]; @@ -230,9 +227,8 @@ describe(' create route', () => { await act(async () => { actions.clickSimulateButton(); - await nextTick(); - component.update(); }); + component.update(); const latestRequest = server.requests[server.requests.length - 1]; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx b/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx index 77e65dfd91c7..481f59093d7d 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx @@ -9,15 +9,10 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import axiosXhrAdapter from 'axios/lib/adapters/xhr'; import axios from 'axios'; + import { getExecuteDetails } from '../../__fixtures__'; import { WATCH_TYPES } from '../../common/constants'; -import { - setupEnvironment, - pageHelpers, - nextTick, - wrapBodyResponse, - unwrapBodyResponse, -} from './helpers'; +import { setupEnvironment, pageHelpers, wrapBodyResponse, unwrapBodyResponse } from './helpers'; import { WatchCreateThresholdTestBed } from './helpers/watch_create_threshold.helpers'; const WATCH_NAME = 'my_test_watch'; @@ -76,7 +71,9 @@ jest.mock('@elastic/eui', () => { // which does not produce a valid component wrapper EuiComboBox: (props: any) => ( { props.onChange([syntheticEvent['0']]); }} @@ -91,7 +88,12 @@ describe(' create route', () => { const { server, httpRequestsMockHelpers } = setupEnvironment(); let testBed: WatchCreateThresholdTestBed; + beforeAll(() => { + jest.useFakeTimers(); + }); + afterAll(() => { + jest.useRealTimers(); server.restore(); }); @@ -99,7 +101,6 @@ describe(' create route', () => { beforeEach(async () => { testBed = await setup(); const { component } = testBed; - await nextTick(); component.update(); }); @@ -159,46 +160,60 @@ describe(' create route', () => { test('it should enable the Create button and render additional content with valid fields', async () => { const { form, find, component, exists } = testBed; - form.setInputValue('nameInput', 'my_test_watch'); - find('mockComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox - form.setInputValue('watchTimeFieldSelect', '@timestamp'); + expect(find('saveWatchButton').props().disabled).toBe(true); await act(async () => { - await nextTick(); - component.update(); + form.setInputValue('nameInput', 'my_test_watch'); + find('indicesComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox + form.setInputValue('watchTimeFieldSelect', '@timestamp'); }); + component.update(); - expect(find('saveWatchButton').props().disabled).toEqual(false); - + expect(find('saveWatchButton').props().disabled).toBe(false); expect(find('watchConditionTitle').text()).toBe('Match the following condition'); expect(exists('watchVisualizationChart')).toBe(true); expect(exists('watchActionsPanel')).toBe(true); }); - // Looks like there is an issue with using 'mockComboBox'. - describe.skip('watch conditions', () => { - beforeEach(() => { - const { form, find } = testBed; + describe('watch conditions', () => { + beforeEach(async () => { + const { form, find, component } = testBed; // Name, index and time fields are required before the watch condition expression renders - form.setInputValue('nameInput', 'my_test_watch'); - act(() => { - find('mockComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox + await act(async () => { + form.setInputValue('nameInput', 'my_test_watch'); + find('indicesComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox + form.setInputValue('watchTimeFieldSelect', '@timestamp'); }); - form.setInputValue('watchTimeFieldSelect', '@timestamp'); + component.update(); }); - test('should require a threshold value', () => { - const { form, find } = testBed; + test('should require a threshold value', async () => { + const { form, find, component } = testBed; + // Display the threshold pannel act(() => { find('watchThresholdButton').simulate('click'); + }); + component.update(); + + await act(async () => { // Provide invalid value form.setInputValue('watchThresholdInput', ''); + }); + + // We need to wait for the debounced validation to be triggered and update the DOM + jest.advanceTimersByTime(500); + component.update(); + + expect(form.getErrorsMessages()).toContain('A value is required.'); + + await act(async () => { // Provide valid value form.setInputValue('watchThresholdInput', '0'); }); - expect(form.getErrorsMessages()).toContain('A value is required.'); + component.update(); + // No need to wait as the validation errors are cleared whenever the field changes expect(form.getErrorsMessages().length).toEqual(0); }); }); @@ -209,14 +224,12 @@ describe(' create route', () => { const { form, find, component } = testBed; // Set up valid fields needed for actions component to render - form.setInputValue('nameInput', WATCH_NAME); - find('mockComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox - form.setInputValue('watchTimeFieldSelect', WATCH_TIME_FIELD); - await act(async () => { - await nextTick(); - component.update(); + form.setInputValue('nameInput', WATCH_NAME); + find('indicesComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); + form.setInputValue('watchTimeFieldSelect', WATCH_TIME_FIELD); }); + component.update(); }); test('should simulate a logging action', async () => { @@ -240,7 +253,6 @@ describe(' create route', () => { await act(async () => { actions.clickSimulateButton(); - await nextTick(); }); // Verify request @@ -303,7 +315,6 @@ describe(' create route', () => { await act(async () => { actions.clickSimulateButton(); - await nextTick(); }); // Verify request @@ -366,7 +377,6 @@ describe(' create route', () => { await act(async () => { actions.clickSimulateButton(); - await nextTick(); }); // Verify request @@ -431,15 +441,14 @@ describe(' create route', () => { expect(exists('watchActionAccordion')).toBe(true); // Provide valid fields and verify - find('watchActionAccordion.mockComboBox').simulate('change', [ + find('watchActionAccordion.toEmailAddressInput').simulate('change', [ { label: EMAIL_RECIPIENT, value: EMAIL_RECIPIENT }, - ]); // Using mocked EuiComboBox + ]); form.setInputValue('emailSubjectInput', EMAIL_SUBJECT); form.setInputValue('emailBodyInput', EMAIL_BODY); await act(async () => { actions.clickSimulateButton(); - await nextTick(); }); // Verify request @@ -532,7 +541,6 @@ describe(' create route', () => { await act(async () => { actions.clickSimulateButton(); - await nextTick(); }); // Verify request @@ -621,7 +629,6 @@ describe(' create route', () => { await act(async () => { actions.clickSimulateButton(); - await nextTick(); }); // Verify request @@ -702,7 +709,6 @@ describe(' create route', () => { await act(async () => { actions.clickSimulateButton(); - await nextTick(); }); // Verify request @@ -753,20 +759,66 @@ describe(' create route', () => { }); }); + describe('watch visualize data payload', () => { + test('should send the correct payload', async () => { + const { form, find, component } = testBed; + + // Set up required fields + await act(async () => { + form.setInputValue('nameInput', WATCH_NAME); + find('indicesComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); + form.setInputValue('watchTimeFieldSelect', WATCH_TIME_FIELD); + }); + component.update(); + + const latestReqToGetVisualizeData = server.requests.find( + (req) => req.method === 'POST' && req.url === '/api/watcher/watch/visualize' + ); + if (!latestReqToGetVisualizeData) { + throw new Error(`No request found to fetch visualize data.`); + } + + const requestBody = unwrapBodyResponse(latestReqToGetVisualizeData.requestBody); + + expect(requestBody.watch).toEqual({ + id: requestBody.watch.id, // id is dynamic + name: 'my_test_watch', + type: 'threshold', + isNew: true, + isActive: true, + actions: [], + index: ['index1'], + timeField: '@timestamp', + triggerIntervalSize: 1, + triggerIntervalUnit: 'm', + aggType: 'count', + termSize: 5, + termOrder: 'desc', + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + hasTermsAgg: false, + threshold: 1000, + }); + + expect(requestBody.options.interval).toBeDefined(); + }); + }); + describe('form payload', () => { test('should send the correct payload', async () => { const { form, find, component, actions } = testBed; // Set up required fields - form.setInputValue('nameInput', WATCH_NAME); - find('mockComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox - form.setInputValue('watchTimeFieldSelect', WATCH_TIME_FIELD); + await act(async () => { + form.setInputValue('nameInput', WATCH_NAME); + find('indicesComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); + form.setInputValue('watchTimeFieldSelect', WATCH_TIME_FIELD); + }); + component.update(); await act(async () => { - await nextTick(); - component.update(); actions.clickSubmitButton(); - await nextTick(); }); // Verify request diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts b/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts index e8782edc829a..1188cc8469a5 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts @@ -12,7 +12,7 @@ import { getRandomString } from '@kbn/test/jest'; import { getWatch } from '../../__fixtures__'; import { defaultWatch } from '../../public/application/models/watch'; -import { setupEnvironment, pageHelpers, nextTick, wrapBodyResponse } from './helpers'; +import { setupEnvironment, pageHelpers, wrapBodyResponse } from './helpers'; import { WatchEditTestBed } from './helpers/watch_edit.helpers'; import { WATCH } from './helpers/jest_constants'; @@ -41,7 +41,12 @@ describe('', () => { const { server, httpRequestsMockHelpers } = setupEnvironment(); let testBed: WatchEditTestBed; + beforeAll(() => { + jest.useFakeTimers(); + }); + afterAll(() => { + jest.useRealTimers(); server.restore(); }); @@ -50,11 +55,7 @@ describe('', () => { httpRequestsMockHelpers.setLoadWatchResponse(WATCH); testBed = await setup(); - - await act(async () => { - await nextTick(); - testBed.component.update(); - }); + testBed.component.update(); }); describe('on component mount', () => { @@ -87,7 +88,6 @@ describe('', () => { await act(async () => { actions.clickSubmitButton(); - await nextTick(); }); const latestRequest = server.requests[server.requests.length - 1]; @@ -141,12 +141,7 @@ describe('', () => { httpRequestsMockHelpers.setLoadWatchResponse({ watch }); testBed = await setup(); - - await act(async () => { - const { component } = testBed; - await nextTick(); - component.update(); - }); + testBed.component.update(); }); describe('on component mount', () => { @@ -172,7 +167,6 @@ describe('', () => { await act(async () => { actions.clickSubmitButton(); - await nextTick(); }); const latestRequest = server.requests[server.requests.length - 1]; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_status.test.ts b/x-pack/plugins/watcher/__jest__/client_integration/watch_status.test.ts index c19ec62b9447..1b1b813617da 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_status.test.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_status.test.ts @@ -9,7 +9,7 @@ import { act } from 'react-dom/test-utils'; import moment from 'moment'; import { getWatchHistory } from '../../__fixtures__'; import { ROUTES, WATCH_STATES, ACTION_STATES } from '../../common/constants'; -import { setupEnvironment, pageHelpers, nextTick } from './helpers'; +import { setupEnvironment, pageHelpers } from './helpers'; import { WatchStatusTestBed } from './helpers/watch_status.helpers'; import { WATCH } from './helpers/jest_constants'; @@ -43,7 +43,12 @@ describe('', () => { const { server, httpRequestsMockHelpers } = setupEnvironment(); let testBed: WatchStatusTestBed; + beforeAll(() => { + jest.useFakeTimers(); + }); + afterAll(() => { + jest.useRealTimers(); server.restore(); }); @@ -53,11 +58,7 @@ describe('', () => { httpRequestsMockHelpers.setLoadWatchHistoryResponse(watchHistoryItems); testBed = await setup(); - - await act(async () => { - await nextTick(); - testBed.component.update(); - }); + testBed.component.update(); }); test('should set the correct page title', () => { @@ -175,9 +176,8 @@ describe('', () => { await act(async () => { confirmButton!.click(); - await nextTick(); - component.update(); }); + component.update(); const latestRequest = server.requests[server.requests.length - 1]; diff --git a/x-pack/plugins/watcher/public/plugin.ts b/x-pack/plugins/watcher/public/plugin.ts index 6c6d6f116965..093f34e70400 100644 --- a/x-pack/plugins/watcher/public/plugin.ts +++ b/x-pack/plugins/watcher/public/plugin.ts @@ -8,13 +8,11 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, Plugin, CoreStart, Capabilities } from 'kibana/public'; import { first, map, skip } from 'rxjs/operators'; - import { Subject, combineLatest } from 'rxjs'; -import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public'; - -import { LicenseStatus } from '../common/types/license_status'; +import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public'; import { ILicense } from '../../licensing/public'; +import { LicenseStatus } from '../common/types/license_status'; import { PLUGIN } from '../common/constants'; import { Dependencies } from './types'; diff --git a/x-pack/plugins/watcher/server/models/watch/lib/get_interval_type/get_interval_type.test.ts b/x-pack/plugins/watcher/server/models/watch/lib/get_interval_type/get_interval_type.test.ts new file mode 100644 index 000000000000..a90876d1baf2 --- /dev/null +++ b/x-pack/plugins/watcher/server/models/watch/lib/get_interval_type/get_interval_type.test.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getIntervalType } from './get_interval_type'; + +describe('get interval type', () => { + test('should detect fixed intervals', () => { + ['1ms', '1s', '1m', '1h', '1d', '21s', '7d'].forEach((interval) => { + const intervalDetected = getIntervalType(interval); + try { + expect(intervalDetected).toBe('fixed_interval'); + } catch (e) { + throw new Error( + `Expected [${interval}] to be a fixed interval but got [${intervalDetected}]` + ); + } + }); + }); + + test('should detect calendar intervals', () => { + ['1w', '1M', '1q', '1y'].forEach((interval) => { + const intervalDetected = getIntervalType(interval); + try { + expect(intervalDetected).toBe('calendar_interval'); + } catch (e) { + throw new Error( + `Expected [${interval}] to be a calendar interval but got [${intervalDetected}]` + ); + } + }); + }); +}); diff --git a/x-pack/plugins/watcher/server/models/watch/lib/get_interval_type/get_interval_type.ts b/x-pack/plugins/watcher/server/models/watch/lib/get_interval_type/get_interval_type.ts new file mode 100644 index 000000000000..5e23523a133c --- /dev/null +++ b/x-pack/plugins/watcher/server/models/watch/lib/get_interval_type/get_interval_type.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * Since 8.x we use the "fixed_interval" or "calendar_interval" parameter instead + * of the less precise "interval". This helper parse the interval and return its type. + * @param interval Interval value (e.g. "1d", "1w"...) + */ +export const getIntervalType = (interval: string): 'fixed_interval' | 'calendar_interval' => { + // We will consider all interval as fixed except if they are + // weekly (w), monthly (M), quarterly (q) or yearly (y) + const intervalMetric = interval.charAt(interval.length - 1); + if (['w', 'M', 'q', 'y'].includes(intervalMetric)) { + return 'calendar_interval'; + } + return 'fixed_interval'; +}; diff --git a/x-pack/plugins/watcher/server/models/watch/lib/get_interval_type/index.ts b/x-pack/plugins/watcher/server/models/watch/lib/get_interval_type/index.ts new file mode 100644 index 000000000000..0bb505e4ea72 --- /dev/null +++ b/x-pack/plugins/watcher/server/models/watch/lib/get_interval_type/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { getIntervalType } from './get_interval_type'; diff --git a/x-pack/plugins/watcher/server/models/watch/threshold_watch/build_visualize_query.js b/x-pack/plugins/watcher/server/models/watch/threshold_watch/build_visualize_query.js index 10ba68c3193a..60b2dd5a546b 100644 --- a/x-pack/plugins/watcher/server/models/watch/threshold_watch/build_visualize_query.js +++ b/x-pack/plugins/watcher/server/models/watch/threshold_watch/build_visualize_query.js @@ -6,8 +6,10 @@ */ import { cloneDeep } from 'lodash'; + import { buildInput } from '../../../../common/lib/serialization'; import { AGG_TYPES } from '../../../../common/constants'; +import { getIntervalType } from '../lib/get_interval_type'; /* input.search.request.body.query.bool.filter.range @@ -22,17 +24,6 @@ function buildRange({ rangeFrom, rangeTo, timeField }) { }; } -function buildDateAgg({ field, interval, timeZone }) { - return { - date_histogram: { - field, - interval, - time_zone: timeZone, - min_doc_count: 1, - }, - }; -} - function buildAggsCount(body, dateAgg) { return { dateAgg, @@ -93,7 +84,7 @@ function buildAggs(body, { aggType, termField }, dateAgg) { } } -export function buildVisualizeQuery(watch, visualizeOptions) { +export function buildVisualizeQuery(watch, visualizeOptions, kibanaVersion) { const { index, timeWindowSize, @@ -117,11 +108,22 @@ export function buildVisualizeQuery(watch, visualizeOptions) { termOrder, }); const body = watchInput.search.request.body; - const dateAgg = buildDateAgg({ - field: watch.timeField, - interval: visualizeOptions.interval, - timeZone: visualizeOptions.timezone, - }); + const dateAgg = { + date_histogram: { + field: watch.timeField, + time_zone: visualizeOptions.timezone, + min_doc_count: 1, + }, + }; + + if (kibanaVersion.major < 8) { + // In 7.x we use the deprecated "interval" in date_histogram agg + dateAgg.date_histogram.interval = visualizeOptions.interval; + } else { + // From 8.x we use the more precise "fixed_interval" or "calendar_interval" + const intervalType = getIntervalType(visualizeOptions.interval); + dateAgg.date_histogram[intervalType] = visualizeOptions.interval; + } // override the query range body.query.bool.filter.range = buildRange({ diff --git a/x-pack/plugins/watcher/server/models/watch/threshold_watch/threshold_watch.js b/x-pack/plugins/watcher/server/models/watch/threshold_watch/threshold_watch.js index 5cc8a5535c8c..a20b83e83e3b 100644 --- a/x-pack/plugins/watcher/server/models/watch/threshold_watch/threshold_watch.js +++ b/x-pack/plugins/watcher/server/models/watch/threshold_watch/threshold_watch.js @@ -48,8 +48,8 @@ export class ThresholdWatch extends BaseWatch { return serializeThresholdWatch(this); } - getVisualizeQuery(visualizeOptions) { - return buildVisualizeQuery(this, visualizeOptions); + getVisualizeQuery(visualizeOptions, kibanaVersion) { + return buildVisualizeQuery(this, visualizeOptions, kibanaVersion); } formatVisualizeData(results) { diff --git a/x-pack/plugins/watcher/server/plugin.ts b/x-pack/plugins/watcher/server/plugin.ts index aea8368c7bbe..52d77520183a 100644 --- a/x-pack/plugins/watcher/server/plugin.ts +++ b/x-pack/plugins/watcher/server/plugin.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; - +import { SemVer } from 'semver'; import { CoreStart, CoreSetup, Logger, Plugin, PluginInitializerContext } from 'kibana/server'; import { PLUGIN, INDEX_NAMES } from '../common/constants'; @@ -27,17 +27,19 @@ export class WatcherServerPlugin implements Plugin { private readonly license: License; private readonly logger: Logger; - constructor(ctx: PluginInitializerContext) { + constructor(private ctx: PluginInitializerContext) { this.logger = ctx.logger.get(); this.license = new License(); } - setup({ http, getStartServices }: CoreSetup, { licensing, features }: SetupDependencies) { + setup({ http }: CoreSetup, { features }: SetupDependencies) { this.license.setup({ pluginName: PLUGIN.getI18nName(i18n), logger: this.logger, }); + const kibanaVersion = new SemVer(this.ctx.env.packageInfo.version); + const router = http.createRouter(); const routeDependencies: RouteDependencies = { router, @@ -45,6 +47,7 @@ export class WatcherServerPlugin implements Plugin { lib: { handleEsError, }, + kibanaVersion, }; features.registerElasticsearchFeature({ diff --git a/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts b/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts index 61836d0ebae4..60442bf43bd6 100644 --- a/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts +++ b/x-pack/plugins/watcher/server/routes/api/watch/register_visualize_route.ts @@ -37,6 +37,7 @@ export function registerVisualizeRoute({ router, license, lib: { handleEsError }, + kibanaVersion, }: RouteDependencies) { router.post( { @@ -48,7 +49,7 @@ export function registerVisualizeRoute({ license.guardApiRoute(async (ctx, request, response) => { const watch = Watch.fromDownstreamJson(request.body.watch); const options = VisualizeOptions.fromDownstreamJson(request.body.options); - const body = watch.getVisualizeQuery(options); + const body = watch.getVisualizeQuery(options, kibanaVersion); try { const hits = await fetchVisualizeData(ctx.core.elasticsearch.client, watch.index, body); diff --git a/x-pack/plugins/watcher/server/types.ts b/x-pack/plugins/watcher/server/types.ts index c9d43528d9ff..87cd5e40c279 100644 --- a/x-pack/plugins/watcher/server/types.ts +++ b/x-pack/plugins/watcher/server/types.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { SemVer } from 'semver'; import type { IRouter } from 'src/core/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; @@ -33,4 +34,5 @@ export interface RouteDependencies { lib: { handleEsError: typeof handleEsError; }; + kibanaVersion: SemVer; } From 52858582527a7821294af4a7fb630ed0224c7290 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Sun, 10 Oct 2021 09:34:17 -0400 Subject: [PATCH 26/74] Update sharing saved objects dev docs (#114395) --- ...jects-faq-multiple-deep-link-objects-1.png | Bin 0 -> 33939 bytes ...jects-faq-multiple-deep-link-objects-2.png | Bin 0 -> 61047 bytes .../images/sharing-saved-objects-step-3.png | Bin 127926 -> 126244 bytes .../advanced/sharing-saved-objects.asciidoc | 15 +++++++++------ 4 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 docs/developer/advanced/images/sharing-saved-objects-faq-multiple-deep-link-objects-1.png create mode 100644 docs/developer/advanced/images/sharing-saved-objects-faq-multiple-deep-link-objects-2.png diff --git a/docs/developer/advanced/images/sharing-saved-objects-faq-multiple-deep-link-objects-1.png b/docs/developer/advanced/images/sharing-saved-objects-faq-multiple-deep-link-objects-1.png new file mode 100644 index 0000000000000000000000000000000000000000..d0fd93574d2fafc34add8aea0d188b5a583e1af8 GIT binary patch literal 33939 zcmeFZWpEwMk~Vn6Ocq*Ql$D*C^|V6eWW*3)v0(uK0D^?LumS)8iV6ULI6;E~b2O17p#T8bEi)k@ zISC;lLODBYV>1gQ06;u6Aqh%O@fgGB!+lOjP%^~l=X4rj20%g36a+2bzc_$2M((?~ zz(COO4s1CDItn#aEk&-Lde{;&T%$fS5k^@3vpc5&09vzl6eH=M9mQ&f0qVK->60*84&PQ=v1F5cdLz0FGESY1E0KLyxSzN-zq9y_+J`C%e2evD5+5Al>ooxDwa^QultDsqI1{TX7WV)o>RwB8pvmcC&ApN9I~r?}8=l@pZQ!+^HWl>M7SF zxCg_;Fr+=PAq%3DmtKRpa%ABO2LZCRF>Dunlc26P|y4c7pBXJ_Jqgo}OFzn+v;!4`DBd`jzyG^Ke4`Fk6zi^0Qahb#we3s8q zY27n8m zDg_#Op#>2efISW#P-8o4%s~LS^8(Oj?2VFfpt9aBAwjw_6@yStgFtqJtAT26L2LPv zUqOHnf(Q7U#sDw?w8%cx`PTz_9BK%#0t`#gjefAzIA;(qJw!HW%>MCP)XmV9emGl* zF5s;K1THW^J?`|d0?3HOLTKVpi~-|>IKwF4`8VVMXpka;FmjAPAwvl{>Txx|(1i-* zAnKt?1Ectda_Xjyis9SH zKIJQcW*A;L-N4K4^(#pia0od{3hYs6qww$9YQ;86#FXe{0u{pOIiqsQ#coPerC^S5 z^O4wso`ME5Xv}z);rGH&c?Q#34yD_e8_`yRSz?=l581&poAibe_=AwUzWN3` z3}(p#$%0a!3nAx1T6|jkkNet(zSl69Lo6WRL`ZiR8YJmUR&&)LEU{W(Hez!5^=&)Y zu(H!>u}AsGZ^PQI+Ctfy*s$4pHb=Xlw-ark5QNC~hhIHCGQNVmn!QSU@Ok~rmZ2wc zLGFd&?p5f86lBRsu*sMf!y@~EOcSEhZ~C3nmZc_LO}3riL&~EpH}`w_P9>wNNO4?| zLZzMBe%!a{QS!JiBdPtwWU+hscln8cu%*oq#*qJ0^09c^0@8Jlo#6pO9dq zAwWgiL{dk7iUh+>Vu56~O+82jN#&jZo#;qCWOn~b5e5?|vaPTnSNau`!c&QQ-fy(9 zP%*qDxH#jNK8-YuNrhSknMQ&}QGKf!MJh+6^jN-SHeSJcfoEnRUnEB)pXGY}vl&~F z=X7S}dD+Aw?X1;wXWpB7a3yMXd`_K2k@{gMt;V8;bLGYNb?sJ6hu}iRB6pcX8M;(2 zhi4%kwJ8lc1?!Xzoepz1l4rgU=g)_?!SJS#osh?nn(#6>@Yo9Q9SDIzn($Lt zo!RCyDYsH?9BpDOA>nBW5mEX;83hNnWd%Jx5uDQM5S8 zI_Yt!J^7$bL0hN2&|2T-;pk|e(7e!w*qYKx)^={wHk8dsj71h9d4c83_n>@1zRu^` z>^96D<@UO5cAB#7+HJ3wI6(bgexlx?GpK`G#iO&n;c#hqNzl>e8R5astIRv)A?@MX z$0fj=T4% zp1PO@3l$f;{p%yXX4Zt!Vr{U{nxINRIuco+XrM{}FI*jBd_cowkV)|P2KUQv+&Ye2 zj)`r;bi7WESBU@-VW*Ra2 zhNb+al_ezGciRHn_1{dps{Ojs)6&Ra>PdG#8K9bf5<7qxU*)W#8zC#9Kfdq?f08`V z)yZxnwC^bi`~k-S7fogmw;P8<>b)IcU&>DM_3S6!*FG%_XPgJJsidYrw&91Jr0W;O z4h2a?sM+>e)*=L04{#$tA|gpOB99FG^iHlfAyJpXPIwVxEn^`JUMh~$W^u)zM4Vm2 zdNXk%+TU?(bDFtUN!-ovt4WmV zzL?^J^=|JXHA66fe3d`rGTc9oC1okk?JWrR#hh}a=bF^IZgse>9opYj#ZpbJYSpo9J+Q0zjhUp2 z&85wiR@tU)-LmbK*I=%)c$~(|Hnnlx<@lTR<$j*a{=#8Bvc>&FXSU|V{64SNkc(j%G~O)nc9 zUFHO#Sgy~!r*B8WxzU^i58mHBP9AX|TD%>8W3DA#SE=Y~b_II5mk%pY^y9*qc%bnU z!O2KNY)$YQ^2z{w{Kk>{T~TlDn)y*T!FR#mQDw)sc6VU;d2$CuNmOtE!Lde}P(G=2 z00RU0dODws^q;4?#L|v-=^-M5g_7f9fjbqvfMyjy=Qp&lnGD#0#UJ@gP=I-&|lok0l0kIS%t7B3*tNKL|6S{gtJ zOhW@8Kt2J$fGH5*9{>a!0P!~s0DJ?%`LDDB2*tnZ08Qp#GXVI%>SzG(e||B*3rPL1 zJ6K#00BBPKJ^``kp5m=)yhfEh40 z;_CJQ00!Bg3q(SJ^a^ORFPSN+IjBiXaT-`#(&`&pe>b9av9$Ta2f*#Z2~1iVIp`C* zSXx-wbGq;l|E<9ZO#jKIBPRS?#lf71SWQ}vP{`WOh>(?*o|c}N7nYEaklW7Cm{UPm z^k3w_Cmv!`2L~HYIyz@(XIf__T5CHKItC674mx^9Iz~nsU=133S1Sj77aA*jl7E=| zn~$)Oy@8#Xjf0uB72zMg`roY`9e9X||1|Vpzkm8^K`Sko_t7hwSgT{%MZ;4>L|VGZ!NZHDNPL;8X*<#>+s@%EiADfvUX;`dC>pUHC~2)weX*L z|EkYT_h;?@Ll*uKw7L{q60onuf-G;O*`Du&w|t7k(UaI%K&y;u@rM%W^nIjEd^>_~)w_93TZ2E;L1(Pb7b! zJ{j7K%Ck@3q@F~CI#H&7o_~d9l~}l#fwC!X-DZcszkgaMYbtRwBt?)5#1ome zwzf~7fu^Rrm{<{m&(?^XY*mELtBbio*|PJURKaaHzaf`9*K61tAW7`+Xgig`9UKi! zUrsOrlqZtO+<|P24F>$kK3c~7qHAH z@OHlE3vRZwwdH}YuC8_wTk>Id?#P!IJ3F&UQk4PS$FaCWRWy?mDkX1oa~!L#nZ*bT zbL+as+8<_ScULx0P{=k84gfWBaZ{>JI{wse;3^-qb0h6XG!|H+-gD&2A6u)vNEPbZZVlIZunN_T== zBrPW%^H z*N1a9930NKH_zF*$%O@_$rQH5jg892MnwC#*vwB^G0YZA;&DakJlp%{rza)^7EyZ@r&am|A@KoB>?27p_o`3nPjnCXtB||wpw`E zskuWb4GlS>mX?+?1Iy}x;~iq6 zfh{d<=^UPCPnaJegNC>U29`GqS-L{;oE#h-ZyQ5oWMs13@8ZIH$dF`Dt^*Yn^Sqrz zIy#vX6BAEQ1^D>1hRQlBD5yA*VbotI5nv*%Oih6Ta&vQIXH#7SmKmRMytjvfjjt4= z%l+dvBuU5f?zQEoiPzmt@)GG0NONuNH;cHkGJ9KjFGHnr8BGCaXH^F&b_^;u@}ZuY z`OQrnxS6FTC3Sgp39d`tgYg2>*P>-`Cf}Bj0ZB5wEZB!F_xE6r7P% ziJ4hgR8@Wf5W{dpze!SIpOgs-lGsc)G&Y`$b7sm)dz~C_M&PozU1qLxHCekIz3^#x z4y_CH^vE?oOpmDQxNYx7Z**AR@i=e3@Jm^o^?oi4oY zi6MO)EMs0YpKeSwS+?BVD~C#u)G@+g(CED0R(aX8KHcs>Fuj3!q@|^Hf!W3q|5Yad z5F=`8YIl0Pq^sn)LmKK~jb>A#(TkK1FYK7`P5J-@5DPd_!H({t?tGEPmIVLym9DJQ z>7sp`insu)wg zJ-fmkMB}iP7PLTM3knGtPo*56o+5zs5cSv}&^S2Q`Os5z1xW>w@9!5*X;zaGGjfx> zl@}EajjJnuH^{4I%h4yYKbVAuh3!^h_=fd~fXn2v1y1JQ&tE-^ktZdffQ}GO{*ISxNp27Feqx8vyle@C7ixW*1#@qoFHH|eb z`;GHWm7P$cE^sfxSHJJHTRrACH{5vc)|MFzhIn3wqkBN?2dG;9F3V#S%2CD}E3NXw zp;|&x(j1$v$l8&cm-?aYkkDD`lm{5skXS>=u zW2u&dbtGOq*6)Uj2D^7usw}K5uMgfl3~u8AAJ6+tj}LEPpc`J7{t07Aoi9BmibYe| z!h;x>2Kw*Mi>kWbbrlT>%Z~?zg-Kvw1fFNMN|v^^IeB@gB6Ql!A3@4ZZ;wT?K|w(| zQO;JI_a|d?Z(B74Y|Q-KS9tExlbNiv#Eel@SRW70T@P2cz`duhvETD@@w^@F#c1&e z{i;`TsDSUb)k*GkA09GXT;%<}*Jk~(Ki6>T>G5{a{+4KxHYV1L`vQHIUeC%0;VqEYBNz^X=!P2Wb+q7LS&u$^9>&Rdk<@Ztgx`_+Syq@zjh>E z7d9_(4`IVSOkG@QUeA-BU;=rsUWxdkmF1O6&6}@%wS4bS1Z*1~r}f87j7+nxaNpQA z>YtZ4-#T-0-aSs9=89yZ@Yrmxf0l7d*k;`(kc=hJjnZzNTZH9l+?Q&hb zKhH`^{Hdo3&0+hR93F1%Ff)Ud z>C>H^nwq-a=CtNG<*C2D9gK8dRZ@9>fB%Jw&ADmF`|*`x43j%A|>UV7)| z%x8a=><-EzgojX53`d8Bs(T);D<>pecHB6BQMV!34l#%V(~d6Ta%WxM-1OegJ(b&Z zc%6^&6M(V*l!1pA@u^a?m79<5A&a2%@bvUynj~67josid6shbKc@3p)*4oua{f zDDk%bhKML9Pc*v4_{@b*chmi%eSf+~h^)>;wr}<5NK&?Zh3Qc zl`psEZ}`4To|$|5u<<0e^0c=%SEQ+t>MALT(Es1^O3`3~U4(iFt5AXZAA#3%9`u6sSo}tZ6 zw+=!+q`=3ux)1C1RPVq5i1~$IowvPkM?)tkC%Ug^V&=SW$o{`(*COzZAbWdeX2vu% zo9CD0x=bhAhX~x+Ntu5pQzcGz+25U0DDLR}@OXR0lIR~CWI*WVCnfrNoFx0fW`FYY z4%k5C5COv2cT!`kl@*Pw*O6%axAzWtc?MH8KE99XX&Va|H}}O!yvRs|?QImcO(z^D zy^oFcU2>`HqG;PNS#e)4L9IG0X*t$eYn(#rBTt@=$_1#|V*s#Lp5mufm5C^tu=9dR_O=Ih^nA z>`I1((L+8Pb~KhZHdIXTO*^kdaIDCQk41%AVNxR>*VhZ1nw6B4OioM~!#l8YxDG~? zv<97fN`{W#q9VNM=;~%MJ1`HRsEmOYhSl=4IWvqd*Kg02kHeR*H`}6OW8>($-?5O3 zk?-iqTuGB<@wT`x%0!Ye$dARcc_Jij8gEx*vt0>11ML%m~WmqDNPE`x^$v{$Pr<>>|_ZS~&~qd*%qi zjVOEKCnT&CJ&3R2M0zGBsppe``b5jKKR=hqGLWmzP(-jU%Z2*s)p3I0g61+RdeBeUQF@laq>yF)Y&w z>E+p3b#*trDbpgw_c(BW>b>nvvg_`6O)PTBTT1|*olqxti%0-$LA;O z<<7$?@3%Hyj9!7#6x8qC=!Jt{Jy|K^A!c@{7#eaB#2v(ff)ZO$Kz4uMC{>ijdncl9 z7Md4-vsa>+Li=4X4MN^vFaiN7+AufGR!YPbqTdJM=_bjYF{is=OqJf5(PYEJV(@;d ziG%)Tcgtn(s)r;Pacjr@zU44v#3kI(JmN}2lZrtGa&_?E?{BL=Q>`Z<6tGUD4-$-oati&;X?~*VVyTmciOI9K|LdY|*QayYVC*(QP+VbClQ{2>94Iy2 z{xjxq#gy#Eh8oLaYmcXbLnwI4HOMB?=k> z#vhhMS(uqGx)vMhEjQMho=_)uOR;_6R?GFVM4${yH}IGuqkVE_Wj#6ue9Q{RAiep0Dd2LXn!G#ukb1TIkD1G%l|L z?LOKrsS(Wm`MRZvd6Vggg35m9QxV_7;`|d7<0(Oe_wjnuhtusa@3%u7(Z~%}%dnT@ zEWS>&UAJ+1_?49v&(|rWP49;)_Hsh>AS<;_fIp}H&RzoB20kk4SR7?JT1kE2{J(U0 z28rJB{`ru}-t*Fy;C8bdHg>95G=6ManyS3~KzMhyPePf>49ojt)}@FDIX#LW+3*`5}^YftY$3aT=Rt!s)r=a&sQrCM{d;6M%!SC(*r9B$lmmsq48GCe)=DDvA!7wKmf%=U&#q>VHrJc?B2 z8*w{3J9Q@2qHt7{ls7kUrDb8{Qt20mmq}@9X&xtV#z9*l-1_lTM9vpHCx--A{=t`r z_Tqroqj-emuvIIaNlynUP~8MB4lV(D$zNe`;VF_+Q)51OK~~hF?9JY)YA)1H6oiD? zojKbUc0aT{nDaU^FETf~uCbj?7jUe7%T1b3G{@!u^=`Zo%Wd>$O1fO)Kkg26vWn#E z8}yLm=6*_k`gV3?h%FS}7nlY&r>3cRvUi61=@UO@S8EtdP?Qfp!3NOt;9OdDdvOu; zRxz#&E@}%7*0hpAsowhG|DKy$n|Sa|wu+D!VOtV~1=7b39;~b;c}pJzJwFjGDL4>n z7eGx_rUwZTjBZ=)v^PZ_Lrd#&UO#ooG+Sr$`!hukcT$3it1A3KS@kE(K*xMbbFwd~Wy|&CHxYPMZ-#aO^I(H6^nT)TW zKou9bcsKJc&~3AQ;knvckKF8qG|)JyGQ^8vi6Oz`bKH~yuO-1naJJoWX~5CoO9-(} zi$7tT2)_3d*N%pfY6&m6&Ie(}^jkngLt+odv(tkX1wxSd3cMTkk~qP!+cJNSk#{XE z7cbyhIZ~t^zdohL5|cCRv(T}^j?K&6osW^JA?*6cc3y?fUQSKL#zL-sKPsahYLXu6 ze$9zc-yXk3yBnR{VB+Rh)AeI{#C=YEtDil~-3f%Uf`_yqywoKJ_rOZk!_(B9?=;XQ zkL^eKJ6()~+0;;l6)`A2;n5*B6vum%Y4xF1%QDK zNK8;n>frr>!x&v6i>=`8O?1KW1QAG7Zcmv;s&M)oQDB{MNRZabB%K6vR%qzFf5ivC zkPs&J84i>B{*4wINBO8BGGPMrb^nG0<57^u^cfA~{s|B=iSbhhr@;AC)BOzvjzEX( z(|=}^_!DLH^GW%`XB>im>2CnA+Yr3pE*UpnLoep94_BGq}GYL`Kl>98y72 zpZ*Cb9{k~B4}yFTG^l`q!_Oc!xL<_6!2ESE>FxjUX#qgL{OxXX1h9MYMDgJNJaGW| zjB5b`Jpb-pDN$heMq(sD|2~c50{Ixz0($>M%b-F3cbMQH|GS!gKL0DU0{^==fu|V% zKT$#6X5iFxc+@#SeV0w7M?(KcV}khN!@+S$N{UfU*wi~c)Zg8Pz%clbumMYeDi4xk zZ*UFesIl6nwszd?@~qr)uG*$4x~9qO_#)P~rA0F6iVpy+2SykRs1x>?393slauYg@ zL(XzH7G?V7vMY5kSi6EHw%TZ=GfD>3oS4YSW40i0EleihXEZcSt6Z4it~^YxhV^Y_ zfdwKVX_5zxeU6rRhzly(Jm0y;)j0zm-l2#2+xdvEEi$3$n`X3^uYu84ETqM=)}Ytt zK~to~tjGwO3JbfMMzl8lOa&q>@6SL*-UcE3 z>rG2W$1|_3W4)vAJM~JdMJQns@J(<_M3|U|1oLEG30)1(R#5O3C9hBN+QHS<)^v!W zUhkAyX1bKBJ^W};T(xLGLacDAkzSr(lUx=8y$OaKGe{5k1VhgRe)d_W-f=w+YXF+0 zsB+uH#Ew|&9JM3E+l~2LN~9q?<2>$24o*^%3NGc0o@oTxsq4?nNH;37odFD7tD<63 znf1--H3p?qM;b5VLu%~Y)as_bp~m2Q-{^O3V*vwqF~6oE5||u#uJnq6j$~gc=x|*y z5HlEEFw#)F1mbSJyn?dT-gR3Y!;tujiILjU&CE$#=XxG>74OpWo{`Dt0{f@KvL6|i zmR6Dxwbf1MM~SkdRSNRRjZ(thD>F08lTk2&DF9{^j!@o56q~xnZ&em99W< zLpwOs$0OSl8AMq_X6IwyLK8kuz_YL68jFFiYN%(@mbtxn)C5KIIm6dmV zqNTE9*Z-zvq0(vGudcncOtZ_am8R9rWBvTY?n_;M30e2@acBQNby*ZK;XMf=yo!t&sAN>fzt0fpg z*Xa@5Xad?aQP!XJM&3t-wsty1Tb=eW2*yj`>*|iD3p;7J?lNpY>r)h=`6KaZrjfYL zLxqpJQM=u~iTfGy#ZgtM1m<3%0i|p6>zcw+f=~B=dn7ZD6D8B=W=c(t=#?CC24pkX)U-3}YzbBX5bWTQ7=+Rsl7q&KL&B;ot2IjlOlsn6$+f}%R;A- z5-cL(``G1?*4A$4WhO_OsxH@9UqO3a+oqWh77`pC)N*uxJGgi=o)1uDKw`wZm4my< z9O9 zz>mt{;c>~0`1t)hs4H^*_w#|bA^ICjUUp#x$*{_*!a6(R!$KkkZ{vdQ&tKZRhm*Fx z0+ll871CU#CvRsKk6T)_0$Y@#-8Q_z6UyWH)nB0LRrnV*>zyjGKW*z4Z+C5Dh0{LVNm*RRl8ct8Pd<~)&aZe|8OvgJM6Jv5bfjf*x(FVf6y8roO5*c%&c zrOC)B8l|M+YLYht}D^2w)FhLwRO|1IampMc^w4B~H zR@NsDeUo@JOSlELz5%_cp#&{1!PR|XpGeV_)-J<}U;ETz*Pp;Ey}8w&ngF(vO7$C44NrXz$A(=dyb~8ugBZgaRFHYq%@b`QlD-yodog zo4rJYjd{%~x7Vre=_xX!3%SL=0#Ect82HPJC@A^k{(uVlrOAxUj{$TcDr(Zn>3*~M zOT^7{)MZW!@Yh^CygVRcrY}fIRW>#@s->lkCguPl8Ag?=sgaS9@lNA9?4Yd;gysmj z2nW>FF{OO0l5SFi*3T;~&Mz#qwQ@d;`KrWb=_+X}udS>os>|z{9GY5Ne-Qi@v12$H4Y5nI6Kt$F)1^s(LjAtS5{{7dVPd|!&FsP z1rD0{A~G@(4wEL8!Q4qg!q#39RRfEx;r0167!k*f_YQXjc1E63PcJZ@Xe1bcpwZz8 zA#X@)_xRXx?+Qgs4C;HFutWtBdh{Sskl1dTF9ZmiFPf5$PQzX+S>9lTjD$oD(`-)r z=){O_v+4T|*`jO)ujSH}yjoRzx`(PN=1O&rgQJi-SooH5mkpRi(B2?X7^xt<5C20oH#k$htg>;xz&}WY>(}5C3JM<_#&y)COv%tFMQ776;UC7 zrDE1rZ09vcYc-dp3+)$f?lt(9&tXT-LX?iNG=UFgIy%SOkfII0JipQctkR3Vhbadt+%jCO-ah ze?K-U&HkpMX)4*(Qbh$)TIlW;N^eIg+@bqoJ{LaNLzq?|7?Q=?0A?4X_~|W{&aW zJnR{}Ft5SGFUD!7Pe0DY*dVWCQ4f(l5(rxmO2`9`XG0~73Z7!lFD~%2V$ksL(qdxN z6tw)5TAG_l5%3N!F64Doe8A^=j}5=Zo~s7dZMD&I^IO*(N$*k+si`Y5(=kiULcBd{IkXv#9uGxYLXN=B!q2$+vxmp>XjOIXf#2O&P@f)2H*&>~$ozPc9 zZjw+H{e!OJ_ZOg2m|;thc~ zsAaFKx>-zbJI|0~I_sBQ`ek>v2h|N4%6_N5sex*n9F=BdKw4@>%0)MBV+EW*jYKZC zhwdQ&*=qcNu!EpH5sc{m$_BV+2LjK5@B}Y`2*JKyIU8;5jLuH4V&{HZ&M!f3K$X+j z==7C_2B={pA|vnaZYIWPlv+3BESbMsAoC23j)rM-IjJa_#)gM~`%1ydiT#0op3~Sk zxv~-le=O(ja(iEh0%mP(4a7@ofIbgQZAMq7CnW*FYs|F@irQr?NK~m}lAe}e2gJn0 zeu6WZ8e_+&(a5N79-bXvq|&vNm7hMmZf@9gynpvSY}Pb6>j8jz(MJngoXk#MzhqES z3m!JL22)r8927y9mzUSp*7lXLHfKPPlGIj+k|r1*jv`oZhbh~aB%OmkWRQ-Q(w{3= zKuuo$YerjfF@=KOu{h$QXaA3775vn+F8Tsd>%VE~b+fA42-F_19^Og2=CbQY-bPi06^H%q}EZ>u=wl*Go@0VDTTw)Zx z70?WPVtyoavFONe@bK`|bX8x#(bwiHa)ebtKsx1Tq_jd}~pWO+| z&h9>1!cy2j#3b>BB;H2bEKDj&^7luj7ZpXw5jG{{*xB0)(2mGgFtwtDvAFNc(ZwOR z4}{s1$EA=bUIAqA_f1XE$nm2@Hg>loNj<&1%q)1kdi-olOJ%Y8j)uVV6H$MEA9&1< zisRo+BI)236S=iT_=+*=aurU9BFL=9RLvo7P0M)g9|G0Uvx zRUdFwTdTnL;S*Baymof8y*;K!$wh@q_pn1&hNW;70*;A}VWHJkSu1-;-QB?Ay3NGW?;rRflZ%Y= zFnu^*(HB#UU*wtCvH~C|ur?vt#qBcdOgv zQ2PVgtJ`a00%Uf#$E{2rcTW%Qp1sVM3mBy6fL|$fhrbpV?G4I30Wrwf8yU{)z zpGP2hdTL2ywd$4-r`n#YHo2qI_L1dDMV0D;kdz^Ok;LkX{1S;%;ebJI&wMG zcJA*-%yD4Mk6NhE7JA8_kx69)!HoCoXe=~dUzs{WC~yC5e5-~$2GOtCJ_y0UNN~%fF_^#hk0ftTPDTaedm(!$g&vz7Hd!qL z!PxW$gSWP*UlI~A@J8P+%p^`)6px&m)0j+j zgbmU7a|RIc*1Fzq@Q88*$}XGBnwzhts=9zWo6}_tvD|vU&BuQH;rkbuXI}VTd6g>dRcyQv29KsUaw1`M@z%#RMwPqR0Skc z{rHW0TBFO3<_m7F?mvGjtyNzKZIjRr-ycZhd@hQsXoY8Fy%H74_J<%*TKi3hxTB!$ zcvAs{qH(wxZjRzu8yddeZ*GoG(WNpNtn`m4K2sLBO{B`Elo*^je)uB^3yXq%MVGGA z=<=#)4Svk&3!+o24oPty<#9i_U6@F_8!SpPE8u9D9%EbgNb?4M&fo))y->A|jGGGr z!QgObJdqg)nA?=V=HzCjeeZC7FO0mP*F%Yo=3~g59XTCOnZsGA0E#H>SK7;9|C~r6 zc;+jA2o-9ku38dc5Xnm+W zkj>WCl{%9sI$hho6Qt-L>})FW+Bu9S9M)Xy-@6cX2_`e_p1%MZ3 z@G*HlGh_8Ef7jQSBuF=9mbrZ#mM0O7!d6s^=`!1G{rPk6=qTBYsSp$0IM33oue!_e zW-#Kmp6EA>{UKdJnyg|d5T9vLTv}Oa^hIuiCXdV6V-m}BwEnY% z1k59NWd;`=tCP5l6UKcZ5JXrg()R@wJ7Y3$p^tKS3OE<|ncZjQ<Z!i5(;#Iw{Y>ZcH5sJV+;)q(AV+oel=z}ad*IKO1vRzy$JYHv)~O7xHrHLZa;{RRgYxA^*=aJDaWGIn znM`)Ct!|w-f2Ic1PuP*l!^7gcv9LULM}#ds*z;lSc)S5}XT$;exmD4azrSMF5E{{X=4W!@eEQUCIun=Mi)#!W^LZ)Q z89bVrTsmcCc6OW@>47Lm9|FQvlhv|2C3GH_!%fL)!8Kj-);hunhm?j|2gmz727~3ZaooS$)?&cL zdkK=$Jt!F|-LCh(j$6fG5QTsMNfx=gUz zRFsoNnT5KH#S@S&hzkr*`6L%!UPSPqgAt#DPj9x~`*d_Rj!HTGP( zdl2XCj((HWd4Ky*WDLLzfv#8h$ztixPkOiYo)P~=y5OPCWKl)%!u=}vwesyOo@s<4 z8=usyr zSUgzID~|mH>BjR@Fdm;5ngE}dl%kEufJlT_Z(m=NrTUngZ+G@q$neryI`axE-U-4f zpH8gO#i^FE9_jUgW5CJVLxFhG5%D_+);ck=U>H+~=#9f2v4?hrgQz}ZLPo9_< z_)()2l$0Jfkx6f_LNj)o-nTG}(V^10Y4G`S??ZgtmlxYzyEh2H{Gw`~B$#Lv_Sl4A zvF@11{i&X5R~)s zN^8yol;0M?me7F`6i0qKPx#!YJJ{G5Hj76VAg;4G&H4R3sLFcy-E-CZde57Q%GmP! zn{yH=f0NfapQnevvaE`cAPE7d2m8bE@u964KmOU=;rbZ&RaJ#;0jfVCG-%BO4XIwxla(O~7`86pSX-iKKd|ZiXYMvexB0Q{pfR(=t z%kSP|>vmaB>A;1v)`H3(oCDo?ess^U&4kRVz(6qCiFxNWJZ$W5>G}EkMn-Q&I00Wp zj=6R`A|*)(AXip?U_EaAAc1-3^yB2@vYpI5t43~fvLeF8YM4~#R~ubsN=f-FhImdi zy-Lo`U`rvSMw7#8X)NTuli}UIeYAJ?w4g2fabgCE(4H&vgv7kPA8 z z-7_{kO-;~79D3Vfe`R&${k4d1X5je5BHMRbJR`k*ZZa!R)~6i!sld$6sS2@1vV!E; zI~C=K{P(`C2=b%=Qh8!*!3;O z#>OOY(6H1rwMTbwRT@q><;`y*cucW+dU{MvA{dCIPJm`1I{Mz6`0MN29a&2RWhpT7 zgF6gvV`gh>D$Xv#WJk<_O~XzK-Qi=b%Hjtx5Gx%7;(L3kbu4$dhc^8|7Mv#*Wwjnr zMjm!-1zXf4u4f~@1wfXWT?`Ki>$O7#!O~vmmg8JoT{Tl&KpwRA^2&^7dMCOJ3lkIt zLq^8H7j7he?b(hxTU%Nmy$V;PV1rad;&DOd7=$$EQY#*zo0gi zhM{pG6hz>f?f+@-oS!qRx!B`@VXG&zadF00=Pah=g*4fzWh_hkO#ME$rst@es8vXUH=o zDN6#)o?r(b$oxT_$oa*;R6e8NB*XR&u|$N45A#KIxBlX1XIWppO>&F@Cgi24^l(4s zx#wYCCS8j4_Kcw70gUl;o;=}aWJP@zsFHYR1gD6*afVU?%>)H%YFd)&nbGjV zGXy#yWCZ0b5{Y^~MNm;Kz|PLjX5?eo@Q*-=bE8Y7Oir-93I7h=ydCHEBshJ6h+P~f@c;TB9Rr2O6__tVwt-> z6zevvzOiaeuG3UYs!Q%ln(O(bG%1mA5ubM_oS?;-y7md$@E8p)Yy0nmQsl2)Dv-zG zlx$?|>@$_NJdXdE8fl&_IVfn3 zZc)11{?Sa>uR`>rq%}fY`$h0jn5$N0W~0U0oYL|kt^Smwq!636TKArYgr53*@JRR@ zu3QIe++R=xVniX$b*te5HlHvuGV%|6xh?b&-aN7HDy=I1IL6UPL$pC_OI=O`4Gv#? zPSQ_yp)2E!I6Q7vO`8Q9k(??{*jO0DqL`aVm~MIid_a#+q%d|MEt0~D{IOtCd*Cn!-H^+s_Hk?_o3dx1&I$0#m=oXck*Q~;1vEN5x-`F_mU)H+2B#dG zc|v9YH z6A1xK8S9F;pnC&Yro6+yAMFTPbNAuh+urO-c{AgMn+?(A=Hwjg;l$-;PEHZtYXzYn zpDgSQt&@@3b$upO6bB8(*VNQtU|^i)@sBTW6Z?-Ls%|f9Z7r^%;NT1<XrK7|o`zaE#Qyc2rG^?YS0d<7Q{&TJQ!mk*Ds--Fqce6)F>C7_;4bw594 z1_cF;{QNss_(r_}8*1gDRk07;pfY(lt(TUW%QfZqW6bHygwAeOE|p)XuB@o43yTzR ze3^sY>J(E`F45Ii(GWB)5qh(TT%fP0XmWsHw5!erFh~H%HzBm-Y!HbJa$qgXYV81( zh)ByJS6teEAm{J+_}JYfOid{k@Ydskli`bt>8VNM42hnH40BL&u}O5uBozAo8gymg z;*k{=Xb{K6=gh1Jb30*4h=aH;CrFg;#DZt&n>684uokMuC>of}rVHk3YM&iP#FN{U z)hpp-*|6x}egRkiSbBFKGa5{TcSEbi4^xppP53SEzU%Tyv+tIgIrJtO`cFVbvsj0IW%NZZX9M}S2j)1g zIMp-b5&7>#xIW$@-qzNC%YY)u2hYcrZa|UYe$$)N@SL-ip(HUO1_Q+{K5)3Z2n%?b z8triBtL^56;ic4I?D%*f$U71g`EXxud;R@YWf*CBr{9JvmXkLs@!A zyswJ@#ted=`k^Jy$i$stsCk;8NM&Zux6=UF}|v^lTlK(WBDzU@Al5AnXaxpf=TD2gbcZEDK! z0u2>PukSFq&F7gmY$&~e4S9@8JZrCnvg`EEK*D;{eR|tjdM3zl+{wx^kHx5mijO}q zrR=HA@K9#ap~P&G=MWK_V@Omto5LFW+nTbk%uoaa%^yzuTREGSp$D?_YFIpqIO4CB zG>W-C^P}_Q$hL|!ghrU5;oDsiWPCmkuAg3nlYPt^!BFY!><43J$5ntvmO=j{UkwWw zVg{(k`SOzjtWu2W24l5H9rBH6#L(tfFTRLkjC0SW=vc6=4))xn6e~Juwu#3`0m+*N zQhMkuP;q_}~2+ zFXK84`Xyy$=?g`g{;nMwkC)1ei;6OIetsWH9>L3N&*-#Nwqan`Umi-k5fZ-c{`xEo zMfTr&flW-)8FC=RB~0^rLm=}JryUjxE>)mv>;V#g$CV0v!re@Ph&S5e;iJS?tFn_; zkO4V##H^g0;OcOiIFAdxKD^TOKfEqDg1}-i?;+i-=hi#jY|Z;3J=iNGuav)gK|8y= z;_jXT51JxCmfR`rIv7JcTg&ZEb9GBgO$&{w&u-6`B5GQm8B(ox4am@ja}Q>Ss%Q~U z?c%3q)>ss&T+_*t=zATq&@HdUA~qKuC}hSmu54G6QB;VY zXZOBbEh-X7$5Kb}=kx$?fDJ6^8;{4W zjei$>t6!LYSu~r=C&MylT)Ct%A-bu`b)(A?f}(?q7vyscuwO)Zt<~*Jd4)4ZdAi(& zx)p>B#&)!>ZZRrr3~Wj|y*g;O91Iy=VK|!fT6Q*Ms4MaE@wdsF2@CB-$}Xs1af^>T z;`4$pxWy(AkszED3JIa5MBD!Z!F5Lq()`1#t;yl|W3GgQ;s;1>$Xlb-i=$%T_l%+qVJ=PJ@l*>W>Gn^WT>^>v0WY%ORt?EHIb#vZv80Rt@$mG#jVGfc*63VYUBu@+Bt>~JRI8OI z$CwDKCxFSLN7q+XbqSYp;IU;dFF#XZw8%qmvWV7VgOX=j?pQ?`W8=yVFpALN$}=e1 zAIpXJ(d1nz^97$K7m!9wg>?)#iH<#9Z7-^-q8I25L*5UQZ8@w>M+02EU;Dx^U1a)t z{9b?HZqr=F3Fb$chj>9mlmq@yvaPWd6AShlnUX+;;jsLnTkVxlD`#MVm5p~8N27y+ zN4?QSf$tEyj386_d-68%GT40}h-N(9B5IY^q3fu^>$5+-)8CpTC=3K90=qNyK$ zhgDoGcmdDgP@h;Nqm{u^x?~2IjQF~knq+imJ5ay8I^PI8!`Vqm-V0BTU z4>)R8Qlk?aSs#b_aPt}pC@n310t~V1KtLC_XL~3j>L5AHC~hwI<`%U^xQiZW!V!<| zuWNlxamh%+)uFufVmBe7ZyCm2?dib~Fs#Cn_vvruFEexZfzR9hQjyOEL~h~Y7()@0f!FB zk&+}Lxu1*Zq5wQBkMsbGgeXj15J)d&AuFQKX5$O9&ddNU1BM7^BW&S22idC_j4ARC z3g?113f-hJO)8=_DU{7`bhSK&HL}WBu(@$?i?Bvfe?(dZY9H8FpctX^S)drBAh=XZ z9>v2^IKi=?AoP+eS!w-KC3bOohzE}$SIqtwbqwmqgPngEc?b_h3@!Lmd#ITe&i*R} zE(Rn%*krLE1(GI7`7%cfh}WX1q||@m>+XN#x`_TbEK?A##)LFIrjvm-gx5y}=}#U| zF#QrQ%#>m?qOGM%{UjwkqvI!@Ctc&C+SXC3Luw!)H24Qo9NjI1lo+;r4%;v|Et zjbaHw4o2zLC8?=DT3l5ovpdiX{kKfZTP1vS0E+H>5;^gGI{i)Bbh|B5$8`#>MZU=ghu1h7&iwO(`2$rx*yvr zqZFCg6h`^!miaLTDH1IT1RU1XJ{(>}Rj*!;M<=iL8Y|3-eAG;HofY)Z$$lt*0Q61} zA}VYmZ4M?P?6}*wq1fQjiHd}d5-_PG0ki=q2ZtdN1?6dM$S%h2ruaMdc5OJ=(dri1 zlDo;b+TDe}1Oilva{1B176u6n80lN7K3YRF%bw zi3)v_K{C!Ej-ekvanS_xY6KZ>I!^SQ7c%X-2IdvmupUiy3Rpn-xR9TSuTI8e`V@yi z6eAEliedqG;+GUc>?ZcSqurRI*e<9z%nZM*07v>zy!$9brdxFlR{%GS9f?g;jgyur z81;GN2Q)vbFzr6J1gps^Q%?eP0o}I{?C)!hHQ9p_*tFb4R$jNmd)=N-dmTmBk~3nq zsn$ksbk#b`_ID2ZptN)lAgP{=$ZlTS2rTq@KyW;-)4#TI+7Y3HhnX`Vp1Wh)y=`)m z76j9BXXAv7dJXm-$zGKok(IW??M;6FVg~2qvpy|Qqhh5~)*7Iad#rl8HsAVUi0_!% z0NT(}Qy>3?VmJ6`Ew4F*u_sSCkgmzAOypS7A7seu!c?b9)X${r!hBG;we-3|N4@P7 z5p?3K=NH-#T0@PVJ>W#s0u6j;tU?{-At#*&Pk>YtYi>rzJl`vP@`8WOSk66?_tRQ= zF{W~Fg zZ&d&{JZh!HA$5c&!!GvC4f4(u!ZYtK8w>EtJzdm9{{KX%hleAf{|p@lf~Zua94W-% zQgtY~VX&k(fP*i3E{7;PMI3d&?m9Q|Fld8Uu5%Hcu z;y)M41FZ!^Ck5zL@*uUUQ#BmENIS|%@EVwv58|F98H`RkOx-HN0E$&TzPcIIM`(FL zY;15?RFsD62V6^=fe?>Y>?|^%@?s>4U=Y)1X(`znt;zm&|NqJ;56=!pr!*E#M+(J_8MY@ZVJKf z*x|&fEtyVN`-> z8RcSWC5P`Rl3^ZVpPOZon+QK(fB3K+nq|$QXxc*h`@?`_m{8@o*iyn%nBPvX!tg$)^*;R^5X7 z1&G|2gpE{^@S4L&+o#ptNWfkGj;{7fUyaT`|KRIgNh@Sk(>o_uJdQs0yatbjSc7=6 zf};EylxQMt(gT>ghK73}Op~X*q@# z_m=9*y$Q-5>jv?{)JKbpqpUZY(w!UwOPfcYt^DWN9 zDT1tT*#ux?Db-9(%4u(2fe6m%szKP2ESuC5YXT7WQ)dwojt%ot4JOFo9K9qgu^Rl0 zgx@4g<=g9~=%m%t^(9yHsk)N>VMk~9&sms$!YoL_iACZkZDbri^W>@{&QBHr&hF#fe~OPUG-_gO6~e-@J_=~86AUU22V3*Ab|-|MvA4$|Hd&ll^7kV zGgY;+3Mnn6gH+VastpFjFFcTX7Z78EV_zu=-NN2po=%B_A|lBS4nRTRs^Tn?5=iN$ z(I1H_ZVCHs$1jIg#Rm%XR)!s6hlK}GL#_(r2UpuT22fr>6hFIjKaSB+4up~;^q0mW z8C|TwCQ^2`JGf@LdN+gbGaN*LWMOy`s}*CHa8s0s!(zo<^NjFM&oz4O=SnS)o=3x2 ztCt=6htU@+^|j2M5Y-yL`Ls<6pVu<4H2< z(J)Y$g8y^Kefq}S^0zpzm#4O-LeXgt35Cw_|eU=)Gtz2m(ENS0>TZ$!v_9;3_l`3iiWOxfxC#(Jqa?&{;|db8T) zu)z1c6}u6=4MxVxp}pDVAYvnDBWxsWH_%7y8uj)k(2bU^f_b|_v>W$G0;LTSe80K_ zk{b2F<)1lZ8yOk7nJLLVlbF>ut}czL@c9J&i3Ovvlx;={S_h4}u=qkcIy#Wv$MrIj z?4>77WtW`VxtN5KHeu`%!09s(ipQ#s@%Wd^Q-LiY^l`n_4Qd~sdNbuH#6NHr* zAq19b<}`6xu1qx+cO~z3b=cH%*M5Ior_Lc?P_51Jt0uPgCgCCl$S0v&u&}1d#76(N z`{|vPFwpd`d}!?rFBqOAqORN1t+lz?zC{!uGaaSbh%*6#+4v<6xnH-J*>+9=>fUk}HvzWZ(R-0|%S@Er=3m!;Fut-Sh zJT_W}LL4xsz$zITTCvKCA49iHs~GbNiX9_*djP6^W`au?i$KRH~-x?bS{0PgOQ0!~9KI5sC*(kH2@?{#C3 zOE6<;2bH%!PFlU?pL%-X;18%;>$H&Lb zFuiuRXH{n|X(yg8KjssV&(j2BNDS@ml}r?_WZgM##b1w)j_@~o6McAn z835_?GmV)ZDEv(W0|8{AF;5Rog(7S3A6LGAqob8JwAT8%LKFK(V(?l4qcwfRS7RJ$ zJgJT>4yTHayJ1*Jen8$&D?Ihmfq;DwUdb^Hqtm=FV79d6a;tmVUX)tMvc7$n$3-u8@SrDfKtwCzYH(?CDyGQA#}oXo6EiHwK@!G`4BwXA3l zy8H6%Pxt^+ERwe{dVmcRcx5&w^A*4!j-*oSHKbPKK6|2M=~z~s&R!J*(fTT<$ImRV*|Kcbv3H8Tth}~v zZ(OYNtLvz2<$32_!d2~hLX|Ew78~?GmBRXC2~%0f7^?iQ$`M49vH{rf@tJPJC25}b zbxp@)jP~|53lq}wL0kG`;U&E+ z9Xu*aAUYMTe-44Sm*8o%^_XPq4}8PBd3Yv9{rV{54Q|JHGI!0%_q9^M;6MNdS=_J| zEm&=JxsfsjAuoUz9{!GNrxT0Q=De(ULzy!jy@tr9(y+!831d=gdS7)Rl4EWv+BGAr zC(B=75%s{39?nq!_ODT=CaJIv*ds2?N7;W0T|Anb~c!oTPW52mljI&JBj~2MJ^Y_NA;ls zXK$`K8Q80&$EqoJB~Q-P2a}ivp)A?iRHC{+DcP8yoj2B>c*(TLh6H76>+9_&CP8vZ z6DZ0Er}ccR{(@nUHWMSTRhmf&#CTUqGgy&X_VK`I&Dj#{mNggPq0G2n6?ogd5p8L3 z{i@af%<9LF;WBlu-vsF%Gcz)=A-ZqhWVB*1G5bk?`T6;!iL)##tjr2xJmML%M%WE6 zuWyNvtb=p3TwFn(GjtF0d_J3WcN$sZ68`M{DyI}IHT_hVBZ4ryu~$vF4I$7P1qi{AKJk(AoDVAv=_GI2+J4UN2$i2W-UBuh7s z)bue~OKgc{tjQKc98>|T2aQWjad9mZ6Lj2^SuzCrvnZ}z$^ni#a@f-8&u-|KklXe4 z`(C%v?DyxJV#N@aW_I?`7{ViF-QtqR!;D6Grc9DxOt5JliY$6w=gk+X!`C+e)27o7 z@}K1CH14ml_`)|U-D$O3kSAAem)_{K?xiESRNm_To?e(NR!?Rzv)K&zz*5Dm;{XXRSKyle?nY;}{}E4ia-Cfjbu_H&rgxgRWxfX6|}DzOpcH(Uiap zf&D4kOJlh>%G>k#p?!1ltmhU>=%(mY8!|6*`N>wd?VFs7?M64I-(Kr^j5Do(PPN>o zA!ho~(L7r=TmKS85$WM^WSAl8dlx?HB9ojdv;M%l_wfPYz0rCm!RIns_xCt|30eGj zdVxTbbJM*MS0~z!@9$h7ZXeJw-l?sZtl(fFrOWAc^T>AK8 zm;wc_rsEDQk;}ZJ*@~wdpBqH8+GOfFHQHj};O8$H{b1`U9ZbsZ7)|^7$J%a4VdqSx zp}u_YZ0B#uYZGR^-rRage_4F|9u?tNv)M{FEFOV8Kzp(fS)fFpo!{>DRb9`edeN%q zu=OHmyVX3`c0{#S_~=67(iB&?RCQckl?M)I)ficzO1>y+r;u%~_hK(p4Zx^txrm`G z-o9mhsp!*C{)^@y+$7pXRUfyT*a7N_k z{;|CyprLyuPliSN%S9_EE9?CFdhsa`8o6RVKOba-XgNMM`;gtM$?hZ{c_(mD$V?Rr ze+&v8*-z3XlJmHT&_lMn0CNEm@(--c*(Zwh9+X%Q z+3BKj@wiH@d14!Xh+8-+)j-^^s7&g559_9?QUR8?8rg_!V zM|Ojgb60S9U4VMwA%O0hsqS4QI+};O!~;+rErZk>asQcLqBi*&6xzGkj0*Qma_{(z zW7i!lmTc9(aaBzl9ihr?bV?}k>0(H$UwMa6yjZi(((EPL?zITa3a!#onsZe>R02)X zJ<`gr+viyWe3=gc8d*Pxz_AKjL2^b;$e-XmboNN{A*e8ATR;gM=krtLG0j!?0fz1E z0Y)az+lrJe$JnIHtGkJnkpW>6WBw~coZ;~1DtG|KaRgn$NimJrYG|y@{Q^rl3woCyAp+){<)K*O;EhpM+^mhQn;-;VhHjr2*mmCT{Hrc&~IeC&y_` z?`WLZmA(|^tiHXTdm4HF!B8+rVlaPod9V}rqbf<*Z!m&j;Edn00`I}VQT)IlrOEu9 zl13sI|2-!5bFxPQoksB!w1l*enRk!J{jZKa(#(I&P^hE9W|VN5eu@0ocBY>j?7#Bj zfBLwk55%ngy);T6Hy1SVzvX#s;9L@xU6P3Z)!~9Z`L`qAV3NeaPN<`?%1Qofd$NoF z?~TFy=V5m7i*HHU|Esf$p9S$>hZJ$YWfijN6@~oQHY?ESe^RG?&|taxSw6VM{SKZ( zX}x0fH$F^N`E#A7wpTh5oWS?m*;BMi4kXjGB^ghGCb9*`P9~GJzMqj8L<`Pk5I+d+ zl0#;ReqoN@2J^^2A2rzGj^@e?8wGmrm2^yvxzC?eT3MJ4n>2O3KD1Qw$4|dy>^#l@bXbW~TO;yF%XQ81vL(?F0E}ma=35*K_u5Q6Oj|B}(9tD4Xcb zvCYuId4)9^^)B%q{iy3;pvi*5dv5hC7 zzT8HQmpg|tcPaCZrPE>s?7Trxo2j)rt3K1hgh@VPfsNKbnh$>%iflh@I*2iaatuGS zeh}L)OUaRgooUm3>P?X9%f-oB^dMi>(59i@ z*SFk~+0*cMvUfgBlcPi5r{uihR;u-UD2a}Y8#6;)eZyPN)VLJidT%NB=ha_4b1Lu8 zh*y@Vhy1IvGd^3YarX9=;#@W?{0o#92+$|=DQH0RxlVNrX*yo1 zC%c~|t6m|m@Tn_IdN^kuo^>TunHj1sdmX_`=Dx%#8|kaB!g&?E9nZm7J-(yY(7l>H zr&T%}nUh44R@UZ>{5s8t$72I-RwH*z*ioCvaxq_YE4Cz9dF$pFhA%!xFih*rH*;Ax)~ zug4p3lkDcK=L*Z}(M>>a{`YLF~KN z6`k!>&l}pFWHk?+vS(}0%K}qTNyvTU>FaBzGS?jbn|3gmcTB5vk-SZIyLDazV~6?k z;ciJqO3o|RHWw?$g8K}6fTR5&S#riNm=N&2h=(bSEvOZyj$`FUG-!^mQzx=aLOV^L ztuvyJV}B~&yzb`G+Nl?>dwK2C7+VI7s_mh_Gp4kLjvHwG$Cxfc^M#q`I}U&qUUiDn z(Uz~36|>RMB-y9LbRLb)yAtCFzMCx>yD#}2H`~n3ZW0uP8y)YPZHb-@*R4{#*)D6Z zl}5bYukzEe>!--Cwzo~XhVZnCCjcfNk>F#_V)L2Ti3D3+%UKWabIpgQ%lrJu5hwXG za?f&RoBS18P@!)i>@L0?vwekUG9+7GQO!udTr9W0!9n|t+IywEILL$WV&|(-b4xy* zaN;=LeFeV-nep{Hu>^taqwS_$Y!q5VMS375zVyZA#j5c+$6iX(sN@nK z#+E_H^f~WV*^5=~z1kfYu_27`{j`L2k4rD}B_aVAtl2*y?^$=-Km&f2Yq_;I!lJ zfNy2H{&by}O{@9-vv%ak4kl^E!sJp?_%qng5}Yga><{rZwZD#hE9-EH7uv@xd(Lmm zm1f@*sS$}B@4Tu)&JOcX-pFhsWa(kb#XZa%&x4w9#Yh7>(snPY^*c3Q|#=G zl_%~!$!yk0^#mr|6w&wI6Q%mzzS_Ge^KeiwNl_WU>WwP}azC|c2KsS>KX6Y4Lkdxm z<=y5QY$b#@3uOF08Lilu#oy?e>BNwlu32|OB!VJ)G*>EAvy%v^)yoxAna`t*#UIgC&qdR$h z-_|Np^dGkWtUX++SX=AYRs9NyPoybEJ^wCcLW!Y{Ebh~2w>7KH)b@LA^h?i)1Fdrz zncvxNIN-^e#kB##oENg{AA75d4AOX7T`W!I5JH$NKfm?;2hi}z&sF<_yK4z6qAW6Q zvLs*$9^5qkaMcfuS_a9QLA$M~!CTYJ)#Kc)_hBU!y0Vj9nV`xFRz-zMT+cZGqeqr> zb1pGP{r%6RZmB4gQNM;=c_Bm-bTXFITbB}9J&Qx+gj7!N5Rah zmhhDBe5C^jnR?jMZWtHqR z{>6w6z0sUMbfqoXqxAhY|!%?YOS5R;V`}9wBG-T`v(QUBp}dw^Lq$&;Uy! zZ}we>;9--j^=fTDJ5^kCEO$PwW=1DUl?o`n7ELrUb@>=d3$e6SGf*Yo(dNO63h~J< z1$M%WgDKkW2!1`~f3T3<7A*etCxtBaDn<|Xh+_zsZhmJFiYQUsZX3v3xas*@L9u$9 z@5m|lc=Bc*|K)~8EfLGsh{EaOv9obsOVuSu_&HhgHKNPS{P%E<;vQ^K9W1yS3_36L zJ-%8&KnyBO8{%#K_=z=8^=}rWw2y4Y=43RIDeLHo>jke=y%^y^kN#m@L%&Rzr@AlT(iq`3b5 QBQltjn7n9>utC880sIs3NdN!< literal 0 HcmV?d00001 diff --git a/docs/developer/advanced/images/sharing-saved-objects-faq-multiple-deep-link-objects-2.png b/docs/developer/advanced/images/sharing-saved-objects-faq-multiple-deep-link-objects-2.png new file mode 100644 index 0000000000000000000000000000000000000000..c959042ab8d53233e504c92580ccb5a9776be21a GIT binary patch literal 61047 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 literal 0 HcmV?d00001 diff --git a/docs/developer/advanced/images/sharing-saved-objects-step-3.png b/docs/developer/advanced/images/sharing-saved-objects-step-3.png index 92dd7ebfef88e03356c137611ba07a114ff1b594..482b5f4e93a4ab9e238926540469c59deca334a5 100644 GIT binary patch delta 43300 zcmb@tbyQu;_AZDAcXtiJg9Ud8E`i`4++B8%g}Xb!Ed+OWcXxLP?rzPw=jNW@yRW;) zc%%ETHTIaR_NrM^YR<2|s@;2+yZFwl8az)FPzF=6b6g?d5VnOqIVlwji5K=J&x6)D z5H6Uwu_yp{a><`w`G9GfRj;(nS1%HT9`wAooah09gQILm@m@9Sa=S2J^7G%ZQaYLUZh$)ZU+Yo z8}oofusL!tbe%?)EQvI<*CcH|S3u#i!YWg1y+ZDeGf-OXqHZ|H$m0p3_)A%0-NUs1 zC`*l&sVC*#sMP>Zn`-R`mIl?4$Roha?3#3{YRh)aE9adu06K*LhHN7)k4G?v$rz+T z&A&^P5{nEkawY9;Up>#SFQcDaY0~QI$`U#(KK!b(R3%!*3z>yI3hI`R>)1kEAC;mQ z`;H(iXyep_h3?N0MKZ~uoNo$nJD+6pNIy5ZR?G@*qy!sP$mk4tMoE-=!aoLco=kT+ zpNDU>lpTjUKW?P~!~zA-n?H0s4DToNXzMe0lQZvIqULZsXo5IWF1ZEV_eSL?`P_qv zTpaBOjauB333cOeyg3vvsRCsv@sF2Wx}|S#%=prpoMS!iMiU3m!+%>XR>x-mPiP*O z33Q9K@z1XM#JZ-5$)@G<9j>4?CrB9c*Vz*F9H#xP(32z}DdSk5Cpqz$?0tG)>eljM zsKNfzuI9`FNj_64%aiS@kY_f*kG?V34l)QN}mH<=?C_DH!4`;JI(AU?BMzW?uqPqDGzoV#i|y1Gg()LIqM>oh9>CfJ2<@h7%`X-ou;tw;kUud4gO{Gw!O<3pA5 z5H{xyMGA*aOrA7$QM)$2O}?>!FC?VVeOlr4`p@qTw(mG(XM9y!;<&Bsq?9lqS2SaizKKg*XDo>r>ua_Dtize+~3)_b}G zRaJnOt1la;Pi-611Nf7A8Oq{VTAkd z)>EHDu$ais&+Q(J?OV^A?Pm_8@F5|ej2^DcTr(tu%;qz*?lx`{7*Y%evH|@G_}KTd zDMd8}it)7Q9`sTwW$HAt_#9Kt3@%&FZOEKX_acL^m0Z7&K}V1h488I)tU#XZ5YmcYO5-87rglGGEVtQzerj z02lGF@lW9D!D_ieefWj_K|$h2ojH{(nJqE5qXIsgHT{kt%=?H}NT2euW&1~>s4YcO z%k-%4NCX4*xw8V+ z)yWhJ)Glf%N;E#Z`5@pY&?~t??w|X}&PR1H&m0W7kXeK^CZB$lZ(i)+3tzL<6M_}n z`ureWpqQsmHcv5QF&o$4FEKNXg3Bqf()3HnX0o`!74$gZ4qJhF0ivf${@P=7Y}+88 z=M!}=Ib)8xn8nPbl^ZnuOIe!#`L~DJay4{0ofk(qNsRT1n-tLMeoskD3%_SEG+`ia zbhtahWz&{;xZ_`4JFXnE9k}@LRrw|3Wyjl+15OozRHD!3#xdoRGq;{ zzh!f2BKh|2+%+oHSP?2$@Af2Z@*$)aJ;0NY-x1a@uio+4xkl7LOG!~N@7vDc#Y>Y- z0hRxjV5L%vF%poqq{j9jWg%R$=!7{{W1%|4ce|RuO@U5X!@R51aDiobTbf2{Lu5#! zLnsk5iGiKd;&$7gm=PJxZF^g&P*91kX_5*F>AQ2obJ|g}6Q>nt{srIK(`W}r&85_I zPG>NYiAh|DTjCP|hXol^nCIt+EC%Y20SS|6AG4z){5=3xC`53PcGQnhh~A-IUgq7( zZp(5nKiIy!_aeJd0eFLCmx70*JoS=yc;uLh3=i^0=hJv^ zj#a3`_>AV~-a~YhY-Zc>@JVEl=j*jejroXIkHLNnl|2%yE~HxGs_V5;)Cm0KYa@7h zH&kO0kV{1mYxo;iVuPgZxln~{^Gks}795?Lf>b-i`Ocn!QQyt+Bqny5HSXmxn&ymh z5hzq;Gm<|bAONBbU$I&Zu0XpieHI~zz2ZsOUmpaX^$cgnH?&@<0&jTTOU5DH@^I}$ zJV6z@9az8Sxwm4zb7yj}w)s7yxvAjwp6N3i@N%it4)$jKzny@Q1p;-L23N_q)ou%B zhIA^C|NZ;VaN_6a86^`C>ty zquTOtN83*3MF)}&ZzFJ|j6Y>n8c-Lu2WEnS34%$A3VmG;M<((6#4YIC(1p5X3v71V zaY5na))c8qQPP8>E!B2hzo*yKQ=@!+%9?Pdu7P#+E~hLyYl48FtvNYAKd=0D&jQymEZe%e6iyWwpS%;S)o4P0(B+m&Z92^ z*6A7_N}r#b7QgcxeW7087acR-1E42RCsCtop79)-}D1SOysCs8@Fkugc9f9$Isi@kZ&lxJsCSZpGUnN=_7tj@`mr$Mr zb!UPaJ4VvjrN7@Ch2Ldhs>MY}?Ukw)^n_U(vFl$Kzzr!Rvl#8zpi1?&engvkn_A8) zMkPAqmq2}n?D)_MVlg4&;02v(rirz6HwO42{cET#vthGe4@{D@AxQr|u zv01JUzG1}rVHW=CHH0NV6y5@pl)(wd;3(u2nyoje_nU|mY(Uah-KSi}JJE;7Hw47s zTci{n>9z zGbeo?TmuTchH!E`END(H%vD1+2lO)u)K zR>-3-cfIQg=RU>OgWMk5J&Jj?3P1Vu-xYjL_r3`J{kF9e;NZ}`%G?tiua-XN>y6Zh ze2w~Gh4K;DsgFVgZT7&RQPi%)kAHp+0c^uS>TL**3aA|zwq|rC=LD2VrLA0=R}g^K z)>FfvB;D*51@yL1nKVJ{S43We@3ieZr5}6a{W4`1Dt`o8%N@>@B^uj0e3KbmE7zKU zm~M0;pfV0bAy&EQ+Em}wN9ArT!U}ZqOZcS_MHT@-Kek`e*bYqHF+@e?~Kw_pNRZ%LiC%*%Y} zgtAFNh>La)cXgHvQl(9vQPt)i{>$I{((tK20w;u6wvVKrb6j0H**2&Jvmq#{Xw*O1 zEauufsKY+OnaWS?GLYUi`RSX0edq>A{G9wdS=djO*@ujF))}>2_kzCls=>ln1(y?3 z((;ErlTKt^`*sWt{2D`m>M&!4{@%!5BNo;%v*ISvgLnmg-azt7CpcHX!-t9L|(5F-**WXdPl& zNI2JEvM?O+^G6;7Pv>?g^YhMo4dgBE+OBUk=qgB!0nDDh*o^tevkea7e!bC-zBR>o z5Vxnq-!nvg4J`%NIaazk$C7}t8J+3yPYLwOQYU!M0$P=YMatb~r@;M`3FRb!2X@%> z$M$-#+sNSXsy40n5s&2N5i}B)v7I0}3kNGR%x;V zdhR8-{l=G+9jKoFTB3pkunU&n41}4z{(7Ij!*U*btx~K7ZAaWQZB;B0ojsr6oYH8B zR;QRZ*blSb0~?A9J9@c0IwN>)49jT3+kvm9v^$oPBiyXS$gI&~o`;#KiCJ4pwXgu= zonnU^24@Fdkr?G9D1yXxK$`sMwpUyG2mYdD#ZAsiZ8#Vmb8@y{4ETYVb4!ig?} z8@HHs$$<5GyRPr*K14mv$BQN1Cf9>l?d;^f&_3E46_&}?=R^1X70-|asFo{@VhmrS z+XFf6S{*owTAEzC2C+(xIUUzYgEqYpcW49??*ys?CJJMtqF!1Pydlf_BCJTW7buxz z_~(j8-}@4M`jhGawcRG9hoqX#MzL`%!#n0)?RCa;S>Fd>fPTaG8CX&q(5J4^M?!^xwkB*M@cPpHkb2H^TOw58 z<{~s{78zT`eeYN+kmwVy(OEswnGyzgpGMDj5HK5py?mLLx+0cgPh5C-`VjVvF^bf?zp(LeW9+~`<85p!a)4glp2FlCyorjilV-t-D;(= zouimd|I)8rVatwYU~}&Lt0MoqCaVpV!)iC}W!ut0<^wuL*!s1TU8wQTzTmP42TN^6ObbFzuy4ifK$9FWLf@d6 zK?yU|C=oc*>Y7diU0=}Wle5a@f7FYDhS1T`+1xs&py3~g`SC;Aj574%DC$l7Z3d`j zJHYK+VEM41AVP8D%8D)9?z{U@@!u=+FZ~UD!Sg6m@@7j-!(cEeTWev z9yset8*6=oZv=IO+C?P2%81*>1(~?)F6wi0bGPd&KC{^)#v8HyaTP>wMA%&B@abvH z;K<1M`FY$ZWLPpYM2a+1S9HX&)6&VwOQR2bi2`yA9Es3s&LZpRfG{ZqbiZtueiWzd z=0G*#ovlfaShb-m2ozdZSLfL{miCj40zV(TPwpRU3d*5o_6u!Ls0)C^XkV0sM7KWi zilL)IpvbkCmAW_67Hvm!6-}KOq&wB4G`4P62gxL+jb(r`;r6O{5}nlZlO=IV*Y@(z z@|h^R0QFmxTb!$zYwcI%7;lO}#mNg-;}IA<3j75ygFvKzx2E7Cq!v&gED`HV1>|Dn z>SHum?g-vZuGFPf5%1KaG_<*<4xd<9Ul^-?p6yd-*`eBFbzt0c-5q1y89H#^HF)cd z$)X?^xK7!*hp>05sfq7-TYuv0b4z$;u@XYMgo=D}eNRbA$!CGR*1WDd;MN!OKi&4< zR^CGbUjj~=h0LH6ziNWh!CeNtMcO4y9#&$|+fVpWf;ZBBYC}u+#Xs@z?|^Bi@@qe?L&j!eDswQmD-He} zjNUU=Alx+nPg?lXu>nxciqZB{BYu7u4FlGof0pcDO+EOVjYUX5eq2x0Otr|i8)er3 z|4X0$xc$G7LE8liy^i(7XH?C1|25njA@WU2gvf>&(?5aoZ!A&k=C1^B6gbcQze!x* z5`dQLaLMPtafScbaJ^EzJ*d}6pnFyb_-|1sMbzP~sn*?ljqpTT2%{loH0$)#qUTtq znNf|V|2e68M6`9f$Gg?mG@eg;jV^BifRwBqr359x(ut5SWlP#0P!XQmX?*|h<@wj6 z9|JZ@ubVpPh{xX929_f`ni>c=zp#XAfR_AhYFmgFKI9NoqWVuX+u~6OW`n!+s=MXC z=@>>WuJijxqnx*0k{AV3SU^q3|1g60GMOzSODeSQ9i@q%z@G&C|1`n>|33A9`=$TY|Nh^Xg5w?NrAqzgbd9M< zr6fkX$yriOEor(yNxV?G=q975i~E1ufiS_VObxxinwu9sodU11_B(@;iHzFYwM*8q zp3e^hz9{^_@6nYe7n}2&o6$4XI&1CQ`=iz{5&@l|&HTJnigsp68JPqOYK&`En&7qA zZOVwe^;D|=#bv#l{k_!LDsdxS);v^oKs#7`O3kizX3O;zo;LI4`T@(Bo_tEXYw3Hb zr<8O{wN|m9%iRI?Ijt>1{`@hgLtu6wozJa%ZccTv&IXV(+UW_!5TX_Kfyf2r*eli< zn~{ya?I(-VBTLBrlgd2XriSznr^o#1v7Y6rx!afwV{?7)awn9H{gUz0P?k<%EnQy zgt$0zLOtD#upNgd&UdZ?ck8K_OAQejynPMF%dEV+!vwkn@}2qve|!A;E8N>Qtq#WH ze5A&w(d2BUUT>TARy?N093GanCB#z8AJ}Pd7>}iUNTnAeFFQ`jUTg%C45qoPCeQ3w zao@~p+6?4pc+x^60N8Ok;*sgfc%XA~c-dTg1_IS+&pEaPgNZ(){Xy8M5Kg@e%8ecb ziM)%R?LxZ4!R`V&!<%f`n3&Wb6rF+{OuDvSUyHPa;r;yk8^hhU+p|nz4H!G*fh>2e|GfSVAyIsplN6Ne@3A zNBc|H?ZE2pJsk0?pA2+UVx=-s>8mb=&-n_v z`QK^GCh9AnSl!Y@k9c!)NqH<;NcNIu3%*E6e5yU+47!l07CS_S0WVX`N)qiMOtj)b z*5`%%nBK$u8Q_#R_96JdLMw+%7?E&y7rSVkpj-3J4i52b4%B7OisZKYe%|Jf$E_Em z_Q#4+P*Fjn42Jz^I{KcN(oDZ4b5FxrtlOqjbhzB$kU;56eYrD~54wxY$jEqnOm5|U zzMAT6OZ~}O!;!z^y36DEJIej(ybsmcJ?;z>p#9o^>6m_>4o`K0J12`$_GD4*q(*PVmP~%-4C5 zW$>gR+0Q%ii?|NH^cZWExWDLYs5D*p1UE!NFJiO*;a=p+N&hL;h1!$ZDb85yyvrXu zk#fKq#?jh4?0`mxG7r%xKKI8^NmH`0DEgKLA`=y!qhSW|VXXKOgSG**WC5V{Xr)Qj ze72aTnwIyx8^g6f-(~+TM3ki=$DEGlzYqgYcIplGd6W$%&Q-cpE1^yftK z7g|LI1&ymkTa>k^%mbfc@^}iA^5rT$9-X;d&oYTaLPBC_l?yrZ*|E3%add27om<23 z*`-#2SC41OVj>RaJ$IeET-(_!Sy?r@gJ0`w@WM0$g8MQ4xuzQmR^-}WW-XTyWbLT1 z;_&Cnz`{?-lG&`{@4CdOibOja41*yqK0?(Anq~t}2GNARnZQhil`P(PQva6w*-5h0%m33$_FjK^t%0@R$W|tWOuM#CNxR+VgKOQ&h7eOQa`clI5 zEpU)r8I3%wTmUa_u|`8Tkk;O84w?&d!$}x)y9U~hfPt>sZ7@c#V(nM52BOXrKLMPf zr=Aol;DwYS<=OdyHb7l({Yv@KzxF}DnCRPnC!6w8rvarQx!|L7ygSr-66x< zRSK5ZwUQIAt?KwBLt>n<#s%w9nwX>E;OB8WDW1Ny7w}ee??*jkdUwLhNh$=XqMyp! zFFf1>24m0U;*mW@nmW4|$8~=SS6fEdTJLCeDVT_;ULZw*i;|ULFE1ZK= zl=6@Yb6D5oTKSDHFbhp=pfe^)mI}z(m%4cFU0t4Sbmhkp;o>Ik>{vQf8IKs=o~)c) zhV8o^*UTBt7OUegZKx?YAI(Q+X_;RDe2?p>s4hZ@{fS>;eWx35et(t!+{O=XP1CbI z+HpK{y?uGQ*Kcg8PXB zDuu^^k3UC!>Gk9g_rD2Wx)~zKv5D%vrtQyAQZa6of%{A~Eg}tDbKaM|S<~tSq}&vo z$z;{DXsO}PGBEXIUGrlTG11MP#9);-#xW_0E>{0~&*My#075M!BA@sPQZ7)a4H`~u z`{I1Wk7xx-ic*M2WC{Y|&=J%fCB~26D0$_z%aFzFS#*U5#2m%MFDcvp)JsuT^u~E* zE7U}ui%U>Y^SS9bsIXm7SwKJn>Uh90W+z&mkP4Kl!YI99>DAG|RSR0AIG>)}(KOdf z5-8b(BvJeFm8S+t5&fy)J@SmcEgYn2P+msSe=Q?q3_pmUt5}Zwp18$Zb3damrmr1b z{#p5h=2vU{aeQ3MD3L&eIIpOW2_ICzemp|;*s{~VLuR(G!_zIbRDxW50rZkTnY3us z2FW+6%)|%D`G%tRk$!BKU#$JbxHE_e({bkSHX#K2s41v{G1dRMkgZggxKuW;2LQx)@otEixx;sRx*Sy;v%F!>QG}yz+sjqd9(SO`AjbSa_ZpkRE>~x zb%aFT2*{NsiQhiHPuHn95kSSNd3`tjI)5#r8LfXZs@g*fCSuE#2QsJ?iPb(A& z2cuIt&QWV%u6jONfqrKN=E@|qEULmN#Tt&U?7h_=F%Q?kVNs3OYzUXbZhsQ9fm|?< z**;SF{rZ54$K_<2)Dr%*nuSx;w^WqG;|dLGkg_h`mq#9?0cC$M8V^#K7dX-A5v3bx zdwoga3Vq}#!KTD>9C>&vb#j%9R4$%x*WM0smviPO4p>abNk`<-dqstJyG^5}IfH#v zRm`^gqZNTlLrGXRe)~Qm7sL3SY~mc}I?rR4@#fy4fDyMWGx@KtmnJ$d+7Uh7MS5c} z)(~8-8^;1h(-bXe2B=hYvnO#SODIxR0a!*)YjlEaay8}(tTNy)FVwB^BI4ilX3mtt zIebS7m?MMeV{+R}9(5NKS4IV@_7v&!6d2w-u4L1kx>wo5% zw@3n{!l;RzN!Yb+M8!ff)risUS~FRUe!G|>A~CnU;=oWbG=Y$p@Qxg6J?QdN{}YvF zu4&d2%jV08B7ygPh3(#iEA((4NiT!X;evZlnf|doW3wRCCMrSuyf@^#9HXlgK!Pg@ z1Ad#5f)d|I3Ok`^s7`WW(%)!0r-h18Gy-6L*Bs#CxwS>ZVzBzX&M9z9&TAR!%)|!h*l~#e6tt3H0RbG%qlU`)7A_Khjoo`sAZHewWtS_ z?b2X-@0{NSq-I>cuFx}#uX)bZ#LRQ_(RptzHyrlfm?{+#_SHvybn-@6|26j-_53bp z8a)e%e+ZaA0aMd_`nVP>Fl0zRG4NfPb13A9@Hc!M}6~Ez+!cyD{v@YRj0sOA(HPViIYdzb?d!x!^1&Qo}f2`{q2g2^y|yBzw0>Pprm@u5LfTm5fJn_np|vJHe56W zd>h0t(UM2*$K80KzP>#eD2HdTw^{MpAVN``Uw#AdC?#q-@NAQ))9z^LA@kJIi9?n)BS2jrs1U&WoO6b zeIbI>{+aNB&R2|cGT{82dbD_xp_=J>A)YivMH$uOS#I=+U97vBc zo*C6&;mM=Y4)%u)WIO7}2b)S&m4d~ZKDXWkE?=~()rT;s&;d{eJf?XHQqVHExKB6K zN{0O&2HOaC7qL-i8Zg63QE~y$UZwn$>>l6Q6l2b(?s`~Ue^cAv=I9p07r4{~D8i`_ z=}3cT2u(7z@JL#>zOv4g;)uMyg&z6TUTI>T0?uJ-i*3$q0|IVMfSWvx-UU|wAd-s* zC1rve8L85*IUwLWGJGqB_$Uevvtybe0Vs@7p=bSD9}AwK$C60K;hGj|ekb&$(E5$i zrl3oMwyhcdr%i(OmtgnnDc+ff+r~mPST%0%9CpLrW~YnAcd=Bko!lklA}5cS4p*5& zP6#tqZ8kobD{Mzf_Zzo8qzvAZ1y&5&JikA`Z;yJT$^di7PTQ1m_$ZMNUzneE_n<#? zNDulr8gaO2^9?VwQGRn=$ad2+P>aV6N)NGH21q@g`_&~AhW;QgoNiEzjba8{z2PUb zb499S&uQ49Vxia#z-4Go$DRxPaeA=kIHP7Qy$JExW%?RTGwT<@;E0IcE=~)M_hS}} zK=pu_RTM|Kh$yaUner|k%D81)b6#4K|E3rPkj z$EnWFv@-nm=MbO$^yg?rVtk;N6{=&ibA9C;U{V!I!?);-srJ=W!1eKGrPk5Sv6qAs zd2VZ;{D|}NfOz$CQs-}eUI49{wOO8>3kGR}TDxO={)5}CM1$Xxu4-h0K=h2rN1^$P@bz;af>XZ!#iATdd2mbl$M0vMnOPQxh*SToy)D6k@iff#{`jU zpQGYZ#IPVj7Ap^EU~iF2L?q!))6%^oMV%6}zGGbz!HfFU*_=MyBS0U?C@;4Dey+jF zO1(tqgF4&r zsbfJeJJ~}&;th+y>7{gA9hz2A97zz`*AP*8Vaj9K)LH)Eah(p! zhjQzE1nGNUMwM#0BZzZbzpkT2LGZW}$y?#eRs2rw6|>({$eIH*i633>#sHPV@wH56 zb+g6H>>(ucE@I&RsgJADV-Agqn}cH3t5gA#KlV-sS}IinGn;1n#9vPbVJS61yctZp z0$Esd3>UGNXFSiplu^W{byD`%{H-XIY3v3jBQCiPEdDAC5_hgRl@z7GJ8xN`)S*@6#I4{`Z z1rJQk(D*FndL;dUd5^1~6;A_}1w53dfYZPaeivW(4M)f=9_l1mw7!(%6lNEq;xAOF zO$tJNY%oo9B`fusy`DVpRI(_3h%<4I2oz@o8xh0UJw7BtPm&~Lrj*%~U>A9mqlc$7 zYrzpbCO`n*Z8TcPKc>h7y7|#GT%JN~KS$gs%pW6fuku1 zv^P~Cwa7U}|DSUU90>1FIkx)8nAlG?z-loM{W%95qya|K8v@5X{$+m64bHk*lap~2a8XmgAl`eK z;t=cXp%y&_1?r);vH}txaGQiinoP*yHV76daw3K4 z!jN$Jac9u%p}WgsTCCehL+`zz*CzuWV!Y>^M%Y=QOnw8N-}rsUHf9U9J-_RB!5Fw5 z59y!|F;^zZg~*>;+}V5m0c=(qy{M$5lieOr$NE#^`fA^?tFC8ZTb!>^v1eK(i%Hgy z?2K9H6w2nlRQJfeb;ch6zrBIXcEW(|jk`*T_+tERy=IG>?F|maV=)YRd81t&+$-iu zH5f40W5idnV)&meRGC<$pR6<;?!Dd$ywTV2nL3i-V4?ulFJ3z~FI>;-cD>%5eN}_f zSsB49$)(y&2QD`PB%@WPc*hJ5Gqc`%a=wEJ)XOPxrDrV{oh+<#=oLQz^u#<_%JS& zY@7CW>2~s{A85!jKmGWz2h}Z&NrxZOZj+dh0baMZb;k`=&T8xn!56Gn^w9~FMTh62TnLwyykkE! zIawcYBA<*gv=X6Rimr!c>U)QN64Y}qsY=vnz-*e%c1p!?<_5_NV9${!Igq`P_%ItR$rs2~1>X8MLFr^&a$pUQ|sPLtp zdV!3%8m7jJ2cJQB87%}Vf|>n^qQdp@-Wz{gF7YP{f5v+Yp|AUcz4eJfM1lIuQgH{$ zoil^w(4Aa;mLDM2Xzom~496z6Q}tR*4Dj>-5@8X@oqIHo9i3^B#CG}C^nG$LQa;BV ziVX+!$3eYw*zROZsNhXG)w~Jk2&Q0M1D&J{0y5Va;t0AGh)A)%x62=2Arzc-q27wP z+Kga7p9F$khZmMHAfM>Q?9VY&DiQ?FN;VoHAw5&uQ)TO%kLDB$XEN}3dkW0GoIRsW zZ}J2;DLDNe7pk}T+F-xq=e_j~nQ(jxi%ta-CRjJ9+v~z^tJmmPhzb_<%htnT*_@SA zvNw;W9gp_dc5>orjjeOr_x-%Y?F`^0+T-yC4i0JntFAeZwD+ZAc{o^TYuPDOC9~Dz zk@h_$6C$t4viR=RPNHsO-7)aIn_`pu)}JI-X-%(TCjB;fIm~z+%(-=rMK9MV?z5KF zC0D4Ut%ck{cy`p8?#1yQ$6v;oEhp@qKgYh0JKy_|7 zoG3!IrV(Ye_MP=bV|qF`X@jppy~^H@`U+_+&m^!Qzm`oDaZ7Xd3=OdAS!IQe!Wq(O(oulxcX58rDcT+$uAE)ksif|h^(E8|Tqb3%# z3C!oR01K?cU1X`{%J|&FhQ7!4XBs)k4zn+rCaHS9L!aE(dDHn=dQPC`u2CYkZwO9p!^qiD6EewAFspE6qQ zSZj5O%NRX$a3RNTD#|P)SyAGP$xzOJ9%7ELg)NX(g!Q?`eQx>EZpJ0?L*yv+oHAO^ zQnrCbERV=}ybwUS<+Fa)gz7ko|{3M&%sM?sgqd z$?am*@k)s664#n#m6r*6l?R@Re7K)iL_~b9(om(#W2eJF^TS|gikhCVKCCVg*-O*& zrzRF!a4R~$CQSI;zpQXJH|3yxWB)BMrUrKt+j(Sul)!#aV^`>r?}iC&0lx#tsb^i= z7uU})EQt?6vTwD?Yy7msM8u8^^g0H(MhWdT_vB1rDwv~W(CuWnZ9T?+B8*zi9vxv z_gNbbat~K~$@ydI4;vg~uPr}eZ<7(g!1Hs!tupI#<-jU~-3)8WcF)tKtEf)CF%IhCkyrP@h_`6kf z1n`H)8cmvO*j0SURrWc3)^iWJgt?rm$@sDP*Lk;=R%#rh_CvMi{v=WOT}m zB#{?miv{>ee`t4eXvAx8v;sf<6<>sZHjY8jSEKf4YYnrTU9$S`$?Y zXp@Ka`~g+GO5n>~?t5;BO)FuBGA2Ly^hjT6bc9R-?4hjnv0LF^p(um$U~rqv@M-Bg z6^M=UU+#uwg0xOV3p!oCN2S{KQQU~vO#kw=(rYsF(9`3sb1@>%(j!V26_H;j*~6dh zIhIRnw|++*Lw=GeN)`_)7%|}m^X;WEtx8kvnhBjnXa(%jw!J{XWUmrBoTneUm84Jb zwmNT^nby)}aX!Gergt_KwUz3D%#Ro3d%eo#ZlX^Cy%fx?Vi<&Hhl{GV?v&T_sSaPx z$(pcZLyzt_HN)QNj=)(Mtq5K`e~*My$^>6y8ZtePr!};zw3mt%-rX>PvW+J_z7P`H z4J})BT|iLA3nkLzJ)T_J6Xj|uPxVDPVMJch@D)c|)oF3!EZ43b`YFY(Qv_>derQ50 zQ`7xvYMuPw7@PMiHiWCAv`llN<;o#dlZ$Z}$tWB;D8h6*cOqeLQgGcJ73pT0N-xip z{xhgqGQ|fQx+ST8t1)L1yuMN)iYG0~xfMV)Xo;h*LO-|;qZ6s!bC^ViFObPEqi_*w zGBK~4VpLrzt(3$pJ)P0AZp{jZzhb)Bad6eo5K(j;NnR>!s5}lWLcEDz(;9%byTRqT z=M?~ji=(CIUp1Mu$#Dfka`p6GYUpe&SdTeyJ$t@?s-z8^tA9vo;)+sMcOHHNQT}#L zP9lgsDYi*vo&2JnQ9KA7G;s6!dzUkb>Q-=G*+IpQm`+XxRvaHgRER{qkt?o4qt(q3*L^)vn3xU=-b)@MSI^yC9o6)!BFvTH zcT@5v_Y99{c18ju$HQ87j443Uz5TPtPiG@1v@$LwpsH~+;-5YZ|BYmTgKt-KT8uXC zbv)%ZmFBeIyiou<9&a-Hbd6sml)Yy*z{y@m#Qdk9e+xBIiZ}ih%cg$YUttFct`Wi* zSJR)P@o4_17<%I$fy0!uq=#C9@Lv)^9g#OakPZPFU=jJ}r0(AYNn1S9 zH-Z=ouIQo9e@UX|P~S+HbDz5x4gS&PUw^Cx^A`u}Ukbsd;(1TAns8C|EoeDNkvX@c z)*qosNJ_pDJmQ90GfYl0OYls3`|WWdUK!q01)l19A7hTJ?CoPxd6P-0ui)>nz~$l?6|XilZtSOp|0~;W+Aa^Uw#)N80ocAx_Af!@@4ml z512|a*3|jg5U<@TeeL?~+l>PUhX?lx-(DDB-=_O}6CHmn`uAD)2&0^dPdfg~nRn@#qDKJgscTtIf+1%VrMad@f_J~_{1-VHBkuSFr zD*;v$vtirsX_&th&Y07_VWGlqp80S-V@Q z-B9Ud%^pK3J@MAi{qkwAxZznE#WnHB@pF=P8hu~ioGJ_=NGlz5ty(NQ78)7J6u{?( z{zF?`oyaA~EC*YAyEJeu=26_8{@KR;YX~+|tS3_z(hIn^+2t3^u8VlUeCWC^3>A1K zO?y<2_h^ko+w$=b8S0`CNNki{q~qdVb4oqEXm0VKqST#N$B|x!^cBG}xM@>}jxoCh zC08|$Ijlx~J-1;#*d-Sg3rx6QDcmPUy_DXp3Q|kj2pGBy$7HlOMXFB_P zliRny?^Wzd(#D|;L_q^FS*}Bnq9Z}Q$uMW+sc$EFk2_^;FO9J5dnL3qY)Y=4NB5_> zMosDHeqt^5*Dx$c=eIkHLMZw|veE8Y{<&89FK?IcXe9lxBNy9-Rbb(vrkyWR&8zur zBpj0w*blSI2U(H@NJ%9Yo$40K#NT@v~}HHKnZVPWU?Bph?ZX zaXk6DpNR(exRjpY;H9gylG1kJ;RH-$gO%18eS1G%MDkA81F66b(i%#5<;mUqe9ORX z1SDZ55Mc05#+^u^qHS?tk}UGW=yuaaF(PZrEnim7+bsaxwbmrfigcvuQ~E%UTbb0? z^uEf@(0f+hB7C#;v5HaJ^VR<4&$|w0l|oNn%2t8?zU|QrF@PQN;JjnmYW8Q-*G`XP z*x8dq-g4U=m*}aYAt{Y>;CvLOfuiKN%Gm$;}4W88jlul;ckbBa>5vL1`?;L6?H0|EhOm9_N5Gn z4M4n8TKVl}b3#d3vkEb=!Be2qRB*#UMn>##2-9FRkHC%GopC6*G&&c|Ara|7QjU@y zY>>d8uy28Q=ybino=|$z7{@(&J<7aSH_CA}wFE4^%cT}{_pXVp&LHfgKRKLAb0{4< zm~!ysG}vz)v-4&xJ>qFIi8r-k)XaiOf4lvv=AE5Xx36L!5n07((mO&EI)=X*>wDt9 zs7zAIq*h<=cw$!t|I9e_Sa8jASLuH9>PX|D^J2^$9YyL?(Yt)pUiAI7oXHz#_N#&;fF?X zDdik7rKD4Ugj6uhuf*pzSGWTlX;HoFgBis)o+bybzP`R#|F;|N7tG8Jd$sai;=+MSZID&|e^t6pd2Ki_SJUq8d3kk{elz=#jeF)g= zub_D6sDFKP9u2R2a6sPXGN;1z-IR&An*ZT!2eMq|ei8Lbsz2+w$#ATcffi8uz3wAv zrGN+DOC7njtP;%ctOjleC)+%bmE4;xyOE4P%*XR7OKUD$49T-$u;x0;3ChV@r%em_ zXyxZldVAL|y)1OaWS17R9e7dzizLqSgZEk&|WV}hz%!FmqMmsKMv>a+((TvkN zny>xb`gCTt&}Nd7Ai_JJ*eTYvbJoFEX|iW*g>tU9d7tz<=i+&fNlMEx_r(|hBrQV% z6Jvk>qFBU%N3q1g7<+ttmu=hG-Q7L7 z2Z!Jq+=IJoRXe+1Z4FwUJkgkten)s!jO7uwzI0*u9bXbMS#TzSvv)yf0dz+ov{GFA7rW4#a zjA~kx>_d0oCLDx=Qp@Ra7#97d+0l!dB;O5SuWb)2@`{!{J6djV6a(W^GymEo(KU_rk5{Lj!gA8WTNO-H~U^>XlJ<_fv1# z5sX!AbC8<1ojj=CsNclS1yBr7pUVeJkqT1LPP6gkRY(G zhI+9n_fMPM&Qh+utA20{0xd`|J6n#ui|PO|{}2{-;=bpY=Z1gj1>;vS3bQP)Nv90- z-{CRnVLEptEeM(Q;+3u#JsTG3STe`&HVad*3bCs~vw=oU*BE14dme`$ zC%i&^sC={f8seOM!lQ+YucKdgS~=358&b~Z>sG4{paZYVv;=97=?C)|7E&xI()rM$ z_>7juE@zTm4N3Yv#~#15xLYE9M1}%{F|Ou^3SE?FM<}#x?7^4yIvTNm&q;LN*;ukT zJ<*)&c`N7DhR}{OrBGh; z7khMJM(l0$au6XFuBGcs<26kLW1X-CAD+VcB2W6mQMVeK?aE=~n(Dw{#cV^t7eu~A z*ppAp$IbacZ{r{kyOTOo&Wq$0?Xps>Pq)+1RA#wDS=StrBt)5ZPPYTpGz~>Xe!Fa4 zi;lz4I-zSwrn!XZ44)o*2k3s}Qd6-`J`=AM*~+qL8mRjO^z&eLsfe@pA5g))=k+G& z=TSz0HNlc2|I5;knM*Gix}#Q&w~J+~V5$8TSyS{Y=D_n5VS|uP0?K@X=1mWR?N!8D zsHeNVdnMN4?*|}w82sa2R^FIGXb0zy26QUGgxPLH-NHK|MBf>@Aa~g_f9f%KvX=ey z{=V(M`3{(+ilag(DhW<|4tf5*}}&HBSIo?anR)fVgwV5uPgqToc12&;+{rZKoX|+ zSQ{%7`O9k5Rn3l_d|BK29N&CvG8Qa^Oudu17&;o?=e4GDmA?i)CYmSNiX~f5%wAbI zp&Ua{{(w8*85mj-r=-NaqM$A*<`28mLnksANcTZpu4OFuG1NN{fgNu^cq&XIY;&75 zDj1k;1Ax84MbbD=2MWudQf}a(&l2aP%p%_4`WFHF`leV=Zo!t>u3j9IFu47;lLWGJl=VafT8!EZyxLDP#0S2wH*D-a*G*RB^$f%= zs+P9@+?3Gy`e!~k*tRewm9XhYRZR-6ZH?NNU+*|iZdAhKEU<{cRCmT{!3zubE#63r zIsKE7?@g5Oq0k6CSqe!TYJzWf3~WeCl_9r`u-AbaPNjQJ#vd7Jplqin^z1JUr^6cn zptE7^hJ4M1mE&j1{qx*%chU50l#=24^%#a7f1%!pVoE9ZrHbVrN&7~r&z3}L?|TGL zGJ0?k9j7d0m%!ovP$HVvzSFa4HntK=pPu(4r`PtiSi86074;Ytmw8SI7iFkgpB52n zmMaGOhK8^uW+WP105bfe(j+08Kjp9j_|$ypa*+G5RJ3=VsUkC4u!M!3nwO^NR+4osY2XJh|;k;DQy3DpqR56D{UAmFfM` z!cey2z9jOF$I~-@ql!gKmcTqQ;BNZO3Hy2j4a$jw zOPd|9In2+v=SB3SHa6;br>^Dw)JOXK!A|(smzIM+9Jmiv2JA9$zW3vM;NTsZ(cLu% zEQT*l4Aqg^XAcJ#A`}XEFVR_sGxa{KRgu@(&=WCLC%Y1>C-I?RV*)BrV7tE)>UZ%rBliN8?mPJ$) zrRr=xZ@{bQY2g>PBHUKFQM(yTQza%llDh)M%!g>jw^xl8k2jU7O7@m!Ir0+42X)`) zeN;r`btlf%3U?#)mBP^b{8wu>Zg9&z2l79`jfU}g{$|?OJ0yEInsP57U-04Qm@~)A z(d24S6YW0y8dT`&b(VLeN1un7|6NR|;i9YGb*g-Zn^6l<@1}1f|N7%pCjRV`>NiP$ z2ZWJHqt0^YF0EHBE3qxT5f5J5nML(NI}`(f4WA=Y8V``r?Wgm?!mAh4*G97DJQa5Q zXEG5$jud$N`bcZ2;?bGZz>s_LI9(5viM6wZ>qA9uF>$Rs6@jiba=b%jjpneveIC71L}0F5;Jvp4&S`Z21Z!oTsqF zhL>fj4IIYS(@)^UhgI(Esft}I-jg-z`P_DFob`t9_VBRg#&q{(YM-u}C@_*J}EJGs8Jr%a);S-3Lr+kXV}6a-Gk^W_0pR z@Edk08JqV0_=CZ7S@P~3*=81W0LIGJm$>kVsbdMzSP_7l!dWJA@|=$x6NkE!`F!hg zkWvnO7?BGr@d2cNRZWd!Pcv;Gg8A;FK#j4G&^-=>U8Kli&fZEO{d=#W_Gh9F#D547 z+|A_H?})V|d%LosmuDC0W=#P95VA+yCDd58!`Rz z8VF@@5m=`4Rrm9`7|7QtD%7M0TzApu-|DJvbaN2J-pdf$D!BikE{k1ZAm_e9hDal1 zBAs+9>p&xNuPdQG#g{zYOm49C<4H3+gQD<#7ZkcfNh_WyGF3xy2M8*8Sp?Zw#OD!t zkF`h>$9Smoz9q`L6t6ZN+T>k}#?;#ZF!`*mUoKj>UT?tK=n&P?%(#46ofI091D#s( z>_01mJUI}Nnq2+I?n}V^!xRe#Zh&XqWj(rjlVrYLkWt&gv2;Kx=5! z`HlIZRHUF6H2U~(8(IXfFN>er6H4(c#}hW-Oa~p9b2X9)qT=2~wp@!hYsH12Th>_? zS3laCFngHJRbBPh+FgMVr7wuiwV7S^2vL&}6O5yw`3gUWLF3D9YLf|5ooKVD_Ux5C zx0TUmQ3r-YAK0M(mYWi8hpmGG>TRYqNkpt7EC<}*>Y%Rne>Z=cwnV;?qR4TMC)`<- zUT0ES+mO)eQ(q)Fqq1}d^^$zgzRwulUQjt2YPLWLQzpQDcvsAM* z>#Jqr^Fek{5z%;3cv(gmHogkFR}?DFR0%b80u?tUh2#Av$({Q65eDK{0DDdb?c$eK z&s{b`>F3~khLPEuuFh07(7p7tIU_;d-rkbT^T;enpXw_Qi*`@A#(Fk%-}TRQg00g|A$SIf9HT?;QGoH^Mk zj<4TZ_ElvwA4Z4LE{0rR(_j`843s?#8m>=|X`S!Ci?V~!C zF0RL@1Kl~QOAwzrJYG4@5~;jrnZp+-_0BEQA77u{zEcbv?oPh~5lG?9tmll?whbXp z%$q*im|9`DXI_8hDv284@laT)YQFvPf_8R4Z!k&@s}pXDW|y_ro>=6&rh6LVR;@k2 z=M*j?XX=xROjXbS{o%4)wY8e*D=*C}Y2WKWWBy8`8@-3eY0iXBg|uw=3=Z3ciJV`z z+#V>ITxD&ML0KJq>@Q zHqsHJLF-t|dhENk!Mwua(W90Ib!jyfkThhd#*3aDqQrs#zkpNRsuSc&Gnd9T%xWdn z+K`U-zdl#g4kf=Lic+a_PBIhq|Z9zQvSibH?Uv|LxO%R*6fvGF% zvp;oKQ6NAa!&+cRT5{W@0TQ0YEb`iDpTm6HcW$;`(-;IO{`i>@RN}c1?L0M@^>G+4 z%M?ew>m%Uydg0sh~A&QbZepwgw= z!Es3bk|b;4!~3MAZs_|&Ta}62?8_rmpd}GwS&g-bockf%`k9#P4JzS%GLz;#s0aNx zR!+Zyu6y`e&I#<9fUPHgHSQ^n{IV?b;x>`+;Mv>@=&400s)friRKn*2YcygC8*Z<6 zC<|{P%AFeG`(5WRpZ5o0PNRlk%Rr66S;iK3u2;q#%)$dOgP+!hMPQm|*-_^hd|n0vpV*v&2$-i{V^gWcWlPBQYt+1arojG^Ch07;F$*grzVa)zr88@` z1KXD9+}hm*^$KKBE~YyYtr5l*t@$Di$LilU(n`ie>Nw?sMJ+Fm zoC*0H$<}F0jf|UxB;xa7^=`DwnQlZ}N6vy|go#(c?8mK~SXzdsUPy}^6cQ#gH=K!o zesS*gzRs>#qn(4@YvWMZRUKAq?Xkx?Hq3T=dHBMT09|J_Py9hC5>DsGfT~*t!DV7kl|E3I1Nw zoOKr%4yfq@uRKCsE?XPhFpS2+7HLP{2CuR5EsIAN1{)*u8?(de_-@tGg%?z*%U5EX zCm1mYbTxcuo&%RnfEWz+O?-8%4ya`>2K1PrzCFg-7surh?zomgNis*YR^fcj%?wdS zilJb$tJuAkeX3PaHd1|ST7UZM&R>l;Z0GIJs>=*T z!)BgA1eE32wM*@ni^MhuW`yQ z8AZQ%gdgX(@{&b!kL88t8Qu9KrLWaZsKAK+%xBhnP>x>c8h$o^K>3svDYH+eK-wMi=l(m_V&MTjx zi+$m?=t1@DLzb^P@1(YR0`eky7ZnkOPN(Ru!Q)Vhd(eiu}rxZcw3nKYaVQxj(F3&@a+v|1V>mhL$m3R1aHsoLI%&g;26#mnz$x0SrB1OzYi2@6Sd|!os|aIV9;#1> zn45VaFz)s4Q3`A-X~bAahAc3KPx>d56NvQGV=($wbo#~hx5hqt#H$bDJWzh7WYrch zpCGoJDMI3+td`;u7B+-?h1j@<|J9kAK}=UyGYYJq2p%8JYh{W`OX(jWelN4F-ryMk z0>ALsPdER*++g{iL2Jh(t~7Uy$K&ez(MDI-f9QY=`{%ne4wpmKrFOR(7UQwLoAdSy ztDNWab`}1gr}Rd?kH36b4B9l0=POD)nrxORYyRA2TsqH{s=1e94_|E!k3pyx8XQ-b zeTWuP#!JKTR1iAyQSa52{Ik|MHNx*!XyY3k$(bpl3 z5c%vu?1F1yC_(0gOm)-fhC~x4(CvsE-`pK#(D;MO$C#5l5cH#YDn~0v1JK$>UK{$H zJ4>#o9aH%6;aQztTttX0iYYKaw10p8Y5+l{T%m8A?eRC0aN2th%t|5AIYYPywZ0(z z_d8;TT&n_TbUKdb+}v9V<(Jj7o-gMYq_$N1-fWmgv~-{qiJmyn0*jE{PqKr)(9Quv zeNiFxPZnln0|IDWj)UZE8vsT_6=5ZzciMJ%9!M2ge?mVG&hOkYX5N2bx-p9I4B8!W zHlpY5(nQHw1WxejfK0e$CAehb({yN4g5tN#lq_G~uC-iCQO}%`xIU4lNLQL|6XhyS z$NUqFHCcK;PVSK1xN5EH-6(s0yK~RWU}qN!#E(9A1nn9`0pyL1Hvr1w1I$hCJ;vuY z>J{u8S$Rw5X}(^Bc>>H63pi`y&xzRdhg~%~MFxLy6VM&k!2+MxUDonc<)Jd|gibk4 zL)^)}zjipoog@yk-Gw*@yH2U(I7gl1y95`a_j^n@3*!S52yNRKoZrg8sx7n>6v_wl za{VfY{ljxI$Wtw1Gr+l}Ma688tkt)0yqyO6Bn`x{!L$}8wZ z=T7zwxfb6##+bKPDF{k-|IPo;%CzCCMw*+=Z=jjSdxTxvgL%igw5l40 zB3|s7+Jc(I#dc3`^xwy)XJ_rtwAGI6YVlXoM1Awn)1Y&~D9~mG7^V_23S?rCUDG-s zgu|PB=qaN<(&X8lZ-&XK$0W;rmoH2LR8}<5YsmHV8v_a63<)iywf5Cs%VgCeTZE%= zCQ@=0L617Z0MF7UaRK_o6`%b#c064=xE~3tQAe7q9w5$Kkb?%;h}FNAG|R{|>w#(e z2WB9CS+A3QHnQ!@9i61tCLaxDZs9NWsg7gCW>u{|W==Q3sKIb!D>_^CG? zIu%FN{QyZ0x<-$6BTEx!5rmW#{abdv&6q;mJA~(Yw~x;jYZJI1bGuisq!>3V1l_Z}|_s5%)q*kk~2TvFj-@~PETw7Gi>oc>JSoA5H} zf{1&9++185hy{Ksa@#H!35Y~P2tVsy7ea2;07&>j)Rfy@kUHoTpPi?thr?l0^iK#b zba7f5{^MEmMiTm^+sQ(e*&$RKX45XkXhp~YIr!8M0!~|5ps^Hgmh?w|l zGZ4hEv?-R8E9BMGarKuGf??QbXb5A?xJ9Rok6_Rm@Sjz89B%)9R zBOznzKgg2fa}m~=B~zC?lxs1PdCQk!6n+>ZS46IG*~kO98o%g2aMB?oc(-V5UJbx3 zC(n?PD*NV?{46uvX>zb^QTcrCqT~r&&F9t?^MY8AZRU_?4pJlE%OWGM9gIoo_+Eed zn!iz_d2ibI6QLNy9xl(rt@t}3u);5JhOJY;Of+`Te)xXD7Ua+dty*e5-Bh9vdN;A! zvn+GaRmx&)>1-v{&#_ey$-F}~puxp9KKyY*xk`*bH(^b1(i4gs%s$zGli>xx>)_Gr zf9%{xxE0eQ6z-$8!ghb~D-mHMtMsOK>L~kM$gPB=re`uXszYqm*yX2yP(|XMHe(wi zdHudD0OGAE--(@^@rqwhqEu29i=!+xmZGuhw}^s^p2q)Nw`jpsN2I_cH}{X|?X%Ts z1dS%n(F{h>z2VQJmji*+>$^^1XvIud=cn%erS3X&$=GAI^F=nF2AU#qCx}<6{up^y z{80aNMfcZ1;iya!i20)z!@0lz#~r*2iEW+dkz@AhZo~_Isc{X%$AdF&BNd@%c}@W& zj~!=}5at~Gn($*cDCgDZF_APCNs-TtMHjQhFBZk7yooHFM0ThaQ>DQGyE@w`^;iJ9 z6Q9h=2dsP}k@g{A`axS2eoub3- zH~S-A?~{3i%4k*H(!w!;-s!t=bLPj#Ab8>>X5OF-Ba;HS7=%&}q%KE&^^@5x&8LNQ z(TK#)ld#txtFR{?5;ZxYIftA{RM--@0qiTb*oT28GE{QmPl$0MM=yRz-@nHQ9qAKE zaMgCu6*>{{4^8`;acbmup5D1&NMDOC4#yj%vBG^6x#&KAQZ%sw7Ni?P!2S9|#bno? zmV@JmKBA4A7@^{guZmIO0Sg{z1a!Fcq-xHh4K!&r-zfR)=8V18G2lsO+&D}TXwwGk zL`8+}Ru5SlEDxfHFAvfOoAZ_0say>I&=7IgB<@@F2Gd4)MBKrPMcgB}bLLo}l)80V zRYpU_x=vh9KY0uR0Vgw33_&NqW9|YT2i@mq9)myh3xqsvhhN?0Xen;D3|%`!eVAyE z?ksrpI__a~Wuk(Lvscs6ukFyI6Bghsj!u1bB?X_Gbg4pFj06*nkqhbNCd_a z|Gg%{Y}ra3eD=Q=E^b7e8YA-VO)0;FAuLD|A68xG8+_aAha(B@Tz|jGUr)h!H zzvG2~<}W`MJhee>O264@%6iT_tD}HB4eA0;dq=97CvDo~ajZ8CLU{hen+~tG3_<%aJau87+ z^vw5Co&rS&I1ieejt9Tqf+RYKdxhtS&AL|L(aUz5Kc8CLD!=HXYDFQekKO`l3A{+H8|^s;*zjvMJt zL?ZB$>o?U{BB{@f8rDa)Kx8ENT*1mW=P@$0o+9uLFcfNY=SL~oud!Hdpag?it%d+C zE9gjiyz|r_j%7BIKr_?gxFhM+uvBlRK)|D1h}AzMN0;3oK|%H1e9sJmllia6W-Q^c zTESKpBs`R2f?))7P8I6@3^eS2Z?yb#+BWnBPDn-iW3-Ki$MlknDG}Hk{Xz6f6W}?d z`icYe-w|nix@&5A27|`^Q4>Qxm9Gm|Ttv5FdMEMw=P+q+yj2p1=zRrmnSGRq7-#sw zdtl^Ql0o9~JJpMrPB^M#*fGMo;vPl_19ge7?-sRO$v?cj@`pI;XV1B#wsPec=W4KF zPhD)PNYkHx6`>%7Ql#Q^5c(S(>YTTKU11gQ{iEg|>oK((s7?Hk6Y^JCk;~Y(ePR$6 zEDRzH!mQ#7STdVU+%;LZ>wZLKtf{>KnE|KaBUy_@BX28uL(j`k%}_nztJdidx%pkN z$f0+QG55CQ*0HZ5S?l%mdq%dv4=4(;(V#jB#fQEmRFg9IH(p zF9d0k9|DrI{yjsMnfK5p#g1s9x=N|_&uD#ZOWsawJ&(yLQliEz^$|*4`0a;$U&OvJ zQUPSQtdG(Bo1BJVkDw+H89G4h?^sU93$@S1OFDbNDiWhu?;fIqxaA|(4Tme2MrI-i zJ-$$Vmy2_8F^^sOb&|7a2vj>^%V1x#x|~tqHG6 zAVuY88J`Ao-m;+sDt9pHdC|p@0$0O{uGInH--%MtnVb}CWyPa5K2!UCF?WVd)NJ&A zhYJ~M+5RxTJGFrcdh`qVuw`e}o5{|;z!qQdFpwvrFI&_8R7MkQKzeP|)C4G#Jc9jCy>)N8>R`!5tB zdKB@GvIyJlx3TupGMYEGeA|5|*)g}}^>R~wd7l9XmxI=-LOn!>*4KQuIk%GZ7!-`R zgd^p-d|r$XP)5H^=RI=Vu<}=)t@1D%ibalo_quVsdPhQQ8Pfv8?Z4We#HQEb^zIg( z*SSmrMLz;FuO|F1XJ*hpEO@{by!Prquq#PTiYrNV?S|60vRd)iA-e_g5&j2n-I`83 z{jWat8FtyFk-f*D`26XTsUJ34wnHI-6DgGvtTP9co+X6RKdh3XzbsoX-`|Y2rYJDv z_O)8A%2#H6GTR3jVqG1KbT_~%^@e@7KdeL?a9LZ`gK5SSHB+sm{+5|OdESDDkG=oc z2yf!+&R#?5y0-BLRQO@0i_%Pu6vuXgQyUfS%gO)Nb?z`*rT($~W`?}Qh6gT1FZ~3q zRN!g>fxJ{dL?v-f@JfilFEL8SAvg(ysaYFW1Q-R(x{L* z8VUns0=rUrm6X?gUL@OI#w9g zVaAd-G-&|dw<&AddHEYoB}7H=C?Q{9n22PXiA}BB5S~P?rdNFLX|H15mH9v>Xwn81 zC;Ty)oz*iIhpvDzd;Xr*E4*Gkl!SrH;@1K#neL&a-=nT}3Vit)GtrU>nL>-UICd{J zcqXTmyZS+=TlTknIEmAP4tNwL#~P8c^wwyWo)uu6gGxxIve=6SVUVdm!TMA(B~PVN z2^0U@R=+Q68zHOs!aF=95yr8Q)K`W%_(5sN{6eJWEW7ICU)Hn0OW!U!n_vN_`l6s} zvQM@a5l_HP`ORD`k0P&{!=c-%pr5Ze=wV#*>vpQk$A`-;9x)P0TFT`wa$gN%kN0=v z|7io1cs+Ugw+FydH0wP4TZM`t=c9OmNp-bhz3Gc_v?Mt&gypAe*FA|SuIi-SW zU_~%plylg|v@U(~X)!l>e)RKj#Cj)z|wht>-bRH`%p% zxQBn8;MO?(+5MozI6p8;ii=lZ56%$Iy$|2_#i>M@1c-%?h0O73jO*qA+qkY{ok~1j zFf|_w|8#NGjuBDXZ1Jw2+-xkjY{`22Rqt}jj3^*GhnpYjyC!skYibC0dXNR8^0)yz zTEQu7EiHB;3XvTEOh%^q;?k)5icy;?lNQG5opiwGpKg+j$8_23r@Q++{P)T^cEh5g zQ~%+9!(J=5Okg*HD3^^Xe0cD;jP9|hz+m~OPWsBWtFhk^c4|qQUJ?NMDq_zF`zx&J z>2f-Z;G!hV&QdoXT)yKa&&6o|@g#uF28D;Y$9~k0k;zWp(u40~w=wO8i?Q5R!O05! zLiFCLJy>QdumSU=!fWu?3ZpRoEoF+Y)s?XcthRPz9?vl$c@Bkw{}Y+bXzJh1;sD5D z*Ter&dUQ00C{3g85W^zEO2gUE7S9C3c82ak$htUCay!6o|GB+J#O~rl!7`8!2Ol|Q zlqM_wOF5H&Fo>;p%}#ym=jtJa7AOEAfG+^^+Bh-823&dferSdz-(Du#shgeyxzr_{ z?K0()i_)9}k_z6h*{n;>{9@0M&(>vU$Lce2ZP>ox{-&GQX)QUaoU3a`6{K6lE6%zbHnrP7yDrTxvZj^7+%hfaX^SLsOp zTZG$d6DD_1AnHbeJEe&2&V6WiU<&A+T2YvQ3Yl{dF@o)CY$X2}6l&ia{?vh?ANV*% zxv-1W=gRcEv3d8GT>(^qqwsMHXVt`FPWxyOSB^o!P_*{TwDj+(V8G$yUAUvh@}d>5 zDI5K-Tc5Iv;lEE+g0IAlKc?7aES^f`LQ&C&ziS=sz zquC*Yh36Vj_Z&=BcLb)g6PC`nO;^YDm6VDYeB5f-SImaWQ8zeK08oDoQ;2_>tVTdi!yF!HA&iCIs~%Sj#y%~vPs5GC_C(6|5epEp0S z@*VIP98~;VGvlxpcpld%vrdzK=3DZ)EPSt5P2F*mL@!qOf)MD$)8k0OJwKAv6p6>1 zcPTHZw4Rr#*UPoM)qGLDpVzBIS(1!J4(Dt78E?a1RAXbv#xUa>UvP!e6Mu}Zv#cd%^SY$-j92!#ZsH5$`Hl>Z6d_@OuZ-Zp}PhKtAs)#ByGVIQoCmI zB|?k5!4U0bhEC>#`)gyC9 zwt+`Ti7_K++{+yu{{#Z?a4YgU#!M#I%a9oZ28Rzh0ix3c4~%Q}Ax*?~bFC73gu>C$ z_*iNT?FZSuehrZ8<&Y1)QZ=|IczQROcp3TCPkJojZ&FPj_r6tN*~bqcx0~(7KKyKN zcR!eR6T|gsU)Z}hl?EA)r%@S_LBX8vAQ7#WZm7Y4ST>iCRG~fH{NV7sTyOk*n$@8! z_<0^~VC)nO6;k$T&a(KU9?wl)FvXl{F-OdiRuAR!Kh&?|RAiu-ex;#5!ZkscgY$1!#R^JB|j|eP=X9hNrGnlH!vQ${%G~OQL%^;#&uQ{1cQ)S`Bf*the@VKlf zH%;6Q7kkKXp9@~|nPA*Uv6+(p2-s~d%HSZWU}MqhX?mwzT{6wjPZvxx!s4>ZwW+3o z5TzKp^>_IR(YdrN3=r3d4kH#x_6b^~eMZ@s{gO38ZLUYA>S)9eu2X7T)6%-JIlH-s zMt+~Q#yZfxxWHw4{p>u;F{zo{xtlDWkoglMohXXtJjdfdKm}V_-9~Vb``L=5(_jIy z-y%Z9JZly$5b#8i z%GY!v0Yv1pkGB&}a9A`~G+4x8-J503-#bz%=Tb$^YlV#w!APcsOW&fLJXDm1 zOBA7o0q2-&Aw5=y^>e-r?nR4O^O!sOz{^O!FWuCJZi1}E`iQ+9c)fzqUxR)zc!GWa zeV(>o;g#9U{EHKbos9=m&T{Z;9Gg+`ce0eVwoIzx++0+g2MI{^c7P>O7`Z znx1Y=BtOw_CUG3J1+9>m$_EVuxr6NB+5s7uZ*qfkC+v^ga@PgnvPm1~fWGBSefTpX z55bddfbuUMDuqY!W64Xor07QLGsYinJn+xohuij!S?d`JV)67TKiSF;N#FK$TMA95 zIvC~dskZ^rpQ2kDE5fyBkH&P&-}WV1BcLuT`~LYe2zt|vuur;Inf zqbK2gQ6>HU+Hcc{(HjsC0y_bNtF3Je^&fAwlk}SHs>ljC_AlZy^DwS14)LINg0Pfj z5easO9J3o<1MN~#Zo9(7v`a*MFt%#L$O73@BFK<3bj6U#464%!Gu+@*LTL>{*rdHL zKM}njI*bhk<8SUB{2Kk6I&ZD$sfn=XzI3G))og}(2p|gm`(m1@43Nwr1uA40@n(BB zv_zP)EW!xo8~)WudwA7HSu6|1uHNEOkeb3x7nZV(4+TyCFtDUxbwP+4R)`QjRa@B# zw&(fw9?V}B_!#l({ark7Y?ynHHDwKrYPq0esevXIL}qbG!5ZF1V1zGFEk$P7-na%d zC>Uy~X$&Pe9*`(i0nVqn{tlL8D>}Ir2&fp0 zt!4LlpdT@yAClJG=jg;~2@el&FA|~K&e;2iTgh*I7VJYik39%_Q~xBLsmkGqD=KO| zo2+L3;RSTo$&bni7a`VE;u}ssIExfGS6Fg>fax$!>1_$(2OgB>3W?&xB)li2oNY5} zdYZ)jm9;$jh)Z&h)YHsYQLddWJM{my_@VxYHK~@Q%6VCp@d<;bmK@?|ZxV`iJhkK$ z%zUAoBB|MTN$W%pOI54fiJB=f>t@i+mi(!L^&`2RrNGMrlM6(uf=~=Gh!~; zlj=s?J0;)i0#tE}lZGv#biKVt(3AvK1ZSJBCU}cGuryYJmG|ia(Dx(+z;03;pW;RQ zaA&c{aqber$)opy?rFBEsJn?NH|&P%tRU`Ef78qF&dOKLMqULO#aC7Z+PZ~KK8Z=W z9L8()w&t88R2gKMjZL#*PKb3jn!^HVo{9=%&-z$CK;&`LY|*=gHc|5?%$5P1ficcH znO2IorMd;mM~Pew$JoiwqlH|U=}6=D#P@T@Zb_;2Q8>>-yREioGHv$;-mB_IhzG4g zsYm;YSKd256isQS&PlE4UNTZ|tVe8GP%2#}M_8tKe?~bpO-(>N2eib8pRVPC;l(>S zYRJjU0fo^c=Nw+NWX2RETj`dRe{Ph)+~b*=zU|jZPfCf!W*m4{<4^sfB?dcOzgU)= z9*f1hknx+KC~iW19Yx}kSa5XyD*E!2&vtr;XL_PD%Pv=Mfk%TKDuphSu%Hp4ftMXJ ztjxQ3kP=+f`;vLCty&>u&WtJ$0hA&`>LFhMXdIQGEd z>AX^qO>9|k=XmTBwBE0KDV>PAlsB(?j#BCddwaY}4R`Z=hpgk$GW~%pbl}GD)a;=! zlCjRp2G+eM?HM}FTqjv~&_9M<1}H1#pU-(69k0FV{j}9mDR^_?qMjWIMb4ma_W2_Q z9LljIt`T0Ogl+9C-|Z#V{92D~CaK}{&C(0GgZ=~dK8kS*;r=_T#U&j7A-#ISfmy)* zSjVo9T?=nEFX$~42`ikHN!)Eyrdlx-DP|ZsB4r8xcbUGHdN^!ja4(xVO?$t7Ckt&4BGqd zFU9`+ig% zdau;Yz-P|^fy{)yY^Bzr5Ux5fzbTM}3^lRKRdMo9+NIXdrQ4mCq@bSJXoofd$r*0# z*OEjdwiv7CjwbQh^sF}Ib{zrXEu_CSr#BV0&$yws#-q!ZjT zTT0QoTr#O1p{c(ZNG)i#IWa4LZ50+-_C-c*NJYDMIor)~J6Ks?VpsX~spP<;-LYAi z&NO%J=hf|8{fKP>&zcdFc7US_P`&s!eSusb)Rh!X7TTs>g+}yE6+Euo9Bgn(Q zBh}|PoXY|9o*m*<#ah{EqKl0typ*6Q+iYvYsz1>bQ;O=SQ9wDEtj;gG!ROz_ zBi*~7?m=ItFVynlVufRlE=omUe^{w)G~r2$`@XNSL$j5pop0e?KO(Ra>?oiIZ*$rI zj1o853maRaExmvemp*m|{_<5EAmUkM%9n>KgV+))9W!UaD);UK?cMt&SraU;Qq;*t4^nsc?n`?&M%q%OSr*f$9T6 z4E};(w%S+u_t9=oLUk5ewHNcm0#`YA!FK;(sAHFeGXF<&&$!Uu20}Qxu=>OF^%6Tl ze9&@;yMUZv8KH zSfCFb;u7IZ*V`?t2P90Q&$8~tp#FP!mh%K3hxtU-w0S>Ot;6OL zez}xL=l|@Ld2`YAZW!_rAW%1^`u%MHd5xWadCOIsNVnElET>#{T&89DzlfH<$&A;U z>zsVh|Erz<*&JN>e~lqy*1EVFIS$}73And;33yeR4#hNeN!jh87fc9VkFC}}2>o9N zou`W7AZE(bJ*m9XPNkG1xCHz}-cU0zf=C}@>K}amzn`X02pD{MydK{$et*4Bl}~1n zg9JEy|C{s)es1QM!d?iGT^wXdSr+0--oxv92=2I@=G%{}S#w7(>E{K;ja&`HZeY2E5d{90PMBEaPeH68vuGOjBOq0lcqoD6Ac9IYN$O zEXDspnDs+4N*+q@ITqt#3~!W)Prw z3IZVj`N!q3axC!va{bGsF9gy}yvLP|H`>t@W`)%Q3 zs=kxl@Nw7w)oIaA40@+x4T(^+WDR9L8jA0`bDUf#X*@k_i&bBF>I!l--X3o4yMR|M zWr4GX2O7@4wo*L*cCj7=wI9!AyF3?E2#HGCD=F6>ovStw zI1(=YQ6zd_sr!43>0@q^d$~?s0Yu+S>pK!l@_&{*e{U+t$4A~{-ebXVlxRG4b{@E}saW@5)-_m|hls{waaFhO6aFU&WTKoI=h?7v-{DfCE|);#>rZ?8 zPb}kLERFczpw4Z17a@d<3^gnN`R0h~v^@H<-g1_<^|XGpa(pnFC{xvs$`vwMR2dDa z4^of>hO;6*KTAwP+yZ`#9Nc`WFQNq>6koLgBOkMg1&beszK>>{kL5;Pz6HYKdzg(8 zr5cjC%aAqS7*`A$lp#9@x#+0mQnqoZH&))xKu%f|Bef>vHZARkqKl5vmqH;J~3ZvV5y7drp%e?;i`tl#{|6Stgd0`_~0e*HxA&ins zv+gkj4cat2tu2#dNO2X5%#iG->-}2*)e5QMZpah`I)5&EQE-=2JlV{8lMtC@5LF_( zr)rEudOX@8SV3Bwn~M#Rsxqu+y*%3<>WZ(;c9xbDNvCbDCrsb4pSAy!*h5}@eMKD& z9*YKU>6fr1VpH;ei|7DE>YzHQZVECA-|LJiOh0B{wp1hER!ik6C-Pb`BQ@Ey(QbXM z8IiF$-uo^eyugnn@5jlb$Je=Al;&#gqS^5O>v+gx5x>(@X@5_R)UMPqiTc528gU<9 zTZKqL^K-fwzUyP}A^h)O?nGPekXO|L8rp2@uU6-Sj9LJr_o{ewG$hbQ9AB|(W=|}U z9U>!L*`aKH%O~jhf4aKrxG1-$uSho%E+r+PgmkKOvvlbK(#z5)9rDnKq;!XLE(nNp zT>&ZSQcAj!6qa}&?!Damd++|cpJ$#qbLKf`&Ue0ZrY_yF_EZreg}^dlrtLf};(M9D zaM_>t;-aiL{%v5;@1`G-wQF&uI%WRaNX_w1@!Cco2Rca!0ZONPW$~phsRkvLQTTOk zBYy=XS$ia{F~%)R4P@Ppd()0^W7T3>^gx^|EPf}X1l0@{W2wLMB zF+Z>2;NI8k2OVpxV z&qs+TKU$OoqtCLr*yzNuL=;bPRiYWs9>f;fnBC@`c+Xw^MIm4aS(xp;IZ^Z=PdRZi zXDA-g5dLW@%ibc#@4)?Mqvn^7!>1KzXZrBOpj!8q#ci@f9>AZ>E=0UjNPuVd$6R{o zseDt$O)Td3hQN=pHmb8(@Yd|yOu#Lu*|L&F?nxz<(s$h&fv5J5zV=N&7_sC`jA}_? zIiM&Xf2J6-Yz;xAr42K0lpw?1!dOsWlS1( z`F_Ge78h&X=mqh|-L-?Mwc}3{ZvqtcYT}2-{+M2J6?B-XzYN_Mg1{vn`81+6^HRet z4fEEm`@a=sB()D&*Jm1P?b;Iy&@ze!iG8PJ@%A>2s&#`ue+o}+d@iT*{Gp3ApdFXf zaG$>B=!6a#9!*{{ZjPBb75igE`BITA|Cu z!4z$LQV~z56)nura*be==2E(_P`fhOmD1wirm1^ZZW zLc9%^*pEmDAB3;1`W6G_4~6gDQL3g_PU7IHSM^ED&J3)+x`Yd;NA)WBjvg!i@~+%) zb>|X5WLxt$o(S%X&3(It7;I!8$7d+Su7i#2G=T@-F z&(yob?o=kf1S3+wa1cy=Au?=-FC?Zvl``4EMiWfTR(dygBedG?=6jU{Nk1#H)UulS zkbr+*@H?bEM^q?ATASW)Ap^;vg(5nCk${9*LIt1YZkb?B#P5U%@k=h{X7p<}Xeb`^ zN8{Fq&f~}nzLz5?hE$1qobjH**Pu^SEd+;PG!sol)d-+IlV^l)rL_7}9Phf5rZS%v zTN(bv@;w@s-8W)W5Ld`y0BkhQtU)LNw}(b;_zcTiNZD%I*3MbIOrF&O_NZ1NdGYq# z0I!`&Rp(fqeBy;g+s~TeLLVLS;*U!+Y6TD0{EK9hL@vAG!;MxhW@kV~lV&r`c~#lg zh(lm%3JTDPZf+5s46Me0U9H~9@6|LsLc;PsSTj;hT3_{yZRW+Kc+k2Tn2BVW=#U2s z_AXiVns@I_1<%0a@3+?4gkZ9z#m88%e_R5TR?lT^mh^g@!Sx^3Q>^av75TjvK&fdk z*xUKxQ6pH{bRTjmJBR!_X-+EkYKTjahD{{I0+IGC5!AY|4Ve&A;U6d8cFkwbR9MXE ze`R~{NNNOSAF*+pn93QhAU`DWbPdMXN<7peAcHVgyo#L1SV3O6HF|$Y`A~=V;llm1 zJgtQbepNMRU}cuZ?Ox{-2;Dz{bn9$z>tj4BJ%d+42F^pWJPx z5`HIs3X*GcfW7%P*+?JOhYr~0=5gxq%Hr)hp81Ak|2o{100QdTo(XAqci^M2h-@>Q zI{mJT<;3DV-*v;HcG&{gqg=V8{r#RN;w*vz(#GAeS`A9oB(X@a+-;0HoN~wMHM^CY zI&HG3SrJcG0nJDsq*JXqIRTL2C|P4=Ck-1n?Mx%@bU$9JZ{bGAj~Pr3)nalb-*l042XMwOZR+|Xhx z$Fc0=F_g!<(>zK_eb!j*rs`!qj}Q#cqJ-%szwvk)C?;R}T%{ZLZUUvvwGLW(E1WPp zr~sOZMc%@fD{6L~Q4WHI&5+6i6$OzdJ*T{a4CuL4sFk?Ss~>)Bf$=d!aiQ|+qN|63 zY;m^H7)q;>{WeM?cZz#QxCri@`44tL%EnRLGhS?Mtz3o~k1nuOgh=zRyaitj=!kwB zX<2kmn;kl^XQN?gRRJzM>Tpmu9LM8aKR)riK- zbAG2}ZyrQ&TB+2dlnM{LiEe5-XvJl1_7zGmwrC`=oEGpu^>cV}-8{>IJZL(>GD*br; z)LNcO^}CJt^2qal(mBqzmXGovE}7tVAq8O8EP4?edOQ(dx?kbtT2x&u4F*{Ew(eT? zG-Rp(m17$RC>o9#QGqzW4*1kN;XV$jPM@(JMa!{U(4=~q48W$^R+BHy=-ApS{1SZX zYnRbgu9PH!KyOcsQ2bB{U^#bC&g^^0V!Q*I8$w-r}Aa!?WrbaRmE4 zfDeD>d(c_l8)c4nZoJ5WKC${E>L4$Z_cM^hgc17y9XqgSV0*LIoxJ{~yG?kq&XGt_ zJWU=f8X00sI-uQsYSsDLW2avOz7seF@z>1pjPFl-r*qzarl)^FKqy|_>@DCPY>mh- zS$}~evK#*C^>2nFXhlHiY_Xjf>g-|1nfe$F4H8BV77C0vo6LUl?8o%j?7$Hf!~h&b z$unodqmtv})t$I#S=S_=o7)b3lt8KRC9)qNrMj)KzH*JOt~O{KKsOzIGCv)o_8S(L z<`ZgilW~c(;2eA^o$Fn-(dDAEC8N7{-p?=H?=PLg$~=7u6Dl#8Y|L%9)pIFq(G+-I z&7+6$_P!w%W%I<`RfMx{+fEOjC;~|DRf&G8i_wNjMKjM8MWB1mq$1zQ<+F7Y^pUxs z$26I_`NNWa52Qco1+kcA@ScQ(07B7jZ+a>6B&Ku_a)Os@m*{eeEXQeGLo*AXE@nM9 z%l5wkms&5uE`=h8gVK4{W5vTwUT&wCsNLB8yeA%D!aEOyivZ2K*A}EK*AR++8}lxB zyj=R>^o_il%%S--e+V<8D3o`?2xtnY5gFbe459P+&G4QeAH9mboX&mT@6T%5T*cVC zCn8|0-+-~9(ieP18acsfo*PBHPAh%6Pm5T=2Az#y{=IL*24rISs%473F$e3e6kq(} z5&`dh$OB$Bc@BS&#j9HgriR`j{l;(JO?-N>3pnH(5m50f94>-zsEWB+IEZIKiwqk+ zMRbAvxgLS|Nhrz-b$f+DSIJG z)uyPIR~MCu5wA96_IVJ~I+*;N96{O^Y;?)p^deV`;SdAv9dCWh*tbNNt)yKGCuQ!VU|<+*)w{40ef|oepY4umRBF-!vemN zdu7&BHQ!8$+2=r0-q~MYQ z7bs$jO^EhIWo;pnm(c#%bMyYxKZU=R)A0WX67kFfhL&jHSD~`XCUCd4gu-mzzX% z7R?C{P{qjcIP^p1&mn;ex&<^&X_#lJkw)ro`oUWKvU|5f$Z%L(Awx(hTK-6?mTqWu zRyr>tZqG&XIh($m5ho1Gc?3>Q9>*>Jz3`Ij!CLTTyQ=XQ@7AZ0rzVQX?50!CHE5l} zh0adH0wTFo>L+#t)zOr1rd=Fg^*3dm=yoz&-~#hVdZ^G`wDF?>GG5&%0z;WM4}N5O zJHqqV1Vr?8IZdAb?(!-f9Ih*FJ?|eiwng}~Q{{!JMn(CloxI4!?-gF%-8X|kaeu?- zc?ik+)xYsLou4-z73g=oZGMm2(-!eahDp_)X44In?StMx9WZco4T=Jg!-k!DWe`FA z60M_KZeS%PON>4kL+{?|D-ITahI%g;gD~TDWhiykd5HLV+v8e?x!Gzo5Dfi@LyrA$ zP&!@Mqst2KXb*1&fd|6D34-5lW6{uGpib1tBtLI}xEl@$Hcm$fQQ~!jbWD5*y*#Z; z+_Lm@sj58?76iUBCmO^&O!f3-SzL?~S2d8s7T&7pC~Y>n*wAxL@7DWO?RjJ^vfFro zzK{*#UHhl2nW^eQ`4}rJAT@xS;ZFS^;d>Bf2DhU&8_m&3XgY_lT$}SOp~q1R7F=L%=Wre4bt}P0Bpg7-=Z|xf~OllO^)}9Y`NIK zbGxH-I2}~ylfJe(yJ?d$&@{Z5T7kep{4>S`$Fl-ylnr%j7MrfgD z9mjkQD7T_zXDOpR*$!=EPB&Q9b*vSlPuHb6^#Y-nba7d z^QDoGD1;BSN%{HtVqItk-_6erDG8uqd(Vt6g(+wui@y#%Xo~XnGZS2~SB^h^mfT9S z5F&o&`uscQ#x0|l&9nj&ff4jeAgD%1SZ!1Wj2?y-YTPSMHpubZAk87^Q^0<-6y;MZ zuC}fBXiPVJ{GlE~a=Lh3NB*dep+W*B=(0o+@mk>Zxm4Zq!fkkhY~f;Tq&-`R_45V& zD?y3=eZgr1XxYfOe=x-l;S$Xl5kEwaz%RZBtlWqr=9QWS*~lHxympZm@cu=~?s=ME z$_R<)HLI|vmG6iay{D}TzHs5KD9AK_*iPeTP{i1G90r@j=NcF`xYjuF^I%hf7&KWR zQIcGwwA7$PN3=e!zhZa5IwS>hMvG3cV>kX*$!T?$-Q6MY*41Y&+Fj{G8p4 z(j{#`NPjV4ZlJ-I7v6L-RqogqhzJ*yc;kXy@Le%TM^H)zpts43T;*y(<=~&0eEQ*N zWl5^dlc-CR+sk~*T79nwr}HC0!|VHSuTk^JBGDv#rZ*J*b((#znx1O%=G! zflJ^Zb2-^EgNJ|!{=y)PHBiJGTHdfD+GdVh#ow3BeTs`i#q&scteoDMK8`FAJ>c0( z6*US@S}PyQ-=@aJd|JuR(J=#WO?E3%F|XGN$=N)}zR>38%!3}XMX-?BCEbH~b2D5q zjzu?SQIA$GP)*}Zw|?@;uzo-#RLGVZSCj$d!w9_v{i8|tWWu-&1n#g4d>~sk`H92& zL?hYuF8&iZ&xmDNMFb{KQ%aWeJeRQ;73{v#{q|YLKh*EHEzLPZ3eNel#rJm4)2%$u zOe~5H8o#sRlCaX&LN6fsjY#Pm?pU}45iF6i*h7L;=2F&rBS1?Or8ruw@p5CV+%^U5 zzf5}1N}Q>m5G{=PLNizLCFX3{92KB)^~;3({n!w?i|T_YilO`87hjmYPyRvGhx zjSWu?VDm+v03y}{LN$ZRfF$JuUx3MO>D@~f%cqIbxOP?2^TDEYaU72QI#@~R0x6m*c z5m_3~!J7zNv7I>MJ=7A3GQ&~?Z0z+i@Fevm>$FXn3@@zMzb<~6XMe8+xTbv`V#}h$ zmQqml&_b?`Is_8jy<|NnpcNhU=J*`!iY6Y}N?gT-%>rvV?RPLA%#wsR2h~v#9x+HV zolf4%{mhDfyDyuf_ytLLOV91jKRPKLAUnw$g?O<9yK?t;u%L?&4Xh_&Ooh=xqtec7 z5J%-WJqCKi59do(%G=*^fiyc0X?@{bqM>-3f$a2)NAQyQ0L*}off};U7sTxLut?R` zw43aznJOGiS94} zcSfGr51ZnhD(PcW57IiqN~Natq~S8oY~6#99uu+SCb`$c#bwW?0gi5TpUt>7K(gbo zeJ>)9e3Lqnr&dCPXO;!3k>JN9Eg6uNRcu%qbF^dgjrOE$=dp>VGb+@kN!A82*1cS$Q}CwL6rkl^ z-<+^9Pq$lnI?4xDYOfFF$T#-<@d~U>iKr`hEVzh=qs5$bGJ4i=k>>7+PtAUpesyhsQ?g}EyI41#)Bm<`rsn5S43!V^$BkH}LQrkA( zi|L`NXUlj)XhYeeCWel8xixtib(u3~VQOJk8$;;y0Sop#D2jE!@m(iZuKo5qs&T+A z;nXNlwB4Z*DjeBqLY@9~j7qwh4-zbTfWjj`TrA1v>mMbaN$8Qq5k=!f&@|KdJCbb= ztNPxeX@Au$gyq56;ZCoeJtV1#n?#_&5J5J86RON)J1YbW(hN*8rU>D}1O|hOC|3+5 zv8l5g{)ke~4wRwX2y2nv4!&B&76$%KtuSMoI~knPC+VDLJoAEKzkt5oW2H9)u{!IV zW?lQbWeT&OBoDZ6o7SWwG=2s02}gbntY)TrpAK@&S@jHEbe`(}ge(L{R|!ij9^#Kg z@SW`XoIEl66zWsMhjZJcFA3ypElbhuYhp2YS?#9Slvlo0EzvFGJph;)Z7WHP?bpM& znu*&{14%p`-jBIsrBJsh9-w2Lv%P&96usBOj)eI963up5AI=*Fb8ZYjcEP?}k8|WA zc%f2a&=O+IZ(=Ch)58%pyQkPF6i7XIaMt4zDD5o?=8;~Z_bn+2tv6g1Wv_gb94&7W zXj{t}YTYVIyL=TX0g$N~NaIOrdUQbEptC_PKM#i7A_>IZf897!x5B{Ct?r| zP)?aeJ!8RU4QwaCVIb%WeRy=zay7&Fiev`w90#fV(;e*E%@TdMGxNQgO85gG1%pX( za4<#$@QziUKJr$dpbwca)TuA?pI!Glp|kQ;?Mqgh(z4V??vXyq# zU$mCH$$#w!5<^nH4m%srh(AQQq*wZSW?-ze%MD98BuL^infkygXF!#D;>!Wk!>!V2 z$Viw_(Ye2O1==1ON-)do^-o-vrKClrb$WijyQ zv2v<60c5$h8bvUj@x1!v#A3DT=PJdt8FsgV!9P z5+u+(U4`yp^I!4?zMm91|Dc>PRW*{iFJ&^#LGNw~o;TN9nWN0BbNRih)(7thh~=a1 zbM~hS&BbrvHE|5NyN0P4WuN?kGf9iR=IqE6kY^rI@fMKQ?wp-Pog5m2uv` zQx$Dx>VyUjn|jlk2rH044aI`?kb4r@NluAc`R#@L`>b(nk8UtH+0ccY)QYNDu!UFB z!FRK6!UB*sD2@JuJ`X}z?Dh}(4ASI)YwpNHTky$;TO@nOH^-L086|$EZ+-EozBsBk zDc@-HH=RwwglXPi&LqmWcK61_e@{919#B|tz=q+!X821S?llt+*9*Pze|ToVpH#gp z2slhR9vbc4n12=iQ=a-H_U9~(Nv8Sje-8G4Yx0E{l=f~R%QNvNVSJ->yId)Phr!pv@Ug}kmTr^=ZnPEz6WwIT*#DaTW}rMWpfpqA!dd2j{oyaAD@Y#d zOrpD^zEJyxQD` zS4kuWeG_|kQ$V^b>YR*C_*PQdL1J;VhNFsm|NqJqM#Fe}OG?hCOpEuEHJikAqxFAu z0)=6bI9l$#aU5*w{r^u!N=RN$nIzNJ(f^nd_|x7fdT{hx0RcOPt~ZPO-$uoyx}GyC z6XbtwdHo(qGdddQ-H-pBvcYRB4`O>={6~RSv1@Na&1ZQf{=e~q*7Wngo-=>B$xXNa zC~z!!?bTRVGY$Vh;x|*qe?4XV?JryYqd*1G^+zKnyf$m`AM<2oflB9OksP1hD8O*z eG;fIguCTt_9f_o*f33U){uE_ZWy+p`j8s4uYv{GhFw^{I`BdMNg|or!7A~2!oUiN{ZC;w9nb(U6ccJ zwTg8yciPqc1x?y#wnS;;fvhN+zG{5Iit=%Q3n}u!Neq;B|(XI#Y*#=cXUe`-9L$S@BLlvudGDA2#h!8(AU~C6H zN9k8eLf-~Iig#%5N#>#pY~K6wkI?2jL*+(RXTHz~ZPINCrgLHF7biM)2Qi8o`AF@N z6iteP+}{%NPQ}(@ird>OY^v$HGk-0Yg@VI*#+AmsjXnabz!I1ZFEg;gD+1=kS_c$X) zIQYG&2ICfz#|gnpevavJ_7M`KGx4)_$sTUcq(3%=yw#sia#dBY_&ANtrul@S-t^Lp zF|eBp^U^r0HRYHz-tZm^xG8m5q6@v=c-OPLrSnvWBvw_XtAMWOKM(}op7wfZIXOF`$NWJi2zI&dv%P2W`BXndB{hi=46!w7n$RrwV-l05 z-USs1LP^=p*LpP`b+s_LqZ-uAGUoCI{kEKkw0U%B{pxX0r(=qzAGZrB?90RE&^fqO8T*|^j8h;rZs zCq^Mn?0dy3t#AUV8%vkf+v;JgPbq0K5SSh${Xd0z0=+ILn+KX zk7C`XXE|LPfva?Mp{9pLj8zuFzpBK zIDdDB#@A$drGV>Rx%fJe?MJ8qYgDTYgG5YD(*BwS1r1#q@*?LDe zj0@b|AAWkmRbyRvlvO0F_L&~rHz`gB#@$O9V+6Tc{9crlkU16b+3M?fkq!Y+NT83p z&x+0o(>xxn;ZabM^YX|uPT$`L-#cFYjGviZ?YX`X(bn0c7rzj-@oA%fWq!I4nn@2T zg9(bgb@%k3=g#0auh;@R^zpitA}rJJ=H;nmWM+nJ!Xt|(h2Gz%F+IA2$B1y<+S&3e z6B~?5Ym{bvsIREd$MC$06XXY8S1qND1mljbWKI~J-@TX6`69nJ*_dtIY`02$zth-} zzm*S0=|bDDGJ9v6kmZW8Ws_jt6A3A;Y}R9(l$J(T*?y6571*NDc*37F$Mbx2N9Iqa>_8a^7pQZ_0jD4^ zudcHDJ16`}2}Ed0(ICrMnqPUrh@TOIy3jy8KD!3(%rq*ovLtW;+h}Y0cayIhwQ^?5 z9w`V@&gWV!o`o4=gkbg0CjC*tuFd&JE7hXS!XVw7d2+baFQkk;>ob_&ZAD1q||@kaNbR$OC^}0Dq?&!GFpkiHp?26^Q2}*`)h!$f+m;r)<5e1S(;5uKxeU(*}3X&0b z_BB~Xz99bWn{W1t5G-R5Rm$$wZ<`9hg4z&i{IR1>bnERN!%Y5J_ZJ8rbNg&vG~&EK zT7{C7MM(eUadk`8vxx7iP|~RFB3@A75ta^e+Q z??6L6&l_NW)!*7R(d)E@BpL6upZp6vA{ahB6y1}>%A+MVIq|aBRnpj*eRv4;#pxat;+LdpAcYie{I*8=M7Mw#gRsMhYPP3+s?`I%XMCjjkZ7F^WzaPEFpQ$NmH zFd^vcPfSfsYn%pL;aUd1PW3t;P~Hlj<*f97GYvrEzK>HHBk#P?5OO#7h8W7N4?IVY zDkp8qwO5=aZAL$T496Upo<1~!y9D~g`_N(#2$8EfmDl-9{_dOyit#PvQ&4Rf83mnu^#@!eZCHpFRWqG7T0;ugN7NJ4AWh3qO4& zDbb+u9^x^6>&s<7vLmX!A@AHuCTW;Bw1+a>kfMYH%u;^@6)H}Q%+{sG5T3zi> zgBO%g%`Dej=Ou!gq(j%ThVd2A3cXx6>Ut0CLh3i$r;dOpk$U2>1 z4yl1C3-5T(#Rbd1+D}6Ou1*|^ly*`)teODJ(W%BswN~MnXOk8$l@JARx6GAB@ua#{ zUiZA(s~+2d{poeuUcT^@D;oOc@E|vB<_;C4dpV zosXQuPF{xZwT9xjABZWE>S}f&W!ly@$KX;_WCq3d^%NeUg+;|$ZsF4!MBjMjw-pvr z)i@6tPw}cPDs}obGjL^gjOKdJz5mKNa7fSV>OH)$wQh#KC|5Bg zEXq$f5H~BeDJz3U9auCGuR^$%t!qUBiPz|j8yapvB7`$a4n(u zx^#1hQkg1Yx~}yxKYO*t>z;#_`f^pwQm$c{?WYhY8tabsqYgRj?g$A zCO+fyS$|(bH1O4i*n6pQotD`T5L)r7${)WEg1EB0G-ut5(6>e=K9!G=efvXHz-# z^HOJH`z8Mo$T`fYG-C7Ui9}= zJd5u4!g)^x6-DYXgaT!bPJ}_v2vg>FKR=-XPkP-|li*kzwMKfmRdUh1Zo`gR+#p2U z74E!LR-AxtYtAy*=?GL4K`Cd8XTvH-#$T6U!uAJp{#my2m}n;Ui^pz1d&3jAUSLU- zkpYKK{;DE(yKUxGi8zcjBqLrLdJ{ak$Xg$fVu;6poG1yMfhc?$czF2eR15shVKqhI z2&3<`vy$M%;DN|x1?1ZnWTLaiyE`&nlcs2S;Be&=-Vk`ak!rut*XkUV6a92TxnU69 zaL%*RvubU{8MX$KBrHkv(m25&VF2Xz8$>a4~a$3+2wa8E_)cmw#APSd!i`7xP1Zpn56*LtAO*UF_~ zDyz&F#k0)u?LWaZ7yrlo6KFa1*Go>XlkS+D16T$3((A4D;W&Y7Yot_2k2fd5i&MUE zmCNX5=>;=^iUL`c+7F2|irr%CWasDSVTxhOvRwi9m(zVAFLwZBRUlgMNYoV)sQD-^ zstyTzr%5NFu`wkhU?{#an;F8NYslf~Laf}=4$2%B28K-Cq}qC=4qV$)(8XY(!>$q` zA^w=RbGcY~!Z3cVML>q^GqF5(o=7=D1DgHhTC-@nh_$XpcqmS0a?0%MsTwz2BM}TI zyKA1oE4U1gvORE017|%7Qo)@pmKjf_R&zvkGFc@)fP`rOR zm||e5n+-qBnc2JXJVYg9kO&ISLxE?{RO8RBw;Dqd7)fvz9eG-G zV$a-h#Ra$wY`UO#o{x1w)?o8S6c!e~KcBC)qOs>+>4oT`K3;0qYrNZb|GerOgbWu$ zEdL2n4g8Euf$(%`n4X=VNWH?iTykXJ8m(_q{zApi_6)E<&Q~kW+ci_Z2V>YsA_y2 z7t!iFku+m%tzTCk5BoQl=EEJ}$|!~$>?w$`_AI^{&SSOH z_15}M6uLz2wmr0`4+rIc{hwa^DliR%b<3+xxN$VzB7zgt*p@&Ye
{visInstance && appState && currentAppState && ( @@ -74,6 +127,7 @@ export const VisualizeEditorCommon = ({ )} {visInstance?.vis?.type?.stage === 'experimental' && } {visInstance?.vis?.type?.getInfoMessage?.(visInstance.vis)} + {getLegacyUrlConflictCallout()} {visInstance && (

diff --git a/src/plugins/visualize/public/application/components/visualize_listing.tsx b/src/plugins/visualize/public/application/components/visualize_listing.tsx index 07c3d18b54b0..7319a9b5e52f 100644 --- a/src/plugins/visualize/public/application/components/visualize_listing.tsx +++ b/src/plugins/visualize/public/application/components/visualize_listing.tsx @@ -31,7 +31,6 @@ export const VisualizeListing = () => { chrome, dashboard, history, - savedVisualizations, toastNotifications, visualizations, stateTransferService, @@ -113,16 +112,16 @@ export const VisualizeListing = () => { } const isLabsEnabled = uiSettings.get(VISUALIZE_ENABLE_LABS_SETTING); - return savedVisualizations - .findListItems(searchTerm, { size: listingLimit, references }) - .then(({ total, hits }: { total: number; hits: object[] }) => ({ + return visualizations + .findListItems(searchTerm, listingLimit, references) + .then(({ total, hits }: { total: number; hits: Array> }) => ({ total, hits: hits.filter( (result: any) => isLabsEnabled || result.type?.stage !== 'experimental' ), })); }, - [listingLimit, savedVisualizations, uiSettings, savedObjectsTagging] + [listingLimit, uiSettings, savedObjectsTagging, visualizations] ); const deleteItems = useCallback( diff --git a/src/plugins/visualize/public/application/types.ts b/src/plugins/visualize/public/application/types.ts index 7e9f69163f5a..4debd9a4a7b7 100644 --- a/src/plugins/visualize/public/application/types.ts +++ b/src/plugins/visualize/public/application/types.ts @@ -42,6 +42,7 @@ import type { SavedObjectsStart, SavedObject } from 'src/plugins/saved_objects/p import type { EmbeddableStart, EmbeddableStateTransfer } from 'src/plugins/embeddable/public'; import type { UrlForwardingStart } from 'src/plugins/url_forwarding/public'; import type { PresentationUtilPluginStart } from 'src/plugins/presentation_util/public'; +import type { SpacesPluginStart } from '../../../../../x-pack/plugins/spaces/public'; import type { DashboardStart } from '../../../dashboard/public'; import type { SavedObjectsTaggingApi } from '../../../saved_objects_tagging_oss/public'; import type { UsageCollectionStart } from '../../../usage_collection/public'; @@ -94,7 +95,6 @@ export interface VisualizeServices extends CoreStart { dashboardCapabilities: Record>; visualizations: VisualizationsStart; savedObjectsPublic: SavedObjectsStart; - savedVisualizations: VisualizationsStart['savedVisualizationsLoader']; setActiveUrl: (newUrl: string) => void; createVisEmbeddableFromObject: VisualizationsStart['__LEGACY']['createVisEmbeddableFromObject']; restorePreviousUrl: () => void; @@ -105,6 +105,7 @@ export interface VisualizeServices extends CoreStart { presentationUtil: PresentationUtilPluginStart; usageCollection?: UsageCollectionStart; getKibanaVersion: () => string; + spaces?: SpacesPluginStart; } export interface SavedVisInstance { diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx index 0dc37ca00a6a..9d1c93f25645 100644 --- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx @@ -14,7 +14,11 @@ import { parse } from 'query-string'; import { Capabilities } from 'src/core/public'; import { TopNavMenuData } from 'src/plugins/navigation/public'; -import { VISUALIZE_EMBEDDABLE_TYPE, VisualizeInput } from '../../../../visualizations/public'; +import { + VISUALIZE_EMBEDDABLE_TYPE, + VisualizeInput, + getFullPath, +} from '../../../../visualizations/public'; import { showSaveModal, SavedObjectSaveModalOrigin, @@ -87,6 +91,7 @@ export const getTopNavConfig = ( data, application, chrome, + overlays, history, share, setActiveUrl, @@ -99,6 +104,8 @@ export const getTopNavConfig = ( presentationUtil, usageCollection, getKibanaVersion, + savedObjects, + visualizations, }: VisualizeServices ) => { const { vis, embeddableHandler } = visInstance; @@ -117,8 +124,10 @@ export const getTopNavConfig = ( /** * Called when the user clicks "Save" button. */ - async function doSave(saveOptions: SavedObjectSaveOpts & { dashboardId?: string }) { - const newlyCreated = !Boolean(savedVis.id) || savedVis.copyOnSave; + async function doSave( + saveOptions: SavedObjectSaveOpts & { dashboardId?: string; copyOnSave?: boolean } + ) { + const newlyCreated = !Boolean(savedVis.id) || saveOptions.copyOnSave; // vis.title was not bound and it's needed to reflect title into visState stateContainer.transitions.setVis({ title: savedVis.title, @@ -129,7 +138,7 @@ export const getTopNavConfig = ( setHasUnsavedChanges(false); try { - const id = await savedVis.save(saveOptions); + const id = await visualizations.saveVisualization(savedVis, saveOptions); if (id) { toastNotifications.addSuccess({ @@ -142,6 +151,8 @@ export const getTopNavConfig = ( 'data-test-subj': 'saveVisualizationSuccess', }); + chrome.recentlyAccessed.add(getFullPath(id), savedVis.title, String(id)); + if ((originatingApp && saveOptions.returnToOrigin) || saveOptions.dashboardId) { if (!embeddableId) { const appPath = `${VisualizeConstants.EDIT_PATH}/${encodeURIComponent(id)}`; @@ -164,7 +175,7 @@ export const getTopNavConfig = ( state: { type: VISUALIZE_EMBEDDABLE_TYPE, input: { savedObjectId: id }, - embeddableId: savedVis.copyOnSave ? undefined : embeddableId, + embeddableId: saveOptions.copyOnSave ? undefined : embeddableId, searchSessionId: data.search.session.getSessionId(), }, path, @@ -392,11 +403,10 @@ export const getTopNavConfig = ( const currentTitle = savedVis.title; savedVis.title = newTitle; embeddableHandler.updateInput({ title: newTitle }); - savedVis.copyOnSave = newCopyOnSave; savedVis.description = newDescription; - if (savedObjectsTagging && savedObjectsTagging.ui.hasTagDecoration(savedVis)) { - savedVis.setTags(selectedTags); + if (savedObjectsTagging) { + savedVis.tags = selectedTags; } const saveOptions = { @@ -405,6 +415,7 @@ export const getTopNavConfig = ( onTitleDuplicate, returnToOrigin, dashboardId: !!dashboardId ? dashboardId : undefined, + copyOnSave: newCopyOnSave, }; // If we're adding to a dashboard and not saving to library, @@ -457,9 +468,7 @@ export const getTopNavConfig = ( let tagOptions: React.ReactNode | undefined; if (savedObjectsTagging) { - if (savedVis && savedObjectsTagging.ui.hasTagDecoration(savedVis)) { - selectedTags = savedVis.getTags(); - } + selectedTags = savedVis.tags || []; tagOptions = ( ({ })), })); +let savedVisMock: VisSavedObject; + describe('getVisualizationInstance', () => { const serializedVisMock = { type: 'area', }; - let savedVisMock: VisSavedObject; let visMock: Vis; let mockServices: jest.Mocked; let subj: BehaviorSubject; @@ -47,13 +48,16 @@ describe('getVisualizationInstance', () => { data: {}, } as Vis; savedVisMock = {} as VisSavedObject; + // @ts-expect-error mockServices.data.search.showError.mockImplementation(() => {}); // @ts-expect-error - mockServices.savedVisualizations.get.mockImplementation(() => savedVisMock); - // @ts-expect-error mockServices.visualizations.convertToSerializedVis.mockImplementation(() => serializedVisMock); // @ts-expect-error + mockServices.visualizations.getSavedVisualization.mockImplementation( + (opts: unknown) => savedVisMock + ); + // @ts-expect-error mockServices.visualizations.createVis.mockImplementation(() => visMock); // @ts-expect-error mockServices.createVisEmbeddableFromObject.mockImplementation(() => ({ @@ -71,7 +75,9 @@ describe('getVisualizationInstance', () => { opts ); - expect(mockServices.savedVisualizations.get).toHaveBeenCalledWith(opts); + expect((mockServices.visualizations.getSavedVisualization as jest.Mock).mock.calls[0][0]).toBe( + opts + ); expect(savedVisMock.searchSourceFields).toEqual({ index: opts.indexPattern, }); @@ -98,7 +104,9 @@ describe('getVisualizationInstance', () => { visMock.type.setup = jest.fn(() => newVisObj); const { vis } = await getVisualizationInstance(mockServices, 'saved_vis_id'); - expect(mockServices.savedVisualizations.get).toHaveBeenCalledWith('saved_vis_id'); + expect((mockServices.visualizations.getSavedVisualization as jest.Mock).mock.calls[0][0]).toBe( + 'saved_vis_id' + ); expect(savedVisMock.searchSourceFields).toBeUndefined(); expect(visMock.type.setup).toHaveBeenCalledWith(visMock); expect(vis).toBe(newVisObj); @@ -128,7 +136,6 @@ describe('getVisualizationInstanceInput', () => { const serializedVisMock = { type: 'pie', }; - let savedVisMock: VisSavedObject; let visMock: Vis; let mockServices: jest.Mocked; let subj: BehaviorSubject; @@ -142,10 +149,12 @@ describe('getVisualizationInstanceInput', () => { } as Vis; savedVisMock = {} as VisSavedObject; // @ts-expect-error - mockServices.savedVisualizations.get.mockImplementation(() => savedVisMock); - // @ts-expect-error mockServices.visualizations.createVis.mockImplementation(() => visMock); // @ts-expect-error + mockServices.visualizations.getSavedVisualization.mockImplementation( + (opts: unknown) => savedVisMock + ); + // @ts-expect-error mockServices.createVisEmbeddableFromObject.mockImplementation(() => ({ getOutput$: jest.fn(() => subj.asObservable()), })); @@ -183,7 +192,7 @@ describe('getVisualizationInstanceInput', () => { const { savedVis, savedSearch, vis, embeddableHandler } = await getVisualizationInstanceFromInput(mockServices, input); - expect(mockServices.savedVisualizations.get).toHaveBeenCalled(); + expect(mockServices.visualizations.getSavedVisualization).toHaveBeenCalled(); expect(mockServices.visualizations.createVis).toHaveBeenCalledWith( serializedVisMock.type, input.savedVis diff --git a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts index 88797ce264e2..faf25ff28cec 100644 --- a/src/plugins/visualize/public/application/utils/get_visualization_instance.ts +++ b/src/plugins/visualize/public/application/utils/get_visualization_instance.ts @@ -66,14 +66,15 @@ export const getVisualizationInstanceFromInput = async ( visualizeServices: VisualizeServices, input: VisualizeInput ) => { - const { visualizations, savedVisualizations } = visualizeServices; + const { visualizations } = visualizeServices; const visState = input.savedVis as SerializedVis; /** * A saved vis is needed even in by value mode to support 'save to library' which converts the 'by value' * state of the visualization, into a new saved object. */ - const savedVis: VisSavedObject = await savedVisualizations.get(); + const savedVis: VisSavedObject = await visualizations.getSavedVisualization(); + if (visState.uiState && Object.keys(visState.uiState).length !== 0) { savedVis.uiStateJSON = JSON.stringify(visState.uiState); } @@ -107,8 +108,8 @@ export const getVisualizationInstance = async ( */ opts?: Record | string ) => { - const { visualizations, savedVisualizations } = visualizeServices; - const savedVis: VisSavedObject = await savedVisualizations.get(opts); + const { visualizations } = visualizeServices; + const savedVis: VisSavedObject = await visualizations.getSavedVisualization(opts); if (typeof opts !== 'string') { savedVis.searchSourceFields = { index: opts?.indexPattern } as SearchSourceFields; diff --git a/src/plugins/visualize/public/application/utils/mocks.ts b/src/plugins/visualize/public/application/utils/mocks.ts index a7029071851c..f26c81ed99a8 100644 --- a/src/plugins/visualize/public/application/utils/mocks.ts +++ b/src/plugins/visualize/public/application/utils/mocks.ts @@ -26,7 +26,6 @@ export const createVisualizeServicesMock = () => { location: { pathname: '' }, }, visualizations, - savedVisualizations: visualizations.savedVisualizationsLoader, createVisEmbeddableFromObject: visualizations.__LEGACY.createVisEmbeddableFromObject, } as unknown as jest.Mocked; }; diff --git a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.test.ts b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.test.ts index b142f3fcd406..f81744326365 100644 --- a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.test.ts +++ b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.test.ts @@ -22,7 +22,6 @@ import { createEmbeddableStateTransferMock } from '../../../../../embeddable/pub const mockDefaultEditorControllerDestroy = jest.fn(); const mockEmbeddableHandlerDestroy = jest.fn(); const mockEmbeddableHandlerRender = jest.fn(); -const mockSavedVisDestroy = jest.fn(); const savedVisId = '9ca7aa90-b892-11e8-a6d9-e546fe2bba5f'; const mockSavedVisInstance = { embeddableHandler: { @@ -32,7 +31,6 @@ const mockSavedVisInstance = { savedVis: { id: savedVisId, title: 'Test Vis', - destroy: mockSavedVisDestroy, }, vis: { type: {}, @@ -103,7 +101,6 @@ describe('useSavedVisInstance', () => { mockDefaultEditorControllerDestroy.mockClear(); mockEmbeddableHandlerDestroy.mockClear(); mockEmbeddableHandlerRender.mockClear(); - mockSavedVisDestroy.mockClear(); toastNotifications.addWarning.mockClear(); mockGetVisualizationInstance.mockClear(); }); @@ -153,7 +150,6 @@ describe('useSavedVisInstance', () => { expect(mockDefaultEditorControllerDestroy.mock.calls.length).toBe(1); expect(mockEmbeddableHandlerDestroy).not.toHaveBeenCalled(); - expect(mockSavedVisDestroy.mock.calls.length).toBe(1); }); }); @@ -236,7 +232,6 @@ describe('useSavedVisInstance', () => { unmount(); expect(mockDefaultEditorControllerDestroy).not.toHaveBeenCalled(); expect(mockEmbeddableHandlerDestroy.mock.calls.length).toBe(1); - expect(mockSavedVisDestroy.mock.calls.length).toBe(1); }); }); }); diff --git a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts index 965951bfbd88..b5919ec07496 100644 --- a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts +++ b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts @@ -176,9 +176,6 @@ export const useSavedVisInstance = ( } else if (state.savedVisInstance?.embeddableHandler) { state.savedVisInstance.embeddableHandler.destroy(); } - if (state.savedVisInstance?.savedVis) { - state.savedVisInstance.savedVis.destroy(); - } }; }, [state]); diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index b128c0920974..c9df6a6ec57d 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -28,6 +28,7 @@ import { createKbnUrlStateStorage, withNotifyOnErrors, } from '../../kibana_utils/public'; +import type { SpacesPluginStart } from '../../../../x-pack/plugins/spaces/public'; import { VisualizeConstants } from './application/visualize_constants'; import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public'; @@ -61,6 +62,7 @@ export interface VisualizePluginStartDependencies { savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart; presentationUtil: PresentationUtilPluginStart; usageCollection?: UsageCollectionStart; + spaces: SpacesPluginStart; } export interface VisualizePluginSetupDependencies { @@ -192,7 +194,6 @@ export class VisualizePlugin data: pluginsStart.data, localStorage: new Storage(localStorage), navigation: pluginsStart.navigation, - savedVisualizations: pluginsStart.visualizations.savedVisualizationsLoader, share: pluginsStart.share, toastNotifications: coreStart.notifications.toasts, visualizeCapabilities: coreStart.application.capabilities.visualize, @@ -212,6 +213,7 @@ export class VisualizePlugin presentationUtil: pluginsStart.presentationUtil, usageCollection: pluginsStart.usageCollection, getKibanaVersion: () => this.initializerContext.env.packageInfo.version, + spaces: pluginsStart.spaces, }; params.element.classList.add('visAppWrapper'); diff --git a/src/plugins/visualize/tsconfig.json b/src/plugins/visualize/tsconfig.json index 3f1f7487085b..9c1e3fd72ff8 100644 --- a/src/plugins/visualize/tsconfig.json +++ b/src/plugins/visualize/tsconfig.json @@ -24,6 +24,7 @@ { "path": "../kibana_react/tsconfig.json" }, { "path": "../home/tsconfig.json" }, { "path": "../presentation_util/tsconfig.json" }, - { "path": "../discover/tsconfig.json" } + { "path": "../discover/tsconfig.json" }, + { "path": "../../../x-pack/plugins/spaces/tsconfig.json" }, ] } From 930fe96260d08830d64cd6e7f52b553341ceaa48 Mon Sep 17 00:00:00 2001 From: juliaElastic <90178898+juliaElastic@users.noreply.github.com> Date: Mon, 11 Oct 2021 11:00:53 +0200 Subject: [PATCH 33/74] [Fleet] added support for installing tag saved objects (#114110) * added tag saved objects to assets * fixed review comments * added translation to constants * added missing icon type Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../package_to_package_policy.test.ts | 1 + .../plugins/fleet/common/types/models/epm.ts | 2 + .../components/assets_facet_group.stories.tsx | 1 + .../integrations/sections/epm/constants.tsx | 65 ++++++++++++++----- .../services/epm/kibana/assets/install.ts | 2 + ...kage_policies_to_agent_permissions.test.ts | 3 + .../context/fixtures/integration.nginx.ts | 1 + .../context/fixtures/integration.okta.ts | 1 + .../apis/epm/install_remove_assets.ts | 11 ++++ .../apis/epm/update_assets.ts | 5 ++ .../kibana/dashboard/sample_dashboard.json | 3 +- .../0.1.0/kibana/tag/sample_tag.json | 17 +++++ .../kibana/dashboard/sample_dashboard.json | 3 +- .../0.2.0/kibana/tag/sample_tag.json | 17 +++++ .../error_handling/0.2.0/manifest.yml | 1 + 15 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/tag/sample_tag.json create mode 100644 x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/tag/sample_tag.json diff --git a/x-pack/plugins/fleet/common/services/package_to_package_policy.test.ts b/x-pack/plugins/fleet/common/services/package_to_package_policy.test.ts index 275cf237a962..e554eb925c38 100644 --- a/x-pack/plugins/fleet/common/services/package_to_package_policy.test.ts +++ b/x-pack/plugins/fleet/common/services/package_to_package_policy.test.ts @@ -33,6 +33,7 @@ describe('Fleet - packageToPackagePolicy', () => { lens: [], ml_module: [], security_rule: [], + tag: [], }, elasticsearch: { ingest_pipeline: [], diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index 1325d74f82b6..a487fd0a37e7 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -69,6 +69,7 @@ export enum KibanaAssetType { lens = 'lens', securityRule = 'security_rule', mlModule = 'ml_module', + tag = 'tag', } /* @@ -83,6 +84,7 @@ export enum KibanaSavedObjectType { lens = 'lens', mlModule = 'ml-module', securityRule = 'security-rule', + tag = 'tag', } export enum ElasticsearchAssetType { diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/assets_facet_group.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/assets_facet_group.stories.tsx index d98f2b2408d5..a7fa069e77a6 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/assets_facet_group.stories.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/assets_facet_group.stories.tsx @@ -36,6 +36,7 @@ export const AssetsFacetGroup = ({ width }: Args) => { lens: [], security_rule: [], ml_module: [], + tag: [], }, elasticsearch: { component_template: [], diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/constants.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/constants.tsx index 8e900e625215..25604bb6b984 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/constants.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/constants.tsx @@ -6,6 +6,7 @@ */ import type { IconType } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import type { ServiceName } from '../../types'; import { ElasticsearchAssetType, KibanaAssetType } from '../../types'; @@ -22,21 +23,54 @@ export const DisplayedAssets: ServiceNameToAssetTypes = { export type DisplayedAssetType = ElasticsearchAssetType | KibanaAssetType | 'view'; export const AssetTitleMap: Record = { - dashboard: 'Dashboards', - ilm_policy: 'ILM policies', - ingest_pipeline: 'Ingest pipelines', - transform: 'Transforms', - index_pattern: 'Index patterns', - index_template: 'Index templates', - component_template: 'Component templates', - search: 'Saved searches', - visualization: 'Visualizations', - map: 'Maps', - data_stream_ilm_policy: 'Data stream ILM policies', - lens: 'Lens', - security_rule: 'Security rules', - ml_module: 'ML modules', - view: 'Views', + dashboard: i18n.translate('xpack.fleet.epm.assetTitles.dashboards', { + defaultMessage: 'Dashboards', + }), + ilm_policy: i18n.translate('xpack.fleet.epm.assetTitles.ilmPolicies', { + defaultMessage: 'ILM policies', + }), + ingest_pipeline: i18n.translate('xpack.fleet.epm.assetTitles.ingestPipelines', { + defaultMessage: 'Ingest pipelines', + }), + transform: i18n.translate('xpack.fleet.epm.assetTitles.transforms', { + defaultMessage: 'Transforms', + }), + index_pattern: i18n.translate('xpack.fleet.epm.assetTitles.indexPatterns', { + defaultMessage: 'Index patterns', + }), + index_template: i18n.translate('xpack.fleet.epm.assetTitles.indexTemplates', { + defaultMessage: 'Index templates', + }), + component_template: i18n.translate('xpack.fleet.epm.assetTitles.componentTemplates', { + defaultMessage: 'Component templates', + }), + search: i18n.translate('xpack.fleet.epm.assetTitles.savedSearches', { + defaultMessage: 'Saved searches', + }), + visualization: i18n.translate('xpack.fleet.epm.assetTitles.visualizations', { + defaultMessage: 'Visualizations', + }), + map: i18n.translate('xpack.fleet.epm.assetTitles.maps', { + defaultMessage: 'Maps', + }), + data_stream_ilm_policy: i18n.translate('xpack.fleet.epm.assetTitles.dataStreamILM', { + defaultMessage: 'Data stream ILM policies', + }), + lens: i18n.translate('xpack.fleet.epm.assetTitles.lens', { + defaultMessage: 'Lens', + }), + security_rule: i18n.translate('xpack.fleet.epm.assetTitles.securityRules', { + defaultMessage: 'Security rules', + }), + ml_module: i18n.translate('xpack.fleet.epm.assetTitles.mlModules', { + defaultMessage: 'ML modules', + }), + view: i18n.translate('xpack.fleet.epm.assetTitles.views', { + defaultMessage: 'Views', + }), + tag: i18n.translate('xpack.fleet.epm.assetTitles.tag', { + defaultMessage: 'Tag', + }), }; export const ServiceTitleMap: Record = { @@ -53,6 +87,7 @@ export const AssetIcons: Record = { lens: 'lensApp', security_rule: 'securityApp', ml_module: 'mlApp', + tag: 'tagApp', }; export const ServiceIcons: Record = { diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts index 0f2d7b6679bf..50c0239cd8c5 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts @@ -39,6 +39,7 @@ const KibanaSavedObjectTypeMapping: Record { diff --git a/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.test.ts b/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.test.ts index 9f8ac01afe6c..845e4f1d2670 100644 --- a/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.test.ts @@ -97,6 +97,7 @@ describe('storedPackagePoliciesToAgentPermissions()', () => { lens: [], security_rule: [], ml_module: [], + tag: [], }, elasticsearch: { component_template: [], @@ -207,6 +208,7 @@ describe('storedPackagePoliciesToAgentPermissions()', () => { lens: [], security_rule: [], ml_module: [], + tag: [], }, elasticsearch: { component_template: [], @@ -323,6 +325,7 @@ describe('storedPackagePoliciesToAgentPermissions()', () => { lens: [], security_rule: [], ml_module: [], + tag: [], }, elasticsearch: { component_template: [], diff --git a/x-pack/plugins/fleet/storybook/context/fixtures/integration.nginx.ts b/x-pack/plugins/fleet/storybook/context/fixtures/integration.nginx.ts index 50262b73a6a4..e0179897a59c 100644 --- a/x-pack/plugins/fleet/storybook/context/fixtures/integration.nginx.ts +++ b/x-pack/plugins/fleet/storybook/context/fixtures/integration.nginx.ts @@ -252,6 +252,7 @@ export const response: GetInfoResponse['response'] = { lens: [], map: [], security_rule: [], + tag: [], }, elasticsearch: { ingest_pipeline: [ diff --git a/x-pack/plugins/fleet/storybook/context/fixtures/integration.okta.ts b/x-pack/plugins/fleet/storybook/context/fixtures/integration.okta.ts index efef00579f4b..387161171485 100644 --- a/x-pack/plugins/fleet/storybook/context/fixtures/integration.okta.ts +++ b/x-pack/plugins/fleet/storybook/context/fixtures/integration.okta.ts @@ -105,6 +105,7 @@ export const response: GetInfoResponse['response'] = { lens: [], ml_module: [], security_rule: [], + tag: [], }, elasticsearch: { ingest_pipeline: [ diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts index e57899531e93..79f3d52821f7 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts @@ -414,6 +414,7 @@ const expectAssetsInstalled = ({ id: 'sample_dashboard', }); expect(resDashboard.id).equal('sample_dashboard'); + expect(resDashboard.references.map((ref: any) => ref.id).includes('sample_tag')).equal(true); const resDashboard2 = await kibanaServer.savedObjects.get({ type: 'dashboard', id: 'sample_dashboard2', @@ -444,6 +445,11 @@ const expectAssetsInstalled = ({ id: 'sample_security_rule', }); expect(resSecurityRule.id).equal('sample_security_rule'); + const resTag = await kibanaServer.savedObjects.get({ + type: 'tag', + id: 'sample_tag', + }); + expect(resTag.id).equal('sample_tag'); const resIndexPattern = await kibanaServer.savedObjects.get({ type: 'index-pattern', id: 'test-*', @@ -521,6 +527,10 @@ const expectAssetsInstalled = ({ id: 'sample_security_rule', type: 'security-rule', }, + { + id: 'sample_tag', + type: 'tag', + }, { id: 'sample_visualization', type: 'visualization', @@ -607,6 +617,7 @@ const expectAssetsInstalled = ({ { id: '4c758d70-ecf1-56b3-b704-6d8374841b34', type: 'epm-packages-assets' }, { id: 'e786cbd9-0f3b-5a0b-82a6-db25145ebf58', type: 'epm-packages-assets' }, { id: 'd8b175c3-0d42-5ec7-90c1-d1e4b307a4c2', type: 'epm-packages-assets' }, + { id: 'b265a5e0-c00b-5eda-ac44-2ddbd36d9ad0', type: 'epm-packages-assets' }, { id: '53c94591-aa33-591d-8200-cd524c2a0561', type: 'epm-packages-assets' }, { id: 'b658d2d4-752e-54b8-afc2-4c76155c1466', type: 'epm-packages-assets' }, ], diff --git a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts index f46dcdb761e6..528231216414 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/update_assets.ts @@ -339,6 +339,10 @@ export default function (providerContext: FtrProviderContext) { id: 'sample_ml_module', type: 'ml-module', }, + { + id: 'sample_tag', + type: 'tag', + }, ], installed_es: [ { @@ -418,6 +422,7 @@ export default function (providerContext: FtrProviderContext) { { id: '4281a436-45a8-54ab-9724-fda6849f789d', type: 'epm-packages-assets' }, { id: '2e56f08b-1d06-55ed-abee-4708e1ccf0aa', type: 'epm-packages-assets' }, { id: '4035007b-9c33-5227-9803-2de8a17523b5', type: 'epm-packages-assets' }, + { id: 'e6ae7d31-6920-5408-9219-91ef1662044b', type: 'epm-packages-assets' }, { id: 'c7bf1a39-e057-58a0-afde-fb4b48751d8c', type: 'epm-packages-assets' }, { id: '8c665f28-a439-5f43-b5fd-8fda7b576735', type: 'epm-packages-assets' }, ], diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/dashboard/sample_dashboard.json b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/dashboard/sample_dashboard.json index 7f416c26cc9a..c75dd7673dc3 100644 --- a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/dashboard/sample_dashboard.json +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/dashboard/sample_dashboard.json @@ -15,7 +15,8 @@ { "id": "sample_visualization", "name": "panel_0", "type": "visualization" }, { "id": "sample_search", "name": "panel_1", "type": "search" }, { "id": "sample_search", "name": "panel_2", "type": "search" }, - { "id": "sample_visualization", "name": "panel_3", "type": "visualization" } + { "id": "sample_visualization", "name": "panel_3", "type": "visualization" }, + { "id": "sample_tag", "type": "tag", "name": "tag-ref-sample_tag" } ], "id": "sample_dashboard", "type": "dashboard" diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/tag/sample_tag.json b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/tag/sample_tag.json new file mode 100644 index 000000000000..c6494d42679b --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/kibana/tag/sample_tag.json @@ -0,0 +1,17 @@ +{ + "id": "sample_tag", + "type": "tag", + "namespaces": [ + "default" + ], + "attributes": { + "name": "my tag", + "description": "", + "color": "#a80853" + }, + "references": [], + "migrationVersion": { + "tag": "8.0.0" + }, + "coreMigrationVersion": "8.0.0" +} \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/dashboard/sample_dashboard.json b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/dashboard/sample_dashboard.json index 4513c07f2778..1215a934c636 100644 --- a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/dashboard/sample_dashboard.json +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/dashboard/sample_dashboard.json @@ -15,7 +15,8 @@ { "id": "sample_visualization", "name": "panel_0", "type": "visualization" }, { "id": "sample_search2", "name": "panel_1", "type": "search" }, { "id": "sample_search2", "name": "panel_2", "type": "search" }, - { "id": "sample_visualization", "name": "panel_3", "type": "visualization" } + { "id": "sample_visualization", "name": "panel_3", "type": "visualization" }, + { "id": "sample_tag", "type": "tag", "name": "tag-ref-sample_tag" } ], "id": "sample_dashboard", "type": "dashboard" diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/tag/sample_tag.json b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/tag/sample_tag.json new file mode 100644 index 000000000000..c6494d42679b --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/kibana/tag/sample_tag.json @@ -0,0 +1,17 @@ +{ + "id": "sample_tag", + "type": "tag", + "namespaces": [ + "default" + ], + "attributes": { + "name": "my tag", + "description": "", + "color": "#a80853" + }, + "references": [], + "migrationVersion": { + "tag": "8.0.0" + }, + "coreMigrationVersion": "8.0.0" +} \ No newline at end of file diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.2.0/manifest.yml b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.2.0/manifest.yml index c92f0ab5ae7f..c473ce29b87d 100644 --- a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.2.0/manifest.yml +++ b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/error_handling/0.2.0/manifest.yml @@ -17,3 +17,4 @@ requirement: icons: - src: '/img/logo_overrides_64_color.svg' size: '16x16' + type: 'image/svg+xml' From 25fef38f123dc6c7b5e6c94f7268d42a248b5db7 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Mon, 11 Oct 2021 04:19:00 -0500 Subject: [PATCH 34/74] [fleet][ui] Fix offset image; scrollbar flashing; missing assets in Stories (#114406) --- packages/kbn-storybook/src/lib/run_storybook_cli.ts | 6 +++++- .../plugins/fleet/public/applications/integrations/app.tsx | 6 ++---- .../public/applications/integrations/layouts/default.tsx | 2 ++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/kbn-storybook/src/lib/run_storybook_cli.ts b/packages/kbn-storybook/src/lib/run_storybook_cli.ts index 24a3e4511f7b..93197a1f2b31 100644 --- a/packages/kbn-storybook/src/lib/run_storybook_cli.ts +++ b/packages/kbn-storybook/src/lib/run_storybook_cli.ts @@ -36,7 +36,11 @@ export function runStorybookCli({ configDir, name }: { configDir: string; name: async ({ flags, log }) => { log.debug('Global config:\n', constants); - const staticDir = [UiSharedDepsNpm.distDir, UiSharedDepsSrc.distDir]; + const staticDir = [ + UiSharedDepsNpm.distDir, + UiSharedDepsSrc.distDir, + 'src/plugins/kibana_react/public/assets:plugins/kibanaReact/assets', + ]; const config: Record = { configDir, mode: flags.site ? 'static' : 'dev', diff --git a/x-pack/plugins/fleet/public/applications/integrations/app.tsx b/x-pack/plugins/fleet/public/applications/integrations/app.tsx index c5cc1e1892ed..b10cef9d3ffe 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/app.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/app.tsx @@ -39,15 +39,13 @@ import { Error, Loading, SettingFlyout } from './components'; import type { UIExtensionsStorage } from './types'; import { EPMApp } from './sections/epm'; -import { DefaultLayout, WithoutHeaderLayout } from './layouts'; +import { DefaultLayout } from './layouts'; import { PackageInstallProvider } from './hooks'; import { useBreadcrumbs, UIExtensionsContext } from './hooks'; const ErrorLayout = ({ children }: { children: JSX.Element }) => ( - - {children} - + {children} ); diff --git a/x-pack/plugins/fleet/public/applications/integrations/layouts/default.tsx b/x-pack/plugins/fleet/public/applications/integrations/layouts/default.tsx index e4de48a85c35..70e55c9bd56b 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/layouts/default.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/layouts/default.tsx @@ -28,6 +28,8 @@ interface Props { const Illustration = styled(EuiImage)` margin-bottom: -68px; + position: relative; + top: -20px; width: 80%; `; From cba91fdaab4153c4a7257c506f187c30755b3031 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Mon, 11 Oct 2021 11:48:10 +0200 Subject: [PATCH 35/74] Status service: improve overall status summary (#114228) * improve getSummaryStatus * fix unit tests --- .../server/status/get_summary_status.test.ts | 153 +++++++++--------- src/core/server/status/get_summary_status.ts | 40 +++-- src/core/server/status/plugins_status.test.ts | 32 ++-- src/core/server/status/status_service.test.ts | 26 +-- 4 files changed, 127 insertions(+), 124 deletions(-) diff --git a/src/core/server/status/get_summary_status.test.ts b/src/core/server/status/get_summary_status.test.ts index 33b2e6f7913a..2c91aa8c7b16 100644 --- a/src/core/server/status/get_summary_status.test.ts +++ b/src/core/server/status/get_summary_status.test.ts @@ -81,93 +81,86 @@ describe('getSummaryStatus', () => { }); describe('summary', () => { - describe('when a single service is at highest level', () => { - it('returns all information about that single service', () => { - expect( - getSummaryStatus( - Object.entries({ - s1: degraded, - s2: { - level: ServiceStatusLevels.unavailable, - summary: 'Lorem ipsum', - meta: { - custom: { data: 'here' }, - }, + it('returns correct summary when a single service is affected', () => { + expect( + getSummaryStatus( + Object.entries({ + s1: degraded, + s2: { + level: ServiceStatusLevels.unavailable, + summary: 'Lorem ipsum', + meta: { + custom: { data: 'here' }, }, - }) - ) - ).toEqual({ - level: ServiceStatusLevels.unavailable, - summary: '[s2]: Lorem ipsum', - detail: 'See the status page for more information', - meta: { - affectedServices: ['s2'], - }, - }); + }, + }) + ) + ).toEqual({ + level: ServiceStatusLevels.unavailable, + summary: '1 service is unavailable: s2', + detail: 'See the status page for more information', + meta: { + affectedServices: ['s2'], + }, }); + }); - it('allows the single service to override the detail and documentationUrl fields', () => { - expect( - getSummaryStatus( - Object.entries({ - s1: degraded, - s2: { - level: ServiceStatusLevels.unavailable, - summary: 'Lorem ipsum', - detail: 'Vivamus pulvinar sem ac luctus ultrices.', - documentationUrl: 'http://helpmenow.com/problem1', - meta: { - custom: { data: 'here' }, - }, + it('returns correct summary when multiple services are affected', () => { + expect( + getSummaryStatus( + Object.entries({ + s1: degraded, + s2: { + level: ServiceStatusLevels.unavailable, + summary: 'Lorem ipsum', + detail: 'Vivamus pulvinar sem ac luctus ultrices.', + documentationUrl: 'http://helpmenow.com/problem1', + meta: { + custom: { data: 'here' }, + }, + }, + s3: { + level: ServiceStatusLevels.unavailable, + summary: 'Proin mattis', + detail: 'Nunc quis nulla at mi lobortis pretium.', + documentationUrl: 'http://helpmenow.com/problem2', + meta: { + other: { data: 'over there' }, }, - }) - ) - ).toEqual({ - level: ServiceStatusLevels.unavailable, - summary: '[s2]: Lorem ipsum', - detail: 'Vivamus pulvinar sem ac luctus ultrices.', - documentationUrl: 'http://helpmenow.com/problem1', - meta: { - affectedServices: ['s2'], - }, - }); + }, + }) + ) + ).toEqual({ + level: ServiceStatusLevels.unavailable, + summary: '2 services are unavailable: s2, s3', + detail: 'See the status page for more information', + meta: { + affectedServices: ['s2', 's3'], + }, }); }); - describe('when multiple services is at highest level', () => { - it('returns aggregated information about the affected services', () => { - expect( - getSummaryStatus( - Object.entries({ - s1: degraded, - s2: { - level: ServiceStatusLevels.unavailable, - summary: 'Lorem ipsum', - detail: 'Vivamus pulvinar sem ac luctus ultrices.', - documentationUrl: 'http://helpmenow.com/problem1', - meta: { - custom: { data: 'here' }, - }, - }, - s3: { - level: ServiceStatusLevels.unavailable, - summary: 'Proin mattis', - detail: 'Nunc quis nulla at mi lobortis pretium.', - documentationUrl: 'http://helpmenow.com/problem2', - meta: { - other: { data: 'over there' }, - }, - }, - }) - ) - ).toEqual({ - level: ServiceStatusLevels.unavailable, - summary: '[2] services are unavailable', - detail: 'See the status page for more information', - meta: { - affectedServices: ['s2', 's3'], - }, - }); + it('returns correct summary more than `maxServices` services are affected', () => { + expect( + getSummaryStatus( + Object.entries({ + s1: degraded, + s2: available, + s3: degraded, + s4: degraded, + s5: degraded, + s6: available, + s7: degraded, + }), + { maxServices: 3 } + ) + ).toEqual({ + level: ServiceStatusLevels.degraded, + summary: '5 services are degraded: s1, s3, s4 and 2 other(s)', + detail: 'See the status page for more information', + meta: { + affectedServices: ['s1', 's3', 's4', 's5', 's7'], + }, }); }); }); diff --git a/src/core/server/status/get_summary_status.ts b/src/core/server/status/get_summary_status.ts index 9124023148dd..1dc939ce3f80 100644 --- a/src/core/server/status/get_summary_status.ts +++ b/src/core/server/status/get_summary_status.ts @@ -10,11 +10,13 @@ import { ServiceStatus, ServiceStatusLevels, ServiceStatusLevel } from './types' /** * Returns a single {@link ServiceStatus} that summarizes the most severe status level from a group of statuses. - * @param statuses */ export const getSummaryStatus = ( statuses: Array<[string, ServiceStatus]>, - { allAvailableSummary = `All services are available` }: { allAvailableSummary?: string } = {} + { + allAvailableSummary = `All services are available`, + maxServices = 3, + }: { allAvailableSummary?: string; maxServices?: number } = {} ): ServiceStatus => { const { highestLevel, highestStatuses } = highestLevelSummary(statuses); @@ -23,30 +25,38 @@ export const getSummaryStatus = ( level: ServiceStatusLevels.available, summary: allAvailableSummary, }; - } else if (highestStatuses.length === 1) { - const [serviceName, status] = highestStatuses[0]! as [string, ServiceStatus]; - return { - ...status, - summary: `[${serviceName}]: ${status.summary!}`, - // TODO: include URL to status page - detail: status.detail ?? `See the status page for more information`, - meta: { - affectedServices: [serviceName], - }, - }; } else { + const affectedServices = highestStatuses.map(([serviceName]) => serviceName); return { level: highestLevel, - summary: `[${highestStatuses.length}] services are ${highestLevel.toString()}`, + summary: getSummaryContent(affectedServices, highestLevel, maxServices), // TODO: include URL to status page detail: `See the status page for more information`, meta: { - affectedServices: highestStatuses.map(([serviceName]) => serviceName), + affectedServices, }, }; } }; +const getSummaryContent = ( + affectedServices: string[], + statusLevel: ServiceStatusLevel, + maxServices: number +): string => { + const serviceCount = affectedServices.length; + if (serviceCount === 1) { + return `1 service is ${statusLevel.toString()}: ${affectedServices[0]}`; + } else if (serviceCount > maxServices) { + const exceedingCount = serviceCount - maxServices; + return `${serviceCount} services are ${statusLevel.toString()}: ${affectedServices + .slice(0, maxServices) + .join(', ')} and ${exceedingCount} other(s)`; + } else { + return `${serviceCount} services are ${statusLevel.toString()}: ${affectedServices.join(', ')}`; + } +}; + type StatusPair = [string, ServiceStatus]; const highestLevelSummary = ( diff --git a/src/core/server/status/plugins_status.test.ts b/src/core/server/status/plugins_status.test.ts index b7c0733de728..0befbf63bd18 100644 --- a/src/core/server/status/plugins_status.test.ts +++ b/src/core/server/status/plugins_status.test.ts @@ -73,7 +73,7 @@ describe('PluginStatusService', () => { }); expect(await serviceDegraded.getDerivedStatus$('a').pipe(first()).toPromise()).toEqual({ level: ServiceStatusLevels.degraded, - summary: '[savedObjects]: savedObjects degraded', + summary: '1 service is degraded: savedObjects', detail: 'See the status page for more information', meta: expect.any(Object), }); @@ -84,7 +84,7 @@ describe('PluginStatusService', () => { }); expect(await serviceCritical.getDerivedStatus$('a').pipe(first()).toPromise()).toEqual({ level: ServiceStatusLevels.critical, - summary: '[elasticsearch]: elasticsearch critical', + summary: '1 service is critical: elasticsearch', detail: 'See the status page for more information', meta: expect.any(Object), }); @@ -95,7 +95,7 @@ describe('PluginStatusService', () => { service.set('a', of({ level: ServiceStatusLevels.degraded, summary: 'a is degraded' })); expect(await service.getDerivedStatus$('b').pipe(first()).toPromise()).toEqual({ level: ServiceStatusLevels.degraded, - summary: '[2] services are degraded', + summary: '2 services are degraded: savedObjects, a', detail: 'See the status page for more information', meta: expect.any(Object), }); @@ -106,7 +106,7 @@ describe('PluginStatusService', () => { service.set('a', of({ level: ServiceStatusLevels.unavailable, summary: 'a is not working' })); expect(await service.getDerivedStatus$('b').pipe(first()).toPromise()).toEqual({ level: ServiceStatusLevels.unavailable, - summary: '[a]: a is not working', + summary: '1 service is unavailable: a', detail: 'See the status page for more information', meta: expect.any(Object), }); @@ -120,7 +120,7 @@ describe('PluginStatusService', () => { service.set('a', of({ level: ServiceStatusLevels.unavailable, summary: 'a is not working' })); expect(await service.getDerivedStatus$('b').pipe(first()).toPromise()).toEqual({ level: ServiceStatusLevels.critical, - summary: '[elasticsearch]: elasticsearch critical', + summary: '1 service is critical: elasticsearch', detail: 'See the status page for more information', meta: expect.any(Object), }); @@ -132,7 +132,7 @@ describe('PluginStatusService', () => { service.set('b', of({ level: ServiceStatusLevels.unavailable, summary: 'b is not working' })); expect(await service.getDerivedStatus$('c').pipe(first()).toPromise()).toEqual({ level: ServiceStatusLevels.unavailable, - summary: '[b]: b is not working', + summary: '1 service is unavailable: b', detail: 'See the status page for more information', meta: expect.any(Object), }); @@ -166,19 +166,19 @@ describe('PluginStatusService', () => { expect(await serviceDegraded.getAll$().pipe(first()).toPromise()).toEqual({ a: { level: ServiceStatusLevels.degraded, - summary: '[savedObjects]: savedObjects degraded', + summary: '1 service is degraded: savedObjects', detail: 'See the status page for more information', meta: expect.any(Object), }, b: { level: ServiceStatusLevels.degraded, - summary: '[savedObjects]: savedObjects degraded', + summary: '1 service is degraded: savedObjects', detail: 'See the status page for more information', meta: expect.any(Object), }, c: { level: ServiceStatusLevels.degraded, - summary: '[savedObjects]: savedObjects degraded', + summary: '1 service is degraded: savedObjects', detail: 'See the status page for more information', meta: expect.any(Object), }, @@ -191,19 +191,19 @@ describe('PluginStatusService', () => { expect(await serviceCritical.getAll$().pipe(first()).toPromise()).toEqual({ a: { level: ServiceStatusLevels.critical, - summary: '[elasticsearch]: elasticsearch critical', + summary: '1 service is critical: elasticsearch', detail: 'See the status page for more information', meta: expect.any(Object), }, b: { level: ServiceStatusLevels.critical, - summary: '[elasticsearch]: elasticsearch critical', + summary: '1 service is critical: elasticsearch', detail: 'See the status page for more information', meta: expect.any(Object), }, c: { level: ServiceStatusLevels.critical, - summary: '[elasticsearch]: elasticsearch critical', + summary: '1 service is critical: elasticsearch', detail: 'See the status page for more information', meta: expect.any(Object), }, @@ -218,13 +218,13 @@ describe('PluginStatusService', () => { a: { level: ServiceStatusLevels.available, summary: 'a status' }, // a is available depsite savedObjects being degraded b: { level: ServiceStatusLevels.degraded, - summary: '[savedObjects]: savedObjects degraded', + summary: '1 service is degraded: savedObjects', detail: 'See the status page for more information', meta: expect.any(Object), }, c: { level: ServiceStatusLevels.degraded, - summary: '[2] services are degraded', + summary: '2 services are degraded: savedObjects, b', detail: 'See the status page for more information', meta: expect.any(Object), }, @@ -298,7 +298,7 @@ describe('PluginStatusService', () => { a: { level: ServiceStatusLevels.unavailable, summary: 'Status check timed out after 30s' }, b: { level: ServiceStatusLevels.unavailable, - summary: '[a]: Status check timed out after 30s', + summary: '1 service is unavailable: a', detail: 'See the status page for more information', meta: { affectedServices: ['a'], @@ -341,7 +341,7 @@ describe('PluginStatusService', () => { a: { level: ServiceStatusLevels.available, summary: 'a status' }, // a is available depsite savedObjects being degraded b: { level: ServiceStatusLevels.degraded, - summary: '[savedObjects]: savedObjects degraded', + summary: '1 service is degraded: savedObjects', detail: 'See the status page for more information', meta: expect.any(Object), }, diff --git a/src/core/server/status/status_service.test.ts b/src/core/server/status/status_service.test.ts index 255ed821bc2f..dfd0ff9a7e10 100644 --- a/src/core/server/status/status_service.test.ts +++ b/src/core/server/status/status_service.test.ts @@ -188,7 +188,7 @@ describe('StatusService', () => { ); expect(await setup.overall$.pipe(first()).toPromise()).toMatchObject({ level: ServiceStatusLevels.degraded, - summary: '[2] services are degraded', + summary: '2 services are degraded: elasticsearch, savedObjects', }); }); @@ -208,15 +208,15 @@ describe('StatusService', () => { const subResult3 = await setup.overall$.pipe(first()).toPromise(); expect(subResult1).toMatchObject({ level: ServiceStatusLevels.degraded, - summary: '[2] services are degraded', + summary: '2 services are degraded: elasticsearch, savedObjects', }); expect(subResult2).toMatchObject({ level: ServiceStatusLevels.degraded, - summary: '[2] services are degraded', + summary: '2 services are degraded: elasticsearch, savedObjects', }); expect(subResult3).toMatchObject({ level: ServiceStatusLevels.degraded, - summary: '[2] services are degraded', + summary: '2 services are degraded: elasticsearch, savedObjects', }); }); @@ -265,7 +265,7 @@ describe('StatusService', () => { "savedObjects", ], }, - "summary": "[savedObjects]: This is degraded!", + "summary": "1 service is degraded: savedObjects", }, Object { "level": available, @@ -315,7 +315,7 @@ describe('StatusService', () => { "savedObjects", ], }, - "summary": "[savedObjects]: This is degraded!", + "summary": "1 service is degraded: savedObjects", }, Object { "level": available, @@ -340,7 +340,7 @@ describe('StatusService', () => { ); expect(await setup.coreOverall$.pipe(first()).toPromise()).toMatchObject({ level: ServiceStatusLevels.degraded, - summary: '[2] services are degraded', + summary: '2 services are degraded: elasticsearch, savedObjects', }); }); @@ -357,7 +357,7 @@ describe('StatusService', () => { ); expect(await setup.coreOverall$.pipe(first()).toPromise()).toMatchObject({ level: ServiceStatusLevels.critical, - summary: '[savedObjects]: This is critical!', + summary: '1 service is critical: savedObjects', }); }); @@ -379,15 +379,15 @@ describe('StatusService', () => { expect(subResult1).toMatchObject({ level: ServiceStatusLevels.degraded, - summary: '[2] services are degraded', + summary: '2 services are degraded: elasticsearch, savedObjects', }); expect(subResult2).toMatchObject({ level: ServiceStatusLevels.degraded, - summary: '[2] services are degraded', + summary: '2 services are degraded: elasticsearch, savedObjects', }); expect(subResult3).toMatchObject({ level: ServiceStatusLevels.degraded, - summary: '[2] services are degraded', + summary: '2 services are degraded: elasticsearch, savedObjects', }); }); @@ -436,7 +436,7 @@ describe('StatusService', () => { "savedObjects", ], }, - "summary": "[savedObjects]: This is degraded!", + "summary": "1 service is degraded: savedObjects", }, Object { "level": available, @@ -486,7 +486,7 @@ describe('StatusService', () => { "savedObjects", ], }, - "summary": "[savedObjects]: This is degraded!", + "summary": "1 service is degraded: savedObjects", }, Object { "level": available, From 9d2c536ccb717bc439c957f6cea1b7c16f217529 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Mon, 11 Oct 2021 11:49:20 +0200 Subject: [PATCH 36/74] [Discover] Unskip Painless date functional test (#114224) * [Discover] Unskip functional test * Remove comment Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- test/functional/apps/management/_scripted_fields.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/functional/apps/management/_scripted_fields.js b/test/functional/apps/management/_scripted_fields.js index 2e965c275d6d..72f45e1fedb4 100644 --- a/test/functional/apps/management/_scripted_fields.js +++ b/test/functional/apps/management/_scripted_fields.js @@ -367,8 +367,7 @@ export default function ({ getService, getPageObjects }) { }); }); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/113745 - describe.skip('creating and using Painless date scripted fields', function describeIndexTests() { + describe('creating and using Painless date scripted fields', function describeIndexTests() { const scriptedPainlessFieldName2 = 'painDate'; it('should create scripted field', async function () { @@ -384,7 +383,7 @@ export default function ({ getService, getPageObjects }) { 'date', { format: 'date', datePattern: 'YYYY-MM-DD HH:00' }, '1', - "doc['utc_time'].value.getMillis() + (1000) * 60 * 60" + "doc['utc_time'].value.toEpochMilli() + (1000) * 60 * 60" ); await retry.try(async function () { expect(parseInt(await PageObjects.settings.getScriptedFieldsTabCount())).to.be( From 1bf09e6930daa027b54f33a1477caa4d3af8310f Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Mon, 11 Oct 2021 12:32:19 +0200 Subject: [PATCH 37/74] [Lens] Thresholds: Add text to markers body (#113629) * :bug: Add padding to the tick label to fit threshold markers * :bug: Better icon detection * :bug: Fix edge cases with no title or labels * :camera_flash: Update snapshots * :sparkles: Add icon placement flag * :sparkles: Sync padding computation with marker positioning * :ok_hand: Make disabled when no icon is selected * :sparkles: First text on marker implementation * :bug: Fix some edge cases with auto positioning * Update x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx Co-authored-by: Michael Marcialis * :bug: Fix minor details * :lipstick: Small tweak * :sparkles: Reduce the padding if no icon is shown on the axis * :white_check_mark: Fix broken unit tests * :lipstick: Fix vertical text centering * :rotating_light: Fix linting issue * :bug: Fix issue * :lipstick: Reorder panel inputs * :lipstick: Move styling to sass * :ok_hand: Address feedback Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Michael Marcialis --- .../expressions/xy_chart/axis_config.ts | 5 + .../dimension_panel/dimension_editor.tsx | 32 ++-- .../dimension_panel/dimension_panel.test.tsx | 20 ++- .../public/xy_visualization/expression.tsx | 1 + .../expression_thresholds.scss | 18 +++ .../expression_thresholds.tsx | 134 +++++++++++++---- .../public/xy_visualization/to_expression.ts | 10 +- .../xy_config_panel/threshold_panel.tsx | 137 +++++++++++------- 8 files changed, 248 insertions(+), 109 deletions(-) create mode 100644 x-pack/plugins/lens/public/xy_visualization/expression_thresholds.scss diff --git a/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts b/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts index 47bb1f91b4ab..9ff1b5a4dc3f 100644 --- a/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts +++ b/x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts @@ -41,6 +41,7 @@ export interface YConfig { lineStyle?: LineStyle; fill?: FillStyle; iconPosition?: IconPosition; + textVisibility?: boolean; } export type AxisTitlesVisibilityConfigResult = AxesSettingsConfig & { @@ -187,6 +188,10 @@ export const yAxisConfig: ExpressionFunctionDefinition< options: ['auto', 'above', 'below', 'left', 'right'], help: 'The placement of the icon for the threshold line', }, + textVisibility: { + types: ['boolean'], + help: 'Visibility of the label on the threshold line', + }, fill: { types: ['string'], options: ['none', 'above', 'below'], diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index 29bbe6a96b9e..2f1c00bc5cca 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -107,16 +107,19 @@ export function DimensionEditor(props: DimensionEditorProps) { ); const setStateWrapper = ( - setter: IndexPatternLayer | ((prevLayer: IndexPatternLayer) => IndexPatternLayer) + setter: IndexPatternLayer | ((prevLayer: IndexPatternLayer) => IndexPatternLayer), + options: { forceRender?: boolean } = {} ) => { const hypotheticalLayer = typeof setter === 'function' ? setter(state.layers[layerId]) : setter; + const isDimensionComplete = Boolean(hypotheticalLayer.columns[columnId]); setState( (prevState) => { const layer = typeof setter === 'function' ? setter(prevState.layers[layerId]) : setter; return mergeLayer({ state: prevState, layerId, newLayer: layer }); }, { - isDimensionComplete: Boolean(hypotheticalLayer.columns[columnId]), + isDimensionComplete, + ...options, } ); }; @@ -169,20 +172,8 @@ export function DimensionEditor(props: DimensionEditorProps) { ) => { if (temporaryStaticValue) { setTemporaryState('none'); - if (typeof setter === 'function') { - return setState( - (prevState) => { - const layer = setter(addStaticValueColumn(prevState.layers[layerId])); - return mergeLayer({ state: prevState, layerId, newLayer: layer }); - }, - { - isDimensionComplete: true, - forceRender: true, - } - ); - } } - return setStateWrapper(setter); + return setStateWrapper(setter, { forceRender: true }); }; const ParamEditor = getParamEditor( @@ -314,7 +305,7 @@ export function DimensionEditor(props: DimensionEditorProps) { temporaryQuickFunction && isQuickFunction(newLayer.columns[columnId].operationType) ) { - // Only switch the tab once the formula is fully removed + // Only switch the tab once the "non quick function" is fully removed setTemporaryState('none'); } setStateWrapper(newLayer); @@ -344,13 +335,12 @@ export function DimensionEditor(props: DimensionEditorProps) { visualizationGroups: dimensionGroups, targetGroup: props.groupId, }); - // ); } if ( temporaryQuickFunction && isQuickFunction(newLayer.columns[columnId].operationType) ) { - // Only switch the tab once the formula is fully removed + // Only switch the tab once the "non quick function" is fully removed setTemporaryState('none'); } setStateWrapper(newLayer); @@ -508,6 +498,9 @@ export function DimensionEditor(props: DimensionEditorProps) { } incompleteOperation={incompleteOperation} onChoose={(choice) => { + if (temporaryQuickFunction) { + setTemporaryState('none'); + } setStateWrapper( insertOrReplaceColumn({ layer: state.layers[layerId], @@ -518,7 +511,8 @@ export function DimensionEditor(props: DimensionEditorProps) { visualizationGroups: dimensionGroups, targetGroup: props.groupId, incompleteParams, - }) + }), + { forceRender: temporaryQuickFunction } ); }} /> diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 6df4360aeac4..6d9e1ae3fe81 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -513,7 +513,10 @@ describe('IndexPatternDimensionEditorPanel', () => { comboBox.prop('onChange')!([option]); }); - expect(setState.mock.calls[0]).toEqual([expect.any(Function), { isDimensionComplete: true }]); + expect(setState.mock.calls[0]).toEqual([ + expect.any(Function), + { isDimensionComplete: true, forceRender: false }, + ]); expect(setState.mock.calls[0][0](defaultProps.state)).toEqual({ ...initialState, layers: { @@ -545,7 +548,10 @@ describe('IndexPatternDimensionEditorPanel', () => { comboBox.prop('onChange')!([option]); }); - expect(setState.mock.calls[0]).toEqual([expect.any(Function), { isDimensionComplete: true }]); + expect(setState.mock.calls[0]).toEqual([ + expect.any(Function), + { isDimensionComplete: true, forceRender: false }, + ]); expect(setState.mock.calls[0][0](defaultProps.state)).toEqual({ ...state, layers: { @@ -1037,7 +1043,10 @@ describe('IndexPatternDimensionEditorPanel', () => { }); expect(setState.mock.calls.length).toEqual(2); - expect(setState.mock.calls[1]).toEqual([expect.any(Function), { isDimensionComplete: true }]); + expect(setState.mock.calls[1]).toEqual([ + expect.any(Function), + { isDimensionComplete: true, forceRender: false }, + ]); expect(setState.mock.calls[1][0](state)).toEqual({ ...state, layers: { @@ -1921,7 +1930,10 @@ describe('IndexPatternDimensionEditorPanel', () => { comboBox.prop('onChange')!([option]); }); - expect(setState.mock.calls[0]).toEqual([expect.any(Function), { isDimensionComplete: true }]); + expect(setState.mock.calls[0]).toEqual([ + expect.any(Function), + { isDimensionComplete: true, forceRender: false }, + ]); expect(setState.mock.calls[0][0](defaultProps.state)).toEqual({ ...state, layers: { diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index 484032e5ffbd..5dfad58f5001 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -924,6 +924,7 @@ export function XYChart({ right: Boolean(yAxesMap.right), }} isHorizontal={shouldRotate} + thresholdPaddingMap={thresholdPaddings} /> ) : null} diff --git a/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.scss b/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.scss new file mode 100644 index 000000000000..41b30ce40676 --- /dev/null +++ b/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.scss @@ -0,0 +1,18 @@ +.lnsXyDecorationRotatedWrapper { + display: inline-block; + overflow: hidden; + line-height: $euiLineHeight; + + .lnsXyDecorationRotatedWrapper__label { + display: inline-block; + white-space: nowrap; + transform: translate(0, 100%) rotate(-90deg); + transform-origin: 0 0; + + &::after { + content: ''; + float: left; + margin-top: 100%; + } + } +} \ No newline at end of file diff --git a/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.tsx b/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.tsx index 7532d41f091d..67e994b734b8 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression_thresholds.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import './expression_thresholds.scss'; import React from 'react'; import { groupBy } from 'lodash'; import { EuiIcon } from '@elastic/eui'; @@ -14,8 +15,9 @@ import type { FieldFormat } from 'src/plugins/field_formats/common'; import { euiLightVars } from '@kbn/ui-shared-deps-src/theme'; import type { LayerArgs, YConfig } from '../../common/expressions'; import type { LensMultiTable } from '../../common/types'; +import { hasIcon } from './xy_config_panel/threshold_panel'; -const THRESHOLD_ICON_SIZE = 20; +const THRESHOLD_MARKER_SIZE = 20; export const computeChartMargins = ( thresholdPaddings: Partial>, @@ -51,27 +53,35 @@ export const computeChartMargins = ( return result; }; -function hasIcon(icon: string | undefined): icon is string { - return icon != null && icon !== 'none'; -} - // Note: it does not take into consideration whether the threshold is in view or not export const getThresholdRequiredPaddings = ( thresholdLayers: LayerArgs[], axesMap: Record<'left' | 'right', unknown> ) => { - const positions = Object.keys(Position); - return thresholdLayers.reduce((memo, layer) => { - if (positions.some((pos) => !(pos in memo))) { - layer.yConfig?.forEach(({ axisMode, icon, iconPosition }) => { - if (axisMode && hasIcon(icon)) { - const placement = getBaseIconPlacement(iconPosition, axisMode, axesMap); - memo[placement] = THRESHOLD_ICON_SIZE; - } - }); + // collect all paddings for the 4 axis: if any text is detected double it. + const paddings: Partial> = {}; + const icons: Partial> = {}; + thresholdLayers.forEach((layer) => { + layer.yConfig?.forEach(({ axisMode, icon, iconPosition, textVisibility }) => { + if (axisMode && (hasIcon(icon) || textVisibility)) { + const placement = getBaseIconPlacement(iconPosition, axisMode, axesMap); + paddings[placement] = Math.max( + paddings[placement] || 0, + THRESHOLD_MARKER_SIZE * (textVisibility ? 2 : 1) // double the padding size if there's text + ); + icons[placement] = (icons[placement] || 0) + (hasIcon(icon) ? 1 : 0); + } + }); + }); + // post-process the padding based on the icon presence: + // if no icon is present for the placement, just reduce the padding + (Object.keys(paddings) as Position[]).forEach((placement) => { + if (!icons[placement]) { + paddings[placement] = THRESHOLD_MARKER_SIZE; } - return memo; - }, {} as Partial>); + }); + + return paddings; }; function mapVerticalToHorizontalPlacement(placement: Position) { @@ -117,17 +127,57 @@ function getBaseIconPlacement( return Position.Top; } -function getIconPlacement( - iconPosition: YConfig['iconPosition'], - axisMode: YConfig['axisMode'], - axesMap: Record, - isHorizontal: boolean -) { - const vPosition = getBaseIconPlacement(iconPosition, axisMode, axesMap); +function getMarkerBody(label: string | undefined, isHorizontal: boolean) { + if (!label) { + return; + } if (isHorizontal) { - return mapVerticalToHorizontalPlacement(vPosition); + return ( +
+ {label} +
+ ); + } + return ( +
+
+ {label} +
+
+ ); +} + +function getMarkerToShow( + yConfig: YConfig, + label: string | undefined, + isHorizontal: boolean, + hasReducedPadding: boolean +) { + // show an icon if present + if (hasIcon(yConfig.icon)) { + return ; + } + // if there's some text, check whether to show it as marker, or just show some padding for the icon + if (yConfig.textVisibility) { + if (hasReducedPadding) { + return getMarkerBody( + label, + (!isHorizontal && yConfig.axisMode === 'bottom') || + (isHorizontal && yConfig.axisMode !== 'bottom') + ); + } + return ; } - return vPosition; } export const ThresholdAnnotations = ({ @@ -138,6 +188,7 @@ export const ThresholdAnnotations = ({ syncColors, axesMap, isHorizontal, + thresholdPaddingMap, }: { thresholdLayers: LayerArgs[]; data: LensMultiTable; @@ -146,6 +197,7 @@ export const ThresholdAnnotations = ({ syncColors: boolean; axesMap: Record<'left' | 'right', boolean>; isHorizontal: boolean; + thresholdPaddingMap: Partial>; }) => { return ( <> @@ -180,15 +232,35 @@ export const ThresholdAnnotations = ({ const defaultColor = euiLightVars.euiColorDarkShade; + // get the position for vertical chart + const markerPositionVertical = getBaseIconPlacement( + yConfig.iconPosition, + yConfig.axisMode, + axesMap + ); + // the padding map is built for vertical chart + const hasReducedPadding = + thresholdPaddingMap[markerPositionVertical] === THRESHOLD_MARKER_SIZE; + const props = { groupId, - marker: hasIcon(yConfig.icon) ? : undefined, - markerPosition: getIconPlacement( - yConfig.iconPosition, - yConfig.axisMode, - axesMap, - isHorizontal + marker: getMarkerToShow( + yConfig, + columnToLabelMap[yConfig.forAccessor], + isHorizontal, + hasReducedPadding + ), + markerBody: getMarkerBody( + yConfig.textVisibility && !hasReducedPadding + ? columnToLabelMap[yConfig.forAccessor] + : undefined, + (!isHorizontal && yConfig.axisMode === 'bottom') || + (isHorizontal && yConfig.axisMode !== 'bottom') ), + // rotate the position if required + markerPosition: isHorizontal + ? mapVerticalToHorizontalPlacement(markerPositionVertical) + : markerPositionVertical, }; const annotations = []; diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index bb65b69a8d12..96ea9b84dd98 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -13,6 +13,7 @@ import { OperationMetadata, DatasourcePublicAPI } from '../types'; import { getColumnToLabelMap } from './state_helpers'; import type { ValidLayer, XYLayerConfig } from '../../common/expressions'; import { layerTypes } from '../../common'; +import { hasIcon } from './xy_config_panel/threshold_panel'; import { defaultThresholdColor } from './color_assignment'; export const getSortedAccessors = (datasource: DatasourcePublicAPI, layer: XYLayerConfig) => { @@ -66,6 +67,7 @@ export function toPreviewExpression( ...config, lineWidth: 1, icon: undefined, + textVisibility: false, })), } ), @@ -344,8 +346,12 @@ export const buildExpression = ( lineStyle: [yConfig.lineStyle || 'solid'], lineWidth: [yConfig.lineWidth || 1], fill: [yConfig.fill || 'none'], - icon: yConfig.icon ? [yConfig.icon] : [], - iconPosition: [yConfig.iconPosition || 'auto'], + icon: hasIcon(yConfig.icon) ? [yConfig.icon] : [], + iconPosition: + hasIcon(yConfig.icon) || yConfig.textVisibility + ? [yConfig.iconPosition || 'auto'] + : ['auto'], + textVisibility: [yConfig.textVisibility || false], }, }, ], diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx index cdf5bb2cc2ef..7c31d72e6cbd 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel/threshold_panel.tsx @@ -8,7 +8,14 @@ import './xy_config_panel.scss'; import React, { useCallback, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiButtonGroup, EuiComboBox, EuiFormRow, EuiIcon, EuiRange } from '@elastic/eui'; +import { + EuiButtonGroup, + EuiComboBox, + EuiFormRow, + EuiIcon, + EuiRange, + EuiSwitch, +} from '@elastic/eui'; import type { PaletteRegistry } from 'src/plugins/charts/public'; import type { VisualizationDimensionEditorProps } from '../../types'; import { State, XYState } from '../types'; @@ -177,6 +184,10 @@ function getIconPositionOptions({ return [...options, ...yOptions]; } +export function hasIcon(icon: string | undefined): icon is string { + return icon != null && icon !== 'none'; +} + export const ThresholdPanel = ( props: VisualizationDimensionEditorProps & { formatFactory: FormatFactory; @@ -220,6 +231,78 @@ export const ThresholdPanel = ( return ( <> + + { + setYConfig({ forAccessor: accessor, textVisibility: !currentYConfig?.textVisibility }); + }} + /> + + + { + setYConfig({ forAccessor: accessor, icon: newIcon }); + }} + /> + + + + { + const newMode = id.replace(idPrefix, '') as IconPosition; + setYConfig({ forAccessor: accessor, iconPosition: newMode }); + }} + /> + + - - { - setYConfig({ forAccessor: accessor, icon: newIcon }); - }} - /> - - - - { - const newMode = id.replace(idPrefix, '') as IconPosition; - setYConfig({ forAccessor: accessor, iconPosition: newMode }); - }} - /> - - ); }; From 9a1779d364044dff27672c110f6aa344e61fce15 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 11 Oct 2021 13:37:01 +0300 Subject: [PATCH 38/74] [Visualize] unskip the reporting funtional test (#114094) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/test/functional/apps/visualize/reporting.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/x-pack/test/functional/apps/visualize/reporting.ts b/x-pack/test/functional/apps/visualize/reporting.ts index efffa0b6a692..07ce3d9b2312 100644 --- a/x-pack/test/functional/apps/visualize/reporting.ts +++ b/x-pack/test/functional/apps/visualize/reporting.ts @@ -25,13 +25,16 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'visEditor', ]); - // Failing: See https://github.com/elastic/kibana/issues/113496 - describe.skip('Visualize Reporting Screenshots', () => { + describe('Visualize Reporting Screenshots', () => { before('initialize tests', async () => { log.debug('ReportingPage:initTests'); await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/reporting/ecommerce'); await kibanaServer.importExport.load(ecommerceSOPath); await browser.setWindowSize(1600, 850); + await kibanaServer.uiSettings.replace({ + 'timepicker:timeDefaults': + '{ "from": "2019-04-27T23:56:51.374Z", "to": "2019-08-23T16:18:51.821Z"}', + }); }); after('clean up archives', async () => { await esArchiver.unload('x-pack/test/functional/es_archives/reporting/ecommerce'); @@ -41,6 +44,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { refresh: true, body: { query: { match_all: {} } }, }); + await kibanaServer.uiSettings.unset('timepicker:timeDefaults'); }); describe('Print PDF button', () => { @@ -54,11 +58,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('becomes available when saved', async () => { - await PageObjects.timePicker.timePickerExists(); - const fromTime = 'Apr 27, 2019 @ 23:56:51.374'; - const toTime = 'Aug 23, 2019 @ 16:18:51.821'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - await PageObjects.visEditor.clickBucket('X-axis'); await PageObjects.visEditor.selectAggregation('Date Histogram'); await PageObjects.visEditor.clickGo(); From b2108f4c2c939f7f9ea8f0cf272c6f856d260b0e Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Mon, 11 Oct 2021 11:53:49 +0100 Subject: [PATCH 39/74] Add all APM configuration settings to the documentation (#114139) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add APM configuration settings to the documentation * Rename the deprecated apm_oss.* configurations to xpack.apm.* * Remove new lines * Add ess icon to config settings * Add link to the APM configuration settings docs Co-authored-by: Søren Louv-Jansen Co-authored-by: Søren Louv-Jansen Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/settings/apm-settings.asciidoc | 48 +++++++++++++++++++---------- x-pack/plugins/apm/server/index.ts | 2 ++ 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/docs/settings/apm-settings.asciidoc b/docs/settings/apm-settings.asciidoc index e565bda0dff4..fb96f6835533 100644 --- a/docs/settings/apm-settings.asciidoc +++ b/docs/settings/apm-settings.asciidoc @@ -40,57 +40,71 @@ Changing these settings may disable features of the APM App. [cols="2*<"] |=== -| `xpack.apm.enabled` +| `xpack.apm.enabled` {ess-icon} | deprecated:[7.16.0,"In 8.0 and later, this setting will no longer be supported."] Set to `false` to disable the APM app. Defaults to `true`. -| `xpack.apm.maxServiceEnvironments` +| `xpack.apm.maxServiceEnvironments` {ess-icon} | Maximum number of unique service environments recognized by the UI. Defaults to `100`. -| `xpack.apm.serviceMapFingerprintBucketSize` +| `xpack.apm.serviceMapFingerprintBucketSize` {ess-icon} | Maximum number of unique transaction combinations sampled for generating service map focused on a specific service. Defaults to `100`. -| `xpack.apm.serviceMapFingerprintGlobalBucketSize` +| `xpack.apm.serviceMapFingerprintGlobalBucketSize` {ess-icon} | Maximum number of unique transaction combinations sampled for generating the global service map. Defaults to `100`. +| `xpack.apm.serviceMapEnabled` {ess-icon} + | Set to `false` to disable service maps. Defaults to `true`. + +| `xpack.apm.serviceMapTraceIdBucketSize` {ess-icon} + | Maximum number of trace IDs sampled for generating service map focused on a specific service. Defaults to `65`. + +| `xpack.apm.serviceMapTraceIdGlobalBucketSize` {ess-icon} + | Maximum number of trace IDs sampled for generating the global service map. Defaults to `6`. + +| `xpack.apm.serviceMapMaxTracesPerRequest` {ess-icon} + | Maximum number of traces per request for generating the global service map. Defaults to `50`. + | `xpack.apm.ui.enabled` {ess-icon} | Set to `false` to hide the APM app from the main menu. Defaults to `true`. -| `xpack.apm.ui.transactionGroupBucketSize` +| `xpack.apm.ui.transactionGroupBucketSize` {ess-icon} | Number of top transaction groups displayed in the APM app. Defaults to `1000`. | `xpack.apm.ui.maxTraceItems` {ess-icon} | Maximum number of child items displayed when viewing trace details. Defaults to `1000`. -| `xpack.observability.annotations.index` +| `xpack.observability.annotations.index` {ess-icon} | Index name where Observability annotations are stored. Defaults to `observability-annotations`. -| `xpack.apm.searchAggregatedTransactions` +| `xpack.apm.searchAggregatedTransactions` {ess-icon} | experimental[] Enables Transaction histogram metrics. Defaults to `never` and aggregated transactions are not used. When set to `auto`, the UI will use metric indices over transaction indices for transactions if aggregated transactions are found. When set to `always`, additional configuration in APM Server is required. See {apm-server-ref-v}/transaction-metrics.html[Configure transaction metrics] for more information. -| `apm_oss.indexPattern` {ess-icon} - | The index pattern used for integrations with Machine Learning and Query Bar. - It must match all apm indices. Defaults to `apm-*`. +| `xpack.apm.metricsInterval` {ess-icon} + | Sets a `fixed_interval` for date histograms in metrics aggregations. Defaults to `30`. + +| `xpack.apm.agent.migrations.enabled` {ess-icon} + | Set to `false` to disable cloud APM migrations. Defaults to `true`. -| `apm_oss.errorIndices` {ess-icon} +| `xpack.apm.errorIndices` {ess-icon} | Matcher for all {apm-server-ref}/error-indices.html[error indices]. Defaults to `apm-*`. -| `apm_oss.onboardingIndices` +| `xpack.apm.onboardingIndices` {ess-icon} | Matcher for all onboarding indices. Defaults to `apm-*`. -| `apm_oss.spanIndices` {ess-icon} +| `xpack.apm.spanIndices` {ess-icon} | Matcher for all {apm-server-ref}/span-indices.html[span indices]. Defaults to `apm-*`. -| `apm_oss.transactionIndices` {ess-icon} +| `xpack.apm.transactionIndices` {ess-icon} | Matcher for all {apm-server-ref}/transaction-indices.html[transaction indices]. Defaults to `apm-*`. -| `apm_oss.metricsIndices` +| `xpack.apm.metricsIndices` {ess-icon} | Matcher for all {apm-server-ref}/metricset-indices.html[metrics indices]. Defaults to `apm-*`. -| `apm_oss.sourcemapIndices` +| `xpack.apm.sourcemapIndices` {ess-icon} | Matcher for all {apm-server-ref}/sourcemap-indices.html[source map indices]. Defaults to `apm-*`. |=== -// end::general-apm-settings[] +// end::general-apm-settings[] \ No newline at end of file diff --git a/x-pack/plugins/apm/server/index.ts b/x-pack/plugins/apm/server/index.ts index b7002ff7cbe7..22787b0301ce 100644 --- a/x-pack/plugins/apm/server/index.ts +++ b/x-pack/plugins/apm/server/index.ts @@ -74,6 +74,8 @@ export type APMXPackConfig = TypeOf; export type APMConfig = ReturnType; // plugin config and ui indices settings +// All options should be documented in the APM configuration settings: https://github.com/elastic/kibana/blob/master/docs/settings/apm-settings.asciidoc +// and be included on cloud allow list unless there are specific reasons not to export function mergeConfigs( apmOssConfig: APMOSSConfig, apmConfig: APMXPackConfig From 75983cf45065fc498feb150f4be8e95b6b85886f Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 11 Oct 2021 12:58:59 +0200 Subject: [PATCH 40/74] [Reporting] Update chromium exit behaviour (#113544) * move uncaught exception out of exit$ * reintroduce original error, but as a log instead * change log level: error -> warning. also update copy to make it explicit that the error will be ignored Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../browsers/chromium/driver_factory/index.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts b/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts index 2170b50f195b..a0487421a9a0 100644 --- a/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts +++ b/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts @@ -220,6 +220,17 @@ export class HeadlessChromiumDriverFactory { }) ); + const uncaughtExceptionPageError$ = Rx.fromEvent(page, 'pageerror').pipe( + map((err) => { + logger.warning( + i18n.translate('xpack.reporting.browsers.chromium.pageErrorDetected', { + defaultMessage: `Reporting encountered an uncaught error on the page that will be ignored: {err}`, + values: { err: err.toString() }, + }) + ); + }) + ); + const pageRequestFailed$ = Rx.fromEvent(page, 'requestfailed').pipe( map((req) => { const failure = req.failure && req.failure(); @@ -231,7 +242,7 @@ export class HeadlessChromiumDriverFactory { }) ); - return Rx.merge(consoleMessages$, pageRequestFailed$); + return Rx.merge(consoleMessages$, uncaughtExceptionPageError$, pageRequestFailed$); } getProcessLogger(browser: puppeteer.Browser, logger: LevelLogger): Rx.Observable { @@ -266,21 +277,10 @@ export class HeadlessChromiumDriverFactory { }) ); - const uncaughtExceptionPageError$ = Rx.fromEvent(page, 'pageerror').pipe( - mergeMap((err) => { - return Rx.throwError( - i18n.translate('xpack.reporting.browsers.chromium.pageErrorDetected', { - defaultMessage: `Reporting encountered an error on the page: {err}`, - values: { err: err.toString() }, - }) - ); - }) - ); - const browserDisconnect$ = Rx.fromEvent(browser, 'disconnected').pipe( mergeMap(() => Rx.throwError(getChromiumDisconnectedError())) ); - return Rx.merge(pageError$, uncaughtExceptionPageError$, browserDisconnect$); + return Rx.merge(pageError$, browserDisconnect$); } } From ce7b1ea6530653ddb910ca57af00d3503f8e3362 Mon Sep 17 00:00:00 2001 From: Dmitry Shevchenko Date: Mon, 11 Oct 2021 13:05:52 +0200 Subject: [PATCH 41/74] Implement writing rule execution events to event_log (#112286) --- .../plugins/event_log/generated/mappings.json | 43 +++ x-pack/plugins/event_log/generated/schemas.ts | 23 ++ x-pack/plugins/event_log/scripts/mappings.js | 48 +++- x-pack/plugins/security_solution/kibana.json | 5 +- .../security_solution/server/config.ts | 14 + .../routes/__mocks__/index.ts | 4 + .../__mocks__/rule_execution_log_client.ts | 2 +- .../event_log_adapter/constants.ts | 15 + .../event_log_adapter/event_log_adapter.ts | 87 ++++++ .../event_log_adapter/event_log_client.ts | 157 ++++++++++ .../register_event_log_provider.ts | 16 ++ .../rule_execution_log_client.ts | 34 ++- .../rule_registry_adapter.ts | 106 ------- .../rule_registry_log_client/constants.ts | 41 --- .../parse_rule_execution_log.ts | 40 --- .../rule_execution_field_map.ts | 32 --- .../rule_registry_log_client.ts | 270 ------------------ .../rule_registry_log_client/utils.ts | 76 ----- .../saved_objects_adapter.ts | 35 ++- .../rule_execution_log/types.ts | 55 ++-- .../rule_types/__mocks__/rule_type.ts | 4 +- .../create_security_rule_type_factory.ts | 49 ++-- .../eql/create_eql_alert_type.test.ts | 6 +- .../rule_types/eql/create_eql_alert_type.ts | 17 +- .../build_alert_group_from_sequence.test.ts | 1 + .../create_indicator_match_alert_type.test.ts | 16 +- .../create_indicator_match_alert_type.ts | 17 +- .../ml/create_ml_alert_type.test.ts | 6 +- .../rule_types/ml/create_ml_alert_type.ts | 8 +- .../query/create_query_alert_type.test.ts | 11 +- .../query/create_query_alert_type.ts | 17 +- .../create_threshold_alert_type.test.ts | 6 +- .../threshold/create_threshold_alert_type.ts | 17 +- .../lib/detection_engine/rule_types/types.ts | 12 +- .../lib/detection_engine/rules/enable_rule.ts | 2 + .../signals/__mocks__/es_results.ts | 1 + .../signals/executors/eql.test.ts | 1 + .../signals/executors/threshold.test.ts | 1 + .../signals/signal_rule_alert_type.test.ts | 23 +- .../signals/signal_rule_alert_type.ts | 63 ++-- .../lib/detection_engine/signals/types.ts | 1 + .../detection_engine/signals/utils.test.ts | 4 + .../lib/detection_engine/signals/utils.ts | 23 +- .../security_solution/server/plugin.ts | 26 +- .../event_log/service_api_integration.ts | 15 + 45 files changed, 653 insertions(+), 797 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/constants.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_client.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/register_event_log_provider.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_adapter.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/constants.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/parse_rule_execution_log.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/rule_execution_field_map.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/rule_registry_log_client.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/utils.ts diff --git a/x-pack/plugins/event_log/generated/mappings.json b/x-pack/plugins/event_log/generated/mappings.json index cbb59cc3204c..aba23eef79e3 100644 --- a/x-pack/plugins/event_log/generated/mappings.json +++ b/x-pack/plugins/event_log/generated/mappings.json @@ -267,6 +267,42 @@ } } }, + "alert": { + "properties": { + "rule": { + "properties": { + "execution": { + "properties": { + "uuid": { + "type": "keyword", + "ignore_above": 1024 + }, + "status": { + "type": "keyword", + "ignore_above": 1024 + }, + "status_order": { + "type": "long" + }, + "metrics": { + "properties": { + "total_indexing_duration_ms": { + "type": "long" + }, + "total_search_duration_ms": { + "type": "long" + }, + "execution_gap_duration_s": { + "type": "long" + } + } + } + } + } + } + } + } + }, "saved_objects": { "type": "nested", "properties": { @@ -292,6 +328,13 @@ } } }, + "space_ids": { + "type": "keyword", + "ignore_above": 1024, + "meta": { + "isArray": "true" + } + }, "version": { "type": "version" } diff --git a/x-pack/plugins/event_log/generated/schemas.ts b/x-pack/plugins/event_log/generated/schemas.ts index 15dc182dbe65..e73bafd9cb81 100644 --- a/x-pack/plugins/event_log/generated/schemas.ts +++ b/x-pack/plugins/event_log/generated/schemas.ts @@ -116,6 +116,28 @@ export const EventSchema = schema.maybe( status: ecsString(), }) ), + alert: schema.maybe( + schema.object({ + rule: schema.maybe( + schema.object({ + execution: schema.maybe( + schema.object({ + uuid: ecsString(), + status: ecsString(), + status_order: ecsNumber(), + metrics: schema.maybe( + schema.object({ + total_indexing_duration_ms: ecsNumber(), + total_search_duration_ms: ecsNumber(), + execution_gap_duration_s: ecsNumber(), + }) + ), + }) + ), + }) + ), + }) + ), saved_objects: schema.maybe( schema.arrayOf( schema.object({ @@ -127,6 +149,7 @@ export const EventSchema = schema.maybe( }) ) ), + space_ids: ecsStringMulti(), version: ecsVersion(), }) ), diff --git a/x-pack/plugins/event_log/scripts/mappings.js b/x-pack/plugins/event_log/scripts/mappings.js index d11460305249..231cc225f7c4 100644 --- a/x-pack/plugins/event_log/scripts/mappings.js +++ b/x-pack/plugins/event_log/scripts/mappings.js @@ -49,6 +49,42 @@ exports.EcsCustomPropertyMappings = { }, }, }, + alert: { + properties: { + rule: { + properties: { + execution: { + properties: { + uuid: { + type: 'keyword', + ignore_above: 1024, + }, + status: { + type: 'keyword', + ignore_above: 1024, + }, + status_order: { + type: 'long', + }, + metrics: { + properties: { + total_indexing_duration_ms: { + type: 'long', + }, + total_search_duration_ms: { + type: 'long', + }, + execution_gap_duration_s: { + type: 'long', + }, + }, + }, + }, + }, + }, + }, + }, + }, // array of saved object references, for "linking" via search saved_objects: { type: 'nested', @@ -77,6 +113,10 @@ exports.EcsCustomPropertyMappings = { }, }, }, + space_ids: { + type: 'keyword', + ignore_above: 1024, + }, version: { type: 'version', }, @@ -105,4 +145,10 @@ exports.EcsPropertiesToGenerate = [ /** * These properties can have multiple values (are arrays in the generated event schema). */ -exports.EcsEventLogMultiValuedProperties = ['tags', 'event.category', 'event.type', 'rule.author']; +exports.EcsEventLogMultiValuedProperties = [ + 'tags', + 'event.category', + 'event.type', + 'rule.author', + 'kibana.space_ids', +]; diff --git a/x-pack/plugins/security_solution/kibana.json b/x-pack/plugins/security_solution/kibana.json index 8bb1f4d75e6b..a76b942e555b 100644 --- a/x-pack/plugins/security_solution/kibana.json +++ b/x-pack/plugins/security_solution/kibana.json @@ -12,15 +12,16 @@ "actions", "alerting", "cases", - "ruleRegistry", "data", "dataEnhanced", "embeddable", + "eventLog", "features", - "taskManager", "inspector", "licensing", "maps", + "ruleRegistry", + "taskManager", "timelines", "triggersActionsUi", "uiActions" diff --git a/x-pack/plugins/security_solution/server/config.ts b/x-pack/plugins/security_solution/server/config.ts index 0850e43b21ed..bc5b43c6d25f 100644 --- a/x-pack/plugins/security_solution/server/config.ts +++ b/x-pack/plugins/security_solution/server/config.ts @@ -12,6 +12,7 @@ import { getExperimentalAllowedValues, isValidExperimentalValue, } from '../common/experimental_features'; +import { UnderlyingLogClient } from './lib/detection_engine/rule_execution_log/types'; const allowedExperimentalValues = getExperimentalAllowedValues(); @@ -103,6 +104,19 @@ export const configSchema = schema.object({ }, }), + /** + * Rule Execution Log Configuration + */ + ruleExecutionLog: schema.object({ + underlyingClient: schema.oneOf( + [ + schema.literal(UnderlyingLogClient.eventLog), + schema.literal(UnderlyingLogClient.savedObjects), + ], + { defaultValue: UnderlyingLogClient.savedObjects } + ), + }), + /** * Host Endpoint Configuration */ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/index.ts index 1ac85f9a2796..2f401d27813a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/index.ts @@ -11,6 +11,7 @@ import { serverMock } from './server'; import { requestMock } from './request'; import { responseMock } from './response_factory'; import { ConfigType } from '../../../../config'; +import { UnderlyingLogClient } from '../../rule_execution_log/types'; export { requestMock, requestContextMock, responseMock, serverMock }; @@ -29,6 +30,9 @@ export const createMockConfig = (): ConfigType => ({ alertIgnoreFields: [], prebuiltRulesFromFileSystem: true, prebuiltRulesFromSavedObjects: false, + ruleExecutionLog: { + underlyingClient: UnderlyingLogClient.savedObjects, + }, }); export const mockGetCurrentUser = { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/__mocks__/rule_execution_log_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/__mocks__/rule_execution_log_client.ts index bc9723e47a9d..910e1ecaa508 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/__mocks__/rule_execution_log_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/__mocks__/rule_execution_log_client.ts @@ -14,7 +14,7 @@ export const ruleExecutionLogClientMock = { update: jest.fn(), delete: jest.fn(), logStatusChange: jest.fn(), - logExecutionMetric: jest.fn(), + logExecutionMetrics: jest.fn(), }), }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/constants.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/constants.ts new file mode 100644 index 000000000000..f09eb43bf15f --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/constants.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const RULE_EXECUTION_LOG_PROVIDER = 'rule-execution.security'; + +export const ALERT_SAVED_OBJECT_TYPE = 'alert'; + +export enum RuleExecutionLogAction { + 'status-change' = 'status-change', + 'execution-metrics' = 'execution-metrics', +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts new file mode 100644 index 000000000000..6b1a0cd5b18d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IEventLogService } from '../../../../../../event_log/server'; +import { + FindBulkExecutionLogArgs, + FindExecutionLogArgs, + IRuleExecutionLogClient, + LogExecutionMetricsArgs, + LogStatusChangeArgs, + UpdateExecutionLogArgs, +} from '../types'; +import { EventLogClient } from './event_log_client'; + +export class EventLogAdapter implements IRuleExecutionLogClient { + private eventLogClient: EventLogClient; + + constructor(eventLogService: IEventLogService) { + this.eventLogClient = new EventLogClient(eventLogService); + } + + public async find({ ruleId, logsCount = 1, spaceId }: FindExecutionLogArgs) { + return []; // TODO Implement + } + + public async findBulk({ ruleIds, logsCount = 1, spaceId }: FindBulkExecutionLogArgs) { + return {}; // TODO Implement + } + + public async update({ attributes, spaceId, ruleName, ruleType }: UpdateExecutionLogArgs) { + // execution events are immutable, so we just log a status change istead of updating previous + if (attributes.status) { + this.eventLogClient.logStatusChange({ + ruleName, + ruleType, + ruleId: attributes.alertId, + newStatus: attributes.status, + spaceId, + }); + } + } + + public async delete(id: string) { + // execution events are immutable, nothing to do here + } + + public async logExecutionMetrics({ + ruleId, + spaceId, + ruleType, + ruleName, + metrics, + }: LogExecutionMetricsArgs) { + this.eventLogClient.logExecutionMetrics({ + ruleId, + ruleName, + ruleType, + spaceId, + metrics: { + executionGapDuration: metrics.executionGap?.asSeconds(), + totalIndexingDuration: metrics.indexingDurations?.reduce( + (acc, cur) => acc + Number(cur), + 0 + ), + totalSearchDuration: metrics.searchDurations?.reduce((acc, cur) => acc + Number(cur), 0), + }, + }); + } + + public async logStatusChange(args: LogStatusChangeArgs) { + if (args.metrics) { + this.logExecutionMetrics({ + ruleId: args.ruleId, + ruleName: args.ruleName, + ruleType: args.ruleType, + spaceId: args.spaceId, + metrics: args.metrics, + }); + } + + this.eventLogClient.logStatusChange(args); + } +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_client.ts new file mode 100644 index 000000000000..d85c67e42203 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_client.ts @@ -0,0 +1,157 @@ +/* + * 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 { SavedObjectsUtils } from '../../../../../../../../src/core/server'; +import { + IEventLogger, + IEventLogService, + SAVED_OBJECT_REL_PRIMARY, +} from '../../../../../../event_log/server'; +import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas'; +import { LogStatusChangeArgs } from '../types'; +import { + RuleExecutionLogAction, + RULE_EXECUTION_LOG_PROVIDER, + ALERT_SAVED_OBJECT_TYPE, +} from './constants'; + +const spaceIdToNamespace = SavedObjectsUtils.namespaceStringToId; + +const statusSeverityDict: Record = { + [RuleExecutionStatus.succeeded]: 0, + [RuleExecutionStatus['going to run']]: 10, + [RuleExecutionStatus.warning]: 20, + [RuleExecutionStatus['partial failure']]: 20, + [RuleExecutionStatus.failed]: 30, +}; + +interface FindExecutionLogArgs { + ruleIds: string[]; + spaceId: string; + logsCount?: number; + statuses?: RuleExecutionStatus[]; +} + +interface LogExecutionMetricsArgs { + ruleId: string; + ruleName: string; + ruleType: string; + spaceId: string; + metrics: EventLogExecutionMetrics; +} + +interface EventLogExecutionMetrics { + totalSearchDuration?: number; + totalIndexingDuration?: number; + executionGapDuration?: number; +} + +interface IExecLogEventLogClient { + find: (args: FindExecutionLogArgs) => Promise<{}>; + logStatusChange: (args: LogStatusChangeArgs) => void; + logExecutionMetrics: (args: LogExecutionMetricsArgs) => void; +} + +export class EventLogClient implements IExecLogEventLogClient { + private sequence = 0; + private eventLogger: IEventLogger; + + constructor(eventLogService: IEventLogService) { + this.eventLogger = eventLogService.getLogger({ + event: { provider: RULE_EXECUTION_LOG_PROVIDER }, + }); + } + + public async find({ ruleIds, spaceId, statuses, logsCount = 1 }: FindExecutionLogArgs) { + return {}; // TODO implement + } + + public logExecutionMetrics({ + ruleId, + ruleName, + ruleType, + metrics, + spaceId, + }: LogExecutionMetricsArgs) { + this.eventLogger.logEvent({ + rule: { + id: ruleId, + name: ruleName, + category: ruleType, + }, + event: { + kind: 'metric', + action: RuleExecutionLogAction['execution-metrics'], + sequence: this.sequence++, + }, + kibana: { + alert: { + rule: { + execution: { + metrics: { + execution_gap_duration_s: metrics.executionGapDuration, + total_search_duration_ms: metrics.totalSearchDuration, + total_indexing_duration_ms: metrics.totalIndexingDuration, + }, + }, + }, + }, + space_ids: [spaceId], + saved_objects: [ + { + rel: SAVED_OBJECT_REL_PRIMARY, + type: ALERT_SAVED_OBJECT_TYPE, + id: ruleId, + namespace: spaceIdToNamespace(spaceId), + }, + ], + }, + }); + } + + public logStatusChange({ + ruleId, + ruleName, + ruleType, + newStatus, + message, + spaceId, + }: LogStatusChangeArgs) { + this.eventLogger.logEvent({ + rule: { + id: ruleId, + name: ruleName, + category: ruleType, + }, + event: { + kind: 'event', + action: RuleExecutionLogAction['status-change'], + sequence: this.sequence++, + }, + message, + kibana: { + alert: { + rule: { + execution: { + status: newStatus, + status_order: statusSeverityDict[newStatus], + }, + }, + }, + space_ids: [spaceId], + saved_objects: [ + { + rel: SAVED_OBJECT_REL_PRIMARY, + type: ALERT_SAVED_OBJECT_TYPE, + id: ruleId, + namespace: spaceIdToNamespace(spaceId), + }, + ], + }, + }); + } +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/register_event_log_provider.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/register_event_log_provider.ts new file mode 100644 index 000000000000..7f28715198da --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/register_event_log_provider.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IEventLogService } from '../../../../../../event_log/server'; +import { RuleExecutionLogAction, RULE_EXECUTION_LOG_PROVIDER } from './constants'; + +export const registerEventLogProvider = (eventLogService: IEventLogService) => { + eventLogService.registerProviderActions( + RULE_EXECUTION_LOG_PROVIDER, + Object.keys(RuleExecutionLogAction) + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts index 135cefe2243b..87a3b00cf4ed 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_log_client.ts @@ -6,34 +6,40 @@ */ import { SavedObjectsClientContract } from '../../../../../../../src/core/server'; -import { RuleRegistryAdapter } from './rule_registry_adapter/rule_registry_adapter'; +import { IEventLogService } from '../../../../../event_log/server'; +import { EventLogAdapter } from './event_log_adapter/event_log_adapter'; import { SavedObjectsAdapter } from './saved_objects_adapter/saved_objects_adapter'; import { - ExecutionMetric, - ExecutionMetricArgs, + LogExecutionMetricsArgs, FindBulkExecutionLogArgs, FindExecutionLogArgs, - IRuleDataPluginService, IRuleExecutionLogClient, LogStatusChangeArgs, UpdateExecutionLogArgs, + UnderlyingLogClient, } from './types'; export interface RuleExecutionLogClientArgs { - ruleDataService: IRuleDataPluginService; savedObjectsClient: SavedObjectsClientContract; + eventLogService: IEventLogService; + underlyingClient: UnderlyingLogClient; } -const RULE_REGISTRY_LOG_ENABLED = false; - export class RuleExecutionLogClient implements IRuleExecutionLogClient { private client: IRuleExecutionLogClient; - constructor({ ruleDataService, savedObjectsClient }: RuleExecutionLogClientArgs) { - if (RULE_REGISTRY_LOG_ENABLED) { - this.client = new RuleRegistryAdapter(ruleDataService); - } else { - this.client = new SavedObjectsAdapter(savedObjectsClient); + constructor({ + savedObjectsClient, + eventLogService, + underlyingClient, + }: RuleExecutionLogClientArgs) { + switch (underlyingClient) { + case UnderlyingLogClient.savedObjects: + this.client = new SavedObjectsAdapter(savedObjectsClient); + break; + case UnderlyingLogClient.eventLog: + this.client = new EventLogAdapter(eventLogService); + break; } } @@ -53,8 +59,8 @@ export class RuleExecutionLogClient implements IRuleExecutionLogClient { return this.client.delete(id); } - public async logExecutionMetric(args: ExecutionMetricArgs) { - return this.client.logExecutionMetric(args); + public async logExecutionMetrics(args: LogExecutionMetricsArgs) { + return this.client.logExecutionMetrics(args); } public async logStatusChange(args: LogStatusChangeArgs) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_adapter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_adapter.ts deleted file mode 100644 index ab8664ae995b..000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_adapter.ts +++ /dev/null @@ -1,106 +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 { merge } from 'lodash'; -import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { RuleRegistryLogClient } from './rule_registry_log_client/rule_registry_log_client'; -import { - CreateExecutionLogArgs, - ExecutionMetric, - ExecutionMetricArgs, - FindBulkExecutionLogArgs, - FindExecutionLogArgs, - IRuleDataPluginService, - IRuleExecutionLogClient, - LogStatusChangeArgs, - UpdateExecutionLogArgs, -} from '../types'; - -/** - * @deprecated RuleRegistryAdapter is kept here only as a reference. It will be superseded with EventLog implementation - */ -export class RuleRegistryAdapter implements IRuleExecutionLogClient { - private ruleRegistryClient: RuleRegistryLogClient; - - constructor(ruleDataService: IRuleDataPluginService) { - this.ruleRegistryClient = new RuleRegistryLogClient(ruleDataService); - } - - public async find({ ruleId, logsCount = 1, spaceId }: FindExecutionLogArgs) { - const logs = await this.ruleRegistryClient.find({ - ruleIds: [ruleId], - logsCount, - spaceId, - }); - - return logs[ruleId].map((log) => ({ - id: '', - type: '', - score: 0, - attributes: log, - references: [], - })); - } - - public async findBulk({ ruleIds, logsCount = 1, spaceId }: FindBulkExecutionLogArgs) { - const [statusesById, lastErrorsById] = await Promise.all([ - this.ruleRegistryClient.find({ ruleIds, spaceId }), - this.ruleRegistryClient.find({ - ruleIds, - statuses: [RuleExecutionStatus.failed], - logsCount, - spaceId, - }), - ]); - return merge(statusesById, lastErrorsById); - } - - private async create({ attributes, spaceId }: CreateExecutionLogArgs) { - if (attributes.status) { - await this.ruleRegistryClient.logStatusChange({ - ruleId: attributes.alertId, - newStatus: attributes.status, - spaceId, - }); - } - - if (attributes.bulkCreateTimeDurations) { - await this.ruleRegistryClient.logExecutionMetric({ - ruleId: attributes.alertId, - metric: ExecutionMetric.indexingDurationMax, - value: Math.max(...attributes.bulkCreateTimeDurations.map(Number)), - spaceId, - }); - } - - if (attributes.gap) { - await this.ruleRegistryClient.logExecutionMetric({ - ruleId: attributes.alertId, - metric: ExecutionMetric.executionGap, - value: Number(attributes.gap), - spaceId, - }); - } - } - - public async update({ attributes, spaceId }: UpdateExecutionLogArgs) { - // execution events are immutable, so we just use 'create' here instead of 'update' - await this.create({ attributes, spaceId }); - } - - public async delete(id: string) { - // execution events are immutable, nothing to do here - } - - public async logExecutionMetric(args: ExecutionMetricArgs) { - return this.ruleRegistryClient.logExecutionMetric(args); - } - - public async logStatusChange(args: LogStatusChangeArgs) { - return this.ruleRegistryClient.logStatusChange(args); - } -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/constants.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/constants.ts deleted file mode 100644 index 8d74c71bf447..000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/constants.ts +++ /dev/null @@ -1,41 +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. - */ - -/** - * @deprecated EVENTS_INDEX_PREFIX is kept here only as a reference. It will be superseded with EventLog implementation - */ -export const EVENTS_INDEX_PREFIX = '.kibana_alerts-security.events'; - -/** - * @deprecated MESSAGE is kept here only as a reference. It will be superseded with EventLog implementation - */ -export const MESSAGE = 'message' as const; - -/** - * @deprecated EVENT_SEQUENCE is kept here only as a reference. It will be superseded with EventLog implementation - */ -export const EVENT_SEQUENCE = 'event.sequence' as const; - -/** - * @deprecated EVENT_DURATION is kept here only as a reference. It will be superseded with EventLog implementation - */ -export const EVENT_DURATION = 'event.duration' as const; - -/** - * @deprecated EVENT_END is kept here only as a reference. It will be superseded with EventLog implementation - */ -export const EVENT_END = 'event.end' as const; - -/** - * @deprecated RULE_STATUS is kept here only as a reference. It will be superseded with EventLog implementation - */ -export const RULE_STATUS = 'kibana.rac.detection_engine.rule_status' as const; - -/** - * @deprecated RULE_STATUS_SEVERITY is kept here only as a reference. It will be superseded with EventLog implementation - */ -export const RULE_STATUS_SEVERITY = 'kibana.rac.detection_engine.rule_status_severity' as const; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/parse_rule_execution_log.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/parse_rule_execution_log.ts deleted file mode 100644 index cbc6e570e936..000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/parse_rule_execution_log.ts +++ /dev/null @@ -1,40 +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 { isLeft } from 'fp-ts/lib/Either'; -import { PathReporter } from 'io-ts/lib/PathReporter'; -import { technicalRuleFieldMap } from '../../../../../../../rule_registry/common/assets/field_maps/technical_rule_field_map'; -import { - mergeFieldMaps, - runtimeTypeFromFieldMap, -} from '../../../../../../../rule_registry/common/field_map'; -import { ruleExecutionFieldMap } from './rule_execution_field_map'; - -const ruleExecutionLogRuntimeType = runtimeTypeFromFieldMap( - mergeFieldMaps(technicalRuleFieldMap, ruleExecutionFieldMap) -); - -/** - * @deprecated parseRuleExecutionLog is kept here only as a reference. It will be superseded with EventLog implementation - */ -export const parseRuleExecutionLog = (input: unknown) => { - const validate = ruleExecutionLogRuntimeType.decode(input); - - if (isLeft(validate)) { - throw new Error(PathReporter.report(validate).join('\n')); - } - - return ruleExecutionLogRuntimeType.encode(validate.right); -}; - -/** - * @deprecated RuleExecutionEvent is kept here only as a reference. It will be superseded with EventLog implementation - * - * It's marked as `Partial` because the field map is not yet appropriate for - * execution log events. - */ -export type RuleExecutionEvent = Partial>; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/rule_execution_field_map.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/rule_execution_field_map.ts deleted file mode 100644 index b3c70cd56d9e..000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/rule_execution_field_map.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - EVENT_DURATION, - EVENT_END, - EVENT_SEQUENCE, - MESSAGE, - RULE_STATUS, - RULE_STATUS_SEVERITY, -} from './constants'; - -/** - * @deprecated ruleExecutionFieldMap is kept here only as a reference. It will be superseded with EventLog implementation - */ -export const ruleExecutionFieldMap = { - [MESSAGE]: { type: 'keyword' }, - [EVENT_SEQUENCE]: { type: 'long' }, - [EVENT_END]: { type: 'date' }, - [EVENT_DURATION]: { type: 'long' }, - [RULE_STATUS]: { type: 'keyword' }, - [RULE_STATUS_SEVERITY]: { type: 'integer' }, -} as const; - -/** - * @deprecated RuleExecutionFieldMap is kept here only as a reference. It will be superseded with EventLog implementation - */ -export type RuleExecutionFieldMap = typeof ruleExecutionFieldMap; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/rule_registry_log_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/rule_registry_log_client.ts deleted file mode 100644 index 3cd6171b5bbe..000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/rule_registry_log_client.ts +++ /dev/null @@ -1,270 +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 { estypes } from '@elastic/elasticsearch'; -import { - ALERT_RULE_CONSUMER, - ALERT_RULE_TYPE_ID, - EVENT_ACTION, - EVENT_KIND, - SPACE_IDS, - TIMESTAMP, - ALERT_RULE_UUID, -} from '@kbn/rule-data-utils'; -import moment from 'moment'; - -import { mappingFromFieldMap } from '../../../../../../../rule_registry/common/mapping_from_field_map'; -import { Dataset, IRuleDataClient } from '../../../../../../../rule_registry/server'; -import { SERVER_APP_ID } from '../../../../../../common/constants'; -import { RuleExecutionStatus } from '../../../../../../common/detection_engine/schemas/common/schemas'; -import { invariant } from '../../../../../../common/utils/invariant'; -import { IRuleStatusSOAttributes } from '../../../rules/types'; -import { makeFloatString } from '../../../signals/utils'; -import { - ExecutionMetric, - ExecutionMetricArgs, - IRuleDataPluginService, - LogStatusChangeArgs, -} from '../../types'; -import { EVENT_SEQUENCE, MESSAGE, RULE_STATUS, RULE_STATUS_SEVERITY } from './constants'; -import { parseRuleExecutionLog, RuleExecutionEvent } from './parse_rule_execution_log'; -import { ruleExecutionFieldMap } from './rule_execution_field_map'; -import { - getLastEntryAggregation, - getMetricAggregation, - getMetricField, - sortByTimeDesc, -} from './utils'; - -const statusSeverityDict: Record = { - [RuleExecutionStatus.succeeded]: 0, - [RuleExecutionStatus['going to run']]: 10, - [RuleExecutionStatus.warning]: 20, - [RuleExecutionStatus['partial failure']]: 20, - [RuleExecutionStatus.failed]: 30, -}; - -interface FindExecutionLogArgs { - ruleIds: string[]; - spaceId: string; - logsCount?: number; - statuses?: RuleExecutionStatus[]; -} - -interface IRuleRegistryLogClient { - find: (args: FindExecutionLogArgs) => Promise<{ - [ruleId: string]: IRuleStatusSOAttributes[] | undefined; - }>; - create: (event: RuleExecutionEvent) => Promise; - logStatusChange: (args: LogStatusChangeArgs) => Promise; - logExecutionMetric: (args: ExecutionMetricArgs) => Promise; -} - -/** - * @deprecated RuleRegistryLogClient is kept here only as a reference. It will be superseded with EventLog implementation - */ -export class RuleRegistryLogClient implements IRuleRegistryLogClient { - private sequence = 0; - private ruleDataClient: IRuleDataClient; - - constructor(ruleDataService: IRuleDataPluginService) { - this.ruleDataClient = ruleDataService.initializeIndex({ - feature: SERVER_APP_ID, - registrationContext: 'security', - dataset: Dataset.events, - componentTemplateRefs: [], - componentTemplates: [ - { - name: 'mappings', - mappings: mappingFromFieldMap(ruleExecutionFieldMap, 'strict'), - }, - ], - }); - } - - public async find({ ruleIds, spaceId, statuses, logsCount = 1 }: FindExecutionLogArgs) { - if (ruleIds.length === 0) { - return {}; - } - - const filter: estypes.QueryDslQueryContainer[] = [ - { terms: { [ALERT_RULE_UUID]: ruleIds } }, - { terms: { [SPACE_IDS]: [spaceId] } }, - ]; - - if (statuses) { - filter.push({ terms: { [RULE_STATUS]: statuses } }); - } - - const result = await this.ruleDataClient.getReader().search({ - size: 0, - body: { - query: { - bool: { - filter, - }, - }, - aggs: { - rules: { - terms: { - field: ALERT_RULE_UUID, - size: ruleIds.length, - }, - aggs: { - most_recent_logs: { - top_hits: { - sort: sortByTimeDesc, - size: logsCount, - }, - }, - last_failure: getLastEntryAggregation(RuleExecutionStatus.failed), - last_success: getLastEntryAggregation(RuleExecutionStatus.succeeded), - execution_gap: getMetricAggregation(ExecutionMetric.executionGap), - search_duration_max: getMetricAggregation(ExecutionMetric.searchDurationMax), - indexing_duration_max: getMetricAggregation(ExecutionMetric.indexingDurationMax), - indexing_lookback: getMetricAggregation(ExecutionMetric.indexingLookback), - }, - }, - }, - }, - }); - - if (result.hits.total.value === 0) { - return {}; - } - - invariant(result.aggregations, 'Search response should contain aggregations'); - - return Object.fromEntries( - result.aggregations.rules.buckets.map<[ruleId: string, logs: IRuleStatusSOAttributes[]]>( - (bucket) => [ - bucket.key as string, - bucket.most_recent_logs.hits.hits.map((event) => { - const logEntry = parseRuleExecutionLog(event._source); - invariant( - logEntry[ALERT_RULE_UUID] ?? '', - 'Malformed execution log entry: rule.id field not found' - ); - - const lastFailure = bucket.last_failure.event.hits.hits[0] - ? parseRuleExecutionLog(bucket.last_failure.event.hits.hits[0]._source) - : undefined; - - const lastSuccess = bucket.last_success.event.hits.hits[0] - ? parseRuleExecutionLog(bucket.last_success.event.hits.hits[0]._source) - : undefined; - - const lookBack = bucket.indexing_lookback.event.hits.hits[0] - ? parseRuleExecutionLog(bucket.indexing_lookback.event.hits.hits[0]._source) - : undefined; - - const executionGap = bucket.execution_gap.event.hits.hits[0] - ? parseRuleExecutionLog(bucket.execution_gap.event.hits.hits[0]._source)[ - getMetricField(ExecutionMetric.executionGap) - ] - : undefined; - - const searchDuration = bucket.search_duration_max.event.hits.hits[0] - ? parseRuleExecutionLog(bucket.search_duration_max.event.hits.hits[0]._source)[ - getMetricField(ExecutionMetric.searchDurationMax) - ] - : undefined; - - const indexingDuration = bucket.indexing_duration_max.event.hits.hits[0] - ? parseRuleExecutionLog(bucket.indexing_duration_max.event.hits.hits[0]._source)[ - getMetricField(ExecutionMetric.indexingDurationMax) - ] - : undefined; - - const alertId = logEntry[ALERT_RULE_UUID] ?? ''; - const statusDate = logEntry[TIMESTAMP]; - const lastFailureAt = lastFailure?.[TIMESTAMP]; - const lastFailureMessage = lastFailure?.[MESSAGE]; - const lastSuccessAt = lastSuccess?.[TIMESTAMP]; - const lastSuccessMessage = lastSuccess?.[MESSAGE]; - const status = (logEntry[RULE_STATUS] as RuleExecutionStatus) || null; - const lastLookBackDate = lookBack?.[getMetricField(ExecutionMetric.indexingLookback)]; - const gap = executionGap ? moment.duration(executionGap).humanize() : null; - const bulkCreateTimeDurations = indexingDuration - ? [makeFloatString(indexingDuration)] - : null; - const searchAfterTimeDurations = searchDuration - ? [makeFloatString(searchDuration)] - : null; - - return { - alertId, - statusDate, - lastFailureAt, - lastFailureMessage, - lastSuccessAt, - lastSuccessMessage, - status, - lastLookBackDate, - gap, - bulkCreateTimeDurations, - searchAfterTimeDurations, - }; - }), - ] - ) - ); - } - - public async logExecutionMetric({ - ruleId, - namespace, - metric, - value, - spaceId, - }: ExecutionMetricArgs) { - await this.create( - { - [SPACE_IDS]: [spaceId], - [EVENT_ACTION]: metric, - [EVENT_KIND]: 'metric', - [getMetricField(metric)]: value, - [ALERT_RULE_UUID]: ruleId ?? '', - [TIMESTAMP]: new Date().toISOString(), - [ALERT_RULE_CONSUMER]: SERVER_APP_ID, - [ALERT_RULE_TYPE_ID]: SERVER_APP_ID, - }, - namespace - ); - } - - public async logStatusChange({ - ruleId, - newStatus, - namespace, - message, - spaceId, - }: LogStatusChangeArgs) { - await this.create( - { - [SPACE_IDS]: [spaceId], - [EVENT_ACTION]: 'status-change', - [EVENT_KIND]: 'event', - [EVENT_SEQUENCE]: this.sequence++, - [MESSAGE]: message, - [ALERT_RULE_UUID]: ruleId ?? '', - [RULE_STATUS_SEVERITY]: statusSeverityDict[newStatus], - [RULE_STATUS]: newStatus, - [TIMESTAMP]: new Date().toISOString(), - [ALERT_RULE_CONSUMER]: SERVER_APP_ID, - [ALERT_RULE_TYPE_ID]: SERVER_APP_ID, - }, - namespace - ); - } - - public async create(event: RuleExecutionEvent, namespace?: string) { - await this.ruleDataClient.getWriter({ namespace }).bulk({ - body: [{ index: {} }, event], - }); - } -} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/utils.ts deleted file mode 100644 index 713cf73890e7..000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_registry_adapter/rule_registry_log_client/utils.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { SearchSort } from '@elastic/elasticsearch/api/types'; -import { EVENT_ACTION, TIMESTAMP } from '@kbn/rule-data-utils'; -import { RuleExecutionStatus } from '../../../../../../common/detection_engine/schemas/common/schemas'; -import { ExecutionMetric } from '../../types'; -import { RULE_STATUS, EVENT_SEQUENCE, EVENT_DURATION, EVENT_END } from './constants'; - -const METRIC_FIELDS = { - [ExecutionMetric.executionGap]: EVENT_DURATION, - [ExecutionMetric.searchDurationMax]: EVENT_DURATION, - [ExecutionMetric.indexingDurationMax]: EVENT_DURATION, - [ExecutionMetric.indexingLookback]: EVENT_END, -}; - -/** - * Returns ECS field in which metric value is stored - * @deprecated getMetricField is kept here only as a reference. It will be superseded with EventLog implementation - * - * @param metric - execution metric - * @returns ECS field - */ -export const getMetricField = (metric: T) => METRIC_FIELDS[metric]; - -/** - * @deprecated sortByTimeDesc is kept here only as a reference. It will be superseded with EventLog implementation - */ -export const sortByTimeDesc: SearchSort = [{ [TIMESTAMP]: 'desc' }, { [EVENT_SEQUENCE]: 'desc' }]; - -/** - * Builds aggregation to retrieve the most recent metric value - * @deprecated getMetricAggregation is kept here only as a reference. It will be superseded with EventLog implementation - * - * @param metric - execution metric - * @returns aggregation - */ -export const getMetricAggregation = (metric: ExecutionMetric) => ({ - filter: { - term: { [EVENT_ACTION]: metric }, - }, - aggs: { - event: { - top_hits: { - size: 1, - sort: sortByTimeDesc, - _source: [TIMESTAMP, getMetricField(metric)], - }, - }, - }, -}); - -/** - * Builds aggregation to retrieve the most recent log entry with the given status - * @deprecated getLastEntryAggregation is kept here only as a reference. It will be superseded with EventLog implementation - * - * @param status - rule execution status - * @returns aggregation - */ -export const getLastEntryAggregation = (status: RuleExecutionStatus) => ({ - filter: { - term: { [RULE_STATUS]: status }, - }, - aggs: { - event: { - top_hits: { - sort: sortByTimeDesc, - size: 1, - }, - }, - }, -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/saved_objects_adapter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/saved_objects_adapter.ts index 27329ebf8f90..ca806bd58e36 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/saved_objects_adapter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/saved_objects_adapter.ts @@ -14,12 +14,11 @@ import { ruleStatusSavedObjectsClientFactory, } from './rule_status_saved_objects_client'; import { - ExecutionMetric, - ExecutionMetricArgs, + LogExecutionMetricsArgs, FindBulkExecutionLogArgs, FindExecutionLogArgs, IRuleExecutionLogClient, - LegacyMetrics, + ExecutionMetrics, LogStatusChangeArgs, UpdateExecutionLogArgs, } from '../types'; @@ -28,14 +27,16 @@ import { assertUnreachable } from '../../../../../common'; // 1st is mutable status, followed by 5 most recent failures export const MAX_RULE_STATUSES = 6; -const METRIC_FIELDS = { - [ExecutionMetric.executionGap]: 'gap', - [ExecutionMetric.searchDurationMax]: 'searchAfterTimeDurations', - [ExecutionMetric.indexingDurationMax]: 'bulkCreateTimeDurations', - [ExecutionMetric.indexingLookback]: 'lastLookBackDate', -} as const; - -const getMetricField = (metric: T) => METRIC_FIELDS[metric]; +const convertMetricFields = ( + metrics: ExecutionMetrics +): Pick< + IRuleStatusSOAttributes, + 'gap' | 'searchAfterTimeDurations' | 'bulkCreateTimeDurations' +> => ({ + gap: metrics.executionGap?.humanize(), + searchAfterTimeDurations: metrics.searchDurations, + bulkCreateTimeDurations: metrics.indexingDurations, +}); export class SavedObjectsAdapter implements IRuleExecutionLogClient { private ruleStatusClient: RuleStatusSavedObjectsClient; @@ -66,16 +67,12 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient { await this.ruleStatusClient.delete(id); } - public async logExecutionMetric({ - ruleId, - metric, - value, - }: ExecutionMetricArgs) { + public async logExecutionMetrics({ ruleId, metrics }: LogExecutionMetricsArgs) { const [currentStatus] = await this.getOrCreateRuleStatuses(ruleId); await this.ruleStatusClient.update(currentStatus.id, { ...currentStatus.attributes, - [getMetricField(metric)]: value, + ...convertMetricFields(metrics), }); } @@ -158,11 +155,11 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient { const buildRuleStatusAttributes: ( status: RuleExecutionStatus, message?: string, - metrics?: LegacyMetrics + metrics?: ExecutionMetrics ) => Partial = (status, message, metrics = {}) => { const now = new Date().toISOString(); const baseAttributes: Partial = { - ...metrics, + ...convertMetricFields(metrics), status: status === RuleExecutionStatus.warning ? RuleExecutionStatus['partial failure'] : status, statusDate: now, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts index 9c66032f681d..e38f974ddee2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts @@ -5,28 +5,16 @@ * 2.0. */ -import { PublicMethodsOf } from '@kbn/utility-types'; +import { Duration } from 'moment'; import { SavedObjectsFindResult } from '../../../../../../../src/core/server'; -import { RuleDataPluginService } from '../../../../../rule_registry/server'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas'; import { IRuleStatusSOAttributes } from '../rules/types'; -export enum ExecutionMetric { - 'executionGap' = 'executionGap', - 'searchDurationMax' = 'searchDurationMax', - 'indexingDurationMax' = 'indexingDurationMax', - 'indexingLookback' = 'indexingLookback', +export enum UnderlyingLogClient { + 'savedObjects' = 'savedObjects', + 'eventLog' = 'eventLog', } -export type IRuleDataPluginService = PublicMethodsOf; - -export type ExecutionMetricValue = { - [ExecutionMetric.executionGap]: number; - [ExecutionMetric.searchDurationMax]: number; - [ExecutionMetric.indexingDurationMax]: number; - [ExecutionMetric.indexingLookback]: Date; -}[T]; - export interface FindExecutionLogArgs { ruleId: string; spaceId: string; @@ -39,29 +27,34 @@ export interface FindBulkExecutionLogArgs { logsCount?: number; } -/** - * @deprecated LegacyMetrics are only kept here for backward compatibility - * and should be replaced by ExecutionMetric in the future - */ -export interface LegacyMetrics { - searchAfterTimeDurations?: string[]; - bulkCreateTimeDurations?: string[]; +export interface ExecutionMetrics { + searchDurations?: string[]; + indexingDurations?: string[]; + /** + * @deprecated lastLookBackDate is logged only by SavedObjectsAdapter and should be removed in the future + */ lastLookBackDate?: string; - gap?: string; + executionGap?: Duration; } export interface LogStatusChangeArgs { ruleId: string; + ruleName: string; + ruleType: string; spaceId: string; newStatus: RuleExecutionStatus; - namespace?: string; message?: string; - metrics?: LegacyMetrics; + /** + * @deprecated Use RuleExecutionLogClient.logExecutionMetrics to write metrics instead + */ + metrics?: ExecutionMetrics; } export interface UpdateExecutionLogArgs { id: string; attributes: IRuleStatusSOAttributes; + ruleName: string; + ruleType: string; spaceId: string; } @@ -70,12 +63,12 @@ export interface CreateExecutionLogArgs { spaceId: string; } -export interface ExecutionMetricArgs { +export interface LogExecutionMetricsArgs { ruleId: string; + ruleName: string; + ruleType: string; spaceId: string; - namespace?: string; - metric: T; - value: ExecutionMetricValue; + metrics: ExecutionMetrics; } export interface FindBulkExecutionLogResponse { @@ -90,5 +83,5 @@ export interface IRuleExecutionLogClient { update: (args: UpdateExecutionLogArgs) => Promise; delete: (id: string) => Promise; logStatusChange: (args: LogStatusChangeArgs) => Promise; - logExecutionMetric: (args: ExecutionMetricArgs) => Promise; + logExecutionMetrics: (args: LogExecutionMetricsArgs) => Promise; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts index bcab8a0af5ff..c6f818f04fc5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/__mocks__/rule_type.ts @@ -14,6 +14,7 @@ import { mlPluginServerMock } from '../../../../../../ml/server/mocks'; import type { IRuleDataClient } from '../../../../../../rule_registry/server'; import { ruleRegistryMocks } from '../../../../../../rule_registry/server/mocks'; +import { eventLogServiceMock } from '../../../../../../event_log/server/mocks'; import { PluginSetupContract as AlertingPluginSetupContract } from '../../../../../../alerting/server'; import { ConfigType } from '../../../../config'; import { AlertAttributes } from '../../signals/types'; @@ -55,6 +56,7 @@ export const createRuleTypeMocks = ( references: [], attributes: { actions: [], + alertTypeId: 'siem.signals', enabled: true, name: 'mock rule', tags: [], @@ -89,7 +91,7 @@ export const createRuleTypeMocks = ( ruleDataClient: ruleRegistryMocks.createRuleDataClient( '.alerts-security.alerts' ) as IRuleDataClient, - ruleDataService: ruleRegistryMocks.createRuleDataPluginService(), + eventLogService: eventLogServiceMock.create(), }, services, scheduleActions, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_factory.ts index df15d4b2c011..9ea36abe997c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_factory.ts @@ -40,8 +40,9 @@ import { scheduleThrottledNotificationActions } from '../notifications/schedule_ /* eslint-disable complexity */ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = - ({ lists, logger, mergeStrategy, ignoreFields, ruleDataClient, ruleDataService }) => + ({ lists, logger, config, ruleDataClient, eventLogService }) => (type) => { + const { alertIgnoreFields: ignoreFields, alertMergeStrategy: mergeStrategy } = config; const persistenceRuleType = createPersistenceRuleTypeFactory({ ruleDataClient, logger }); return persistenceRuleType({ ...type, @@ -65,13 +66,15 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = const ruleStatusClient = new RuleExecutionLogClient({ savedObjectsClient, - ruleDataService, + eventLogService, + underlyingClient: config.ruleExecutionLog.underlyingClient, }); const ruleSO = await savedObjectsClient.get('alert', alertId); const { actions, name, + alertTypeId, schedule: { interval }, } = ruleSO.attributes; const refresh = actions.length ? 'wait_for' : false; @@ -87,9 +90,14 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = logger.debug(buildRuleMessage(`interval: ${interval}`)); let wroteWarningStatus = false; - await ruleStatusClient.logStatusChange({ + const basicLogArguments = { spaceId, ruleId: alertId, + ruleName: name, + ruleType: alertTypeId, + }; + await ruleStatusClient.logStatusChange({ + ...basicLogArguments, newStatus: RuleExecutionStatus['going to run'], }); @@ -125,8 +133,7 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = tryCatch( () => hasReadIndexPrivileges({ - spaceId, - ruleId: alertId, + ...basicLogArguments, privileges, logger, buildRuleMessage, @@ -138,8 +145,7 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = tryCatch( () => hasTimestampFields({ - spaceId, - ruleId: alertId, + ...basicLogArguments, wroteStatus: wroteStatus as boolean, timestampField: hasTimestampOverride ? (timestampOverride as string) @@ -179,11 +185,10 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = logger.warn(gapMessage); hasError = true; await ruleStatusClient.logStatusChange({ - spaceId, - ruleId: alertId, + ...basicLogArguments, newStatus: RuleExecutionStatus.failed, message: gapMessage, - metrics: { gap: gapString }, + metrics: { executionGap: remainingGap }, }); } @@ -262,8 +267,7 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = if (result.warningMessages.length) { const warningMessage = buildRuleMessage(result.warningMessages.join()); await ruleStatusClient.logStatusChange({ - spaceId, - ruleId: alertId, + ...basicLogArguments, newStatus: RuleExecutionStatus['partial failure'], message: warningMessage, }); @@ -327,13 +331,12 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = if (!hasError && !wroteWarningStatus && !result.warning) { await ruleStatusClient.logStatusChange({ - spaceId, - ruleId: alertId, + ...basicLogArguments, newStatus: RuleExecutionStatus.succeeded, message: 'succeeded', metrics: { - bulkCreateTimeDurations: result.bulkCreateTimes, - searchAfterTimeDurations: result.searchAfterTimes, + indexingDurations: result.bulkCreateTimes, + searchDurations: result.searchAfterTimes, lastLookBackDate: result.lastLookbackDate?.toISOString(), }, }); @@ -356,13 +359,12 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = ); logger.error(errorMessage); await ruleStatusClient.logStatusChange({ - spaceId, - ruleId: alertId, + ...basicLogArguments, newStatus: RuleExecutionStatus.failed, message: errorMessage, metrics: { - bulkCreateTimeDurations: result.bulkCreateTimes, - searchAfterTimeDurations: result.searchAfterTimes, + indexingDurations: result.bulkCreateTimes, + searchDurations: result.searchAfterTimes, lastLookBackDate: result.lastLookbackDate?.toISOString(), }, }); @@ -376,13 +378,12 @@ export const createSecurityRuleTypeFactory: CreateSecurityRuleTypeFactory = logger.error(message); await ruleStatusClient.logStatusChange({ - spaceId, - ruleId: alertId, + ...basicLogArguments, newStatus: RuleExecutionStatus.failed, message, metrics: { - bulkCreateTimeDurations: result.bulkCreateTimes, - searchAfterTimeDurations: result.searchAfterTimes, + indexingDurations: result.bulkCreateTimes, + searchDurations: result.searchAfterTimes, lastLookBackDate: result.lastLookbackDate?.toISOString(), }, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.test.ts index 868419179c76..43860d396ac5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.test.ts @@ -12,6 +12,7 @@ import { allowedExperimentalValues } from '../../../../../common/experimental_fe import { createEqlAlertType } from './create_eql_alert_type'; import { createRuleTypeMocks } from '../__mocks__/rule_type'; import { getEqlRuleParams } from '../../schemas/rule_schemas.mock'; +import { createMockConfig } from '../../routes/__mocks__'; jest.mock('../../rule_execution_log/rule_execution_log_client'); @@ -26,10 +27,9 @@ describe('Event correlation alerts', () => { experimentalFeatures: allowedExperimentalValues, lists: dependencies.lists, logger: dependencies.logger, - ignoreFields: [], - mergeStrategy: 'allFields', + config: createMockConfig(), ruleDataClient: dependencies.ruleDataClient, - ruleDataService: dependencies.ruleDataService, + eventLogService: dependencies.eventLogService, version: '1.0.0', }); dependencies.alerting.registerType(eqlAlertType); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts index 5893c6fdc86c..9324b469bf64 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts @@ -14,23 +14,14 @@ import { createSecurityRuleTypeFactory } from '../create_security_rule_type_fact import { CreateRuleOptions } from '../types'; export const createEqlAlertType = (createOptions: CreateRuleOptions) => { - const { - experimentalFeatures, - lists, - logger, - ignoreFields, - mergeStrategy, - ruleDataClient, - version, - ruleDataService, - } = createOptions; + const { experimentalFeatures, lists, logger, config, ruleDataClient, version, eventLogService } = + createOptions; const createSecurityRuleType = createSecurityRuleTypeFactory({ lists, logger, - ignoreFields, - mergeStrategy, + config, ruleDataClient, - ruleDataService, + eventLogService, }); return createSecurityRuleType({ id: EQL_RULE_TYPE_ID, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts index 6daafbfae40f..a7accc4ae8a0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts @@ -44,6 +44,7 @@ describe('buildAlert', () => { const ruleSO = { attributes: { actions: [], + alertTypeId: 'siem.signals', createdAt: new Date().toISOString(), createdBy: 'gandalf', params: getQueryRuleParams(), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts index fe836c872dca..3db4f5686abd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts @@ -16,6 +16,7 @@ import { createIndicatorMatchAlertType } from './create_indicator_match_alert_ty import { sampleDocNoSortId } from '../../signals/__mocks__/es_results'; import { CountResponse } from 'kibana/server'; import { RuleParams } from '../../schemas/rule_schemas'; +import { createMockConfig } from '../../routes/__mocks__'; jest.mock('../utils/get_list_client', () => ({ getListClient: jest.fn().mockReturnValue({ @@ -56,10 +57,9 @@ describe('Indicator Match Alerts', () => { experimentalFeatures: allowedExperimentalValues, lists: dependencies.lists, logger: dependencies.logger, - ignoreFields: [], - mergeStrategy: 'allFields', + config: createMockConfig(), ruleDataClient: dependencies.ruleDataClient, - ruleDataService: dependencies.ruleDataService, + eventLogService: dependencies.eventLogService, version: '1.0.0', }); @@ -97,10 +97,9 @@ describe('Indicator Match Alerts', () => { experimentalFeatures: allowedExperimentalValues, lists: dependencies.lists, logger: dependencies.logger, - mergeStrategy: 'allFields', - ignoreFields: [], + config: createMockConfig(), ruleDataClient: dependencies.ruleDataClient, - ruleDataService: dependencies.ruleDataService, + eventLogService: dependencies.eventLogService, version: '1.0.0', }); @@ -136,10 +135,9 @@ describe('Indicator Match Alerts', () => { experimentalFeatures: allowedExperimentalValues, lists: dependencies.lists, logger: dependencies.logger, - mergeStrategy: 'allFields', - ignoreFields: [], + config: createMockConfig(), ruleDataClient: dependencies.ruleDataClient, - ruleDataService: dependencies.ruleDataService, + eventLogService: dependencies.eventLogService, version: '1.0.0', }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts index e2d5da1def70..c30fdd7d99c2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts @@ -14,23 +14,14 @@ import { createSecurityRuleTypeFactory } from '../create_security_rule_type_fact import { CreateRuleOptions } from '../types'; export const createIndicatorMatchAlertType = (createOptions: CreateRuleOptions) => { - const { - experimentalFeatures, - lists, - logger, - mergeStrategy, - ignoreFields, - ruleDataClient, - version, - ruleDataService, - } = createOptions; + const { experimentalFeatures, lists, logger, config, ruleDataClient, version, eventLogService } = + createOptions; const createSecurityRuleType = createSecurityRuleTypeFactory({ lists, logger, - mergeStrategy, - ignoreFields, + config, ruleDataClient, - ruleDataService, + eventLogService, }); return createSecurityRuleType({ id: INDICATOR_RULE_TYPE_ID, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.test.ts index 23cd2e94aedf..bffc20c3df1e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.test.ts @@ -14,6 +14,7 @@ import { createRuleTypeMocks } from '../__mocks__/rule_type'; import { createMlAlertType } from './create_ml_alert_type'; import { RuleParams } from '../../schemas/rule_schemas'; +import { createMockConfig } from '../../routes/__mocks__'; jest.mock('../../signals/bulk_create_ml_signals'); @@ -97,11 +98,10 @@ describe('Machine Learning Alerts', () => { experimentalFeatures: allowedExperimentalValues, lists: dependencies.lists, logger: dependencies.logger, - mergeStrategy: 'allFields', - ignoreFields: [], + config: createMockConfig(), ml: mlMock, ruleDataClient: dependencies.ruleDataClient, - ruleDataService: dependencies.ruleDataService, + eventLogService: dependencies.eventLogService, version: '1.0.0', }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts index ec2f5dd10464..ac2d3f14831a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts @@ -14,15 +14,13 @@ import { createSecurityRuleTypeFactory } from '../create_security_rule_type_fact import { CreateRuleOptions } from '../types'; export const createMlAlertType = (createOptions: CreateRuleOptions) => { - const { lists, logger, mergeStrategy, ignoreFields, ml, ruleDataClient, ruleDataService } = - createOptions; + const { lists, logger, config, ml, ruleDataClient, eventLogService } = createOptions; const createSecurityRuleType = createSecurityRuleTypeFactory({ lists, logger, - mergeStrategy, - ignoreFields, + config, ruleDataClient, - ruleDataService, + eventLogService, }); return createSecurityRuleType({ id: ML_RULE_TYPE_ID, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts index e45d8440386f..4fdeac8047b1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts @@ -14,6 +14,7 @@ import { allowedExperimentalValues } from '../../../../../common/experimental_fe import { sampleDocNoSortId } from '../../signals/__mocks__/es_results'; import { createQueryAlertType } from './create_query_alert_type'; import { createRuleTypeMocks } from '../__mocks__/rule_type'; +import { createMockConfig } from '../../routes/__mocks__'; jest.mock('../utils/get_list_client', () => ({ getListClient: jest.fn().mockReturnValue({ @@ -31,10 +32,9 @@ describe('Custom Query Alerts', () => { experimentalFeatures: allowedExperimentalValues, lists: dependencies.lists, logger: dependencies.logger, - mergeStrategy: 'allFields', - ignoreFields: [], + config: createMockConfig(), ruleDataClient: dependencies.ruleDataClient, - ruleDataService: dependencies.ruleDataService, + eventLogService: dependencies.eventLogService, version: '1.0.0', }); @@ -79,10 +79,9 @@ describe('Custom Query Alerts', () => { experimentalFeatures: allowedExperimentalValues, lists: dependencies.lists, logger: dependencies.logger, - mergeStrategy: 'allFields', - ignoreFields: [], + config: createMockConfig(), ruleDataClient: dependencies.ruleDataClient, - ruleDataService: dependencies.ruleDataService, + eventLogService: dependencies.eventLogService, version: '1.0.0', }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts index d5af7a4c8b5a..469c237112dc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts @@ -14,23 +14,14 @@ import { createSecurityRuleTypeFactory } from '../create_security_rule_type_fact import { CreateRuleOptions } from '../types'; export const createQueryAlertType = (createOptions: CreateRuleOptions) => { - const { - experimentalFeatures, - lists, - logger, - mergeStrategy, - ignoreFields, - ruleDataClient, - version, - ruleDataService, - } = createOptions; + const { experimentalFeatures, lists, logger, config, ruleDataClient, version, eventLogService } = + createOptions; const createSecurityRuleType = createSecurityRuleTypeFactory({ lists, logger, - mergeStrategy, - ignoreFields, + config, ruleDataClient, - ruleDataService, + eventLogService, }); return createSecurityRuleType({ id: QUERY_RULE_TYPE_ID, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.test.ts index 74435cb30047..aff57dbdf3cd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.test.ts @@ -9,6 +9,7 @@ import { allowedExperimentalValues } from '../../../../../common/experimental_fe import { createThresholdAlertType } from './create_threshold_alert_type'; import { createRuleTypeMocks } from '../__mocks__/rule_type'; import { getThresholdRuleParams } from '../../schemas/rule_schemas.mock'; +import { createMockConfig } from '../../routes/__mocks__'; jest.mock('../../rule_execution_log/rule_execution_log_client'); @@ -20,10 +21,9 @@ describe('Threshold Alerts', () => { experimentalFeatures: allowedExperimentalValues, lists: dependencies.lists, logger: dependencies.logger, - mergeStrategy: 'allFields', - ignoreFields: [], + config: createMockConfig(), ruleDataClient: dependencies.ruleDataClient, - ruleDataService: dependencies.ruleDataService, + eventLogService: dependencies.eventLogService, version: '1.0.0', }); dependencies.alerting.registerType(thresholdAlertTpe); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts index a503cf5aedbe..789e4525c58a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts @@ -16,23 +16,14 @@ import { createSecurityRuleTypeFactory } from '../create_security_rule_type_fact import { CreateRuleOptions } from '../types'; export const createThresholdAlertType = (createOptions: CreateRuleOptions) => { - const { - experimentalFeatures, - lists, - logger, - mergeStrategy, - ignoreFields, - ruleDataClient, - version, - ruleDataService, - } = createOptions; + const { experimentalFeatures, lists, logger, config, ruleDataClient, version, eventLogService } = + createOptions; const createSecurityRuleType = createSecurityRuleTypeFactory({ lists, logger, - mergeStrategy, - ignoreFields, + config, ruleDataClient, - ruleDataService, + eventLogService, }); return createSecurityRuleType({ id: THRESHOLD_RULE_TYPE_ID, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index 6280a50d4981..c94339da03b9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -30,12 +30,12 @@ import { import { BaseHit } from '../../../../common/detection_engine/types'; import { ConfigType } from '../../../config'; import { SetupPlugins } from '../../../plugin'; -import { IRuleDataPluginService } from '../rule_execution_log/types'; import { RuleParams } from '../schemas/rule_schemas'; import { BuildRuleMessage } from '../signals/rule_messages'; import { AlertAttributes, BulkCreate, WrapHits, WrapSequences } from '../signals/types'; import { AlertsFieldMap, RulesFieldMap } from './field_maps'; import { ExperimentalFeatures } from '../../../../common/experimental_features'; +import { IEventLogService } from '../../../../../event_log/server'; export interface SecurityAlertTypeReturnValue { bulkCreateTimes: string[]; @@ -98,10 +98,9 @@ type SecurityAlertTypeWithExecutor< export type CreateSecurityRuleTypeFactory = (options: { lists: SetupPlugins['lists']; logger: Logger; - mergeStrategy: ConfigType['alertMergeStrategy']; - ignoreFields: ConfigType['alertIgnoreFields']; + config: ConfigType; ruleDataClient: IRuleDataClient; - ruleDataService: IRuleDataPluginService; + eventLogService: IEventLogService; }) => < TParams extends RuleParams & { index?: string[] | undefined }, TAlertInstanceContext extends AlertInstanceContext, @@ -127,10 +126,9 @@ export interface CreateRuleOptions { experimentalFeatures: ExperimentalFeatures; lists: SetupPlugins['lists']; logger: Logger; - mergeStrategy: ConfigType['alertMergeStrategy']; - ignoreFields: ConfigType['alertIgnoreFields']; + config: ConfigType; ml?: SetupPlugins['ml']; ruleDataClient: IRuleDataClient; version: string; - ruleDataService: IRuleDataPluginService; + eventLogService: IEventLogService; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts index c6a5c0038024..2f3d05e0c958 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts @@ -44,6 +44,8 @@ export const enableRule = async ({ const currentStatusToDisable = ruleCurrentStatus[0]; await ruleStatusClient.update({ id: currentStatusToDisable.id, + ruleName: rule.name, + ruleType: rule.alertTypeId, attributes: { ...currentStatusToDisable.attributes, status: RuleExecutionStatus['going to run'], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts index 850eee3993b6..207ea497c7e8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -33,6 +33,7 @@ export const sampleRuleSO = (params: T): SavedObject { updated_at: '2020-03-27T22:55:59.577Z', attributes: { actions: [], + alertTypeId: 'siem.signals', enabled: true, name: 'rule-name', tags: ['some fake tag 1', 'some fake tag 2'], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts index 5766390099e2..11145405dcc9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts @@ -30,6 +30,7 @@ describe('threshold_executor', () => { updated_at: '2020-03-27T22:55:59.577Z', attributes: { actions: [], + alertTypeId: 'siem.signals', enabled: true, name: 'rule-name', tags: ['some fake tag 1', 'some fake tag 2'], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index 2696d6981083..c2923b566175 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -32,10 +32,11 @@ import { mlExecutor } from './executors/ml'; import { getMlRuleParams, getQueryRuleParams } from '../schemas/rule_schemas.mock'; import { ResponseError } from '@elastic/elasticsearch/lib/errors'; import { allowedExperimentalValues } from '../../../../common/experimental_features'; -import { ruleRegistryMocks } from '../../../../../rule_registry/server/mocks'; import { scheduleNotificationActions } from '../notifications/schedule_notification_actions'; import { ruleExecutionLogClientMock } from '../rule_execution_log/__mocks__/rule_execution_log_client'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas'; +import { eventLogServiceMock } from '../../../../../event_log/server/mocks'; +import { createMockConfig } from '../routes/__mocks__'; jest.mock('./utils', () => { const original = jest.requireActual('./utils'); @@ -124,12 +125,12 @@ describe('signal_rule_alert_type', () => { let alert: ReturnType; let logger: ReturnType; let alertServices: AlertServicesMock; - let ruleDataService: ReturnType; + let eventLogService: ReturnType; beforeEach(() => { alertServices = alertsMock.createAlertServices(); logger = loggingSystemMock.createLogger(); - ruleDataService = ruleRegistryMocks.createRuleDataPluginService(); + eventLogService = eventLogServiceMock.create(); (getListsClient as jest.Mock).mockReturnValue({ listClient: getListClientMock(), exceptionsClient: getExceptionListClientMock(), @@ -194,9 +195,8 @@ describe('signal_rule_alert_type', () => { version, ml: mlMock, lists: listMock.createSetup(), - mergeStrategy: 'missingFields', - ignoreFields: [], - ruleDataService, + config: createMockConfig(), + eventLogService, }); mockRuleExecutionLogClient.logStatusChange.mockClear(); @@ -217,11 +217,18 @@ describe('signal_rule_alert_type', () => { payload.previousStartedAt = moment().subtract(100, 'm').toDate(); await alert.executor(payload); expect(logger.warn).toHaveBeenCalled(); - expect(mockRuleExecutionLogClient.logStatusChange).toHaveBeenLastCalledWith( + expect(mockRuleExecutionLogClient.logStatusChange).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + newStatus: RuleExecutionStatus['going to run'], + }) + ); + expect(mockRuleExecutionLogClient.logStatusChange).toHaveBeenNthCalledWith( + 2, expect.objectContaining({ newStatus: RuleExecutionStatus.failed, metrics: { - gap: 'an hour', + executionGap: expect.any(Object), }, }) ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 9a6c099ed176..1e3a8a513c4a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -70,9 +70,9 @@ import { ConfigType } from '../../../config'; import { ExperimentalFeatures } from '../../../../common/experimental_features'; import { injectReferences, extractReferences } from './saved_object_references'; import { RuleExecutionLogClient } from '../rule_execution_log/rule_execution_log_client'; -import { IRuleDataPluginService } from '../rule_execution_log/types'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas'; import { scheduleThrottledNotificationActions } from '../notifications/schedule_throttle_notification_actions'; +import { IEventLogService } from '../../../../../event_log/server'; export const signalRulesAlertType = ({ logger, @@ -81,9 +81,8 @@ export const signalRulesAlertType = ({ version, ml, lists, - mergeStrategy, - ignoreFields, - ruleDataService, + config, + eventLogService, }: { logger: Logger; eventsTelemetry: TelemetryEventsSender | undefined; @@ -91,10 +90,10 @@ export const signalRulesAlertType = ({ version: string; ml: SetupPlugins['ml']; lists: SetupPlugins['lists'] | undefined; - mergeStrategy: ConfigType['alertMergeStrategy']; - ignoreFields: ConfigType['alertIgnoreFields']; - ruleDataService: IRuleDataPluginService; + config: ConfigType; + eventLogService: IEventLogService; }): SignalRuleAlertTypeDefinition => { + const { alertMergeStrategy: mergeStrategy, alertIgnoreFields: ignoreFields } = config; return { id: SIGNALS_ID, name: 'SIEM signal', @@ -138,14 +137,16 @@ export const signalRulesAlertType = ({ let hasError: boolean = false; let result = createSearchAfterReturnType(); const ruleStatusClient = new RuleExecutionLogClient({ - ruleDataService, + eventLogService, savedObjectsClient: services.savedObjectsClient, + underlyingClient: config.ruleExecutionLog.underlyingClient, }); const savedObject = await services.savedObjectsClient.get('alert', alertId); const { actions, name, + alertTypeId, schedule: { interval }, } = savedObject.attributes; const refresh = actions.length ? 'wait_for' : false; @@ -159,10 +160,16 @@ export const signalRulesAlertType = ({ logger.debug(buildRuleMessage('[+] Starting Signal Rule execution')); logger.debug(buildRuleMessage(`interval: ${interval}`)); let wroteWarningStatus = false; - await ruleStatusClient.logStatusChange({ + const basicLogArguments = { + spaceId, ruleId: alertId, + ruleName: name, + ruleType: alertTypeId, + }; + + await ruleStatusClient.logStatusChange({ + ...basicLogArguments, newStatus: RuleExecutionStatus['going to run'], - spaceId, }); // check if rule has permissions to access given index pattern @@ -194,8 +201,7 @@ export const signalRulesAlertType = ({ tryCatch( () => hasReadIndexPrivileges({ - spaceId, - ruleId: alertId, + ...basicLogArguments, privileges, logger, buildRuleMessage, @@ -207,13 +213,11 @@ export const signalRulesAlertType = ({ tryCatch( () => hasTimestampFields({ - spaceId, - ruleId: alertId, + ...basicLogArguments, wroteStatus: wroteStatus as boolean, timestampField: hasTimestampOverride ? (timestampOverride as string) : '@timestamp', - ruleName: name, timestampFieldCapsResponse: timestampFieldCaps, inputIndices, ruleStatusClient, @@ -247,11 +251,10 @@ export const signalRulesAlertType = ({ logger.warn(gapMessage); hasError = true; await ruleStatusClient.logStatusChange({ - spaceId, - ruleId: alertId, + ...basicLogArguments, newStatus: RuleExecutionStatus.failed, message: gapMessage, - metrics: { gap: gapString }, + metrics: { executionGap: remainingGap }, }); } try { @@ -383,8 +386,7 @@ export const signalRulesAlertType = ({ if (result.warningMessages.length) { const warningMessage = buildRuleMessage(result.warningMessages.join()); await ruleStatusClient.logStatusChange({ - spaceId, - ruleId: alertId, + ...basicLogArguments, newStatus: RuleExecutionStatus['partial failure'], message: warningMessage, }); @@ -445,13 +447,12 @@ export const signalRulesAlertType = ({ ); if (!hasError && !wroteWarningStatus && !result.warning) { await ruleStatusClient.logStatusChange({ - spaceId, - ruleId: alertId, + ...basicLogArguments, newStatus: RuleExecutionStatus.succeeded, message: 'succeeded', metrics: { - bulkCreateTimeDurations: result.bulkCreateTimes, - searchAfterTimeDurations: result.searchAfterTimes, + indexingDurations: result.bulkCreateTimes, + searchDurations: result.searchAfterTimes, lastLookBackDate: result.lastLookBackDate?.toISOString(), }, }); @@ -474,13 +475,12 @@ export const signalRulesAlertType = ({ ); logger.error(errorMessage); await ruleStatusClient.logStatusChange({ - spaceId, - ruleId: alertId, + ...basicLogArguments, newStatus: RuleExecutionStatus.failed, message: errorMessage, metrics: { - bulkCreateTimeDurations: result.bulkCreateTimes, - searchAfterTimeDurations: result.searchAfterTimes, + indexingDurations: result.bulkCreateTimes, + searchDurations: result.searchAfterTimes, lastLookBackDate: result.lastLookBackDate?.toISOString(), }, }); @@ -494,13 +494,12 @@ export const signalRulesAlertType = ({ logger.error(message); await ruleStatusClient.logStatusChange({ - spaceId, - ruleId: alertId, + ...basicLogArguments, newStatus: RuleExecutionStatus.failed, message, metrics: { - bulkCreateTimeDurations: result.bulkCreateTimes, - searchAfterTimeDurations: result.searchAfterTimes, + indexingDurations: result.bulkCreateTimes, + searchDurations: result.searchAfterTimes, lastLookBackDate: result.lastLookBackDate?.toISOString(), }, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index c1e7e23c3b16..82b4a46f482b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -255,6 +255,7 @@ export interface SignalHit { export interface AlertAttributes { actions: RuleAlertAction[]; + alertTypeId: string; enabled: boolean; name: string; tags: string[]; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index c3e95d6d196c..7d2eafa46d38 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -789,6 +789,7 @@ describe('utils', () => { inputIndices: ['myfa*'], ruleStatusClient, ruleId: 'ruleId', + ruleType: 'ruleType', spaceId: 'default', logger: mockLogger, buildRuleMessage, @@ -832,6 +833,7 @@ describe('utils', () => { inputIndices: ['myfa*'], ruleStatusClient, ruleId: 'ruleId', + ruleType: 'ruleType', spaceId: 'default', logger: mockLogger, buildRuleMessage, @@ -861,6 +863,7 @@ describe('utils', () => { inputIndices: ['logs-endpoint.alerts-*'], ruleStatusClient, ruleId: 'ruleId', + ruleType: 'ruleType', spaceId: 'default', logger: mockLogger, buildRuleMessage, @@ -890,6 +893,7 @@ describe('utils', () => { inputIndices: ['logs-endpoint.alerts-*'], ruleStatusClient, ruleId: 'ruleId', + ruleType: 'ruleType', spaceId: 'default', logger: mockLogger, buildRuleMessage, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index 5993dd626729..2aefc7ea0bd6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -99,9 +99,20 @@ export const hasReadIndexPrivileges = async (args: { buildRuleMessage: BuildRuleMessage; ruleStatusClient: IRuleExecutionLogClient; ruleId: string; + ruleName: string; + ruleType: string; spaceId: string; }): Promise => { - const { privileges, logger, buildRuleMessage, ruleStatusClient, ruleId, spaceId } = args; + const { + privileges, + logger, + buildRuleMessage, + ruleStatusClient, + ruleId, + ruleName, + ruleType, + spaceId, + } = args; const indexNames = Object.keys(privileges.index); const [indexesWithReadPrivileges, indexesWithNoReadPrivileges] = partition( @@ -119,6 +130,8 @@ export const hasReadIndexPrivileges = async (args: { await ruleStatusClient.logStatusChange({ message: errorString, ruleId, + ruleName, + ruleType, spaceId, newStatus: RuleExecutionStatus['partial failure'], }); @@ -136,6 +149,8 @@ export const hasReadIndexPrivileges = async (args: { await ruleStatusClient.logStatusChange({ message: errorString, ruleId, + ruleName, + ruleType, spaceId, newStatus: RuleExecutionStatus['partial failure'], }); @@ -156,6 +171,7 @@ export const hasTimestampFields = async (args: { ruleStatusClient: IRuleExecutionLogClient; ruleId: string; spaceId: string; + ruleType: string; logger: Logger; buildRuleMessage: BuildRuleMessage; }): Promise => { @@ -167,6 +183,7 @@ export const hasTimestampFields = async (args: { inputIndices, ruleStatusClient, ruleId, + ruleType, spaceId, logger, buildRuleMessage, @@ -184,6 +201,8 @@ export const hasTimestampFields = async (args: { await ruleStatusClient.logStatusChange({ message: errorString.trimEnd(), ruleId, + ruleName, + ruleType, spaceId, newStatus: RuleExecutionStatus['partial failure'], }); @@ -210,6 +229,8 @@ export const hasTimestampFields = async (args: { await ruleStatusClient.logStatusChange({ message: errorString, ruleId, + ruleName, + ruleType, spaceId, newStatus: RuleExecutionStatus['partial failure'], }); diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 9c4d739e0f43..bffcc823d047 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -109,11 +109,14 @@ import { ctiFieldMap } from './lib/detection_engine/rule_types/field_maps/cti'; import { legacyRulesNotificationAlertType } from './lib/detection_engine/notifications/legacy_rules_notification_alert_type'; // eslint-disable-next-line no-restricted-imports import { legacyIsNotificationAlertExecutor } from './lib/detection_engine/notifications/legacy_types'; +import { IEventLogClientService, IEventLogService } from '../../event_log/server'; +import { registerEventLogProvider } from './lib/detection_engine/rule_execution_log/event_log_adapter/register_event_log_provider'; export interface SetupPlugins { alerting: AlertingSetup; data: DataPluginSetup; encryptedSavedObjects?: EncryptedSavedObjectsSetup; + eventLog: IEventLogService; features: FeaturesSetup; lists?: ListPluginSetup; ml?: MlSetup; @@ -121,20 +124,21 @@ export interface SetupPlugins { security?: SecuritySetup; spaces?: SpacesSetup; taskManager?: TaskManagerSetupContract; - usageCollection?: UsageCollectionSetup; telemetry?: TelemetryPluginSetup; + usageCollection?: UsageCollectionSetup; } export interface StartPlugins { alerting: AlertPluginStartContract; + cases?: CasesPluginStartContract; data: DataPluginStart; + eventLog: IEventLogClientService; fleet?: FleetStartContract; licensing: LicensingPluginStart; ruleRegistry: RuleRegistryPluginStartContract; + security: SecurityPluginStart; taskManager?: TaskManagerStartContract; telemetry?: TelemetryPluginStart; - security: SecurityPluginStart; - cases?: CasesPluginStartContract; } // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -202,6 +206,9 @@ export class Plugin implements IPlugin(); core.http.registerRouteHandlerContext( APP_ID, @@ -210,8 +217,9 @@ export class Plugin implements IPlugin plugins.spaces?.spacesService?.getSpaceId(request) || DEFAULT_SPACE_ID, getExecutionLogClient: () => new RuleExecutionLogClient({ - ruleDataService: plugins.ruleRegistry.ruleDataService, savedObjectsClient: context.core.savedObjects.client, + eventLogService, + underlyingClient: config.ruleExecutionLog.underlyingClient, }), }) ); @@ -262,11 +270,10 @@ export class Plugin implements IPlugin Date: Mon, 11 Oct 2021 13:49:54 +0200 Subject: [PATCH 42/74] [bfetch] Fix memory leak (#113756) --- .../create_streaming_batched_function.test.ts | 50 ++++++++--------- .../create_streaming_batched_function.ts | 7 +-- src/plugins/bfetch/public/plugin.ts | 29 ++++------ .../public/streaming/fetch_streaming.test.ts | 29 +++++----- .../public/streaming/fetch_streaming.ts | 55 ++++++++----------- 5 files changed, 76 insertions(+), 94 deletions(-) diff --git a/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts b/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts index 0b6dbe49d0e8..32adc0d7df0c 100644 --- a/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts +++ b/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts @@ -9,7 +9,7 @@ import { createStreamingBatchedFunction } from './create_streaming_batched_function'; import { fetchStreaming as fetchStreamingReal } from '../streaming/fetch_streaming'; import { AbortError, defer, of } from '../../../kibana_utils/public'; -import { Subject, of as rxof } from 'rxjs'; +import { Subject } from 'rxjs'; const flushPromises = () => new Promise((resolve) => setImmediate(resolve)); @@ -61,7 +61,7 @@ describe('createStreamingBatchedFunction()', () => { const fn = createStreamingBatchedFunction({ url: '/test', fetchStreaming, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); expect(typeof fn).toBe('function'); }); @@ -71,7 +71,7 @@ describe('createStreamingBatchedFunction()', () => { const fn = createStreamingBatchedFunction({ url: '/test', fetchStreaming, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const res = fn({}); expect(typeof res.then).toBe('function'); @@ -85,7 +85,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); expect(fetchStreaming).toHaveBeenCalledTimes(0); @@ -105,7 +105,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); expect(fetchStreaming).toHaveBeenCalledTimes(0); @@ -120,7 +120,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); fn({ foo: 'bar' }); @@ -139,7 +139,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); fn({ foo: 'bar' }); @@ -161,7 +161,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); expect(fetchStreaming).toHaveBeenCalledTimes(0); @@ -180,7 +180,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const abortController = new AbortController(); @@ -203,7 +203,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); fn({ a: '1' }); @@ -227,7 +227,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); fn({ a: '1' }); @@ -248,7 +248,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const promise1 = fn({ a: '1' }); @@ -266,7 +266,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); await flushPromises(); @@ -310,7 +310,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const promise1 = fn({ a: '1' }); @@ -348,7 +348,7 @@ describe('createStreamingBatchedFunction()', () => { fn({ a: '1' }); - const dontCompress = await fetchStreaming.mock.calls[0][0].compressionDisabled$.toPromise(); + const dontCompress = await fetchStreaming.mock.calls[0][0].getIsCompressionDisabled(); expect(dontCompress).toBe(false); }); @@ -359,7 +359,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const promise1 = fn({ a: '1' }); @@ -401,7 +401,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const promise = fn({ a: '1' }); @@ -430,7 +430,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const promise1 = of(fn({ a: '1' })); @@ -483,7 +483,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const abortController = new AbortController(); @@ -514,7 +514,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const abortController = new AbortController(); @@ -554,7 +554,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const promise1 = of(fn({ a: '1' })); @@ -585,7 +585,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const promise1 = of(fn({ a: '1' })); @@ -623,7 +623,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const promise1 = of(fn({ a: '1' })); @@ -656,7 +656,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); const promise1 = of(fn({ a: '1' })); @@ -693,7 +693,7 @@ describe('createStreamingBatchedFunction()', () => { fetchStreaming, maxItemAge: 5, flushOnMaxItems: 3, - compressionDisabled$: rxof(true), + getIsCompressionDisabled: () => true, }); await flushPromises(); diff --git a/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts b/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts index d5f955f517d1..3ff8da08cfce 100644 --- a/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts +++ b/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { Observable, of } from 'rxjs'; import { AbortError, abortSignalToPromise, defer } from '../../../kibana_utils/public'; import { ItemBufferParams, @@ -51,7 +50,7 @@ export interface StreamingBatchedFunctionParams { /** * Disabled zlib compression of response chunks. */ - compressionDisabled$?: Observable; + getIsCompressionDisabled?: () => boolean; } /** @@ -69,7 +68,7 @@ export const createStreamingBatchedFunction = ( fetchStreaming: fetchStreamingInjected = fetchStreaming, flushOnMaxItems = 25, maxItemAge = 10, - compressionDisabled$ = of(false), + getIsCompressionDisabled = () => false, } = params; const [fn] = createBatchedFunction({ onCall: (payload: Payload, signal?: AbortSignal) => { @@ -125,7 +124,7 @@ export const createStreamingBatchedFunction = ( body: JSON.stringify({ batch }), method: 'POST', signal: abortController.signal, - compressionDisabled$, + getIsCompressionDisabled, }); const handleStreamError = (error: any) => { diff --git a/src/plugins/bfetch/public/plugin.ts b/src/plugins/bfetch/public/plugin.ts index 3ad451c7713e..54bcb305d867 100644 --- a/src/plugins/bfetch/public/plugin.ts +++ b/src/plugins/bfetch/public/plugin.ts @@ -6,13 +6,12 @@ * Side Public License, v 1. */ -import { CoreStart, PluginInitializerContext, CoreSetup, Plugin } from 'src/core/public'; -import { from, Observable, of } from 'rxjs'; -import { switchMap } from 'rxjs/operators'; +import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/public'; import { fetchStreaming as fetchStreamingStatic, FetchStreamingParams } from './streaming'; import { DISABLE_BFETCH_COMPRESSION, removeLeadingSlash } from '../common'; import { createStreamingBatchedFunction, StreamingBatchedFunctionParams } from './batching'; import { BatchedFunc } from './batching/types'; +import { createStartServicesGetter } from '../../kibana_utils/public'; // eslint-disable-next-line export interface BfetchPublicSetupDependencies {} @@ -50,16 +49,12 @@ export class BfetchPublicPlugin const { version } = this.initializerContext.env.packageInfo; const basePath = core.http.basePath.get(); - const compressionDisabled$ = from(core.getStartServices()).pipe( - switchMap((deps) => { - return of(deps[0]); - }), - switchMap((coreStart) => { - return coreStart.uiSettings.get$(DISABLE_BFETCH_COMPRESSION); - }) - ); - const fetchStreaming = this.fetchStreaming(version, basePath, compressionDisabled$); - const batchedFunction = this.batchedFunction(fetchStreaming, compressionDisabled$); + const startServices = createStartServicesGetter(core.getStartServices); + const getIsCompressionDisabled = () => + startServices().core.uiSettings.get(DISABLE_BFETCH_COMPRESSION); + + const fetchStreaming = this.fetchStreaming(version, basePath, getIsCompressionDisabled); + const batchedFunction = this.batchedFunction(fetchStreaming, getIsCompressionDisabled); this.contract = { fetchStreaming, @@ -79,7 +74,7 @@ export class BfetchPublicPlugin ( version: string, basePath: string, - compressionDisabled$: Observable + getIsCompressionDisabled: () => boolean ): BfetchPublicSetup['fetchStreaming'] => (params) => fetchStreamingStatic({ @@ -90,18 +85,18 @@ export class BfetchPublicPlugin 'kbn-version': version, ...(params.headers || {}), }, - compressionDisabled$, + getIsCompressionDisabled, }); private batchedFunction = ( fetchStreaming: BfetchPublicContract['fetchStreaming'], - compressionDisabled$: Observable + getIsCompressionDisabled: () => boolean ): BfetchPublicContract['batchedFunction'] => (params) => createStreamingBatchedFunction({ ...params, - compressionDisabled$, + getIsCompressionDisabled, fetchStreaming: params.fetchStreaming || fetchStreaming, }); } diff --git a/src/plugins/bfetch/public/streaming/fetch_streaming.test.ts b/src/plugins/bfetch/public/streaming/fetch_streaming.test.ts index a5d066f6d9a2..67ebf8d5a1c2 100644 --- a/src/plugins/bfetch/public/streaming/fetch_streaming.test.ts +++ b/src/plugins/bfetch/public/streaming/fetch_streaming.test.ts @@ -8,7 +8,6 @@ import { fetchStreaming } from './fetch_streaming'; import { mockXMLHttpRequest } from '../test_helpers/xhr'; -import { of } from 'rxjs'; import { promisify } from 'util'; import { deflate } from 'zlib'; const pDeflate = promisify(deflate); @@ -30,7 +29,7 @@ test('returns XHR request', () => { setup(); const { xhr } = fetchStreaming({ url: 'http://example.com', - compressionDisabled$: of(true), + getIsCompressionDisabled: () => true, }); expect(typeof xhr.readyState).toBe('number'); }); @@ -39,7 +38,7 @@ test('returns stream', () => { setup(); const { stream } = fetchStreaming({ url: 'http://example.com', - compressionDisabled$: of(true), + getIsCompressionDisabled: () => true, }); expect(typeof stream.subscribe).toBe('function'); }); @@ -48,7 +47,7 @@ test('promise resolves when request completes', async () => { const env = setup(); const { stream } = fetchStreaming({ url: 'http://example.com', - compressionDisabled$: of(true), + getIsCompressionDisabled: () => true, }); let resolved = false; @@ -81,7 +80,7 @@ test('promise resolves when compressed request completes', async () => { const env = setup(); const { stream } = fetchStreaming({ url: 'http://example.com', - compressionDisabled$: of(false), + getIsCompressionDisabled: () => false, }); let resolved = false; @@ -116,7 +115,7 @@ test('promise resolves when compressed chunked request completes', async () => { const env = setup(); const { stream } = fetchStreaming({ url: 'http://example.com', - compressionDisabled$: of(false), + getIsCompressionDisabled: () => false, }); let resolved = false; @@ -160,7 +159,7 @@ test('streams incoming text as it comes through, according to separators', async const env = setup(); const { stream } = fetchStreaming({ url: 'http://example.com', - compressionDisabled$: of(true), + getIsCompressionDisabled: () => true, }); const spy = jest.fn(); @@ -201,7 +200,7 @@ test('completes stream observable when request finishes', async () => { const env = setup(); const { stream } = fetchStreaming({ url: 'http://example.com', - compressionDisabled$: of(true), + getIsCompressionDisabled: () => true, }); const spy = jest.fn(); @@ -226,7 +225,7 @@ test('completes stream observable when aborted', async () => { const { stream } = fetchStreaming({ url: 'http://example.com', signal: abort.signal, - compressionDisabled$: of(true), + getIsCompressionDisabled: () => true, }); const spy = jest.fn(); @@ -252,7 +251,7 @@ test('promise throws when request errors', async () => { const env = setup(); const { stream } = fetchStreaming({ url: 'http://example.com', - compressionDisabled$: of(true), + getIsCompressionDisabled: () => true, }); const spy = jest.fn(); @@ -279,7 +278,7 @@ test('stream observable errors when request errors', async () => { const env = setup(); const { stream } = fetchStreaming({ url: 'http://example.com', - compressionDisabled$: of(true), + getIsCompressionDisabled: () => true, }); const spy = jest.fn(); @@ -312,7 +311,7 @@ test('sets custom headers', async () => { 'Content-Type': 'text/plain', Authorization: 'Bearer 123', }, - compressionDisabled$: of(true), + getIsCompressionDisabled: () => true, }); expect(env.xhr.setRequestHeader).toHaveBeenCalledWith('Content-Type', 'text/plain'); @@ -326,7 +325,7 @@ test('uses credentials', async () => { fetchStreaming({ url: 'http://example.com', - compressionDisabled$: of(true), + getIsCompressionDisabled: () => true, }); expect(env.xhr.withCredentials).toBe(true); @@ -342,7 +341,7 @@ test('opens XHR request and sends specified body', async () => { url: 'http://elastic.co', method: 'GET', body: 'foobar', - compressionDisabled$: of(true), + getIsCompressionDisabled: () => true, }); expect(env.xhr.open).toHaveBeenCalledTimes(1); @@ -355,7 +354,7 @@ test('uses POST request method by default', async () => { const env = setup(); fetchStreaming({ url: 'http://elastic.co', - compressionDisabled$: of(true), + getIsCompressionDisabled: () => true, }); expect(env.xhr.open).toHaveBeenCalledWith('POST', 'http://elastic.co'); }); diff --git a/src/plugins/bfetch/public/streaming/fetch_streaming.ts b/src/plugins/bfetch/public/streaming/fetch_streaming.ts index 1af35ef68fb8..a94c8d3980cb 100644 --- a/src/plugins/bfetch/public/streaming/fetch_streaming.ts +++ b/src/plugins/bfetch/public/streaming/fetch_streaming.ts @@ -6,8 +6,7 @@ * Side Public License, v 1. */ -import { Observable, of } from 'rxjs'; -import { map, share, switchMap } from 'rxjs/operators'; +import { map, share } from 'rxjs/operators'; import { inflateResponse } from '.'; import { fromStreamingXhr } from './from_streaming_xhr'; import { split } from './split'; @@ -18,7 +17,7 @@ export interface FetchStreamingParams { method?: 'GET' | 'POST'; body?: string; signal?: AbortSignal; - compressionDisabled$?: Observable; + getIsCompressionDisabled?: () => boolean; } /** @@ -31,49 +30,39 @@ export function fetchStreaming({ method = 'POST', body = '', signal, - compressionDisabled$ = of(false), + getIsCompressionDisabled = () => false, }: FetchStreamingParams) { const xhr = new window.XMLHttpRequest(); - const msgStream = compressionDisabled$.pipe( - switchMap((compressionDisabled) => { - // Begin the request - xhr.open(method, url); - xhr.withCredentials = true; + // Begin the request + xhr.open(method, url); + xhr.withCredentials = true; - if (!compressionDisabled) { - headers['X-Chunk-Encoding'] = 'deflate'; - } + const isCompressionDisabled = getIsCompressionDisabled(); - // Set the HTTP headers - Object.entries(headers).forEach(([k, v]) => xhr.setRequestHeader(k, v)); + if (!isCompressionDisabled) { + headers['X-Chunk-Encoding'] = 'deflate'; + } - const stream = fromStreamingXhr(xhr, signal); + // Set the HTTP headers + Object.entries(headers).forEach(([k, v]) => xhr.setRequestHeader(k, v)); - // Send the payload to the server - xhr.send(body); + const stream = fromStreamingXhr(xhr, signal); - // Return a stream of chunked decompressed messages - return stream.pipe( - split('\n'), - map((msg) => { - return compressionDisabled ? msg : inflateResponse(msg); - }) - ); + // Send the payload to the server + xhr.send(body); + + // Return a stream of chunked decompressed messages + const stream$ = stream.pipe( + split('\n'), + map((msg) => { + return isCompressionDisabled ? msg : inflateResponse(msg); }), share() ); - // start execution - const msgStreamSub = msgStream.subscribe({ - error: (e) => {}, - complete: () => { - msgStreamSub.unsubscribe(); - }, - }); - return { xhr, - stream: msgStream, + stream: stream$, }; } From 5ff38a122b7fb4b0c77f9b3bbc43c6878ebb060c Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 11 Oct 2021 13:10:52 +0100 Subject: [PATCH 43/74] skip failing es promotion suites (#114471) --- x-pack/test/api_integration/apis/maps/get_tile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/api_integration/apis/maps/get_tile.js b/x-pack/test/api_integration/apis/maps/get_tile.js index 03a16175931a..b153cc1ff030 100644 --- a/x-pack/test/api_integration/apis/maps/get_tile.js +++ b/x-pack/test/api_integration/apis/maps/get_tile.js @@ -13,7 +13,8 @@ import { MVT_SOURCE_LAYER_NAME } from '../../../../plugins/maps/common/constants export default function ({ getService }) { const supertest = getService('supertest'); - describe('getTile', () => { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/114471 + describe.skip('getTile', () => { it('should return vector tile containing document', async () => { const resp = await supertest .get( From 76546920fc3daab9fb3ddb891e1973775f4c61aa Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 11 Oct 2021 13:17:33 +0100 Subject: [PATCH 44/74] skip failing es promotion suites (#114473, #114474) --- .../functional/apps/index_lifecycle_management/home_page.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts b/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts index c51e2968baee..e2540d80280c 100644 --- a/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts +++ b/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts @@ -16,7 +16,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const retry = getService('retry'); const esClient = getService('es'); - describe('Home page', function () { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/114473 and https://github.com/elastic/kibana/issues/114474 + describe.skip('Home page', function () { before(async () => { await pageObjects.common.navigateToApp('indexLifecycleManagement'); }); From c34e99ee73ede89c3425dfdd13ad05747637c4b7 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 11 Oct 2021 13:37:04 +0100 Subject: [PATCH 45/74] skip flaky suites (#100951) --- .../__jest__/client_integration/follower_indices_list.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js index b9e47b029e30..bcd4aaf82eeb 100644 --- a/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js +++ b/x-pack/plugins/cross_cluster_replication/public/__jest__/client_integration/follower_indices_list.test.js @@ -314,7 +314,8 @@ describe('', () => { }); }); - describe('detail panel', () => { + // FLAKY: https://github.com/elastic/kibana/issues/100951 + describe.skip('detail panel', () => { test('should open a detail panel when clicking on a follower index', async () => { expect(exists('followerIndexDetail')).toBe(false); From f2bfa595ee4cbc4cdea33947f8c539c40ffbf1ed Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 11 Oct 2021 13:50:27 +0100 Subject: [PATCH 46/74] skip flaky suite (#106053) --- .../test/functional/apps/ml/anomaly_detection/custom_urls.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/custom_urls.ts b/x-pack/test/functional/apps/ml/anomaly_detection/custom_urls.ts index 0dcb76730960..7d4df75ccdcf 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/custom_urls.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/custom_urls.ts @@ -81,7 +81,8 @@ export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); const browser = getService('browser'); - describe('custom urls', function () { + // FLAKY: https://github.com/elastic/kibana/issues/106053 + describe.skip('custom urls', function () { this.tags(['mlqa']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); From 2ffbf6e58eadade0bb9b5386de7d3406d3cc5ae8 Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Mon, 11 Oct 2021 15:20:27 +0200 Subject: [PATCH 47/74] [Security Solution] Add host isolation exception IPs UI (#113762) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../host_isolation_exceptions/service.ts | 14 ++ .../host_isolation_exceptions/store/action.ts | 17 +- .../store/builders.ts | 4 + .../store/middleware.test.ts | 80 ++++++- .../store/middleware.ts | 46 +++- .../store/reducer.test.ts | 10 + .../store/reducer.ts | 18 ++ .../pages/host_isolation_exceptions/types.ts | 5 + .../pages/host_isolation_exceptions/utils.ts | 41 ++++ .../view/components/empty.tsx | 12 +- .../view/components/form.test.tsx | 75 +++++++ .../view/components/form.tsx | 206 ++++++++++++++++++ .../view/components/form_flyout.test.tsx | 114 ++++++++++ .../view/components/form_flyout.tsx | 180 +++++++++++++++ .../view/components/translations.ts | 64 ++++++ .../host_isolation_exceptions_list.test.tsx | 22 +- .../view/host_isolation_exceptions_list.tsx | 33 ++- 17 files changed, 925 insertions(+), 16 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/utils.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.test.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form_flyout.test.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form_flyout.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/translations.ts diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/service.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/service.ts index 79ca595fbb61..8af353a3c953 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/service.ts +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/service.ts @@ -6,6 +6,7 @@ */ import { + CreateExceptionListItemSchema, ExceptionListItemSchema, FoundExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; @@ -65,6 +66,19 @@ export async function getHostIsolationExceptionItems({ return entries; } +export async function createHostIsolationExceptionItem({ + http, + exception, +}: { + http: HttpStart; + exception: CreateExceptionListItemSchema; +}): Promise { + await ensureHostIsolationExceptionsListExists(http); + return http.post(EXCEPTION_LIST_ITEM_URL, { + body: JSON.stringify(exception), + }); +} + export async function deleteHostIsolationExceptionItems(http: HttpStart, id: string) { await ensureHostIsolationExceptionsListExists(http); return http.delete(EXCEPTION_LIST_ITEM_URL, { diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/action.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/action.ts index 0a9f77665537..a5fae36486f9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/action.ts +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/action.ts @@ -14,6 +14,20 @@ export type HostIsolationExceptionsPageDataChanged = payload: HostIsolationExceptionsPageState['entries']; }; +export type HostIsolationExceptionsFormStateChanged = + Action<'hostIsolationExceptionsFormStateChanged'> & { + payload: HostIsolationExceptionsPageState['form']['status']; + }; + +export type HostIsolationExceptionsFormEntryChanged = + Action<'hostIsolationExceptionsFormEntryChanged'> & { + payload: HostIsolationExceptionsPageState['form']['entry']; + }; + +export type HostIsolationExceptionsCreateEntry = Action<'hostIsolationExceptionsCreateEntry'> & { + payload: HostIsolationExceptionsPageState['form']['entry']; +}; + export type HostIsolationExceptionsDeleteItem = Action<'hostIsolationExceptionsMarkToDelete'> & { payload?: ExceptionListItemSchema; }; @@ -24,9 +38,10 @@ export type HostIsolationExceptionsDeleteStatusChanged = Action<'hostIsolationExceptionsDeleteStatusChanged'> & { payload: HostIsolationExceptionsPageState['deletion']['status']; }; - export type HostIsolationExceptionsPageAction = | HostIsolationExceptionsPageDataChanged + | HostIsolationExceptionsCreateEntry + | HostIsolationExceptionsFormStateChanged | HostIsolationExceptionsDeleteItem | HostIsolationExceptionsSubmitDelete | HostIsolationExceptionsDeleteStatusChanged; diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/builders.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/builders.ts index 68a50f9c813f..8f32d9cf8d45 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/builders.ts +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/builders.ts @@ -16,6 +16,10 @@ export const initialHostIsolationExceptionsPageState = (): HostIsolationExceptio page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, filter: '', }, + form: { + entry: undefined, + status: createUninitialisedResourceState(), + }, deletion: { item: undefined, status: createUninitialisedResourceState(), diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.test.ts index 984794e074eb..266853fdab5e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.test.ts @@ -5,10 +5,11 @@ * 2.0. */ +import { CreateExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { applyMiddleware, createStore, Store } from 'redux'; -import { HOST_ISOLATION_EXCEPTIONS_PATH } from '../../../../../common/constants'; import { coreMock } from '../../../../../../../../src/core/public/mocks'; import { getFoundExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/found_exception_list_item_schema.mock'; +import { HOST_ISOLATION_EXCEPTIONS_PATH } from '../../../../../common/constants'; import { AppAction } from '../../../../common/store/actions'; import { createSpyMiddleware, @@ -19,8 +20,13 @@ import { isLoadedResourceState, isLoadingResourceState, } from '../../../state'; -import { getHostIsolationExceptionItems, deleteHostIsolationExceptionItems } from '../service'; +import { + createHostIsolationExceptionItem, + deleteHostIsolationExceptionItems, + getHostIsolationExceptionItems, +} from '../service'; import { HostIsolationExceptionsPageState } from '../types'; +import { createEmptyHostIsolationException } from '../utils'; import { initialHostIsolationExceptionsPageState } from './builders'; import { createHostIsolationExceptionsPageMiddleware } from './middleware'; import { hostIsolationExceptionsPageReducer } from './reducer'; @@ -29,6 +35,7 @@ import { getListFetchError } from './selector'; jest.mock('../service'); const getHostIsolationExceptionItemsMock = getHostIsolationExceptionItems as jest.Mock; const deleteHostIsolationExceptionItemsMock = deleteHostIsolationExceptionItems as jest.Mock; +const createHostIsolationExceptionItemMock = createHostIsolationExceptionItem as jest.Mock; const fakeCoreStart = coreMock.createStart({ basePath: '/mock' }); @@ -81,7 +88,7 @@ describe('Host isolation exceptions middleware', () => { }; beforeEach(() => { - getHostIsolationExceptionItemsMock.mockClear(); + getHostIsolationExceptionItemsMock.mockReset(); getHostIsolationExceptionItemsMock.mockImplementation(getFoundExceptionListItemSchemaMock); }); @@ -145,11 +152,74 @@ describe('Host isolation exceptions middleware', () => { }); }); + describe('When adding an item to host isolation exceptions', () => { + let entry: CreateExceptionListItemSchema; + beforeEach(() => { + createHostIsolationExceptionItemMock.mockReset(); + entry = { + ...createEmptyHostIsolationException(), + name: 'test name', + description: 'description', + entries: [ + { + field: 'destination.ip', + operator: 'included', + type: 'match', + value: '10.0.0.1', + }, + ], + }; + }); + it('should dispatch a form loading state when an entry is submited', async () => { + const waiter = spyMiddleware.waitForAction('hostIsolationExceptionsFormStateChanged', { + validate({ payload }) { + return isLoadingResourceState(payload); + }, + }); + store.dispatch({ + type: 'hostIsolationExceptionsCreateEntry', + payload: entry, + }); + await waiter; + }); + it('should dispatch a form success state when an entry is confirmed by the API', async () => { + const waiter = spyMiddleware.waitForAction('hostIsolationExceptionsFormStateChanged', { + validate({ payload }) { + return isLoadedResourceState(payload); + }, + }); + store.dispatch({ + type: 'hostIsolationExceptionsCreateEntry', + payload: entry, + }); + await waiter; + expect(createHostIsolationExceptionItemMock).toHaveBeenCalledWith({ + http: fakeCoreStart.http, + exception: entry, + }); + }); + it('should dispatch a form failure state when an entry is rejected by the API', async () => { + createHostIsolationExceptionItemMock.mockRejectedValue({ + body: { message: 'error message', statusCode: 500, error: 'Not today' }, + }); + const waiter = spyMiddleware.waitForAction('hostIsolationExceptionsFormStateChanged', { + validate({ payload }) { + return isFailedResourceState(payload); + }, + }); + store.dispatch({ + type: 'hostIsolationExceptionsCreateEntry', + payload: entry, + }); + await waiter; + }); + }); + describe('When deleting an item from host isolation exceptions', () => { beforeEach(() => { - deleteHostIsolationExceptionItemsMock.mockClear(); + deleteHostIsolationExceptionItemsMock.mockReset(); deleteHostIsolationExceptionItemsMock.mockReturnValue(undefined); - getHostIsolationExceptionItemsMock.mockClear(); + getHostIsolationExceptionItemsMock.mockReset(); getHostIsolationExceptionItemsMock.mockImplementation(getFoundExceptionListItemSchemaMock); store.dispatch({ type: 'hostIsolationExceptionsMarkToDelete', diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.ts index 4946cac48870..bbc754e8155b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/middleware.ts @@ -6,11 +6,13 @@ */ import { + CreateExceptionListItemSchema, ExceptionListItemSchema, FoundExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; import { CoreStart, HttpSetup, HttpStart } from 'kibana/public'; import { matchPath } from 'react-router-dom'; +import { transformNewItemOutput } from '@kbn/securitysolution-list-hooks'; import { AppLocation, Immutable } from '../../../../../common/endpoint/types'; import { ImmutableMiddleware, ImmutableMiddlewareAPI } from '../../../../common/store'; import { AppAction } from '../../../../common/store/actions'; @@ -20,7 +22,11 @@ import { createFailedResourceState, createLoadedResourceState, } from '../../../state/async_resource_builders'; -import { deleteHostIsolationExceptionItems, getHostIsolationExceptionItems } from '../service'; +import { + deleteHostIsolationExceptionItems, + getHostIsolationExceptionItems, + createHostIsolationExceptionItem, +} from '../service'; import { HostIsolationExceptionsPageState } from '../types'; import { getCurrentListPageDataState, getCurrentLocation, getItemToDelete } from './selector'; @@ -39,12 +45,50 @@ export const createHostIsolationExceptionsPageMiddleware = ( if (action.type === 'userChangedUrl' && isHostIsolationExceptionsPage(action.payload)) { loadHostIsolationExceptionsList(store, coreStart.http); } + + if (action.type === 'hostIsolationExceptionsCreateEntry') { + createHostIsolationException(store, coreStart.http); + } + if (action.type === 'hostIsolationExceptionsSubmitDelete') { deleteHostIsolationExceptionsItem(store, coreStart.http); } }; }; +async function createHostIsolationException( + store: ImmutableMiddlewareAPI, + http: HttpStart +) { + const { dispatch } = store; + const entry = transformNewItemOutput( + store.getState().form.entry as CreateExceptionListItemSchema + ); + dispatch({ + type: 'hostIsolationExceptionsFormStateChanged', + payload: { + type: 'LoadingResourceState', + // @ts-expect-error-next-line will be fixed with when AsyncResourceState is refactored (#830) + previousState: entry, + }, + }); + try { + const response = await createHostIsolationExceptionItem({ + http, + exception: entry, + }); + dispatch({ + type: 'hostIsolationExceptionsFormStateChanged', + payload: createLoadedResourceState(response), + }); + } catch (error) { + dispatch({ + type: 'hostIsolationExceptionsFormStateChanged', + payload: createFailedResourceState(error.body ?? error), + }); + } +} + async function loadHostIsolationExceptionsList( store: ImmutableMiddlewareAPI, http: HttpStart diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.test.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.test.ts index 211b03f36d96..98b459fac41d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.test.ts @@ -11,6 +11,7 @@ import { initialHostIsolationExceptionsPageState } from './builders'; import { HOST_ISOLATION_EXCEPTIONS_PATH } from '../../../../../common/constants'; import { hostIsolationExceptionsPageReducer } from './reducer'; import { getCurrentLocation } from './selector'; +import { createEmptyHostIsolationException } from '../utils'; describe('Host Isolation Exceptions Reducer', () => { let initialState: HostIsolationExceptionsPageState; @@ -41,4 +42,13 @@ describe('Host Isolation Exceptions Reducer', () => { }); }); }); + it('should set an initial loading state when creating new entries', () => { + const entry = createEmptyHostIsolationException(); + const result = hostIsolationExceptionsPageReducer(initialState, { + type: 'hostIsolationExceptionsCreateEntry', + payload: entry, + }); + expect(result.form.status).toEqual({ type: 'UninitialisedResourceState' }); + expect(result.form.entry).toBe(entry); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.ts index 09182661a80b..d97295598f44 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/store/reducer.ts @@ -38,6 +38,24 @@ export const hostIsolationExceptionsPageReducer: StateReducer = ( action ) => { switch (action.type) { + case 'hostIsolationExceptionsCreateEntry': { + return { + ...state, + form: { + entry: action.payload, + status: createUninitialisedResourceState(), + }, + }; + } + case 'hostIsolationExceptionsFormStateChanged': { + return { + ...state, + form: { + ...state.form, + status: action.payload, + }, + }; + } case 'hostIsolationExceptionsPageDataChanged': { return { ...state, diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/types.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/types.ts index 443a86fefab8..1a74042fb652 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/types.ts @@ -6,6 +6,7 @@ */ import type { + CreateExceptionListItemSchema, ExceptionListItemSchema, FoundExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; @@ -27,4 +28,8 @@ export interface HostIsolationExceptionsPageState { item?: ExceptionListItemSchema; status: AsyncResourceState; }; + form: { + entry?: CreateExceptionListItemSchema; + status: AsyncResourceState; + }; } diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/utils.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/utils.ts new file mode 100644 index 000000000000..bfb1ac048e28 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/utils.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 { CreateExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID } from '@kbn/securitysolution-list-constants'; +import ipaddr from 'ipaddr.js'; + +export function createEmptyHostIsolationException(): CreateExceptionListItemSchema { + return { + comments: [], + description: '', + entries: [ + { + field: 'destination.ip', + operator: 'included', + type: 'match', + value: '', + }, + ], + item_id: undefined, + list_id: ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID, + name: '', + namespace_type: 'agnostic', + os_types: ['windows', 'linux', 'macos'], + tags: ['policy:all'], + type: 'simple', + }; +} + +export function isValidIPv4OrCIDR(maybeIp: string): boolean { + try { + ipaddr.IPv4.parseCIDR(maybeIp); + return true; + } catch (e) { + return ipaddr.IPv4.isValid(maybeIp); + } +} diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/empty.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/empty.tsx index d7c512794173..eb53268a9fbd 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/empty.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/empty.tsx @@ -7,7 +7,7 @@ import React, { memo } from 'react'; import styled, { css } from 'styled-components'; -import { EuiEmptyPrompt } from '@elastic/eui'; +import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; const EmptyPrompt = styled(EuiEmptyPrompt)` @@ -16,7 +16,7 @@ const EmptyPrompt = styled(EuiEmptyPrompt)` `} `; -export const HostIsolationExceptionsEmptyState = memo<{}>(() => { +export const HostIsolationExceptionsEmptyState = memo<{ onAdd: () => void }>(({ onAdd }) => { return ( (() => { defaultMessage="There are currently no host isolation exceptions" /> } + actions={ + + + + } /> ); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.test.tsx new file mode 100644 index 000000000000..b06449de69d8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.test.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createEmptyHostIsolationException } from '../../utils'; +import { HostIsolationExceptionsForm } from './form'; +import React from 'react'; +import { + AppContextTestRender, + createAppRootMockRenderer, +} from '../../../../../common/mock/endpoint'; +import { CreateExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import userEvent from '@testing-library/user-event'; + +describe('When on the host isolation exceptions add entry form', () => { + let render: ( + exception: CreateExceptionListItemSchema + ) => ReturnType; + let renderResult: ReturnType; + const onChange = jest.fn(); + const onError = jest.fn(); + + beforeEach(() => { + onChange.mockReset(); + onError.mockReset(); + const mockedContext = createAppRootMockRenderer(); + render = (exception: CreateExceptionListItemSchema) => { + return mockedContext.render( + + ); + }; + }); + + describe('When creating a new exception', () => { + let newException: CreateExceptionListItemSchema; + beforeEach(() => { + newException = createEmptyHostIsolationException(); + renderResult = render(newException); + }); + it('should render the form with empty inputs', () => { + expect(renderResult.getByTestId('hostIsolationExceptions-form-name-input')).toHaveValue(''); + expect(renderResult.getByTestId('hostIsolationExceptions-form-ip-input')).toHaveValue(''); + expect( + renderResult.getByTestId('hostIsolationExceptions-form-description-input') + ).toHaveValue(''); + }); + it('should call onError with true when a wrong ip value is introduced', () => { + const ipInput = renderResult.getByTestId('hostIsolationExceptions-form-ip-input'); + userEvent.type(ipInput, 'not an ip'); + expect(onError).toHaveBeenCalledWith(true); + }); + it('should call onError with false when a correct values are introduced', () => { + const ipInput = renderResult.getByTestId('hostIsolationExceptions-form-ip-input'); + const nameInput = renderResult.getByTestId('hostIsolationExceptions-form-name-input'); + + userEvent.type(nameInput, 'test name'); + userEvent.type(ipInput, '10.0.0.1'); + + expect(onError).toHaveBeenLastCalledWith(false); + }); + it('should call onChange when a value is introduced in a field', () => { + const ipInput = renderResult.getByTestId('hostIsolationExceptions-form-ip-input'); + userEvent.type(ipInput, '10.0.0.1'); + expect(onChange).toHaveBeenLastCalledWith({ + ...newException, + entries: [ + { field: 'destination.ip', operator: 'included', type: 'match', value: '10.0.0.1' }, + ], + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.tsx new file mode 100644 index 000000000000..84263f9d07c8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.tsx @@ -0,0 +1,206 @@ +/* + * 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 { + EuiFieldText, + EuiForm, + EuiFormRow, + EuiHorizontalRule, + EuiSpacer, + EuiText, + EuiTextArea, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { CreateExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; +import { isValidIPv4OrCIDR } from '../../utils'; +import { + DESCRIPTION_LABEL, + DESCRIPTION_PLACEHOLDER, + IP_ERROR, + IP_LABEL, + IP_PLACEHOLDER, + NAME_ERROR, + NAME_LABEL, + NAME_PLACEHOLDER, +} from './translations'; + +interface ExceptionIpEntry { + field: 'destination.ip'; + operator: 'included'; + type: 'match'; + value: ''; +} + +export const HostIsolationExceptionsForm: React.FC<{ + exception: CreateExceptionListItemSchema; + onError: (error: boolean) => void; + onChange: (exception: CreateExceptionListItemSchema) => void; +}> = memo(({ exception, onError, onChange }) => { + const [hasBeenInputNameVisited, setHasBeenInputNameVisited] = useState(false); + const [hasBeenInputIpVisited, setHasBeenInputIpVisited] = useState(false); + const [hasNameError, setHasNameError] = useState(true); + const [hasIpError, setHasIpError] = useState(true); + + useEffect(() => { + onError(hasNameError || hasIpError); + }, [hasNameError, hasIpError, onError]); + + const handleOnChangeName = useCallback( + (event: React.ChangeEvent) => { + const name = event.target.value; + if (!name.trim()) { + setHasNameError(true); + return; + } + setHasNameError(false); + onChange({ ...exception, name }); + }, + [exception, onChange] + ); + + const handleOnIpChange = useCallback( + (event: React.ChangeEvent) => { + const ip = event.target.value; + if (!isValidIPv4OrCIDR(ip)) { + setHasIpError(true); + return; + } + setHasIpError(false); + onChange({ + ...exception, + entries: [ + { + field: 'destination.ip', + operator: 'included', + type: 'match', + value: ip, + }, + ], + }); + }, + [exception, onChange] + ); + + const handleOnDescriptionChange = useCallback( + (event: React.ChangeEvent) => { + onChange({ ...exception, description: event.target.value }); + }, + [exception, onChange] + ); + + const nameInput = useMemo( + () => ( + + !hasBeenInputNameVisited && setHasBeenInputNameVisited(true)} + /> + + ), + [hasNameError, hasBeenInputNameVisited, exception.name, handleOnChangeName] + ); + + const ipInput = useMemo( + () => ( + + !hasBeenInputIpVisited && setHasBeenInputIpVisited(true)} + /> + + ), + [hasIpError, hasBeenInputIpVisited, exception.entries, handleOnIpChange] + ); + + const descriptionInput = useMemo( + () => ( + + + + ), + [exception.description, handleOnDescriptionChange] + ); + + return ( + + +

+ +

+
+ + + + + {nameInput} + {descriptionInput} + + + +

+ +

+
+ + + + + {ipInput} +
+ ); +}); + +HostIsolationExceptionsForm.displayName = 'HostIsolationExceptionsForm'; diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form_flyout.test.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form_flyout.test.tsx new file mode 100644 index 000000000000..6cfc9f56bead --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form_flyout.test.tsx @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { + AppContextTestRender, + createAppRootMockRenderer, +} from '../../../../../common/mock/endpoint'; +import userEvent from '@testing-library/user-event'; +import { HostIsolationExceptionsFormFlyout } from './form_flyout'; +import { act } from 'react-dom/test-utils'; +import { HOST_ISOLATION_EXCEPTIONS_PATH } from '../../../../../../common/constants'; + +jest.mock('../../service.ts'); + +describe('When on the host isolation exceptions flyout form', () => { + let mockedContext: AppContextTestRender; + let render: () => ReturnType; + let renderResult: ReturnType; + let waitForAction: AppContextTestRender['middlewareSpy']['waitForAction']; + + // const createHostIsolationExceptionItemMock = createHostIsolationExceptionItem as jest.mock; + + beforeEach(() => { + mockedContext = createAppRootMockRenderer(); + render = () => { + return mockedContext.render(); + }; + waitForAction = mockedContext.middlewareSpy.waitForAction; + }); + + describe('When creating a new exception', () => { + describe('with invalid data', () => { + it('should show disabled buttons when the form first load', () => { + renderResult = render(); + expect(renderResult.getByTestId('add-exception-cancel-button')).not.toHaveAttribute( + 'disabled' + ); + expect(renderResult.getByTestId('add-exception-confirm-button')).toHaveAttribute( + 'disabled' + ); + }); + }); + describe('with valid data', () => { + beforeEach(() => { + renderResult = render(); + const ipInput = renderResult.getByTestId('hostIsolationExceptions-form-ip-input'); + const nameInput = renderResult.getByTestId('hostIsolationExceptions-form-name-input'); + userEvent.type(nameInput, 'test name'); + userEvent.type(ipInput, '10.0.0.1'); + }); + it('should show enable buttons when the form is valid', () => { + expect(renderResult.getByTestId('add-exception-cancel-button')).not.toHaveAttribute( + 'disabled' + ); + expect(renderResult.getByTestId('add-exception-confirm-button')).not.toHaveAttribute( + 'disabled' + ); + }); + it('should submit the entry data when submit is pressed with valid data', async () => { + const confirmButton = renderResult.getByTestId('add-exception-confirm-button'); + expect(confirmButton).not.toHaveAttribute('disabled'); + const waiter = waitForAction('hostIsolationExceptionsCreateEntry'); + userEvent.click(confirmButton); + await waiter; + }); + it('should disable the submit button when an operation is in progress', () => { + act(() => { + mockedContext.store.dispatch({ + type: 'hostIsolationExceptionsFormStateChanged', + payload: { + type: 'LoadingResourceState', + previousState: { type: 'UninitialisedResourceState' }, + }, + }); + }); + const confirmButton = renderResult.getByTestId('add-exception-confirm-button'); + expect(confirmButton).toHaveAttribute('disabled'); + }); + it('should show a toast and close the flyout when the operation is finished', () => { + mockedContext.history.push(`${HOST_ISOLATION_EXCEPTIONS_PATH}?show=create`); + act(() => { + mockedContext.store.dispatch({ + type: 'hostIsolationExceptionsFormStateChanged', + payload: { + type: 'LoadedResourceState', + previousState: { type: 'UninitialisedResourceState' }, + }, + }); + }); + expect(mockedContext.coreStart.notifications.toasts.addSuccess).toHaveBeenCalled(); + expect(mockedContext.history.location.search).toBe(''); + }); + it('should show an error toast operation fails and enable the submit button', () => { + act(() => { + mockedContext.store.dispatch({ + type: 'hostIsolationExceptionsFormStateChanged', + payload: { + type: 'FailedResourceState', + previousState: { type: 'UninitialisedResourceState' }, + }, + }); + }); + expect(mockedContext.coreStart.notifications.toasts.addDanger).toHaveBeenCalled(); + const confirmButton = renderResult.getByTestId('add-exception-confirm-button'); + expect(confirmButton).not.toHaveAttribute('disabled'); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form_flyout.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form_flyout.tsx new file mode 100644 index 000000000000..5502a1b8ea2b --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form_flyout.tsx @@ -0,0 +1,180 @@ +/* + * 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 { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { CreateExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { Dispatch } from 'redux'; +import { Loader } from '../../../../../common/components/loader'; +import { useToasts } from '../../../../../common/lib/kibana'; +import { + isFailedResourceState, + isLoadedResourceState, + isLoadingResourceState, +} from '../../../../state/async_resource_state'; +import { HostIsolationExceptionsPageAction } from '../../store/action'; +import { createEmptyHostIsolationException } from '../../utils'; +import { + useHostIsolationExceptionsNavigateCallback, + useHostIsolationExceptionsSelector, +} from '../hooks'; +import { HostIsolationExceptionsForm } from './form'; + +export const HostIsolationExceptionsFormFlyout: React.FC<{}> = memo(() => { + const dispatch = useDispatch>(); + const toasts = useToasts(); + + const creationInProgress = useHostIsolationExceptionsSelector((state) => + isLoadingResourceState(state.form.status) + ); + const creationSuccessful = useHostIsolationExceptionsSelector((state) => + isLoadedResourceState(state.form.status) + ); + const creationFailure = useHostIsolationExceptionsSelector((state) => + isFailedResourceState(state.form.status) + ); + + const navigateCallback = useHostIsolationExceptionsNavigateCallback(); + + const [formHasError, setFormHasError] = useState(true); + const [exception, setException] = useState(undefined); + + const onCancel = useCallback( + () => + navigateCallback({ + show: undefined, + id: undefined, + }), + [navigateCallback] + ); + + useEffect(() => { + setException(createEmptyHostIsolationException()); + }, []); + + useEffect(() => { + if (creationSuccessful) { + onCancel(); + dispatch({ + type: 'hostIsolationExceptionsFormStateChanged', + payload: { + type: 'UninitialisedResourceState', + }, + }); + toasts.addSuccess( + i18n.translate( + 'xpack.securitySolution.hostIsolationExceptions.form.creationSuccessToastTitle', + { + defaultMessage: '"{name}" has been added to the host isolation exceptions list.', + values: { name: exception?.name }, + } + ) + ); + } + }, [creationSuccessful, onCancel, dispatch, toasts, exception?.name]); + + useEffect(() => { + if (creationFailure) { + toasts.addDanger( + i18n.translate( + 'xpack.securitySolution.hostIsolationExceptions.form.creationFailureToastTitle', + { + defaultMessage: 'There was an error creating the exception', + } + ) + ); + } + }, [dispatch, toasts, creationFailure]); + + const handleOnCancel = useCallback(() => { + if (creationInProgress) return; + onCancel(); + }, [creationInProgress, onCancel]); + + const handleOnSubmit = useCallback(() => { + dispatch({ + type: 'hostIsolationExceptionsCreateEntry', + payload: exception, + }); + }, [dispatch, exception]); + + const confirmButtonMemo = useMemo( + () => ( + + + + ), + [formHasError, creationInProgress, handleOnSubmit] + ); + + return exception ? ( + + + +

+ +

+
+
+ + + + + + + + + + + + + {confirmButtonMemo} + + +
+ ) : ( + + ); +}); + +HostIsolationExceptionsFormFlyout.displayName = 'HostIsolationExceptionsFormFlyout'; diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/translations.ts b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/translations.ts new file mode 100644 index 000000000000..df179c7a2221 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/translations.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const NAME_PLACEHOLDER = i18n.translate( + 'xpack.securitySolution.hostIsolationExceptions.form.name.placeholder', + { + defaultMessage: 'New IP', + } +); + +export const NAME_LABEL = i18n.translate( + 'xpack.securitySolution.hostIsolationExceptions.form.name.label', + { + defaultMessage: 'Name your host isolation exceptions', + } +); + +export const NAME_ERROR = i18n.translate( + 'xpack.securitySolution.hostIsolationExceptions.form.name.error', + { + defaultMessage: "The name can't be empty", + } +); + +export const DESCRIPTION_PLACEHOLDER = i18n.translate( + 'xpack.securitySolution.hostIsolationExceptions.form.description.placeholder', + { + defaultMessage: 'Describe your Host Isolation Exception', + } +); + +export const DESCRIPTION_LABEL = i18n.translate( + 'xpack.securitySolution.hostIsolationExceptions.form.description.label', + { + defaultMessage: 'Description', + } +); + +export const IP_PLACEHOLDER = i18n.translate( + 'xpack.securitySolution.hostIsolationExceptions.form.ip.placeholder', + { + defaultMessage: 'Ex 0.0.0.0/24', + } +); + +export const IP_LABEL = i18n.translate( + 'xpack.securitySolution.hostIsolationExceptions.form.ip.label', + { + defaultMessage: 'Enter IP Address', + } +); + +export const IP_ERROR = i18n.translate( + 'xpack.securitySolution.hostIsolationExceptions.form.ip.error', + { + defaultMessage: 'The ip is invalid. Only IPv4 with optional CIDR is supported', + } +); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx index 53b8bc33c252..ac472fdae4d7 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx @@ -5,16 +5,18 @@ * 2.0. */ -import React from 'react'; import { act } from '@testing-library/react'; -import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { getFoundExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/found_exception_list_item_schema.mock'; import { HOST_ISOLATION_EXCEPTIONS_PATH } from '../../../../../common/constants'; -import { HostIsolationExceptionsList } from './host_isolation_exceptions_list'; +import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; import { isFailedResourceState, isLoadedResourceState } from '../../../state'; import { getHostIsolationExceptionItems } from '../service'; -import { getFoundExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/found_exception_list_item_schema.mock'; +import { HostIsolationExceptionsList } from './host_isolation_exceptions_list'; jest.mock('../service'); + const getHostIsolationExceptionItemsMock = getHostIsolationExceptionItems as jest.Mock; describe('When on the host isolation exceptions page', () => { @@ -103,5 +105,17 @@ describe('When on the host isolation exceptions page', () => { ).toEqual(' Server is too far away'); }); }); + it('should show the create flyout when the add button is pressed', () => { + render(); + act(() => { + userEvent.click(renderResult.getByTestId('hostIsolationExceptionsListAddButton')); + }); + expect(renderResult.getByTestId('hostIsolationExceptionsCreateEditFlyout')).toBeTruthy(); + }); + it('should show the create flyout when the show location is create', () => { + history.push(`${HOST_ISOLATION_EXCEPTIONS_PATH}?show=create`); + render(); + expect(renderResult.getByTestId('hostIsolationExceptionsCreateEditFlyout')).toBeTruthy(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx index 53fb74d5bd8f..cfb0121396e2 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.tsx @@ -8,7 +8,7 @@ import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { i18n } from '@kbn/i18n'; import React, { Dispatch, useCallback } from 'react'; -import { EuiSpacer } from '@elastic/eui'; +import { EuiButton, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { useDispatch } from 'react-redux'; import { ExceptionItem } from '../../../../common/components/exceptions/viewer/exception_item'; @@ -32,6 +32,7 @@ import { ArtifactEntryCard, ArtifactEntryCardProps } from '../../../components/a import { HostIsolationExceptionsEmptyState } from './components/empty'; import { HostIsolationExceptionsPageAction } from '../store/action'; import { HostIsolationExceptionDeleteModal } from './components/delete_modal'; +import { HostIsolationExceptionsFormFlyout } from './components/form_flyout'; type HostIsolationExceptionPaginatedContent = PaginatedContentProps< Immutable, @@ -54,6 +55,8 @@ export const HostIsolationExceptionsList = () => { const dispatch = useDispatch>(); const itemToDelete = useHostIsolationExceptionsSelector(getItemToDelete); + const showFlyout = !!location.show; + const navigateCallback = useHostIsolationExceptionsNavigateCallback(); const handleOnSearch = useCallback( @@ -92,6 +95,15 @@ export const HostIsolationExceptionsList = () => { [navigateCallback] ); + const handleAddButtonClick = useCallback( + () => + navigateCallback({ + show: 'create', + id: undefined, + }), + [navigateCallback] + ); + return ( { defaultMessage="Host Isolation Exceptions" /> } - actions={[]} + actions={ + + + + } > + {showFlyout && } + { pagination={pagination} contentClassName="host-isolation-exceptions-container" data-test-subj="hostIsolationExceptionsContent" - noItemsMessage={} + noItemsMessage={} /> ); From 7c5c566696fa3837b48d6147ae61dd97526f1a1d Mon Sep 17 00:00:00 2001 From: Orhan Toy Date: Mon, 11 Oct 2021 15:52:15 +0200 Subject: [PATCH 48/74] [Enterprise Search] Fix typo (#114462) Fixing a small typo (`recieve` -> `receive`) I noticed in comments. --- .../crawler_status_indicator/crawler_status_indicator.test.tsx | 2 +- .../applications/shared/flash_messages/handle_api_errors.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/crawler_status_indicator.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/crawler_status_indicator.test.tsx index 9d585789d8e5..c46c360934d0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/crawler_status_indicator.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/crawler_status_indicator/crawler_status_indicator.test.tsx @@ -37,7 +37,7 @@ describe('CrawlerStatusIndicator', () => { describe('when status is not a valid status', () => { it('is disabled', () => { // this tests a codepath that should be impossible to reach, status should always be a CrawlerStatus - // but we use a switch statement and need to test the default case for this to recieve 100% coverage + // but we use a switch statement and need to test the default case for this to receive 100% coverage setMockValues({ ...MOCK_VALUES, mostRecentCrawlRequestStatus: null, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.ts b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.ts index 4fe8ad1cb851..abaa67e06f60 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/flash_messages/handle_api_errors.ts @@ -14,7 +14,7 @@ import { IFlashMessage } from './types'; /** * The API errors we are handling can come from one of two ways: - * - When our http calls recieve a response containing an error code, such as a 404 or 500 + * - When our http calls receive a response containing an error code, such as a 404 or 500 * - Our own JS while handling a successful response * * In the first case, if it is a purposeful error (like a 404) we will receive an From 53109bdcd5fb00c8e4287facd03ed923362f8540 Mon Sep 17 00:00:00 2001 From: Pete Hampton Date: Mon, 11 Oct 2021 15:20:39 +0100 Subject: [PATCH 49/74] Detection Rule Exception List telemetry (#113239) * Add telemetry for detection rule exception lists to improve UX. * Add length for debugging. * Fix type. * Clean up exception list telemetry document. * Dynamically set kibana index (just in case). * Update task title. * Rename version to rule_version. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/lib/telemetry/constants.ts | 6 +- .../server/lib/telemetry/filters.ts | 5 +- .../server/lib/telemetry/helpers.test.ts | 80 +++++++--- .../server/lib/telemetry/helpers.ts | 48 ++++-- .../server/lib/telemetry/mocks.ts | 11 +- .../server/lib/telemetry/receiver.ts | 68 +++++++- .../server/lib/telemetry/sender.ts | 10 +- .../telemetry/tasks/detection_rule.test.ts | 127 +++++++++++++++ .../lib/telemetry/tasks/detection_rule.ts | 149 ++++++++++++++++++ .../server/lib/telemetry/tasks/endpoint.ts | 4 +- .../server/lib/telemetry/tasks/index.ts | 1 + .../server/lib/telemetry/types.ts | 39 ++++- .../security_solution/server/plugin.ts | 8 +- 13 files changed, 499 insertions(+), 57 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/telemetry/tasks/detection_rule.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/telemetry/tasks/detection_rule.ts diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts b/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts index 771e3e059c33..91f83d5e7cb3 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/constants.ts @@ -7,12 +7,14 @@ export const TELEMETRY_MAX_BUFFER_SIZE = 100; -export const TELEMETRY_CHANNEL_LISTS = 'security-lists'; +export const TELEMETRY_CHANNEL_LISTS = 'security-lists-v2'; export const TELEMETRY_CHANNEL_ENDPOINT_META = 'endpoint-metadata'; -export const LIST_TRUSTED_APPLICATION = 'trusted_application'; +export const LIST_DETECTION_RULE_EXCEPTION = 'detection_rule_exception'; export const LIST_ENDPOINT_EXCEPTION = 'endpoint_exception'; export const LIST_ENDPOINT_EVENT_FILTER = 'endpoint_event_filter'; + +export const LIST_TRUSTED_APPLICATION = 'trusted_application'; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/filters.ts b/x-pack/plugins/security_solution/server/lib/telemetry/filters.ts index 61172fac511f..a29f195ed5ec 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/filters.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/filters.ts @@ -129,13 +129,12 @@ export const allowlistEventFields: AllowlistFields = { export const exceptionListEventFields: AllowlistFields = { created_at: true, - description: true, effectScope: true, entries: true, id: true, name: true, - os: true, os_types: true, + rule_version: true, }; /** @@ -143,7 +142,7 @@ export const exceptionListEventFields: AllowlistFields = { * * @param allowlist * @param event - * @returns + * @returns TelemetryEvent with explicitly required fields */ export function copyAllowlistedFields( allowlist: AllowlistFields, diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts index 647219e8c558..528082d8cb5b 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.test.ts @@ -8,13 +8,14 @@ import moment from 'moment'; import { createMockPackagePolicy } from './mocks'; import { + LIST_DETECTION_RULE_EXCEPTION, LIST_ENDPOINT_EXCEPTION, LIST_ENDPOINT_EVENT_FILTER, LIST_TRUSTED_APPLICATION, } from './constants'; import { getPreviousDiagTaskTimestamp, - getPreviousEpMetaTaskTimestamp, + getPreviousDailyTaskTimestamp, batchTelemetryRecords, isPackagePolicyList, templateExceptionList, @@ -53,7 +54,7 @@ describe('test endpoint meta telemetry scheduled task timing helper', () => { test('test -24 hours is returned when there is no previous task run', async () => { const executeTo = moment().utc().toISOString(); const executeFrom = undefined; - const newExecuteFrom = getPreviousEpMetaTaskTimestamp(executeTo, executeFrom); + const newExecuteFrom = getPreviousDailyTaskTimestamp(executeTo, executeFrom); expect(newExecuteFrom).toEqual(moment(executeTo).subtract(24, 'hours').toISOString()); }); @@ -61,7 +62,7 @@ describe('test endpoint meta telemetry scheduled task timing helper', () => { test('test -24 hours is returned when there was a previous task run', async () => { const executeTo = moment().utc().toISOString(); const executeFrom = moment(executeTo).subtract(24, 'hours').toISOString(); - const newExecuteFrom = getPreviousEpMetaTaskTimestamp(executeTo, executeFrom); + const newExecuteFrom = getPreviousDailyTaskTimestamp(executeTo, executeFrom); expect(newExecuteFrom).toEqual(executeFrom); }); @@ -71,7 +72,7 @@ describe('test endpoint meta telemetry scheduled task timing helper', () => { test('test 24 hours is returned when previous task run took longer than 24 hours', async () => { const executeTo = moment().utc().toISOString(); const executeFrom = moment(executeTo).subtract(72, 'hours').toISOString(); // down 3 days - const newExecuteFrom = getPreviousEpMetaTaskTimestamp(executeTo, executeFrom); + const newExecuteFrom = getPreviousDailyTaskTimestamp(executeTo, executeFrom); expect(newExecuteFrom).toEqual(moment(executeTo).subtract(24, 'hours').toISOString()); }); @@ -134,61 +135,88 @@ describe('test package policy type guard', () => { }); describe('list telemetry schema', () => { + test('detection rules document is correctly formed', () => { + const data = [{ id: 'test_1' }] as ExceptionListItem[]; + const templatedItems = templateExceptionList(data, LIST_DETECTION_RULE_EXCEPTION); + + expect(templatedItems[0]?.detection_rule).not.toBeUndefined(); + expect(templatedItems[0]?.endpoint_exception).toBeUndefined(); + expect(templatedItems[0]?.endpoint_event_filter).toBeUndefined(); + expect(templatedItems[0]?.trusted_application).toBeUndefined(); + }); + + test('detection rules document is correctly formed with multiple entries', () => { + const data = [{ id: 'test_2' }, { id: 'test_2' }] as ExceptionListItem[]; + const templatedItems = templateExceptionList(data, LIST_DETECTION_RULE_EXCEPTION); + + expect(templatedItems[0]?.detection_rule).not.toBeUndefined(); + expect(templatedItems[1]?.detection_rule).not.toBeUndefined(); + expect(templatedItems[0]?.endpoint_exception).toBeUndefined(); + expect(templatedItems[0]?.endpoint_event_filter).toBeUndefined(); + expect(templatedItems[0]?.trusted_application).toBeUndefined(); + }); + test('trusted apps document is correctly formed', () => { const data = [{ id: 'test_1' }] as ExceptionListItem[]; const templatedItems = templateExceptionList(data, LIST_TRUSTED_APPLICATION); - expect(templatedItems[0]?.trusted_application.length).toEqual(1); - expect(templatedItems[0]?.endpoint_exception.length).toEqual(0); - expect(templatedItems[0]?.endpoint_event_filter.length).toEqual(0); + expect(templatedItems[0]?.detection_rule).toBeUndefined(); + expect(templatedItems[0]?.endpoint_exception).toBeUndefined(); + expect(templatedItems[0]?.endpoint_event_filter).toBeUndefined(); + expect(templatedItems[0]?.trusted_application).not.toBeUndefined(); }); test('trusted apps document is correctly formed with multiple entries', () => { const data = [{ id: 'test_2' }, { id: 'test_2' }] as ExceptionListItem[]; const templatedItems = templateExceptionList(data, LIST_TRUSTED_APPLICATION); - expect(templatedItems[0]?.trusted_application.length).toEqual(1); - expect(templatedItems[1]?.trusted_application.length).toEqual(1); - expect(templatedItems[0]?.endpoint_exception.length).toEqual(0); - expect(templatedItems[0]?.endpoint_event_filter.length).toEqual(0); + expect(templatedItems[0]?.detection_rule).toBeUndefined(); + expect(templatedItems[0]?.endpoint_exception).toBeUndefined(); + expect(templatedItems[0]?.endpoint_event_filter).toBeUndefined(); + expect(templatedItems[0]?.trusted_application).not.toBeUndefined(); + expect(templatedItems[1]?.trusted_application).not.toBeUndefined(); }); test('endpoint exception document is correctly formed', () => { const data = [{ id: 'test_3' }] as ExceptionListItem[]; const templatedItems = templateExceptionList(data, LIST_ENDPOINT_EXCEPTION); - expect(templatedItems[0]?.trusted_application.length).toEqual(0); - expect(templatedItems[0]?.endpoint_exception.length).toEqual(1); - expect(templatedItems[0]?.endpoint_event_filter.length).toEqual(0); + expect(templatedItems[0]?.detection_rule).toBeUndefined(); + expect(templatedItems[0]?.endpoint_exception).not.toBeUndefined(); + expect(templatedItems[0]?.endpoint_event_filter).toBeUndefined(); + expect(templatedItems[0]?.trusted_application).toBeUndefined(); }); test('endpoint exception document is correctly formed with multiple entries', () => { const data = [{ id: 'test_4' }, { id: 'test_4' }, { id: 'test_4' }] as ExceptionListItem[]; const templatedItems = templateExceptionList(data, LIST_ENDPOINT_EXCEPTION); - expect(templatedItems[0]?.trusted_application.length).toEqual(0); - expect(templatedItems[0]?.endpoint_exception.length).toEqual(1); - expect(templatedItems[1]?.endpoint_exception.length).toEqual(1); - expect(templatedItems[2]?.endpoint_exception.length).toEqual(1); - expect(templatedItems[0]?.endpoint_event_filter.length).toEqual(0); + expect(templatedItems[0]?.detection_rule).toBeUndefined(); + expect(templatedItems[0]?.endpoint_event_filter).toBeUndefined(); + expect(templatedItems[0]?.endpoint_exception).not.toBeUndefined(); + expect(templatedItems[1]?.endpoint_exception).not.toBeUndefined(); + expect(templatedItems[2]?.endpoint_exception).not.toBeUndefined(); + expect(templatedItems[0]?.trusted_application).toBeUndefined(); }); test('endpoint event filters document is correctly formed', () => { const data = [{ id: 'test_5' }] as ExceptionListItem[]; const templatedItems = templateExceptionList(data, LIST_ENDPOINT_EVENT_FILTER); - expect(templatedItems[0]?.trusted_application.length).toEqual(0); - expect(templatedItems[0]?.endpoint_exception.length).toEqual(0); - expect(templatedItems[0]?.endpoint_event_filter.length).toEqual(1); + expect(templatedItems[0]?.detection_rule).toBeUndefined(); + expect(templatedItems[0]?.endpoint_event_filter).not.toBeUndefined(); + expect(templatedItems[0]?.endpoint_exception).toBeUndefined(); + expect(templatedItems[0]?.trusted_application).toBeUndefined(); }); test('endpoint event filters document is correctly formed with multiple entries', () => { const data = [{ id: 'test_6' }, { id: 'test_6' }] as ExceptionListItem[]; const templatedItems = templateExceptionList(data, LIST_ENDPOINT_EVENT_FILTER); - expect(templatedItems[0]?.trusted_application.length).toEqual(0); - expect(templatedItems[0]?.endpoint_exception.length).toEqual(0); - expect(templatedItems[0]?.endpoint_event_filter.length).toEqual(1); - expect(templatedItems[1]?.endpoint_event_filter.length).toEqual(1); + expect(templatedItems[0]?.detection_rule).toBeUndefined(); + expect(templatedItems[0]?.endpoint_event_filter).not.toBeUndefined(); + expect(templatedItems[1]?.endpoint_event_filter).not.toBeUndefined(); + expect(templatedItems[0]?.endpoint_exception).toBeUndefined(); + expect(templatedItems[0]?.trusted_application).toBeUndefined(); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts index a9eaef3ce6ed..e72b0ba7d16f 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts @@ -11,6 +11,7 @@ import { PackagePolicy } from '../../../../fleet/common/types/models/package_pol import { copyAllowlistedFields, exceptionListEventFields } from './filters'; import { ExceptionListItem, ListTemplate, TelemetryEvent } from './types'; import { + LIST_DETECTION_RULE_EXCEPTION, LIST_ENDPOINT_EXCEPTION, LIST_ENDPOINT_EVENT_FILTER, LIST_TRUSTED_APPLICATION, @@ -46,7 +47,7 @@ export const getPreviousDiagTaskTimestamp = ( * @param lastExecutionTimestamp * @returns the timestamp to search from */ -export const getPreviousEpMetaTaskTimestamp = ( +export const getPreviousDailyTaskTimestamp = ( executeTo: string, lastExecutionTimestamp?: string ) => { @@ -97,18 +98,16 @@ export function isPackagePolicyList( * Maps trusted application to shared telemetry object * * @param exceptionListItem - * @returns collection of endpoint exceptions + * @returns collection of trusted applications */ export const trustedApplicationToTelemetryEntry = (trustedApplication: TrustedApp) => { return { id: trustedApplication.id, - version: trustedApplication.version || '', name: trustedApplication.name, - description: trustedApplication.description, created_at: trustedApplication.created_at, updated_at: trustedApplication.updated_at, entries: trustedApplication.entries, - os: trustedApplication.os, + os_types: [trustedApplication.os], } as ExceptionListItem; }; @@ -121,9 +120,29 @@ export const trustedApplicationToTelemetryEntry = (trustedApplication: TrustedAp export const exceptionListItemToTelemetryEntry = (exceptionListItem: ExceptionListItemSchema) => { return { id: exceptionListItem.id, - version: exceptionListItem._version || '', name: exceptionListItem.name, - description: exceptionListItem.description, + created_at: exceptionListItem.created_at, + updated_at: exceptionListItem.updated_at, + entries: exceptionListItem.entries, + os_types: exceptionListItem.os_types, + } as ExceptionListItem; +}; + +/** + * Maps detection rule exception list items to shared telemetry object + * + * @param exceptionListItem + * @param ruleVersion + * @returns collection of detection rule exceptions + */ +export const ruleExceptionListItemToTelemetryEvent = ( + exceptionListItem: ExceptionListItemSchema, + ruleVersion: number +) => { + return { + id: exceptionListItem.item_id, + name: exceptionListItem.description, + rule_version: ruleVersion, created_at: exceptionListItem.created_at, updated_at: exceptionListItem.updated_at, entries: exceptionListItem.entries, @@ -141,9 +160,7 @@ export const exceptionListItemToTelemetryEntry = (exceptionListItem: ExceptionLi export const templateExceptionList = (listData: ExceptionListItem[], listType: string) => { return listData.map((item) => { const template: ListTemplate = { - trusted_application: [], - endpoint_exception: [], - endpoint_event_filter: [], + '@timestamp': new Date().getTime(), }; // cast exception list type to a TelemetryEvent for allowlist filtering @@ -152,18 +169,23 @@ export const templateExceptionList = (listData: ExceptionListItem[], listType: s item as unknown as TelemetryEvent ); + if (listType === LIST_DETECTION_RULE_EXCEPTION) { + template.detection_rule = filteredListItem; + return template; + } + if (listType === LIST_TRUSTED_APPLICATION) { - template.trusted_application.push(filteredListItem); + template.trusted_application = filteredListItem; return template; } if (listType === LIST_ENDPOINT_EXCEPTION) { - template.endpoint_exception.push(filteredListItem); + template.endpoint_exception = filteredListItem; return template; } if (listType === LIST_ENDPOINT_EVENT_FILTER) { - template.endpoint_event_filter.push(filteredListItem); + template.endpoint_event_filter = filteredListItem; return template; } diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/mocks.ts b/x-pack/plugins/security_solution/server/lib/telemetry/mocks.ts index 20a71657b2ff..9168683141e4 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/mocks.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/mocks.ts @@ -8,7 +8,7 @@ // eslint-disable-next-line max-classes-per-file import { TelemetryEventsSender } from './sender'; import { TelemetryReceiver } from './receiver'; -import { DiagnosticTask, EndpointTask, ExceptionListsTask } from './tasks'; +import { DiagnosticTask, EndpointTask, ExceptionListsTask, DetectionRulesTask } from './tasks'; import { PackagePolicy } from '../../../../fleet/common/types/models/package_policy'; /** @@ -40,6 +40,8 @@ export const createMockTelemetryReceiver = (): jest.Mocked => fetchEndpointMetrics: jest.fn(), fetchEndpointPolicyResponses: jest.fn(), fetchTrustedApplications: jest.fn(), + fetchDetectionRules: jest.fn(), + fetchDetectionExceptionList: jest.fn(), } as unknown as jest.Mocked; }; @@ -79,3 +81,10 @@ export class MockTelemetryEndpointTask extends EndpointTask { export class MockExceptionListsTask extends ExceptionListsTask { public runTask = jest.fn(); } + +/** + * Creates a mocked Telemetry detection rules lists Task + */ +export class MockDetectionRuleListsTask extends DetectionRulesTask { + public runTask = jest.fn(); +} diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts index 038b7687784f..94aa6c867304 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts @@ -17,8 +17,18 @@ import { AgentService, AgentPolicyServiceInterface } from '../../../../fleet/ser import { ExceptionListClient } from '../../../../lists/server'; import { EndpointAppContextService } from '../../endpoint/endpoint_app_context_services'; import { TELEMETRY_MAX_BUFFER_SIZE } from './constants'; -import { exceptionListItemToTelemetryEntry, trustedApplicationToTelemetryEntry } from './helpers'; -import { TelemetryEvent, ESLicense, ESClusterInfo, GetEndpointListResponse } from './types'; +import { + exceptionListItemToTelemetryEntry, + trustedApplicationToTelemetryEntry, + ruleExceptionListItemToTelemetryEvent, +} from './helpers'; +import { + TelemetryEvent, + ESLicense, + ESClusterInfo, + GetEndpointListResponse, + RuleSearchResult, +} from './types'; export class TelemetryReceiver { private readonly logger: Logger; @@ -27,6 +37,7 @@ export class TelemetryReceiver { private esClient?: ElasticsearchClient; private exceptionListClient?: ExceptionListClient; private soClient?: SavedObjectsClientContract; + private kibanaIndex?: string; private readonly max_records = 10_000; constructor(logger: Logger) { @@ -35,9 +46,11 @@ export class TelemetryReceiver { public async start( core?: CoreStart, + kibanaIndex?: string, endpointContextService?: EndpointAppContextService, exceptionListClient?: ExceptionListClient ) { + this.kibanaIndex = kibanaIndex; this.agentService = endpointContextService?.getAgentService(); this.agentPolicyService = endpointContextService?.getAgentPolicyService(); this.esClient = core?.elasticsearch.client.asInternalUser; @@ -240,6 +253,57 @@ export class TelemetryReceiver { }; } + public async fetchDetectionRules() { + if (this.esClient === undefined || this.esClient === null) { + throw Error('elasticsearch client is unavailable: cannot retrieve diagnostic alerts'); + } + + const query: SearchRequest = { + expand_wildcards: 'open,hidden', + index: `${this.kibanaIndex}*`, + ignore_unavailable: true, + size: this.max_records, + body: { + query: { + bool: { + filter: [ + { term: { 'alert.alertTypeId': 'siem.signals' } }, + { term: { 'alert.params.immutable': true } }, + ], + }, + }, + }, + }; + + return this.esClient.search(query); + } + + public async fetchDetectionExceptionList(listId: string, ruleVersion: number) { + if (this?.exceptionListClient === undefined || this?.exceptionListClient === null) { + throw Error('exception list client is unavailable: could not retrieve trusted applications'); + } + + // Ensure list is created if it does not exist + await this.exceptionListClient.createTrustedAppsList(); + + const results = await this.exceptionListClient?.findExceptionListsItem({ + listId: [listId], + filter: [], + perPage: this.max_records, + page: 1, + sortField: 'exception-list.created_at', + sortOrder: 'desc', + namespaceType: ['single'], + }); + + return { + data: results?.data.map((r) => ruleExceptionListItemToTelemetryEvent(r, ruleVersion)) ?? [], + total: results?.total ?? 0, + page: results?.page ?? 1, + per_page: results?.per_page ?? this.max_records, + }; + } + public async fetchClusterInfo(): Promise { if (this.esClient === undefined || this.esClient === null) { throw Error('elasticsearch client is unavailable: cannot retrieve cluster infomation'); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts index 0037aaa28fee..b0792ed7b461 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts @@ -18,7 +18,7 @@ import { } from '../../../../task_manager/server'; import { TelemetryReceiver } from './receiver'; import { allowlistEventFields, copyAllowlistedFields } from './filters'; -import { DiagnosticTask, EndpointTask, ExceptionListsTask } from './tasks'; +import { DiagnosticTask, EndpointTask, ExceptionListsTask, DetectionRulesTask } from './tasks'; import { createUsageCounterLabel } from './helpers'; import { TelemetryEvent } from './types'; import { TELEMETRY_MAX_BUFFER_SIZE } from './constants'; @@ -42,6 +42,7 @@ export class TelemetryEventsSender { private diagnosticTask?: DiagnosticTask; private endpointTask?: EndpointTask; private exceptionListsTask?: ExceptionListsTask; + private detectionRulesTask?: DetectionRulesTask; constructor(logger: Logger) { this.logger = logger.get('telemetry_events'); @@ -59,6 +60,12 @@ export class TelemetryEventsSender { if (taskManager) { this.diagnosticTask = new DiagnosticTask(this.logger, taskManager, this, telemetryReceiver); this.endpointTask = new EndpointTask(this.logger, taskManager, this, telemetryReceiver); + this.detectionRulesTask = new DetectionRulesTask( + this.logger, + taskManager, + this, + telemetryReceiver + ); this.exceptionListsTask = new ExceptionListsTask( this.logger, taskManager, @@ -80,6 +87,7 @@ export class TelemetryEventsSender { this.logger.debug(`starting security telemetry tasks`); this.diagnosticTask.start(taskManager); this.endpointTask.start(taskManager); + this.detectionRulesTask?.start(taskManager); this.exceptionListsTask?.start(taskManager); } diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/detection_rule.test.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/detection_rule.test.ts new file mode 100644 index 000000000000..0a05afb8a653 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/detection_rule.test.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 { loggingSystemMock } from 'src/core/server/mocks'; +import { taskManagerMock } from '../../../../../task_manager/server/mocks'; +import { TaskStatus } from '../../../../../task_manager/server'; +import { + TelemetryDetectionRulesTask, + TelemetryDetectionRuleListsTaskConstants, +} from './detection_rule'; +import { + createMockTelemetryEventsSender, + MockDetectionRuleListsTask, + createMockTelemetryReceiver, +} from '../mocks'; + +describe('test detection rule exception lists telemetry', () => { + let logger: ReturnType; + + beforeEach(() => { + logger = loggingSystemMock.createLogger(); + }); + + describe('basic telemetry sanity checks', () => { + test('detection rule lists task can register', () => { + const telemetryDiagTask = new TelemetryDetectionRulesTask( + logger, + taskManagerMock.createSetup(), + createMockTelemetryEventsSender(true), + createMockTelemetryReceiver() + ); + + expect(telemetryDiagTask).toBeInstanceOf(TelemetryDetectionRulesTask); + }); + }); + + test('detection rule task should be registered', () => { + const mockTaskManager = taskManagerMock.createSetup(); + new TelemetryDetectionRulesTask( + logger, + mockTaskManager, + createMockTelemetryEventsSender(true), + createMockTelemetryReceiver() + ); + + expect(mockTaskManager.registerTaskDefinitions).toHaveBeenCalled(); + }); + + test('detection rule task should be scheduled', async () => { + const mockTaskManagerSetup = taskManagerMock.createSetup(); + const telemetryDiagTask = new TelemetryDetectionRulesTask( + logger, + mockTaskManagerSetup, + createMockTelemetryEventsSender(true), + createMockTelemetryReceiver() + ); + + const mockTaskManagerStart = taskManagerMock.createStart(); + await telemetryDiagTask.start(mockTaskManagerStart); + expect(mockTaskManagerStart.ensureScheduled).toHaveBeenCalled(); + }); + + test('detection rule task should run', async () => { + const mockContext = createMockTelemetryEventsSender(true); + const mockTaskManager = taskManagerMock.createSetup(); + const mockReceiver = createMockTelemetryReceiver(); + const telemetryDiagTask = new MockDetectionRuleListsTask( + logger, + mockTaskManager, + mockContext, + mockReceiver + ); + + const mockTaskInstance = { + id: TelemetryDetectionRuleListsTaskConstants.TYPE, + runAt: new Date(), + attempts: 0, + ownerId: '', + status: TaskStatus.Running, + startedAt: new Date(), + scheduledAt: new Date(), + retryAt: new Date(), + params: {}, + state: {}, + taskType: TelemetryDetectionRuleListsTaskConstants.TYPE, + }; + const createTaskRunner = + mockTaskManager.registerTaskDefinitions.mock.calls[0][0][ + TelemetryDetectionRuleListsTaskConstants.TYPE + ].createTaskRunner; + const taskRunner = createTaskRunner({ taskInstance: mockTaskInstance }); + await taskRunner.run(); + expect(telemetryDiagTask.runTask).toHaveBeenCalled(); + }); + + test('detection rule task should not query elastic if telemetry is not opted in', async () => { + const mockSender = createMockTelemetryEventsSender(false); + const mockTaskManager = taskManagerMock.createSetup(); + const mockReceiver = createMockTelemetryReceiver(); + new MockDetectionRuleListsTask(logger, mockTaskManager, mockSender, mockReceiver); + + const mockTaskInstance = { + id: TelemetryDetectionRuleListsTaskConstants.TYPE, + runAt: new Date(), + attempts: 0, + ownerId: '', + status: TaskStatus.Running, + startedAt: new Date(), + scheduledAt: new Date(), + retryAt: new Date(), + params: {}, + state: {}, + taskType: TelemetryDetectionRuleListsTaskConstants.TYPE, + }; + const createTaskRunner = + mockTaskManager.registerTaskDefinitions.mock.calls[0][0][ + TelemetryDetectionRuleListsTaskConstants.TYPE + ].createTaskRunner; + const taskRunner = createTaskRunner({ taskInstance: mockTaskInstance }); + await taskRunner.run(); + expect(mockReceiver.fetchDiagnosticAlerts).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/detection_rule.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/detection_rule.ts new file mode 100644 index 000000000000..a362be187921 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/detection_rule.ts @@ -0,0 +1,149 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import { Logger } from 'src/core/server'; +import { + ConcreteTaskInstance, + TaskManagerSetupContract, + TaskManagerStartContract, +} from '../../../../../task_manager/server'; +import { LIST_DETECTION_RULE_EXCEPTION, TELEMETRY_CHANNEL_LISTS } from '../constants'; +import { batchTelemetryRecords, templateExceptionList } from '../helpers'; +import { TelemetryEventsSender } from '../sender'; +import { TelemetryReceiver } from '../receiver'; +import { ExceptionListItem, RuleSearchResult } from '../types'; + +export const TelemetryDetectionRuleListsTaskConstants = { + TIMEOUT: '10m', + TYPE: 'security:telemetry-detection-rules', + INTERVAL: '24h', + VERSION: '1.0.0', +}; + +const MAX_TELEMETRY_BATCH = 1_000; + +export class TelemetryDetectionRulesTask { + private readonly logger: Logger; + private readonly sender: TelemetryEventsSender; + private readonly receiver: TelemetryReceiver; + + constructor( + logger: Logger, + taskManager: TaskManagerSetupContract, + sender: TelemetryEventsSender, + receiver: TelemetryReceiver + ) { + this.logger = logger; + this.sender = sender; + this.receiver = receiver; + + taskManager.registerTaskDefinitions({ + [TelemetryDetectionRuleListsTaskConstants.TYPE]: { + title: 'Security Solution Detection Rule Lists Telemetry', + timeout: TelemetryDetectionRuleListsTaskConstants.TIMEOUT, + createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => { + const { state } = taskInstance; + + return { + run: async () => { + const taskExecutionTime = moment().utc().toISOString(); + const hits = await this.runTask(taskInstance.id); + + return { + state: { + lastExecutionTimestamp: taskExecutionTime, + runs: (state.runs || 0) + 1, + hits, + }, + }; + }, + cancel: async () => {}, + }; + }, + }, + }); + } + + public start = async (taskManager: TaskManagerStartContract) => { + try { + await taskManager.ensureScheduled({ + id: this.getTaskId(), + taskType: TelemetryDetectionRuleListsTaskConstants.TYPE, + scope: ['securitySolution'], + schedule: { + interval: TelemetryDetectionRuleListsTaskConstants.INTERVAL, + }, + state: { runs: 0 }, + params: { version: TelemetryDetectionRuleListsTaskConstants.VERSION }, + }); + } catch (e) { + this.logger.error(`Error scheduling task, received ${e.message}`); + } + }; + + private getTaskId = (): string => { + return `${TelemetryDetectionRuleListsTaskConstants.TYPE}:${TelemetryDetectionRuleListsTaskConstants.VERSION}`; + }; + + public runTask = async (taskId: string) => { + if (taskId !== this.getTaskId()) { + return 0; + } + + const isOptedIn = await this.sender.isTelemetryOptedIn(); + if (!isOptedIn) { + return 0; + } + + // Lists Telemetry: Detection Rules + + const { body: prebuiltRules } = await this.receiver.fetchDetectionRules(); + + const cacheArray = prebuiltRules.hits.hits.reduce((cache, searchHit) => { + const rule = searchHit._source as RuleSearchResult; + const ruleId = rule.alert.params.ruleId; + + const shouldNotProcess = + rule === null || + rule === undefined || + ruleId === null || + ruleId === undefined || + searchHit._source?.alert.params.exceptionsList.length === 0; + + if (shouldNotProcess) { + return cache; + } + + cache.push(rule); + return cache; + }, [] as RuleSearchResult[]); + + const detectionRuleExceptions = [] as ExceptionListItem[]; + for (const item of cacheArray) { + const ruleVersion = item.alert.params.version; + + for (const ex of item.alert.params.exceptionsList) { + const listItem = await this.receiver.fetchDetectionExceptionList(ex.list_id, ruleVersion); + for (const exceptionItem of listItem.data) { + detectionRuleExceptions.push(exceptionItem); + } + } + } + + const detectionRuleExceptionsJson = templateExceptionList( + detectionRuleExceptions, + LIST_DETECTION_RULE_EXCEPTION + ); + + batchTelemetryRecords(detectionRuleExceptionsJson, MAX_TELEMETRY_BATCH).forEach((batch) => { + this.sender.sendOnDemand(TELEMETRY_CHANNEL_LISTS, batch); + }); + + return detectionRuleExceptions.length; + }; +} diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/endpoint.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/endpoint.ts index 0c066deea17d..c6bf4b06e70f 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/endpoint.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/endpoint.ts @@ -14,7 +14,7 @@ import { } from '../../../../../task_manager/server'; import { batchTelemetryRecords, - getPreviousEpMetaTaskTimestamp, + getPreviousDailyTaskTimestamp, isPackagePolicyList, } from '../helpers'; import { TelemetryEventsSender } from '../sender'; @@ -76,7 +76,7 @@ export class TelemetryEndpointTask { return { run: async () => { const taskExecutionTime = moment().utc().toISOString(); - const lastExecutionTimestamp = getPreviousEpMetaTaskTimestamp( + const lastExecutionTimestamp = getPreviousDailyTaskTimestamp( taskExecutionTime, taskInstance.state?.lastExecutionTimestamp ); diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/index.ts b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/index.ts index e090252b88d8..a850f848567c 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/tasks/index.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/tasks/index.ts @@ -8,3 +8,4 @@ export { TelemetryDiagTask as DiagnosticTask } from './diagnostic'; export { TelemetryEndpointTask as EndpointTask } from './endpoint'; export { TelemetryExceptionListsTask as ExceptionListsTask } from './security_lists'; +export { TelemetryDetectionRulesTask as DetectionRulesTask } from './detection_rule'; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/types.ts b/x-pack/plugins/security_solution/server/lib/telemetry/types.ts index abcad26ed000..6aaf6f437147 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/types.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/types.ts @@ -217,18 +217,45 @@ export interface GetEndpointListResponse { export interface ExceptionListItem { id: string; - version: string; + rule_version?: number; name: string; - description: string; created_at: string; updated_at: string; entries: object; - os: string; os_types: object; } export interface ListTemplate { - trusted_application: TelemetryEvent[]; - endpoint_exception: TelemetryEvent[]; - endpoint_event_filter: TelemetryEvent[]; + '@timestamp': number; + detection_rule?: TelemetryEvent; + endpoint_exception?: TelemetryEvent; + endpoint_event_filter?: TelemetryEvent; + trusted_application?: TelemetryEvent; +} + +// Detection Rule types + +interface ExceptionListEntry { + id: string; + list_id: string; + type: string; + namespace_type: string; +} + +interface DetectionRuleParms { + ruleId: string; + version: number; + type: string; + exceptionsList: ExceptionListEntry[]; +} + +export interface RuleSearchResult { + alert: { + name: string; + enabled: boolean; + tags: string[]; + createdAt: string; + updatedAt: string; + params: DetectionRuleParms; + }; } diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index bffcc823d047..f0a91f8b06c0 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -459,7 +459,13 @@ export class Plugin implements IPlugin Date: Mon, 11 Oct 2021 17:30:18 +0200 Subject: [PATCH 50/74] [bfetch] Pass `compress` flag in query instead of headers (#113929) --- src/plugins/bfetch/common/util/index.ts | 1 + .../bfetch/common/util/query_params.ts | 12 +++ .../public/streaming/fetch_streaming.ts | 12 +-- src/plugins/bfetch/server/index.ts | 1 - src/plugins/bfetch/server/mocks.ts | 1 - src/plugins/bfetch/server/plugin.ts | 78 +------------------ .../bfetch/server/streaming/create_stream.ts | 8 +- src/plugins/bfetch/server/types.ts | 27 ------- test/api_integration/apis/search/bsearch.ts | 66 +++++++--------- 9 files changed, 56 insertions(+), 150 deletions(-) create mode 100644 src/plugins/bfetch/common/util/query_params.ts delete mode 100644 src/plugins/bfetch/server/types.ts diff --git a/src/plugins/bfetch/common/util/index.ts b/src/plugins/bfetch/common/util/index.ts index 1651d24d96b1..f20d30eb3cdf 100644 --- a/src/plugins/bfetch/common/util/index.ts +++ b/src/plugins/bfetch/common/util/index.ts @@ -8,3 +8,4 @@ export * from './normalize_error'; export * from './remove_leading_slash'; +export * from './query_params'; diff --git a/src/plugins/bfetch/common/util/query_params.ts b/src/plugins/bfetch/common/util/query_params.ts new file mode 100644 index 000000000000..ed65699fbcaf --- /dev/null +++ b/src/plugins/bfetch/common/util/query_params.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const appendQueryParam = (url: string, key: string, value: string): string => { + const separator = url.includes('?') ? '&' : '?'; + return `${url}${separator}${key}=${value}`; +}; diff --git a/src/plugins/bfetch/public/streaming/fetch_streaming.ts b/src/plugins/bfetch/public/streaming/fetch_streaming.ts index a94c8d3980cb..77e5acffc1af 100644 --- a/src/plugins/bfetch/public/streaming/fetch_streaming.ts +++ b/src/plugins/bfetch/public/streaming/fetch_streaming.ts @@ -10,6 +10,7 @@ import { map, share } from 'rxjs/operators'; import { inflateResponse } from '.'; import { fromStreamingXhr } from './from_streaming_xhr'; import { split } from './split'; +import { appendQueryParam } from '../../common'; export interface FetchStreamingParams { url: string; @@ -34,16 +35,15 @@ export function fetchStreaming({ }: FetchStreamingParams) { const xhr = new window.XMLHttpRequest(); - // Begin the request - xhr.open(method, url); - xhr.withCredentials = true; - const isCompressionDisabled = getIsCompressionDisabled(); - if (!isCompressionDisabled) { - headers['X-Chunk-Encoding'] = 'deflate'; + url = appendQueryParam(url, 'compress', 'true'); } + // Begin the request + xhr.open(method, url); + xhr.withCredentials = true; + // Set the HTTP headers Object.entries(headers).forEach(([k, v]) => xhr.setRequestHeader(k, v)); diff --git a/src/plugins/bfetch/server/index.ts b/src/plugins/bfetch/server/index.ts index c533b2ad7a3d..f4c41d10e42c 100644 --- a/src/plugins/bfetch/server/index.ts +++ b/src/plugins/bfetch/server/index.ts @@ -10,7 +10,6 @@ import { PluginInitializerContext } from '../../../core/server'; import { BfetchServerPlugin } from './plugin'; export { BfetchServerSetup, BfetchServerStart, BatchProcessingRouteParams } from './plugin'; -export { StreamingRequestHandler } from './types'; export function plugin(initializerContext: PluginInitializerContext) { return new BfetchServerPlugin(initializerContext); diff --git a/src/plugins/bfetch/server/mocks.ts b/src/plugins/bfetch/server/mocks.ts index bbb596bf8d5f..dfa365d9e70b 100644 --- a/src/plugins/bfetch/server/mocks.ts +++ b/src/plugins/bfetch/server/mocks.ts @@ -17,7 +17,6 @@ const createSetupContract = (): Setup => { const setupContract: Setup = { addBatchProcessingRoute: jest.fn(), addStreamingResponseRoute: jest.fn(), - createStreamingRequestHandler: jest.fn(), }; return setupContract; }; diff --git a/src/plugins/bfetch/server/plugin.ts b/src/plugins/bfetch/server/plugin.ts index 7b60be9a8fc7..f7127445f96c 100644 --- a/src/plugins/bfetch/server/plugin.ts +++ b/src/plugins/bfetch/server/plugin.ts @@ -13,9 +13,6 @@ import type { Plugin, Logger, KibanaRequest, - RouteMethod, - RequestHandler, - RequestHandlerContext, StartServicesAccessor, } from 'src/core/server'; import { schema } from '@kbn/config-schema'; @@ -28,7 +25,6 @@ import { removeLeadingSlash, normalizeError, } from '../common'; -import { StreamingRequestHandler } from './types'; import { createStream } from './streaming'; import { getUiSettings } from './ui_settings'; @@ -52,44 +48,6 @@ export interface BfetchServerSetup { path: string, params: (request: KibanaRequest) => StreamingResponseHandler ) => void; - /** - * Create a streaming request handler to be able to use an Observable to return chunked content to the client. - * This is meant to be used with the `fetchStreaming` API of the `bfetch` client-side plugin. - * - * @example - * ```ts - * setup({ http }: CoreStart, { bfetch }: SetupDeps) { - * const router = http.createRouter(); - * router.post( - * { - * path: '/api/my-plugin/stream-endpoint, - * validate: { - * body: schema.object({ - * term: schema.string(), - * }), - * } - * }, - * bfetch.createStreamingResponseHandler(async (ctx, req) => { - * const { term } = req.body; - * const results$ = await myApi.getResults$(term); - * return results$; - * }) - * )} - * - * ``` - * - * @param streamHandler - */ - createStreamingRequestHandler: < - Response, - P, - Q, - B, - Context extends RequestHandlerContext = RequestHandlerContext, - Method extends RouteMethod = any - >( - streamHandler: StreamingRequestHandler - ) => RequestHandler; } // eslint-disable-next-line @@ -124,15 +82,10 @@ export class BfetchServerPlugin logger, }); const addBatchProcessingRoute = this.addBatchProcessingRoute(addStreamingResponseRoute); - const createStreamingRequestHandler = this.createStreamingRequestHandler({ - getStartServices: core.getStartServices, - logger, - }); return { addBatchProcessingRoute, addStreamingResponseRoute, - createStreamingRequestHandler, }; } @@ -142,10 +95,6 @@ export class BfetchServerPlugin public stop() {} - private getCompressionDisabled(request: KibanaRequest) { - return request.headers['x-chunk-encoding'] !== 'deflate'; - } - private addStreamingResponseRoute = ({ getStartServices, @@ -162,42 +111,21 @@ export class BfetchServerPlugin path: `/${removeLeadingSlash(path)}`, validate: { body: schema.any(), + query: schema.object({ compress: schema.boolean({ defaultValue: false }) }), }, }, async (context, request, response) => { const handlerInstance = handler(request); const data = request.body; - const compressionDisabled = this.getCompressionDisabled(request); + const compress = request.query.compress; return response.ok({ headers: streamingHeaders, - body: createStream( - handlerInstance.getResponseStream(data), - logger, - compressionDisabled - ), + body: createStream(handlerInstance.getResponseStream(data), logger, compress), }); } ); }; - private createStreamingRequestHandler = - ({ - logger, - getStartServices, - }: { - logger: Logger; - getStartServices: StartServicesAccessor; - }): BfetchServerSetup['createStreamingRequestHandler'] => - (streamHandler) => - async (context, request, response) => { - const response$ = await streamHandler(context, request); - const compressionDisabled = this.getCompressionDisabled(request); - return response.ok({ - headers: streamingHeaders, - body: createStream(response$, logger, compressionDisabled), - }); - }; - private addBatchProcessingRoute = ( addStreamingResponseRoute: BfetchServerSetup['addStreamingResponseRoute'] diff --git a/src/plugins/bfetch/server/streaming/create_stream.ts b/src/plugins/bfetch/server/streaming/create_stream.ts index 7d6981294341..756a806a6022 100644 --- a/src/plugins/bfetch/server/streaming/create_stream.ts +++ b/src/plugins/bfetch/server/streaming/create_stream.ts @@ -15,9 +15,9 @@ import { createNDJSONStream } from './create_ndjson_stream'; export function createStream( response$: Observable, logger: Logger, - compressionDisabled: boolean + compress: boolean ): Stream { - return compressionDisabled - ? createNDJSONStream(response$, logger) - : createCompressedStream(response$, logger); + return compress + ? createCompressedStream(response$, logger) + : createNDJSONStream(response$, logger); } diff --git a/src/plugins/bfetch/server/types.ts b/src/plugins/bfetch/server/types.ts deleted file mode 100644 index 4e54744f4c37..000000000000 --- a/src/plugins/bfetch/server/types.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 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 { Observable } from 'rxjs'; -import { KibanaRequest, RequestHandlerContext, RouteMethod } from 'kibana/server'; - -/** - * Request handler modified to allow to return an observable. - * - * See {@link BfetchServerSetup.createStreamingRequestHandler} for usage example. - * @public - */ -export type StreamingRequestHandler< - Response = unknown, - P = unknown, - Q = unknown, - B = unknown, - Method extends RouteMethod = any -> = ( - context: RequestHandlerContext, - request: KibanaRequest -) => Observable | Promise>; diff --git a/test/api_integration/apis/search/bsearch.ts b/test/api_integration/apis/search/bsearch.ts index f80bc1d0d9df..6aee2b542da0 100644 --- a/test/api_integration/apis/search/bsearch.ts +++ b/test/api_integration/apis/search/bsearch.ts @@ -29,28 +29,25 @@ export default function ({ getService }: FtrProviderContext) { describe('bsearch', () => { describe('post', () => { it('should return 200 a single response', async () => { - const resp = await supertest - .post(`/internal/bsearch`) - .set({ 'X-Chunk-Encoding': '' }) - .send({ - batch: [ - { - request: { - params: { - index: '.kibana', - body: { - query: { - match_all: {}, - }, + const resp = await supertest.post(`/internal/bsearch`).send({ + batch: [ + { + request: { + params: { + index: '.kibana', + body: { + query: { + match_all: {}, }, }, }, - options: { - strategy: 'es', - }, }, - ], - }); + options: { + strategy: 'es', + }, + }, + ], + }); const jsonBody = parseBfetchResponse(resp); @@ -62,28 +59,25 @@ export default function ({ getService }: FtrProviderContext) { }); it('should return 200 a single response from compressed', async () => { - const resp = await supertest - .post(`/internal/bsearch`) - .set({ 'X-Chunk-Encoding': 'deflate' }) - .send({ - batch: [ - { - request: { - params: { - index: '.kibana', - body: { - query: { - match_all: {}, - }, + const resp = await supertest.post(`/internal/bsearch?compress=true`).send({ + batch: [ + { + request: { + params: { + index: '.kibana', + body: { + query: { + match_all: {}, }, }, }, - options: { - strategy: 'es', - }, }, - ], - }); + options: { + strategy: 'es', + }, + }, + ], + }); const jsonBody = parseBfetchResponse(resp, true); From 32e00f1b0cd9b87a492b2bafa3f8fa2ed46731db Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Mon, 11 Oct 2021 11:41:01 -0400 Subject: [PATCH 51/74] [Fleet] Fix previous configuration modal title (#114475) * Fix previous configuration modal title * Revert translation --- .../sections/agent_policy/edit_package_policy_page/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index 7a2f46247d14..4d940534c4a7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -687,7 +687,7 @@ const UpgradeStatusCallout: React.FunctionComponent<{

From b03237a72d6675bd6deb976a73c2dddcdcb6b445 Mon Sep 17 00:00:00 2001 From: Josh Dover <1813008+joshdover@users.noreply.github.com> Date: Mon, 11 Oct 2021 18:16:26 +0200 Subject: [PATCH 52/74] Enable `bearer` scheme by default to support service token authorization (#112654) Co-authored-by: Aleh Zasypkin --- docs/settings/security-settings.asciidoc | 2 +- .../security/authentication/index.asciidoc | 6 +- .../authentication/authenticator.test.ts | 6 +- x-pack/plugins/security/server/config.test.ts | 9 ++ x-pack/plugins/security/server/config.ts | 2 +- .../security_usage_collector.test.ts | 2 +- x-pack/scripts/functional_tests.js | 1 + .../http_bearer.config.ts | 36 ++++++ .../tests/http_bearer/header.ts | 103 ++++++++++++++++++ .../tests/http_bearer/index.ts | 15 +++ 10 files changed, 174 insertions(+), 8 deletions(-) create mode 100644 x-pack/test/security_api_integration/http_bearer.config.ts create mode 100644 x-pack/test/security_api_integration/tests/http_bearer/header.ts create mode 100644 x-pack/test/security_api_integration/tests/http_bearer/index.ts diff --git a/docs/settings/security-settings.asciidoc b/docs/settings/security-settings.asciidoc index 906af1dfbb28..11072509da1f 100644 --- a/docs/settings/security-settings.asciidoc +++ b/docs/settings/security-settings.asciidoc @@ -218,7 +218,7 @@ There is a very limited set of cases when you'd want to change these settings. F | Determines if HTTP authentication schemes used by the enabled authentication providers should be automatically supported during HTTP authentication. By default, this setting is set to `true`. | `xpack.security.authc.http.schemes[]` -| List of HTTP authentication schemes that {kib} HTTP authentication should support. By default, this setting is set to `['apikey']` to support HTTP authentication with <> scheme. +| List of HTTP authentication schemes that {kib} HTTP authentication should support. By default, this setting is set to `['apikey', 'bearer']` to support HTTP authentication with the <> and <> schemes. |=== diff --git a/docs/user/security/authentication/index.asciidoc b/docs/user/security/authentication/index.asciidoc index bc564308c057..2f2b27938979 100644 --- a/docs/user/security/authentication/index.asciidoc +++ b/docs/user/security/authentication/index.asciidoc @@ -437,14 +437,14 @@ This type of authentication is usually useful for machine-to-machine interaction By default {kib} supports <> authentication scheme _and_ any scheme supported by the currently enabled authentication provider. For example, `Basic` authentication scheme is automatically supported when basic authentication provider is enabled, or `Bearer` scheme when any of the token based authentication providers is enabled (Token, SAML, OpenID Connect, PKI or Kerberos). But it's also possible to add support for any other authentication scheme in the `kibana.yml` configuration file, as follows: -NOTE: Don't forget to explicitly specify default `apikey` scheme when you just want to add a new one to the list. +NOTE: Don't forget to explicitly specify the default `apikey` and `bearer` schemes when you just want to add a new one to the list. [source,yaml] -------------------------------------------------------------------------------- -xpack.security.authc.http.schemes: [apikey, basic, something-custom] +xpack.security.authc.http.schemes: [apikey, bearer, basic, something-custom] -------------------------------------------------------------------------------- -With this configuration, you can send requests to {kib} with the `Authorization` header using `ApiKey`, `Basic` or `Something-Custom` HTTP schemes (case insensitive). Under the hood, {kib} relays this header to {es}, then {es} authenticates the request using the credentials in the header. +With this configuration, you can send requests to {kib} with the `Authorization` header using `ApiKey`, `Bearer`, `Basic` or `Something-Custom` HTTP schemes (case insensitive). Under the hood, {kib} relays this header to {es}, then {es} authenticates the request using the credentials in the header. [float] [[embedded-content-authentication]] diff --git a/x-pack/plugins/security/server/authentication/authenticator.test.ts b/x-pack/plugins/security/server/authentication/authenticator.test.ts index ce97c142f558..4e35b84a9311 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.test.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.test.ts @@ -210,7 +210,7 @@ describe('Authenticator', () => { expect( jest.requireMock('./providers/http').HTTPAuthenticationProvider ).toHaveBeenCalledWith(expect.anything(), { - supportedSchemes: new Set(['apikey', 'basic']), + supportedSchemes: new Set(['apikey', 'bearer', 'basic']), }); }); @@ -238,7 +238,9 @@ describe('Authenticator', () => { expect( jest.requireMock('./providers/http').HTTPAuthenticationProvider - ).toHaveBeenCalledWith(expect.anything(), { supportedSchemes: new Set(['apikey']) }); + ).toHaveBeenCalledWith(expect.anything(), { + supportedSchemes: new Set(['apikey', 'bearer']), + }); }); it('disabled if explicitly disabled', () => { diff --git a/x-pack/plugins/security/server/config.test.ts b/x-pack/plugins/security/server/config.test.ts index 4a7d8c7961cf..1baf3fd4aac5 100644 --- a/x-pack/plugins/security/server/config.test.ts +++ b/x-pack/plugins/security/server/config.test.ts @@ -27,6 +27,7 @@ describe('config schema', () => { "enabled": true, "schemes": Array [ "apikey", + "bearer", ], }, "providers": Object { @@ -80,6 +81,7 @@ describe('config schema', () => { "enabled": true, "schemes": Array [ "apikey", + "bearer", ], }, "providers": Object { @@ -133,6 +135,7 @@ describe('config schema', () => { "enabled": true, "schemes": Array [ "apikey", + "bearer", ], }, "providers": Object { @@ -311,6 +314,7 @@ describe('config schema', () => { "enabled": true, "schemes": Array [ "apikey", + "bearer", ], }, "oidc": Object { @@ -342,6 +346,7 @@ describe('config schema', () => { "enabled": true, "schemes": Array [ "apikey", + "bearer", ], }, "oidc": Object { @@ -373,6 +378,7 @@ describe('config schema', () => { "enabled": true, "schemes": Array [ "apikey", + "bearer", ], }, "providers": Array [ @@ -391,6 +397,7 @@ describe('config schema', () => { "enabled": true, "schemes": Array [ "apikey", + "bearer", ], }, "providers": Array [ @@ -412,6 +419,7 @@ describe('config schema', () => { "enabled": true, "schemes": Array [ "apikey", + "bearer", ], }, "providers": Array [ @@ -1485,6 +1493,7 @@ describe('createConfig()', () => { "enabled": true, "schemes": Array [ "apikey", + "bearer", ], }, "providers": Object { diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index 07ff81e092f5..89918e73369d 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -269,7 +269,7 @@ export const ConfigSchema = schema.object({ http: schema.object({ enabled: schema.boolean({ defaultValue: true }), autoSchemesEnabled: schema.boolean({ defaultValue: true }), - schemes: schema.arrayOf(schema.string(), { defaultValue: ['apikey'] }), + schemes: schema.arrayOf(schema.string(), { defaultValue: ['apikey', 'bearer'] }), }), }), audit: schema.object( diff --git a/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts b/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts index 0515a1e1969b..83f09ef017b0 100644 --- a/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts +++ b/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts @@ -46,7 +46,7 @@ describe('Security UsageCollector', () => { authProviderCount: 1, enabledAuthProviders: ['basic'], loginSelectorEnabled: false, - httpAuthSchemes: ['apikey'], + httpAuthSchemes: ['apikey', 'bearer'], sessionIdleTimeoutInMinutes: 60, sessionLifespanInMinutes: 43200, sessionCleanupInMinutes: 60, diff --git a/x-pack/scripts/functional_tests.js b/x-pack/scripts/functional_tests.js index 3c1cdd5790f3..f7b978c2b58b 100644 --- a/x-pack/scripts/functional_tests.js +++ b/x-pack/scripts/functional_tests.js @@ -54,6 +54,7 @@ const onlyNotInCoverageTests = [ require.resolve('../test/security_api_integration/session_lifespan.config.ts'), require.resolve('../test/security_api_integration/login_selector.config.ts'), require.resolve('../test/security_api_integration/audit.config.ts'), + require.resolve('../test/security_api_integration/http_bearer.config.ts'), require.resolve('../test/security_api_integration/kerberos.config.ts'), require.resolve('../test/security_api_integration/kerberos_anonymous_access.config.ts'), require.resolve('../test/security_api_integration/pki.config.ts'), diff --git a/x-pack/test/security_api_integration/http_bearer.config.ts b/x-pack/test/security_api_integration/http_bearer.config.ts new file mode 100644 index 000000000000..b0a9f4a92034 --- /dev/null +++ b/x-pack/test/security_api_integration/http_bearer.config.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrConfigProviderContext } from '@kbn/test'; +import { services } from './services'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); + + return { + testFiles: [require.resolve('./tests/http_bearer')], + servers: xPackAPITestsConfig.get('servers'), + security: { disableTestUser: true }, + services, + junit: { + reportName: 'X-Pack Security API Integration Tests (HTTP Bearer)', + }, + + esTestCluster: { + ...xPackAPITestsConfig.get('esTestCluster'), + serverArgs: [ + ...xPackAPITestsConfig.get('esTestCluster.serverArgs'), + 'xpack.security.authc.token.enabled=true', + 'xpack.security.authc.token.timeout=15s', + ], + }, + + kbnTestServer: { + ...xPackAPITestsConfig.get('kbnTestServer'), + }, + }; +} diff --git a/x-pack/test/security_api_integration/tests/http_bearer/header.ts b/x-pack/test/security_api_integration/tests/http_bearer/header.ts new file mode 100644 index 000000000000..f7ebef4f16d0 --- /dev/null +++ b/x-pack/test/security_api_integration/tests/http_bearer/header.ts @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { adminTestUser } from '@kbn/test'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertestWithoutAuth'); + const es = getService('es'); + + async function createToken() { + const { + body: { access_token: accessToken, authentication }, + } = await es.security.getToken({ + body: { + grant_type: 'password', + ...adminTestUser, + }, + }); + + return { + accessToken, + expectedUser: { + ...authentication, + authentication_provider: { name: '__http__', type: 'http' }, + authentication_type: 'token', + }, + }; + } + + describe('header', () => { + it('accepts valid access token via authorization Bearer header', async () => { + const { accessToken, expectedUser } = await createToken(); + + const response = await supertest + .get('/internal/security/me') + .set('kbn-xsrf', 'true') + .set('authorization', `Bearer ${accessToken}`) + .expect(200, expectedUser); + + // Make sure we don't automatically create a session + expect(response.headers['set-cookie']).to.be(undefined); + }); + + it('accepts multiple requests for a single valid access token', async () => { + const { accessToken, expectedUser } = await createToken(); + + // try it once + await supertest + .get('/internal/security/me') + .set('kbn-xsrf', 'true') + .set('authorization', `Bearer ${accessToken}`) + .expect(200, expectedUser); + + // try it again to verity it isn't invalidated after a single request + await supertest + .get('/internal/security/me') + .set('kbn-xsrf', 'true') + .set('authorization', `Bearer ${accessToken}`) + .expect(200, expectedUser); + }); + + it('rejects invalid access token via authorization Bearer header', async () => { + await supertest + .get('/internal/security/me') + .set('kbn-xsrf', 'true') + .set('authorization', 'Bearer notreal') + .expect(401); + }); + + it('rejects invalidated access token via authorization Bearer header', async () => { + const { accessToken } = await createToken(); + await es.security.invalidateToken({ body: { token: accessToken } }); + + await supertest + .get('/internal/security/me') + .set('kbn-xsrf', 'true') + .set('authorization', `Bearer ${accessToken}`) + .expect(401); + }); + + it('rejects expired access token via authorization Bearer header', async function () { + this.timeout(40000); + + const { accessToken } = await createToken(); + + // Access token expiration is set to 15s for API integration tests. + // Let's wait for 20s to make sure token expires. + await new Promise((resolve) => setTimeout(resolve, 20000)); + + await supertest + .get('/internal/security/me') + .set('kbn-xsrf', 'true') + .set('authorization', `Bearer ${accessToken}`) + .expect(401); + }); + }); +} diff --git a/x-pack/test/security_api_integration/tests/http_bearer/index.ts b/x-pack/test/security_api_integration/tests/http_bearer/index.ts new file mode 100644 index 000000000000..4dbad2660eba --- /dev/null +++ b/x-pack/test/security_api_integration/tests/http_bearer/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('security APIs - HTTP Bearer', function () { + this.tags('ciGroup6'); + loadTestFile(require.resolve('./header')); + }); +} From c8a01082696a94453060ded28ee67a32a2487e0d Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Mon, 11 Oct 2021 10:53:51 -0600 Subject: [PATCH 53/74] [Stack Monitoring] Adding alerts to react app (#114029) * [Stack Monitoring] Adding alerts to react app * Fixing global state context path * adding alerts to pages; adding alerts model to cluster_overview; removing loadAlerts from page template * Fixing request for enable alerts * remove loadAlerts from page template * Adding request error handlers * removing redundent error handling * Changing useRequestErrorHandler function to be async due to error.response.json call * removing old comment * Fixing contexts paths * Converting ajaxRequestErrorHandler to useRequestErrorHandler * Refactoring error handler for page template and setup mode * Removing unnecessary async/await * Removing unnecessary async/await in useClusters * adding alertTypeIds to each page * fixing instance count * Adding alertTypeIds to index page * Adding alert filters for specific pages * Adding alerts to Logstash nodes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/monitoring/common/types/alerts.ts | 1 + .../public/alerts/alerts_dropdown.tsx | 5 +- .../external_config_context.tsx | 0 .../{ => contexts}/global_state_context.tsx | 8 +- .../contexts/header_action_menu_context.tsx | 15 + .../application/hooks/use_alerts_modal.ts | 10 +- .../public/application/hooks/use_clusters.ts | 6 +- .../application/hooks/use_monitoring_time.ts | 2 +- .../hooks/use_request_error_handler.tsx | 79 +++ .../monitoring/public/application/index.tsx | 467 +++++++++--------- .../public/application/pages/apm/instance.tsx | 2 +- .../application/pages/apm/instances.tsx | 2 +- .../public/application/pages/apm/overview.tsx | 2 +- .../application/pages/beats/instance.tsx | 2 +- .../application/pages/beats/instances.tsx | 2 +- .../application/pages/beats/overview.tsx | 2 +- .../pages/cluster/overview_page.tsx | 49 +- .../pages/elasticsearch/ccr_page.tsx | 41 +- .../pages/elasticsearch/ccr_shard_page.tsx | 47 +- .../elasticsearch/index_advanced_page.tsx | 46 +- .../pages/elasticsearch/index_page.tsx | 58 ++- .../pages/elasticsearch/indices_page.tsx | 51 +- .../pages/elasticsearch/ml_jobs_page.tsx | 2 +- .../elasticsearch/node_advanced_page.tsx | 63 ++- .../pages/elasticsearch/node_page.tsx | 70 ++- .../pages/elasticsearch/nodes_page.tsx | 66 ++- .../pages/elasticsearch/overview.tsx | 2 +- .../pages/home/cluster_listing.tsx | 32 +- .../application/pages/kibana/instance.tsx | 41 +- .../application/pages/kibana/instances.tsx | 43 +- .../application/pages/kibana/overview.tsx | 2 +- .../public/application/pages/license_page.tsx | 2 +- .../application/pages/logstash/advanced.tsx | 41 +- .../application/pages/logstash/node.tsx | 41 +- .../pages/logstash/node_pipelines.tsx | 2 +- .../application/pages/logstash/nodes.tsx | 40 +- .../application/pages/logstash/overview.tsx | 2 +- .../application/pages/logstash/pipeline.tsx | 4 +- .../application/pages/logstash/pipelines.tsx | 2 +- .../pages/no_data/no_data_page.tsx | 6 +- .../application/pages/page_template.tsx | 44 +- .../public/application/route_init.tsx | 2 +- .../application/setup_mode/setup_mode.tsx | 15 +- .../setup_mode/setup_mode_renderer.js | 16 +- .../public/components/action_menu/index.tsx | 34 ++ .../public/components/shared/toolbar.tsx | 2 +- .../monitoring/public/lib/fetch_alerts.ts | 36 ++ 47 files changed, 990 insertions(+), 517 deletions(-) rename x-pack/plugins/monitoring/public/application/{ => contexts}/external_config_context.tsx (100%) rename x-pack/plugins/monitoring/public/application/{ => contexts}/global_state_context.tsx (90%) create mode 100644 x-pack/plugins/monitoring/public/application/contexts/header_action_menu_context.tsx create mode 100644 x-pack/plugins/monitoring/public/application/hooks/use_request_error_handler.tsx create mode 100644 x-pack/plugins/monitoring/public/components/action_menu/index.tsx create mode 100644 x-pack/plugins/monitoring/public/lib/fetch_alerts.ts diff --git a/x-pack/plugins/monitoring/common/types/alerts.ts b/x-pack/plugins/monitoring/common/types/alerts.ts index 1f68b0c55a04..bbd217169469 100644 --- a/x-pack/plugins/monitoring/common/types/alerts.ts +++ b/x-pack/plugins/monitoring/common/types/alerts.ts @@ -32,6 +32,7 @@ export interface CommonAlertState { export interface CommonAlertFilter { nodeUuid?: string; shardId?: string; + shardIndex?: string; } export interface CommonAlertParamDetail { diff --git a/x-pack/plugins/monitoring/public/alerts/alerts_dropdown.tsx b/x-pack/plugins/monitoring/public/alerts/alerts_dropdown.tsx index 261685a53288..976569f39de4 100644 --- a/x-pack/plugins/monitoring/public/alerts/alerts_dropdown.tsx +++ b/x-pack/plugins/monitoring/public/alerts/alerts_dropdown.tsx @@ -14,13 +14,12 @@ import { import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { Legacy } from '../legacy_shims'; import { useKibana } from '../../../../../src/plugins/kibana_react/public'; import { MonitoringStartPluginDependencies } from '../types'; +import { useAlertsModal } from '../application/hooks/use_alerts_modal'; export const AlertsDropdown: React.FC<{}> = () => { - const $injector = Legacy.shims.getAngularInjector(); - const alertsEnableModalProvider: any = $injector.get('enableAlertsModal'); + const alertsEnableModalProvider = useAlertsModal(); const { navigateToApp } = useKibana().services.application; diff --git a/x-pack/plugins/monitoring/public/application/external_config_context.tsx b/x-pack/plugins/monitoring/public/application/contexts/external_config_context.tsx similarity index 100% rename from x-pack/plugins/monitoring/public/application/external_config_context.tsx rename to x-pack/plugins/monitoring/public/application/contexts/external_config_context.tsx diff --git a/x-pack/plugins/monitoring/public/application/global_state_context.tsx b/x-pack/plugins/monitoring/public/application/contexts/global_state_context.tsx similarity index 90% rename from x-pack/plugins/monitoring/public/application/global_state_context.tsx rename to x-pack/plugins/monitoring/public/application/contexts/global_state_context.tsx index 6c952f80eff5..e6638b4c4fed 100644 --- a/x-pack/plugins/monitoring/public/application/global_state_context.tsx +++ b/x-pack/plugins/monitoring/public/application/contexts/global_state_context.tsx @@ -5,10 +5,10 @@ * 2.0. */ import React, { createContext } from 'react'; -import { GlobalState } from '../url_state'; -import { MonitoringStartPluginDependencies } from '../types'; -import { TimeRange, RefreshInterval } from '../../../../../src/plugins/data/public'; -import { Legacy } from '../legacy_shims'; +import { GlobalState } from '../../url_state'; +import { MonitoringStartPluginDependencies } from '../../types'; +import { TimeRange, RefreshInterval } from '../../../../../../src/plugins/data/public'; +import { Legacy } from '../../legacy_shims'; interface GlobalStateProviderProps { query: MonitoringStartPluginDependencies['data']['query']; diff --git a/x-pack/plugins/monitoring/public/application/contexts/header_action_menu_context.tsx b/x-pack/plugins/monitoring/public/application/contexts/header_action_menu_context.tsx new file mode 100644 index 000000000000..88862d9e6a80 --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/contexts/header_action_menu_context.tsx @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { AppMountParameters } from 'kibana/public'; + +interface ContextProps { + setHeaderActionMenu?: AppMountParameters['setHeaderActionMenu']; +} + +export const HeaderActionMenuContext = React.createContext({}); diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_alerts_modal.ts b/x-pack/plugins/monitoring/public/application/hooks/use_alerts_modal.ts index 9a2a2b80cc40..123dd39f7b54 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_alerts_modal.ts +++ b/x-pack/plugins/monitoring/public/application/hooks/use_alerts_modal.ts @@ -6,10 +6,11 @@ */ import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { showAlertsToast } from '../../alerts/lib/alerts_toast'; -import { ajaxErrorHandlersProvider } from '../../lib/ajax_error_handler'; +import { useRequestErrorHandler } from './use_request_error_handler'; export const useAlertsModal = () => { const { services } = useKibana(); + const handleRequestError = useRequestErrorHandler(); function shouldShowAlertsModal(alerts: {}) { const modalHasBeenShown = @@ -28,12 +29,11 @@ export const useAlertsModal = () => { async function enableAlerts() { try { - const { data } = await services.http?.post('../api/monitoring/v1/alerts/enable', {}); + const response = await services.http?.post('../api/monitoring/v1/alerts/enable', {}); window.localStorage.setItem('ALERTS_MODAL_DECISION_MADE', 'true'); - showAlertsToast(data); + showAlertsToast(response); } catch (err) { - const ajaxErrorHandlers = ajaxErrorHandlersProvider(); - return ajaxErrorHandlers(err); + await handleRequestError(err); } } diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_clusters.ts b/x-pack/plugins/monitoring/public/application/hooks/use_clusters.ts index b4b8c21ca4d4..1961bd53b909 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_clusters.ts +++ b/x-pack/plugins/monitoring/public/application/hooks/use_clusters.ts @@ -7,6 +7,7 @@ import { useState, useEffect } from 'react'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { fetchClusters } from '../../lib/fetch_clusters'; +import { useRequestErrorHandler } from './use_request_error_handler'; export function useClusters(clusterUuid?: string | null, ccs?: any, codePaths?: string[]) { const { services } = useKibana<{ data: any }>(); @@ -17,6 +18,7 @@ export function useClusters(clusterUuid?: string | null, ccs?: any, codePaths?: const [clusters, setClusters] = useState([] as any); const [loaded, setLoaded] = useState(false); + const handleRequestError = useRequestErrorHandler(); useEffect(() => { async function makeRequest() { @@ -34,13 +36,13 @@ export function useClusters(clusterUuid?: string | null, ccs?: any, codePaths?: setClusters(response); } } catch (e) { - // TODO: Handle errors + handleRequestError(e); } finally { setLoaded(true); } } makeRequest(); - }, [clusterUuid, ccs, services.http, codePaths, min, max]); + }, [handleRequestError, clusterUuid, ccs, services.http, codePaths, min, max]); return { clusters, loaded }; } diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts b/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts index 3054714ec3aa..e8973ce18232 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts +++ b/x-pack/plugins/monitoring/public/application/hooks/use_monitoring_time.ts @@ -8,7 +8,7 @@ import { useCallback, useState, useContext, useEffect } from 'react'; import createContainer from 'constate'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { Legacy } from '../../legacy_shims'; -import { GlobalStateContext } from '../../application/global_state_context'; +import { GlobalStateContext } from '../contexts/global_state_context'; interface TimeOptions { from: string; diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_request_error_handler.tsx b/x-pack/plugins/monitoring/public/application/hooks/use_request_error_handler.tsx new file mode 100644 index 000000000000..3a6453184445 --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/hooks/use_request_error_handler.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useCallback } from 'react'; +import { includes } from 'lodash'; +import { IHttpFetchError } from 'kibana/public'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiButton, EuiSpacer, EuiText } from '@elastic/eui'; +import { formatMsg } from '../../../../../../src/plugins/kibana_legacy/public'; +import { toMountPoint, useKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { MonitoringStartPluginDependencies } from '../../types'; + +export function formatMonitoringError(err: IHttpFetchError) { + if (err.response?.status && err.response?.status !== -1) { + return ( + +

{err.body?.message}

+ + + +
+ ); + } + + return formatMsg(err); +} + +export const useRequestErrorHandler = () => { + const { services } = useKibana(); + return useCallback( + (err: IHttpFetchError) => { + if (err.response?.status === 403) { + // redirect to error message view + history.replaceState(null, '', '#/access-denied'); + } else if (err.response?.status === 404 && !includes(window.location.hash, 'no-data')) { + // pass through if this is a 404 and we're already on the no-data page + const formattedError = formatMonitoringError(err); + services.notifications?.toasts.addDanger({ + title: toMountPoint( + + ), + text: toMountPoint( +
+ {formattedError} + + window.location.reload()}> + + +
+ ), + }); + } else { + services.notifications?.toasts.addDanger({ + title: toMountPoint( + + ), + text: toMountPoint(formatMonitoringError(err)), + }); + } + }, + [services.notifications] + ); +}; diff --git a/x-pack/plugins/monitoring/public/application/index.tsx b/x-pack/plugins/monitoring/public/application/index.tsx index bc81dd826f84..7b4c73475338 100644 --- a/x-pack/plugins/monitoring/public/application/index.tsx +++ b/x-pack/plugins/monitoring/public/application/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { CoreStart, AppMountParameters } from 'kibana/public'; +import { CoreStart, AppMountParameters, MountPoint } from 'kibana/public'; import React from 'react'; import ReactDOM from 'react-dom'; import { Route, Switch, Redirect, Router } from 'react-router-dom'; @@ -15,8 +15,8 @@ import { LicensePage } from './pages/license_page'; import { ClusterOverview } from './pages/cluster/overview_page'; import { ClusterListing } from './pages/home/cluster_listing'; import { MonitoringStartPluginDependencies } from '../types'; -import { GlobalStateProvider } from './global_state_context'; -import { ExternalConfigContext, ExternalConfig } from './external_config_context'; +import { GlobalStateProvider } from './contexts/global_state_context'; +import { ExternalConfigContext, ExternalConfig } from './contexts/external_config_context'; import { createPreserveQueryHistory } from './preserve_query_history'; import { RouteInit } from './route_init'; import { NoDataPage } from './pages/no_data'; @@ -45,6 +45,7 @@ import { ElasticsearchCcrPage } from './pages/elasticsearch/ccr_page'; import { ElasticsearchCcrShardPage } from './pages/elasticsearch/ccr_shard_page'; import { MonitoringTimeContainer } from './hooks/use_monitoring_time'; import { BreadcrumbContainer } from './hooks/use_breadcrumbs'; +import { HeaderActionMenuContext } from './contexts/header_action_menu_context'; import { LogStashOverviewPage } from './pages/logstash/overview'; import { LogStashNodesPage } from './pages/logstash/nodes'; import { LogStashPipelinesPage } from './pages/logstash/pipelines'; @@ -58,11 +59,16 @@ import { LogStashNodePipelinesPage } from './pages/logstash/node_pipelines'; export const renderApp = ( core: CoreStart, plugins: MonitoringStartPluginDependencies, - { element }: AppMountParameters, + { element, setHeaderActionMenu }: AppMountParameters, externalConfig: ExternalConfig ) => { ReactDOM.render( - , + , element ); @@ -75,236 +81,239 @@ const MonitoringApp: React.FC<{ core: CoreStart; plugins: MonitoringStartPluginDependencies; externalConfig: ExternalConfig; -}> = ({ core, plugins, externalConfig }) => { + setHeaderActionMenu: (element: MountPoint | undefined) => void; +}> = ({ core, plugins, externalConfig, setHeaderActionMenu }) => { const history = createPreserveQueryHistory(); return ( - - - - - - - - - - - {/* ElasticSearch Views */} - - - - - - - - - - - - - - - - - - - - - {/* Kibana Views */} - - - - - - - {/* Beats Views */} - - - - - - - {/* Logstash Routes */} - - - - - - - - - - - - - - - {/* APM Views */} - - - - - - - - - - - + + + + + + + + + + + + {/* ElasticSearch Views */} + + + + + + + + + + + + + + + + + + + + + {/* Kibana Views */} + + + + + + + {/* Beats Views */} + + + + + + + {/* Logstash Routes */} + + + + + + + + + + + + + + + {/* APM Views */} + + + + + + + + + + + + diff --git a/x-pack/plugins/monitoring/public/application/pages/apm/instance.tsx b/x-pack/plugins/monitoring/public/application/pages/apm/instance.tsx index dc55ecb22b61..3fa7819c5e41 100644 --- a/x-pack/plugins/monitoring/public/application/pages/apm/instance.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/apm/instance.tsx @@ -10,7 +10,7 @@ import { useParams } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; import { ComponentProps } from '../../route_init'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { useCharts } from '../../hooks/use_charts'; import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; diff --git a/x-pack/plugins/monitoring/public/application/pages/apm/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/apm/instances.tsx index bc60f26cdbfa..fedb07fa65a4 100644 --- a/x-pack/plugins/monitoring/public/application/pages/apm/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/apm/instances.tsx @@ -9,7 +9,7 @@ import React, { useContext, useState, useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; import { ComponentProps } from '../../route_init'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { useTable } from '../../hooks/use_table'; import { ApmTemplate } from './apm_template'; diff --git a/x-pack/plugins/monitoring/public/application/pages/apm/overview.tsx b/x-pack/plugins/monitoring/public/application/pages/apm/overview.tsx index cca31c0a7e65..516c293c5354 100644 --- a/x-pack/plugins/monitoring/public/application/pages/apm/overview.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/apm/overview.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; import { ComponentProps } from '../../route_init'; import { ApmTemplate } from './apm_template'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { useCharts } from '../../hooks/use_charts'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; diff --git a/x-pack/plugins/monitoring/public/application/pages/beats/instance.tsx b/x-pack/plugins/monitoring/public/application/pages/beats/instance.tsx index f7ff03898fda..4c66bbba631f 100644 --- a/x-pack/plugins/monitoring/public/application/pages/beats/instance.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/beats/instance.tsx @@ -10,7 +10,7 @@ import { useParams } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; import { ComponentProps } from '../../route_init'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { useCharts } from '../../hooks/use_charts'; // @ts-ignore diff --git a/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx index 18f941c398af..489ad110c40f 100644 --- a/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx @@ -9,7 +9,7 @@ import React, { useContext, useState, useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; import { ComponentProps } from '../../route_init'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { useTable } from '../../hooks/use_table'; import { BeatsTemplate } from './beats_template'; diff --git a/x-pack/plugins/monitoring/public/application/pages/beats/overview.tsx b/x-pack/plugins/monitoring/public/application/pages/beats/overview.tsx index 8d28119c4ec1..1fa37a2c7b3e 100644 --- a/x-pack/plugins/monitoring/public/application/pages/beats/overview.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/beats/overview.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; import { ComponentProps } from '../../route_init'; import { BeatsTemplate } from './beats_template'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { useCharts } from '../../hooks/use_charts'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; // @ts-ignore diff --git a/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx b/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx index 3a717036396e..b78df27cd12c 100644 --- a/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx @@ -10,14 +10,17 @@ import { i18n } from '@kbn/i18n'; import { CODE_PATH_ALL } from '../../../../common/constants'; import { PageTemplate } from '../page_template'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { TabMenuItem } from '../page_template'; import { Overview } from '../../../components/cluster/overview'; -import { ExternalConfigContext } from '../../external_config_context'; +import { ExternalConfigContext } from '../../contexts/external_config_context'; import { SetupModeRenderer, SetupModeProps } from '../../setup_mode/setup_mode_renderer'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; import { fetchClusters } from '../../../lib/fetch_clusters'; +import { AlertsByName } from '../../../alerts/types'; +import { fetchAlerts } from '../../../lib/fetch_alerts'; +import { EnableAlertsModal } from '../../../alerts/enable_alerts_modal'; const CODE_PATHS = [CODE_PATH_ALL]; @@ -28,6 +31,7 @@ export const ClusterOverview: React.FC<{}> = () => { const clusterUuid = state.cluster_uuid; const ccs = state.ccs; const [clusters, setClusters] = useState([] as any); + const [alerts, setAlerts] = useState({}); const [loaded, setLoaded] = useState(false); const { generate: generateBreadcrumbs } = useContext(BreadcrumbContainer.Context); @@ -54,23 +58,27 @@ export const ClusterOverview: React.FC<{}> = () => { const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); - try { - if (services.http?.fetch) { - const response = await fetchClusters({ - fetch: services.http.fetch, - timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), - }, - ccs, - clusterUuid, - codePaths: CODE_PATHS, - }); - setClusters(response); - } - } catch (err) { - // TODO: handle errors - } finally { + if (services.http?.fetch && clusterUuid) { + const response = await fetchClusters({ + fetch: services.http.fetch, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + ccs, + clusterUuid, + codePaths: CODE_PATHS, + }); + setClusters(response); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + clusterUuid, + timeRange: { + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), + }, + }); + setAlerts(alertsResponse); setLoaded(true); } }, [ccs, clusterUuid, services.data?.query.timefilter.timefilter, services.http]); @@ -89,7 +97,7 @@ export const ClusterOverview: React.FC<{}> = () => { {flyoutComponent} @@ -98,6 +106,7 @@ export const ClusterOverview: React.FC<{}> = () => { )} /> + ); }; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_page.tsx index 294aeade5e38..8a9a736286c3 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_page.tsx @@ -9,13 +9,15 @@ import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; import { ElasticsearchTemplate } from './elasticsearch_template'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; // @ts-ignore import { Ccr } from '../../../components/elasticsearch/ccr'; import { ComponentProps } from '../../route_init'; import { SetupModeRenderer } from '../../setup_mode/setup_mode_renderer'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; -import { ELASTICSEARCH_SYSTEM_ID } from '../../../../common/constants'; +import { AlertsByName } from '../../../alerts/types'; +import { fetchAlerts } from '../../../lib/fetch_alerts'; +import { ELASTICSEARCH_SYSTEM_ID, RULE_CCR_READ_EXCEPTIONS } from '../../../../common/constants'; interface SetupModeProps { setupMode: any; @@ -33,6 +35,7 @@ export const ElasticsearchCcrPage: React.FC = ({ clusters }) => }) as any; const ccs = globalState.ccs; const [data, setData] = useState({} as any); + const [alerts, setAlerts] = useState({}); const title = i18n.translate('xpack.monitoring.elasticsearch.ccr.title', { defaultMessage: 'Elasticsearch - Ccr', @@ -46,18 +49,30 @@ export const ElasticsearchCcrPage: React.FC = ({ clusters }) => const bounds = services.data?.query.timefilter.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${clusterUuid}/elasticsearch/ccr`; - const response = await services.http?.fetch(url, { - method: 'POST', - body: JSON.stringify({ - ccs, + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + ccs, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + }), + }); + + setData(response); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + alertTypeIds: [RULE_CCR_READ_EXCEPTIONS], + clusterUuid, timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - }), - }); - - setData(response); + }); + setAlerts(alertsResponse); + } }, [ccs, clusterUuid, services.data?.query.timefilter.timefilter, services.http]); return ( @@ -73,7 +88,7 @@ export const ElasticsearchCcrPage: React.FC = ({ clusters }) => render={({ flyoutComponent, bottomBarComponent }: SetupModeProps) => ( {flyoutComponent} - + {bottomBarComponent} )} diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_shard_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_shard_page.tsx index bec2f278f177..21f9fd10f080 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_shard_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_shard_page.tsx @@ -10,13 +10,15 @@ import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; import { PageTemplate } from '../page_template'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; // @ts-ignore import { CcrShardReact } from '../../../components/elasticsearch/ccr_shard'; import { ComponentProps } from '../../route_init'; import { SetupModeRenderer } from '../../setup_mode/setup_mode_renderer'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; -import { ELASTICSEARCH_SYSTEM_ID } from '../../../../common/constants'; +import { AlertsByName } from '../../../alerts/types'; +import { fetchAlerts } from '../../../lib/fetch_alerts'; +import { ELASTICSEARCH_SYSTEM_ID, RULE_CCR_READ_EXCEPTIONS } from '../../../../common/constants'; interface SetupModeProps { setupMode: any; @@ -24,7 +26,7 @@ interface SetupModeProps { bottomBarComponent: any; } -export const ElasticsearchCcrShardPage: React.FC = ({ clusters }) => { +export const ElasticsearchCcrShardPage: React.FC = () => { const globalState = useContext(GlobalStateContext); const { services } = useKibana<{ data: any }>(); const { index, shardId }: { index: string; shardId: string } = useParams(); @@ -32,6 +34,7 @@ export const ElasticsearchCcrShardPage: React.FC = ({ clusters } const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; const [data, setData] = useState({} as any); + const [alerts, setAlerts] = useState({}); const title = i18n.translate('xpack.monitoring.elasticsearch.ccr.shard.title', { defaultMessage: 'Elasticsearch - Ccr - Shard', @@ -57,18 +60,34 @@ export const ElasticsearchCcrShardPage: React.FC = ({ clusters } const bounds = services.data?.query.timefilter.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${clusterUuid}/elasticsearch/ccr/${index}/shard/${shardId}`; - const response = await services.http?.fetch(url, { - method: 'POST', - body: JSON.stringify({ - ccs, + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + ccs, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + }), + }); + setData(response); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + alertTypeIds: [RULE_CCR_READ_EXCEPTIONS], + clusterUuid, + filters: [ + { + shardId, + }, + ], timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - }), - }); - - setData(response); + }); + setAlerts(alertsResponse); + } }, [ccs, clusterUuid, services.data?.query.timefilter.timefilter, services.http, index, shardId]); return ( @@ -84,7 +103,7 @@ export const ElasticsearchCcrShardPage: React.FC = ({ clusters } render={({ flyoutComponent, bottomBarComponent }: SetupModeProps) => ( {flyoutComponent} - + {bottomBarComponent} )} diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx index a635d98fcbbb..86dba4e2f921 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx @@ -8,7 +8,7 @@ import React, { useContext, useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { useParams } from 'react-router-dom'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { ComponentProps } from '../../route_init'; import { SetupModeRenderer, SetupModeProps } from '../../setup_mode/setup_mode_renderer'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; @@ -16,15 +16,18 @@ import { useCharts } from '../../hooks/use_charts'; import { ItemTemplate } from './item_template'; // @ts-ignore import { AdvancedIndex } from '../../../components/elasticsearch/index/advanced'; -import { ELASTICSEARCH_SYSTEM_ID } from '../../../../common/constants'; +import { AlertsByName } from '../../../alerts/types'; +import { fetchAlerts } from '../../../lib/fetch_alerts'; +import { ELASTICSEARCH_SYSTEM_ID, RULE_LARGE_SHARD_SIZE } from '../../../../common/constants'; -export const ElasticsearchIndexAdvancedPage: React.FC = ({ clusters }) => { +export const ElasticsearchIndexAdvancedPage: React.FC = () => { const globalState = useContext(GlobalStateContext); const { services } = useKibana<{ data: any }>(); const { index }: { index: string } = useParams(); const { zoomInfo, onBrush } = useCharts(); const clusterUuid = globalState.cluster_uuid; const [data, setData] = useState({} as any); + const [alerts, setAlerts] = useState({}); const title = i18n.translate('xpack.monitoring.elasticsearch.index.advanced.title', { defaultMessage: 'Elasticsearch - Indices - {indexName} - Advanced', @@ -36,17 +39,34 @@ export const ElasticsearchIndexAdvancedPage: React.FC = ({ clust const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${clusterUuid}/elasticsearch/indices/${index}`; - const response = await services.http?.fetch(url, { - method: 'POST', - body: JSON.stringify({ + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + is_advanced: true, + }), + }); + setData(response); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + alertTypeIds: [RULE_LARGE_SHARD_SIZE], + filters: [ + { + shardIndex: index, + }, + ], + clusterUuid, timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - is_advanced: true, - }), - }); - setData(response); + }); + setAlerts(alertsResponse); + } }, [clusterUuid, services.data?.query.timefilter.timefilter, services.http, index]); return ( @@ -58,7 +78,7 @@ export const ElasticsearchIndexAdvancedPage: React.FC = ({ clust {flyoutComponent} = ({ clusters }) => { +export const ElasticsearchIndexPage: React.FC = () => { const globalState = useContext(GlobalStateContext); const { services } = useKibana<{ data: any }>(); const { index }: { index: string } = useParams(); @@ -31,6 +33,7 @@ export const ElasticsearchIndexPage: React.FC = ({ clusters }) = const [data, setData] = useState({} as any); const [indexLabel, setIndexLabel] = useState(labels.index as any); const [nodesByIndicesData, setNodesByIndicesData] = useState([]); + const [alerts, setAlerts] = useState({}); const title = i18n.translate('xpack.monitoring.elasticsearch.index.overview.title', { defaultMessage: 'Elasticsearch - Indices - {indexName} - Overview', @@ -49,23 +52,40 @@ export const ElasticsearchIndexPage: React.FC = ({ clusters }) = const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${clusterUuid}/elasticsearch/indices/${index}`; - const response = await services.http?.fetch(url, { - method: 'POST', - body: JSON.stringify({ + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + is_advanced: false, + }), + }); + setData(response); + const transformer = indicesByNodes(); + setNodesByIndicesData(transformer(response.shards, response.nodes)); + + const shards = response.shards; + if (shards.some((shard: any) => shard.state === 'UNASSIGNED')) { + setIndexLabel(labels.indexWithUnassigned); + } + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + alertTypeIds: [RULE_LARGE_SHARD_SIZE], + filters: [ + { + shardIndex: index, + }, + ], + clusterUuid, timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - is_advanced: false, - }), - }); - setData(response); - const transformer = indicesByNodes(); - setNodesByIndicesData(transformer(response.shards, response.nodes)); - - const shards = response.shards; - if (shards.some((shard: any) => shard.state === 'UNASSIGNED')) { - setIndexLabel(labels.indexWithUnassigned); + }); + setAlerts(alertsResponse); } }, [clusterUuid, services.data?.query.timefilter.timefilter, services.http, index]); @@ -85,7 +105,7 @@ export const ElasticsearchIndexPage: React.FC = ({ clusters }) = = ({ clusters }) => { const globalState = useContext(GlobalStateContext); @@ -32,6 +34,7 @@ export const ElasticsearchIndicesPage: React.FC = ({ clusters }) 'showSystemIndices', false ); + const [alerts, setAlerts] = useState({}); const title = i18n.translate('xpack.monitoring.elasticsearch.indices.routeTitle', { defaultMessage: 'Elasticsearch - Indices', @@ -49,26 +52,38 @@ export const ElasticsearchIndicesPage: React.FC = ({ clusters }) const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${clusterUuid}/elasticsearch/indices`; - const response = await services.http?.fetch(url, { - method: 'POST', - query: { - show_system_indices: showSystemIndices, - }, - body: JSON.stringify({ - ccs, + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + query: { + show_system_indices: showSystemIndices, + }, + body: JSON.stringify({ + ccs, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + }), + }); + setData(response); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + clusterUuid, + alertTypeIds: [RULE_LARGE_SHARD_SIZE], timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - }), - }); - setData(response); + }); + setAlerts(alertsResponse); + } }, [ - ccs, - showSystemIndices, - clusterUuid, services.data?.query.timefilter.timefilter, services.http, + clusterUuid, + showSystemIndices, + ccs, ]); return ( @@ -88,7 +103,7 @@ export const ElasticsearchIndicesPage: React.FC = ({ clusters }) = ({ clusters }) => { +export const ElasticsearchNodeAdvancedPage: React.FC = () => { const globalState = useContext(GlobalStateContext); const { zoomInfo, onBrush } = useCharts(); @@ -25,6 +35,7 @@ export const ElasticsearchNodeAdvancedPage: React.FC = ({ cluste const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; const [data, setData] = useState({} as any); + const [alerts, setAlerts] = useState({}); const title = i18n.translate('xpack.monitoring.elasticsearch.node.advanced.title', { defaultMessage: 'Elasticsearch - Nodes - {nodeName} - Advanced', @@ -43,20 +54,42 @@ export const ElasticsearchNodeAdvancedPage: React.FC = ({ cluste const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${clusterUuid}/elasticsearch/nodes/${node}`; - - const response = await services.http?.fetch(url, { - method: 'POST', - body: JSON.stringify({ - ccs, + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + ccs, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + is_advanced: true, + }), + }); + setData(response); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + clusterUuid, + alertTypeIds: [ + RULE_CPU_USAGE, + RULE_THREAD_POOL_SEARCH_REJECTIONS, + RULE_THREAD_POOL_WRITE_REJECTIONS, + RULE_MISSING_MONITORING_DATA, + RULE_DISK_USAGE, + RULE_MEMORY_USAGE, + ], + filters: [ + { + nodeUuid: node, + }, + ], timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - is_advanced: true, - }), - }); - - setData(response); + }); + setAlerts(alertsResponse); + } }, [ccs, clusterUuid, services.data?.query.timefilter.timefilter, services.http, node]); return ( @@ -69,7 +102,7 @@ export const ElasticsearchNodeAdvancedPage: React.FC = ({ cluste > = ({ clusters }) => { +export const ElasticsearchNodePage: React.FC = () => { const globalState = useContext(GlobalStateContext); const { zoomInfo, onBrush } = useCharts(); const [showSystemIndices, setShowSystemIndices] = useLocalStorage( 'showSystemIndices', false ); + const [alerts, setAlerts] = useState({}); const { node }: { node: string } = useParams(); const { services } = useKibana<{ data: any }>(); @@ -54,30 +65,49 @@ export const ElasticsearchNodePage: React.FC = ({ clusters }) => const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${clusterUuid}/elasticsearch/nodes/${node}`; + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + showSystemIndices, + ccs, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + is_advanced: false, + }), + }); - const response = await services.http?.fetch(url, { - method: 'POST', - body: JSON.stringify({ - showSystemIndices, - ccs, + setData(response); + const transformer = nodesByIndices(); + setNodesByIndicesData(transformer(response.shards, response.nodes)); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + alertTypeIds: [ + RULE_CPU_USAGE, + RULE_THREAD_POOL_SEARCH_REJECTIONS, + RULE_THREAD_POOL_WRITE_REJECTIONS, + RULE_MISSING_MONITORING_DATA, + RULE_DISK_USAGE, + RULE_MEMORY_USAGE, + ], + filters: [{ nodeUuid: node }], + clusterUuid, timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - is_advanced: false, - }), - }); - - setData(response); - const transformer = nodesByIndices(); - setNodesByIndicesData(transformer(response.shards, response.nodes)); + }); + setAlerts(alertsResponse); + } }, [ - ccs, - clusterUuid, services.data?.query.timefilter.timefilter, services.http, + clusterUuid, node, showSystemIndices, + ccs, ]); const toggleShowSystemIndices = useCallback(() => { @@ -98,7 +128,7 @@ export const ElasticsearchNodePage: React.FC = ({ clusters }) => {flyoutComponent} = ({ clusters }) => { const globalState = useContext(GlobalStateContext); @@ -32,6 +42,7 @@ export const ElasticsearchNodesPage: React.FC = ({ clusters }) = cluster_uuid: clusterUuid, }) as any; const [data, setData] = useState({} as any); + const [alerts, setAlerts] = useState({}); const title = i18n.translate('xpack.monitoring.elasticsearch.nodes.routeTitle', { defaultMessage: 'Elasticsearch - Nodes', @@ -52,25 +63,44 @@ export const ElasticsearchNodesPage: React.FC = ({ clusters }) = const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${clusterUuid}/elasticsearch/nodes`; - const response = await services.http?.fetch(url, { - method: 'POST', - body: JSON.stringify({ - ccs, + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + ccs, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + ...getPaginationRouteOptions(), + }), + }); + + setData(response); + updateTotalItemCount(response.totalNodeCount); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + clusterUuid, + alertTypeIds: [ + RULE_CPU_USAGE, + RULE_DISK_USAGE, + RULE_THREAD_POOL_SEARCH_REJECTIONS, + RULE_THREAD_POOL_WRITE_REJECTIONS, + RULE_MEMORY_USAGE, + RULE_MISSING_MONITORING_DATA, + ], timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - ...getPaginationRouteOptions(), - }), - }); - - setData(response); - updateTotalItemCount(response.totalNodeCount); + }); + setAlerts(alertsResponse); + } }, [ - ccs, - clusterUuid, services.data?.query.timefilter.timefilter, services.http, + clusterUuid, + ccs, getPaginationRouteOptions, updateTotalItemCount, ]); @@ -94,7 +124,7 @@ export const ElasticsearchNodesPage: React.FC = ({ clusters }) = clusterUuid={globalState.cluster_uuid} setupMode={setupMode} nodes={data.nodes} - alerts={{}} + alerts={alerts} showCgroupMetricsElasticsearch={showCgroupMetricsElasticsearch} {...getPaginationTableProps()} /> diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/overview.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/overview.tsx index 3334c7e7b880..c58aaa5dffb0 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/overview.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/overview.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; import { ElasticsearchTemplate } from './elasticsearch_template'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { ElasticsearchOverview } from '../../../components/elasticsearch'; import { ComponentProps } from '../../route_init'; import { useCharts } from '../../hooks/use_charts'; diff --git a/x-pack/plugins/monitoring/public/application/pages/home/cluster_listing.tsx b/x-pack/plugins/monitoring/public/application/pages/home/cluster_listing.tsx index 906db1b57f0f..a31f2bc317fa 100644 --- a/x-pack/plugins/monitoring/public/application/pages/home/cluster_listing.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/home/cluster_listing.tsx @@ -12,8 +12,8 @@ import { useKibana } from '../../../../../../../src/plugins/kibana_react/public' // @ts-ignore import { Listing } from '../../../components/cluster/listing'; import { EnableAlertsModal } from '../../../alerts/enable_alerts_modal'; -import { GlobalStateContext } from '../../global_state_context'; -import { ExternalConfigContext } from '../../external_config_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; +import { ExternalConfigContext } from '../../contexts/external_config_context'; import { ComponentProps } from '../../route_init'; import { useTable } from '../../hooks/use_table'; import { PageTemplate, TabMenuItem } from '../page_template'; @@ -69,23 +69,19 @@ export const ClusterListing: React.FC = () => { const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); - try { - if (services.http?.fetch) { - const response = await fetchClusters({ - fetch: services.http.fetch, - timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), - }, - ccs: globalState.ccs, - codePaths: ['all'], - }); - setClusters(response); - } - } catch (err) { - // TODO: handle errors + if (services.http?.fetch) { + const response = await fetchClusters({ + fetch: services.http.fetch, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + ccs: globalState.ccs, + codePaths: ['all'], + }); + setClusters(response); } - }, [globalState, services.data?.query.timefilter.timefilter, services.http]); + }, [globalState.ccs, services.data?.query.timefilter.timefilter, services.http]); if (globalState.save && clusters.length === 1) { globalState.cluster_uuid = clusters[0].cluster_uuid; diff --git a/x-pack/plugins/monitoring/public/application/pages/kibana/instance.tsx b/x-pack/plugins/monitoring/public/application/pages/kibana/instance.tsx index 8b88fc47a900..444794d118b0 100644 --- a/x-pack/plugins/monitoring/public/application/pages/kibana/instance.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/kibana/instance.tsx @@ -19,7 +19,7 @@ import { EuiPanel, } from '@elastic/eui'; import { ComponentProps } from '../../route_init'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { useCharts } from '../../hooks/use_charts'; // @ts-ignore @@ -30,6 +30,9 @@ import { MonitoringTimeseriesContainer } from '../../../components/chart'; import { DetailStatus } from '../../../components/kibana/detail_status'; import { PageTemplate } from '../page_template'; import { AlertsCallout } from '../../../alerts/callout'; +import { AlertsByName } from '../../../alerts/types'; +import { fetchAlerts } from '../../../lib/fetch_alerts'; +import { RULE_KIBANA_VERSION_MISMATCH } from '../../../../common/constants'; const KibanaInstance = ({ data, alerts }: { data: any; alerts: any }) => { const { zoomInfo, onBrush } = useCharts(); @@ -112,6 +115,7 @@ export const KibanaInstancePage: React.FC = ({ clusters }) => { }) as any; const [data, setData] = useState({} as any); const [instanceName, setInstanceName] = useState(''); + const [alerts, setAlerts] = useState({}); const title = `Kibana - ${instanceName}`; const pageTitle = i18n.translate('xpack.monitoring.kibana.instance.pageTitle', { @@ -133,19 +137,30 @@ export const KibanaInstancePage: React.FC = ({ clusters }) => { const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${clusterUuid}/kibana/${instance}`; - const response = await services.http?.fetch(url, { - method: 'POST', - body: JSON.stringify({ - ccs, + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + ccs, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + }), + }); + setData(response); + setInstanceName(response.kibanaSummary.name); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + alertTypeIds: [RULE_KIBANA_VERSION_MISMATCH], + clusterUuid, timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - }), - }); - - setData(response); - setInstanceName(response.kibanaSummary.name); + }); + setAlerts(alertsResponse); + } }, [ccs, clusterUuid, instance, services.data?.query.timefilter.timefilter, services.http]); return ( @@ -156,7 +171,7 @@ export const KibanaInstancePage: React.FC = ({ clusters }) => { data-test-subj="kibanaInstancePage" >
- +
); diff --git a/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx index 436a1a72b2fd..ae0237ea4047 100644 --- a/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx @@ -9,7 +9,7 @@ import React, { useContext, useState, useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; import { ComponentProps } from '../../route_init'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { useTable } from '../../hooks/use_table'; import { KibanaTemplate } from './kibana_template'; @@ -19,7 +19,9 @@ import { KibanaInstances } from '../../../components/kibana/instances'; import { SetupModeRenderer, SetupModeProps } from '../../setup_mode/setup_mode_renderer'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; -import { KIBANA_SYSTEM_ID } from '../../../../common/constants'; +import { AlertsByName } from '../../../alerts/types'; +import { fetchAlerts } from '../../../lib/fetch_alerts'; +import { KIBANA_SYSTEM_ID, RULE_KIBANA_VERSION_MISMATCH } from '../../../../common/constants'; export const KibanaInstancesPage: React.FC = ({ clusters }) => { const { cluster_uuid: clusterUuid, ccs } = useContext(GlobalStateContext); @@ -30,6 +32,7 @@ export const KibanaInstancesPage: React.FC = ({ clusters }) => { cluster_uuid: clusterUuid, }) as any; const [data, setData] = useState({} as any); + const [alerts, setAlerts] = useState({}); const title = i18n.translate('xpack.monitoring.kibana.instances.routeTitle', { defaultMessage: 'Kibana - Instances', @@ -50,19 +53,31 @@ export const KibanaInstancesPage: React.FC = ({ clusters }) => { const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${clusterUuid}/kibana/instances`; - const response = await services.http?.fetch(url, { - method: 'POST', - body: JSON.stringify({ - ccs, + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + ccs, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + }), + }); + + setData(response); + updateTotalItemCount(response.kibanas.length); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + alertTypeIds: [RULE_KIBANA_VERSION_MISMATCH], + clusterUuid, timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - }), - }); - - setData(response); - updateTotalItemCount(response.stats.total); + }); + setAlerts(alertsResponse); + } }, [ ccs, clusterUuid, @@ -85,7 +100,7 @@ export const KibanaInstancesPage: React.FC = ({ clusters }) => { {flyoutComponent} = ({ clusters }) => { const globalState = useContext(GlobalStateContext); @@ -42,6 +45,7 @@ export const LogStashNodeAdvancedPage: React.FC = ({ clusters }) }); const [data, setData] = useState({} as any); + const [alerts, setAlerts] = useState({}); const title = i18n.translate('xpack.monitoring.logstash.node.advanced.routeTitle', { defaultMessage: 'Logstash - {nodeName} - Advanced', @@ -60,19 +64,30 @@ export const LogStashNodeAdvancedPage: React.FC = ({ clusters }) const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${clusterUuid}/logstash/node/${match.params.uuid}`; - const response = await services.http?.fetch(url, { - method: 'POST', - body: JSON.stringify({ - ccs, + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + ccs, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + is_advanced: true, + }), + }); + setData(response); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + alertTypeIds: [RULE_LOGSTASH_VERSION_MISMATCH], + clusterUuid, timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - is_advanced: true, - }), - }); - - setData(response); + }); + setAlerts(alertsResponse); + } }, [ ccs, clusterUuid, @@ -105,7 +120,7 @@ export const LogStashNodeAdvancedPage: React.FC = ({ clusters }) {data.nodeSummary && } - + {metricsToShow.map((metric, index) => ( diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/node.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/node.tsx index 301d3c45dedb..1163a619dd84 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/node.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/node.tsx @@ -18,7 +18,7 @@ import { EuiFlexItem, } from '@elastic/eui'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { ComponentProps } from '../../route_init'; // @ts-ignore import { List } from '../../../components/logstash/pipeline_viewer/models/list'; @@ -30,6 +30,9 @@ import { DetailStatus } from '../../../components/logstash/detail_status'; import { MonitoringTimeseriesContainer } from '../../../components/chart'; import { AlertsCallout } from '../../../alerts/callout'; import { useCharts } from '../../hooks/use_charts'; +import { AlertsByName } from '../../../alerts/types'; +import { fetchAlerts } from '../../../lib/fetch_alerts'; +import { RULE_LOGSTASH_VERSION_MISMATCH } from '../../../../common/constants'; export const LogStashNodePage: React.FC = ({ clusters }) => { const match = useRouteMatch<{ uuid: string | undefined }>(); @@ -41,6 +44,7 @@ export const LogStashNodePage: React.FC = ({ clusters }) => { cluster_uuid: clusterUuid, }); const [data, setData] = useState({} as any); + const [alerts, setAlerts] = useState({}); const { zoomInfo, onBrush } = useCharts(); const title = i18n.translate('xpack.monitoring.logstash.node.routeTitle', { defaultMessage: 'Logstash - {nodeName}', @@ -59,19 +63,30 @@ export const LogStashNodePage: React.FC = ({ clusters }) => { const getPageData = useCallback(async () => { const url = `../api/monitoring/v1/clusters/${clusterUuid}/logstash/node/${match.params.uuid}`; const bounds = services.data?.query.timefilter.timefilter.getBounds(); - const response = await services.http?.fetch(url, { - method: 'POST', - body: JSON.stringify({ - ccs, + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + ccs, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + is_advanced: false, + }), + }); + setData(response); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + alertTypeIds: [RULE_LOGSTASH_VERSION_MISMATCH], + clusterUuid, timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - is_advanced: false, - }), - }); - - setData(response); + }); + setAlerts(alertsResponse); + } }, [ccs, clusterUuid, services.data?.query.timefilter.timefilter, services.http, match.params]); const metricsToShow = useMemo(() => { @@ -99,7 +114,7 @@ export const LogStashNodePage: React.FC = ({ clusters }) => { {data.nodeSummary && } - + {metricsToShow.map((metric, index) => ( diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/node_pipelines.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/node_pipelines.tsx index 1c956603f99b..e09850eaad5c 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/node_pipelines.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/node_pipelines.tsx @@ -12,7 +12,7 @@ import { useRouteMatch } from 'react-router-dom'; // @ts-ignore import { isPipelineMonitoringSupportedInVersion } from '../../../lib/logstash/pipelines'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { ComponentProps } from '../../route_init'; // @ts-ignore import { Listing } from '../../../components/logstash/listing'; diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/nodes.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/nodes.tsx index 09a97925c56f..0fd10a93bcd8 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/nodes.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/nodes.tsx @@ -8,7 +8,7 @@ import React, { useContext, useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { ComponentProps } from '../../route_init'; // @ts-ignore import { Listing } from '../../../components/logstash/listing'; @@ -16,7 +16,9 @@ import { LogstashTemplate } from './logstash_template'; import { SetupModeRenderer } from '../../setup_mode/setup_mode_renderer'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { useTable } from '../../hooks/use_table'; -import { LOGSTASH_SYSTEM_ID } from '../../../../common/constants'; +import { LOGSTASH_SYSTEM_ID, RULE_LOGSTASH_VERSION_MISMATCH } from '../../../../common/constants'; +import { AlertsByName } from '../../../alerts/types'; +import { fetchAlerts } from '../../../lib/fetch_alerts'; interface SetupModeProps { setupMode: any; @@ -33,6 +35,7 @@ export const LogStashNodesPage: React.FC = ({ clusters }) => { cluster_uuid: clusterUuid, }); const [data, setData] = useState({} as any); + const [alerts, setAlerts] = useState({}); const { getPaginationTableProps } = useTable('logstash.nodes'); const title = i18n.translate('xpack.monitoring.logstash.nodes.routeTitle', { @@ -46,18 +49,30 @@ export const LogStashNodesPage: React.FC = ({ clusters }) => { const getPageData = useCallback(async () => { const bounds = services.data?.query.timefilter.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${clusterUuid}/logstash/nodes`; - const response = await services.http?.fetch(url, { - method: 'POST', - body: JSON.stringify({ - ccs, + if (services.http?.fetch && clusterUuid) { + const response = await services.http?.fetch(url, { + method: 'POST', + body: JSON.stringify({ + ccs, + timeRange: { + min: bounds.min.toISOString(), + max: bounds.max.toISOString(), + }, + }), + }); + + setData(response); + const alertsResponse = await fetchAlerts({ + fetch: services.http.fetch, + alertTypeIds: [RULE_LOGSTASH_VERSION_MISMATCH], + clusterUuid, timeRange: { - min: bounds.min.toISOString(), - max: bounds.max.toISOString(), + min: bounds.min.valueOf(), + max: bounds.max.valueOf(), }, - }), - }); - - setData(response); + }); + setAlerts(alertsResponse); + } }, [ccs, clusterUuid, services.data?.query.timefilter.timefilter, services.http]); return ( @@ -78,6 +93,7 @@ export const LogStashNodesPage: React.FC = ({ clusters }) => { metrics={data.metrics} data={data.nodes} setupMode={setupMode} + alerts={alerts} {...getPaginationTableProps()} /> {bottomBarComponent} diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/overview.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/overview.tsx index 1edbe5cf71e7..339b9e939556 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/overview.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/overview.tsx @@ -8,7 +8,7 @@ import React, { useContext, useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { ComponentProps } from '../../route_init'; import { useCharts } from '../../hooks/use_charts'; // @ts-ignore diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/pipeline.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/pipeline.tsx index abff0ab17b99..20f1caee2b1d 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/pipeline.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/pipeline.tsx @@ -10,7 +10,7 @@ import { find } from 'lodash'; import moment from 'moment'; import { useRouteMatch } from 'react-router-dom'; import { useKibana, useUiSetting } from '../../../../../../../src/plugins/kibana_react/public'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { ComponentProps } from '../../route_init'; // @ts-ignore import { List } from '../../../components/logstash/pipeline_viewer/models/list'; @@ -24,7 +24,7 @@ import { PipelineState } from '../../../components/logstash/pipeline_viewer/mode import { vertexFactory } from '../../../components/logstash/pipeline_viewer/models/graph/vertex_factory'; import { LogstashTemplate } from './logstash_template'; import { useTable } from '../../hooks/use_table'; -import { ExternalConfigContext } from '../../external_config_context'; +import { ExternalConfigContext } from '../../contexts/external_config_context'; import { formatTimestampToDuration } from '../../../../common'; import { CALCULATE_DURATION_SINCE } from '../../../../common/constants'; import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/pipelines.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/pipelines.tsx index 5f4fe634177d..ac750ff81dda 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/pipelines.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/pipelines.tsx @@ -8,7 +8,7 @@ import React, { useContext, useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; import { useKibana, useUiSetting } from '../../../../../../../src/plugins/kibana_react/public'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; import { ComponentProps } from '../../route_init'; import { useCharts } from '../../hooks/use_charts'; // @ts-ignore diff --git a/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx b/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx index b05bd783b2ff..26072f53f475 100644 --- a/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx @@ -18,7 +18,8 @@ import { Legacy } from '../../../legacy_shims'; import { Enabler } from './enabler'; import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; import { initSetupModeState } from '../../setup_mode/setup_mode'; -import { GlobalStateContext } from '../../global_state_context'; +import { GlobalStateContext } from '../../contexts/global_state_context'; +import { useRequestErrorHandler } from '../../hooks/use_request_error_handler'; const CODE_PATHS = [CODE_PATH_LICENSE]; @@ -77,7 +78,8 @@ export const NoDataPage = () => { ]); const globalState = useContext(GlobalStateContext); - initSetupModeState(globalState, services.http); + const handleRequestError = useRequestErrorHandler(); + initSetupModeState(globalState, services.http, handleRequestError); // From x-pack/plugins/monitoring/public/views/no_data/model_updater.js const updateModel = useCallback( diff --git a/x-pack/plugins/monitoring/public/application/pages/page_template.tsx b/x-pack/plugins/monitoring/public/application/pages/page_template.tsx index 927c46455208..5c030814d9cd 100644 --- a/x-pack/plugins/monitoring/public/application/pages/page_template.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/page_template.tsx @@ -6,8 +6,9 @@ */ import { EuiTab, EuiTabs } from '@elastic/eui'; -import React, { useContext, useState, useEffect } from 'react'; +import React, { useContext, useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; +import { IHttpFetchError } from 'kibana/public'; import { useTitle } from '../hooks/use_title'; import { MonitoringToolbar } from '../../components/shared/toolbar'; import { MonitoringTimeContainer } from '../hooks/use_monitoring_time'; @@ -18,6 +19,9 @@ import { updateSetupModeData, } from '../setup_mode/setup_mode'; import { SetupModeFeature } from '../../../common/enums'; +import { AlertsDropdown } from '../../alerts/alerts_dropdown'; +import { ActionMenu } from '../../components/action_menu'; +import { useRequestErrorHandler } from '../hooks/use_request_error_handler'; export interface TabMenuItem { id: string; @@ -46,34 +50,52 @@ export const PageTemplate: React.FC = ({ const { currentTimerange } = useContext(MonitoringTimeContainer.Context); const [loaded, setLoaded] = useState(false); const history = useHistory(); + const [hasError, setHasError] = useState(false); + const handleRequestError = useRequestErrorHandler(); + + const getPageDataResponseHandler = useCallback( + (result: any) => { + setHasError(false); + return result; + }, + [setHasError] + ); useEffect(() => { getPageData?.() - .catch((err) => { - // TODO: handle errors + .then(getPageDataResponseHandler) + .catch((err: IHttpFetchError) => { + handleRequestError(err); + setHasError(true); }) .finally(() => { setLoaded(true); }); - }, [getPageData, currentTimerange]); + }, [getPageData, currentTimerange, getPageDataResponseHandler, handleRequestError]); const onRefresh = () => { - const requests = [getPageData?.()]; + getPageData?.().then(getPageDataResponseHandler).catch(handleRequestError); + if (isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { - requests.push(updateSetupModeData()); + updateSetupModeData(); } - - Promise.allSettled(requests).then((results) => { - // TODO: handle errors - }); }; const createHref = (route: string) => history.createHref({ pathname: route }); const isTabSelected = (route: string) => history.location.pathname === route; + const renderContent = () => { + if (hasError) return null; + if (getPageData && !loaded) return ; + return children; + }; + return (
+ + + {tabs && ( @@ -93,7 +115,7 @@ export const PageTemplate: React.FC = ({ })} )} -
{!getPageData ? children : loaded ? children : }
+
{renderContent()}
); }; diff --git a/x-pack/plugins/monitoring/public/application/route_init.tsx b/x-pack/plugins/monitoring/public/application/route_init.tsx index 8a9a906dbd56..8a11df3de50a 100644 --- a/x-pack/plugins/monitoring/public/application/route_init.tsx +++ b/x-pack/plugins/monitoring/public/application/route_init.tsx @@ -7,7 +7,7 @@ import React, { useContext } from 'react'; import { Route, Redirect, useLocation } from 'react-router-dom'; import { useClusters } from './hooks/use_clusters'; -import { GlobalStateContext } from './global_state_context'; +import { GlobalStateContext } from './contexts/global_state_context'; import { getClusterFromClusters } from '../lib/get_cluster_from_clusters'; export interface ComponentProps { diff --git a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode.tsx b/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode.tsx index 70932e517733..bfdf96ef5b2c 100644 --- a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode.tsx +++ b/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode.tsx @@ -9,13 +9,13 @@ import React from 'react'; import { render } from 'react-dom'; import { get, includes } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { HttpStart } from 'kibana/public'; +import { HttpStart, IHttpFetchError } from 'kibana/public'; import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { Legacy } from '../../legacy_shims'; import { SetupModeEnterButton } from '../../components/setup_mode/enter_button'; import { SetupModeFeature } from '../../../common/enums'; import { ISetupModeContext } from '../../components/setup_mode/setup_mode_context'; -import { State as GlobalState } from '../../application/global_state_context'; +import { State as GlobalState } from '../contexts/global_state_context'; function isOnPage(hash: string) { return includes(window.location.hash, hash); @@ -23,6 +23,7 @@ function isOnPage(hash: string) { let globalState: GlobalState; let httpService: HttpStart; +let errorHandler: (error: IHttpFetchError) => void; interface ISetupModeState { enabled: boolean; @@ -65,8 +66,8 @@ export const fetchCollectionData = async (uuid?: string, fetchWithoutClusterUuid }); return response; } catch (err) { - // TODO: handle errors - throw new Error(err); + errorHandler(err); + throw err; } }; @@ -122,8 +123,8 @@ export const disableElasticsearchInternalCollection = async () => { const response = await httpService.post(url); return response; } catch (err) { - // TODO: handle errors - throw new Error(err); + errorHandler(err); + throw err; } }; @@ -161,10 +162,12 @@ export const setSetupModeMenuItem = () => { export const initSetupModeState = async ( state: GlobalState, http: HttpStart, + handleErrors: (error: IHttpFetchError) => void, callback?: () => void ) => { globalState = state; httpService = http; + errorHandler = handleErrors; if (callback) { setupModeState.callback = callback; } diff --git a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.js b/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.js index 337dacd4ecae..a9ee2464cd42 100644 --- a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.js +++ b/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.js @@ -27,8 +27,9 @@ import { import { findNewUuid } from '../../components/renderers/lib/find_new_uuid'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { GlobalStateContext } from '../../application/global_state_context'; +import { GlobalStateContext } from '../../application/contexts/global_state_context'; import { withKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { useRequestErrorHandler } from '../hooks/use_request_error_handler'; class WrappedSetupModeRenderer extends React.Component { globalState; @@ -42,8 +43,8 @@ class WrappedSetupModeRenderer extends React.Component { UNSAFE_componentWillMount() { this.globalState = this.context; - const { kibana } = this.props; - initSetupModeState(this.globalState, kibana.services.http, (_oldData) => { + const { kibana, onHttpError } = this.props; + initSetupModeState(this.globalState, kibana.services.http, onHttpError, (_oldData) => { const newState = { renderState: true }; const { productName } = this.props; @@ -213,5 +214,12 @@ class WrappedSetupModeRenderer extends React.Component { } } +function withErrorHandler(Component) { + return function WrappedComponent(props) { + const handleRequestError = useRequestErrorHandler(); + return ; + }; +} + WrappedSetupModeRenderer.contextType = GlobalStateContext; -export const SetupModeRenderer = withKibana(WrappedSetupModeRenderer); +export const SetupModeRenderer = withKibana(withErrorHandler(WrappedSetupModeRenderer)); diff --git a/x-pack/plugins/monitoring/public/components/action_menu/index.tsx b/x-pack/plugins/monitoring/public/components/action_menu/index.tsx new file mode 100644 index 000000000000..1348ac170395 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/action_menu/index.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useContext, useEffect } from 'react'; +import { + KibanaContextProvider, + toMountPoint, + useKibana, +} from '../../../../../../src/plugins/kibana_react/public'; +import { HeaderActionMenuContext } from '../../application/contexts/header_action_menu_context'; + +export const ActionMenu: React.FC<{}> = ({ children }) => { + const { services } = useKibana(); + const { setHeaderActionMenu } = useContext(HeaderActionMenuContext); + useEffect(() => { + if (setHeaderActionMenu) { + setHeaderActionMenu((element) => { + const mount = toMountPoint( + {children} + ); + return mount(element); + }); + return () => { + setHeaderActionMenu(undefined); + }; + } + }, [children, setHeaderActionMenu, services]); + + return null; +}; diff --git a/x-pack/plugins/monitoring/public/components/shared/toolbar.tsx b/x-pack/plugins/monitoring/public/components/shared/toolbar.tsx index 32bbdd6ecbed..6a1ed1dd16f4 100644 --- a/x-pack/plugins/monitoring/public/components/shared/toolbar.tsx +++ b/x-pack/plugins/monitoring/public/components/shared/toolbar.tsx @@ -14,7 +14,7 @@ import { } from '@elastic/eui'; import React, { useContext, useCallback } from 'react'; import { MonitoringTimeContainer } from '../../application/hooks/use_monitoring_time'; -import { GlobalStateContext } from '../../application/global_state_context'; +import { GlobalStateContext } from '../../application/contexts/global_state_context'; import { Legacy } from '../../legacy_shims'; interface MonitoringToolbarProps { diff --git a/x-pack/plugins/monitoring/public/lib/fetch_alerts.ts b/x-pack/plugins/monitoring/public/lib/fetch_alerts.ts new file mode 100644 index 000000000000..c0ce7ed26088 --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/fetch_alerts.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { HttpHandler } from 'kibana/public'; +import { CommonAlertFilter } from '../../common/types/alerts'; +import { AlertsByName } from '../alerts/types'; + +interface FetchAlertsParams { + alertTypeIds?: string[]; + filters?: CommonAlertFilter[]; + timeRange: { min: number; max: number }; + clusterUuid: string; + fetch: HttpHandler; +} + +export const fetchAlerts = async ({ + alertTypeIds, + filters, + timeRange, + clusterUuid, + fetch, +}: FetchAlertsParams): Promise => { + const url = `../api/monitoring/v1/alert/${clusterUuid}/status`; + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify({ + alertTypeIds, + filters, + timeRange, + }), + }); + return response as unknown as AlertsByName; +}; From badc77828ec21960708531e4470952ae2d051040 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Mon, 11 Oct 2021 12:55:06 -0400 Subject: [PATCH 54/74] [Cases][Observability] Do not sync alerts status with case status (#114318) * set sync status according to disable alerts * Adding test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/create/form_context.test.tsx | 23 +++++++++++++++++++ .../public/components/create/form_context.tsx | 10 +++++++- .../cases/public/components/create/index.tsx | 2 ++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/cases/public/components/create/form_context.test.tsx b/x-pack/plugins/cases/public/components/create/form_context.test.tsx index b988f13ee34c..b55542499fbe 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.test.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.test.tsx @@ -239,6 +239,29 @@ describe('Create case', () => { ); }); + it('should set sync alerts to false when the sync setting is passed in as false and alerts are disabled', async () => { + useConnectorsMock.mockReturnValue({ + ...sampleConnectorData, + connectors: connectorsMock, + }); + + const wrapper = mount( + + + + + + + ); + + fillForm(wrapper); + wrapper.find(`[data-test-subj="create-case-submit"]`).first().simulate('click'); + + await waitFor(() => + expect(postCase).toBeCalledWith({ ...sampleData, settings: { syncAlerts: false } }) + ); + }); + it('it should select the default connector set in the configuration', async () => { useCaseConfigureMock.mockImplementation(() => ({ ...useCaseConfigureResponse, diff --git a/x-pack/plugins/cases/public/components/create/form_context.tsx b/x-pack/plugins/cases/public/components/create/form_context.tsx index f59e1822c70b..03d8ec56fb0a 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.tsx @@ -34,6 +34,7 @@ interface Props { children?: JSX.Element | JSX.Element[]; hideConnectorServiceNowSir?: boolean; onSuccess?: (theCase: Case) => Promise; + syncAlertsDefaultValue?: boolean; } export const FormContext: React.FC = ({ @@ -42,6 +43,7 @@ export const FormContext: React.FC = ({ children, hideConnectorServiceNowSir, onSuccess, + syncAlertsDefaultValue = true, }) => { const { connectors, loading: isLoadingConnectors } = useConnectors(); const owner = useOwnerContext(); @@ -51,7 +53,12 @@ export const FormContext: React.FC = ({ const submitCase = useCallback( async ( - { connectorId: dataConnectorId, fields, syncAlerts = true, ...dataWithoutConnectorId }, + { + connectorId: dataConnectorId, + fields, + syncAlerts = syncAlertsDefaultValue, + ...dataWithoutConnectorId + }, isValid ) => { if (isValid) { @@ -94,6 +101,7 @@ export const FormContext: React.FC = ({ onSuccess, postComment, pushCaseToExternalService, + syncAlertsDefaultValue, ] ); diff --git a/x-pack/plugins/cases/public/components/create/index.tsx b/x-pack/plugins/cases/public/components/create/index.tsx index 7f8b8f664529..d3eaba1ea0bc 100644 --- a/x-pack/plugins/cases/public/components/create/index.tsx +++ b/x-pack/plugins/cases/public/components/create/index.tsx @@ -58,6 +58,8 @@ const CreateCaseComponent = ({ caseType={caseType} hideConnectorServiceNowSir={hideConnectorServiceNowSir} onSuccess={onSuccess} + // if we are disabling alerts, then we should not sync alerts + syncAlertsDefaultValue={!disableAlerts} > Date: Mon, 11 Oct 2021 12:30:14 -0500 Subject: [PATCH 55/74] [monitoring] fixup types (#114342) Co-authored-by: spalger Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/application/hooks/use_table.ts | 8 +--- .../setup_mode/setup_mode_renderer.d.ts | 3 +- .../public/components/apm/status_icon.js | 6 +-- .../components/cluster/overview/index.d.ts | 11 ++++- .../elasticsearch/cluster_status/index.d.ts | 9 +++- .../components/elasticsearch/index.d.ts | 12 ------ .../elasticsearch/{index.js => index.ts} | 0 .../indices/{index.js => index.ts} | 0 .../elasticsearch/indices/indices.d.ts | 20 +++++++++ .../ml_job_listing/status_icon.tsx | 10 ++--- .../elasticsearch/ml_jobs/ml_jobs.tsx | 2 +- .../elasticsearch/node/{index.js => index.ts} | 0 .../components/elasticsearch/node/node.d.ts | 20 +++++++++ .../elasticsearch/node/node_react.d.ts | 19 +++++++++ .../node/status_icon.d.ts} | 8 +++- .../elasticsearch/node/status_icon.js | 4 +- .../nodes/{index.js => index.ts} | 0 .../components/elasticsearch/nodes/nodes.d.ts | 15 +++++++ .../overview/{index.js => index.ts} | 0 .../elasticsearch/overview/overview.d.ts | 18 ++++++++ .../transformers/nodes_by_indices.d.ts | 2 +- .../components/elasticsearch/status_icon.js | 4 +- .../public/components/{index.js => index.ts} | 0 .../components/kibana/instances/instances.tsx | 5 +-- .../public/components/kibana/status_icon.js | 6 +-- .../license/{index.js => index.tsx} | 35 ++++++++++++---- .../components/no_data/{index.js => index.ts} | 0 .../{index.d.ts => no_data/no_data.d.ts} | 5 ++- .../page_loading/{index.js => index.tsx} | 12 ++++-- .../{formatting.js => formatting.ts} | 4 +- .../public/components/status_icon/index.js | 28 ------------- .../public/components/status_icon/index.tsx | 42 +++++++++++++++++++ .../summary_status/summary_status.js | 2 +- .../table/{eui_table.js => eui_table.tsx} | 16 +++---- .../table/eui_table_ssp.d.ts} | 8 ++-- .../public/components/table/index.d.ts | 10 ----- .../components/table/{index.js => index.ts} | 0 .../table/{storage.js => storage.ts} | 29 +++++++++---- ...usters.js => get_cluster_from_clusters.ts} | 13 ++++-- 39 files changed, 267 insertions(+), 119 deletions(-) delete mode 100644 x-pack/plugins/monitoring/public/components/elasticsearch/index.d.ts rename x-pack/plugins/monitoring/public/components/elasticsearch/{index.js => index.ts} (100%) rename x-pack/plugins/monitoring/public/components/elasticsearch/indices/{index.js => index.ts} (100%) create mode 100644 x-pack/plugins/monitoring/public/components/elasticsearch/indices/indices.d.ts rename x-pack/plugins/monitoring/public/components/elasticsearch/node/{index.js => index.ts} (100%) create mode 100644 x-pack/plugins/monitoring/public/components/elasticsearch/node/node.d.ts create mode 100644 x-pack/plugins/monitoring/public/components/elasticsearch/node/node_react.d.ts rename x-pack/plugins/monitoring/public/components/{status_icon/index.d.ts => elasticsearch/node/status_icon.d.ts} (56%) rename x-pack/plugins/monitoring/public/components/elasticsearch/nodes/{index.js => index.ts} (100%) create mode 100644 x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.d.ts rename x-pack/plugins/monitoring/public/components/elasticsearch/overview/{index.js => index.ts} (100%) create mode 100644 x-pack/plugins/monitoring/public/components/elasticsearch/overview/overview.d.ts rename x-pack/plugins/monitoring/public/components/{index.js => index.ts} (100%) rename x-pack/plugins/monitoring/public/components/license/{index.js => index.tsx} (86%) rename x-pack/plugins/monitoring/public/components/no_data/{index.js => index.ts} (100%) rename x-pack/plugins/monitoring/public/components/{index.d.ts => no_data/no_data.d.ts} (71%) rename x-pack/plugins/monitoring/public/components/page_loading/{index.js => index.tsx} (89%) rename x-pack/plugins/monitoring/public/components/setup_mode/{formatting.js => formatting.ts} (93%) delete mode 100644 x-pack/plugins/monitoring/public/components/status_icon/index.js create mode 100644 x-pack/plugins/monitoring/public/components/status_icon/index.tsx rename x-pack/plugins/monitoring/public/components/table/{eui_table.js => eui_table.tsx} (88%) rename x-pack/plugins/monitoring/public/{lib/get_cluster_from_clusters.d.ts => components/table/eui_table_ssp.d.ts} (68%) delete mode 100644 x-pack/plugins/monitoring/public/components/table/index.d.ts rename x-pack/plugins/monitoring/public/components/table/{index.js => index.ts} (100%) rename x-pack/plugins/monitoring/public/components/table/{storage.js => storage.ts} (71%) rename x-pack/plugins/monitoring/public/lib/{get_cluster_from_clusters.js => get_cluster_from_clusters.ts} (74%) diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_table.ts b/x-pack/plugins/monitoring/public/application/hooks/use_table.ts index 2e6018ec8980..45d1f717f5d4 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_table.ts +++ b/x-pack/plugins/monitoring/public/application/hooks/use_table.ts @@ -6,6 +6,7 @@ */ import { useState, useCallback } from 'react'; +import { EuiTableSortingType } from '@elastic/eui'; import { euiTableStorageGetter, euiTableStorageSetter } from '../../components/table'; import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; @@ -23,12 +24,7 @@ interface Page { index: number; } -interface Sorting { - sort: { - field: string; - direction: string; - }; -} +type Sorting = EuiTableSortingType; const PAGE_SIZE_OPTIONS = [5, 10, 20, 50]; diff --git a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.d.ts b/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.d.ts index 48e8ee13059c..c0eda496a09b 100644 --- a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.d.ts +++ b/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.d.ts @@ -5,8 +5,9 @@ * 2.0. */ -export const SetupModeRenderer: FunctionComponent; +import { FunctionComponent } from 'react'; +export const SetupModeRenderer: FunctionComponent>; export interface SetupModeProps { setupMode: any; flyoutComponent: any; diff --git a/x-pack/plugins/monitoring/public/components/apm/status_icon.js b/x-pack/plugins/monitoring/public/components/apm/status_icon.js index f27bcefc20bc..14a51313e4aa 100644 --- a/x-pack/plugins/monitoring/public/components/apm/status_icon.js +++ b/x-pack/plugins/monitoring/public/components/apm/status_icon.js @@ -6,17 +6,17 @@ */ import React from 'react'; -import { StatusIcon } from '../../components/status_icon'; +import { StatusIcon, STATUS_ICON_TYPES } from '../../components/status_icon'; import { i18n } from '@kbn/i18n'; export function ApmStatusIcon({ status, availability = true }) { const type = (() => { if (!availability) { - return StatusIcon.TYPES.GRAY; + return STATUS_ICON_TYPES.GRAY; } const statusKey = status.toUpperCase(); - return StatusIcon.TYPES[statusKey] || StatusIcon.TYPES.YELLOW; + return STATUS_ICON_TYPES[statusKey] || STATUS_ICON_TYPES.YELLOW; })(); return ( diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/index.d.ts b/x-pack/plugins/monitoring/public/components/cluster/overview/index.d.ts index 2cfd37e8e27e..3dc7121446a7 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/index.d.ts +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/index.d.ts @@ -5,4 +5,13 @@ * 2.0. */ -export const Overview: FunctionComponent; +import { FunctionComponent } from 'react'; + +export const Overview: FunctionComponent; + +export interface OverviewProps { + cluster: unknown; + setupMode: unknown; + showLicenseExpiration: boolean; + alerts: unknown; +} diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/cluster_status/index.d.ts b/x-pack/plugins/monitoring/public/components/elasticsearch/cluster_status/index.d.ts index b7196d25d179..4f314101ed29 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/cluster_status/index.d.ts +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/cluster_status/index.d.ts @@ -5,4 +5,11 @@ * 2.0. */ -export const ClusterStatus: FunctionComponent; +import { FunctionComponent } from 'react'; + +export const ClusterStatus: FunctionComponent; + +export interface ClusterStatusProps { + stats: unknown; + alerts?: unknown; +} diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/index.d.ts b/x-pack/plugins/monitoring/public/components/elasticsearch/index.d.ts deleted file mode 100644 index 09f6c1085cfa..000000000000 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/index.d.ts +++ /dev/null @@ -1,12 +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 const ElasticsearchOverview: FunctionComponent; -export const ElasticsearchNodes: FunctionComponent; -export const ElasticsearchIndices: FunctionComponent; -export const ElasticsearchMLJobs: FunctionComponent; -export const NodeReact: FunctionComponent; diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/index.ts similarity index 100% rename from x-pack/plugins/monitoring/public/components/elasticsearch/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/index.ts diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/indices/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/indices/index.ts similarity index 100% rename from x-pack/plugins/monitoring/public/components/elasticsearch/indices/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/indices/index.ts diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/indices/indices.d.ts b/x-pack/plugins/monitoring/public/components/elasticsearch/indices/indices.d.ts new file mode 100644 index 000000000000..2b8ea60b651a --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/indices/indices.d.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FunctionComponent } from 'react'; + +export const ElasticsearchIndices: FunctionComponent; +export interface ElasticsearchIndicesProps { + clusterStatus: unknown; + indices: unknown; + sorting: unknown; + pagination: unknown; + onTableChange: unknown; + toggleShowSystemIndices: unknown; + showSystemIndices: unknown; + alerts: unknown; +} diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/ml_job_listing/status_icon.tsx b/x-pack/plugins/monitoring/public/components/elasticsearch/ml_job_listing/status_icon.tsx index d5c65aecdec2..a45c8316d1aa 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/ml_job_listing/status_icon.tsx +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/ml_job_listing/status_icon.tsx @@ -7,22 +7,22 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { StatusIcon } from '../../status_icon'; +import { StatusIcon, STATUS_ICON_TYPES } from '../../status_icon'; export function MachineLearningJobStatusIcon({ status }: { status: string }) { const type = (() => { const statusKey = status.toUpperCase(); if (statusKey === 'OPENED') { - return StatusIcon.TYPES.GREEN; + return STATUS_ICON_TYPES.GREEN; } else if (statusKey === 'CLOSED') { - return StatusIcon.TYPES.GRAY; + return STATUS_ICON_TYPES.GRAY; } else if (statusKey === 'FAILED') { - return StatusIcon.TYPES.RED; + return STATUS_ICON_TYPES.RED; } // basically a "changing" state like OPENING or CLOSING - return StatusIcon.TYPES.YELLOW; + return STATUS_ICON_TYPES.YELLOW; })(); return ( diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/ml_jobs/ml_jobs.tsx b/x-pack/plugins/monitoring/public/components/elasticsearch/ml_jobs/ml_jobs.tsx index 635f9ecd1e10..dba9c40fabb2 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/ml_jobs/ml_jobs.tsx +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/ml_jobs/ml_jobs.tsx @@ -31,7 +31,7 @@ import { ClusterStatus } from '../cluster_status'; interface Props { clusterStatus: boolean; jobs: MLJobs; - onTableChange: () => void; + onTableChange: (props: any) => void; sorting: EuiTableSortingType; pagination: Pagination; } diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/node/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/node/index.ts similarity index 100% rename from x-pack/plugins/monitoring/public/components/elasticsearch/node/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/node/index.ts diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/node/node.d.ts b/x-pack/plugins/monitoring/public/components/elasticsearch/node/node.d.ts new file mode 100644 index 000000000000..9d7a062e942b --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/node/node.d.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FunctionComponent } from 'react'; + +export const Node: FunctionComponent; +export interface NodeProps { + nodeSummary: unknown; + metrics: unknown; + logs: unknown; + alerts: unknown; + nodeId: unknown; + clusterUuid: unknown; + scope: unknown; + [key: string]: any; +} diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/node/node_react.d.ts b/x-pack/plugins/monitoring/public/components/elasticsearch/node/node_react.d.ts new file mode 100644 index 000000000000..e0c4f6b301fd --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/node/node_react.d.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FunctionComponent } from 'react'; + +export const NodeReact: FunctionComponent; +export interface NodeReactProps { + nodeSummary: unknown; + metrics: unknown; + logs: unknown; + alerts: unknown; + nodeId: unknown; + clusterUuid: unknown; + [key: string]: any; +} diff --git a/x-pack/plugins/monitoring/public/components/status_icon/index.d.ts b/x-pack/plugins/monitoring/public/components/elasticsearch/node/status_icon.d.ts similarity index 56% rename from x-pack/plugins/monitoring/public/components/status_icon/index.d.ts rename to x-pack/plugins/monitoring/public/components/elasticsearch/node/status_icon.d.ts index 147c2821e3a2..dfa07524619c 100644 --- a/x-pack/plugins/monitoring/public/components/status_icon/index.d.ts +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/node/status_icon.d.ts @@ -5,4 +5,10 @@ * 2.0. */ -export const StatusIcon: FunctionComponent; +import { FunctionComponent } from 'react'; + +export const NodeStatusIcon: FunctionComponent; +export interface NodeStatusIconProps { + isOnline: boolean; + status: string; +} diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/node/status_icon.js b/x-pack/plugins/monitoring/public/components/elasticsearch/node/status_icon.js index 7bfffc7b7395..9905a6c3573f 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/node/status_icon.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/node/status_icon.js @@ -6,11 +6,11 @@ */ import React from 'react'; -import { StatusIcon } from '../../status_icon'; +import { StatusIcon, STATUS_ICON_TYPES } from '../../status_icon'; import { i18n } from '@kbn/i18n'; export function NodeStatusIcon({ isOnline, status }) { - const type = isOnline ? StatusIcon.TYPES.GREEN : StatusIcon.TYPES.GRAY; + const type = isOnline ? STATUS_ICON_TYPES.GREEN : STATUS_ICON_TYPES.GRAY; return ( ; +export interface ElasticsearchNodesProps { + clusterStatus: unknown; + showCgroupMetricsElasticsearch: unknown; + [key: string]: any; +} diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/overview/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/overview/index.ts similarity index 100% rename from x-pack/plugins/monitoring/public/components/elasticsearch/overview/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/overview/index.ts diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/overview/overview.d.ts b/x-pack/plugins/monitoring/public/components/elasticsearch/overview/overview.d.ts new file mode 100644 index 000000000000..d4c893f87cbd --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/overview/overview.d.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FunctionComponent } from 'react'; + +export const ElasticsearchOverview: FunctionComponent; +export interface ElasticsearchOverviewProps { + clusterStatus: unknown; + metrics: unknown; + logs: unknown; + cluster: unknown; + shardActivity: unknown; + [key: string]: any; +} diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.d.ts b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.d.ts index d0ec9b85edae..c430c0ee7b48 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.d.ts +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.d.ts @@ -5,4 +5,4 @@ * 2.0. */ -export const nodesByIndices: () => (shards, nodes) => any; +export const nodesByIndices: () => (shards: any, nodes: any) => any; diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/status_icon.js b/x-pack/plugins/monitoring/public/components/elasticsearch/status_icon.js index 7c51a1e89d91..ec027d71a192 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/status_icon.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/status_icon.js @@ -6,13 +6,13 @@ */ import React from 'react'; -import { StatusIcon } from '../status_icon'; +import { StatusIcon, STATUS_ICON_TYPES } from '../status_icon'; import { i18n } from '@kbn/i18n'; export function ElasticsearchStatusIcon({ status }) { const type = (() => { const statusKey = status.toUpperCase(); - return StatusIcon.TYPES[statusKey] || StatusIcon.TYPES.GRAY; + return STATUS_ICON_TYPES[statusKey] || STATUS_ICON_TYPES.GRAY; })(); return ( diff --git a/x-pack/plugins/monitoring/public/components/index.js b/x-pack/plugins/monitoring/public/components/index.ts similarity index 100% rename from x-pack/plugins/monitoring/public/components/index.js rename to x-pack/plugins/monitoring/public/components/index.ts diff --git a/x-pack/plugins/monitoring/public/components/kibana/instances/instances.tsx b/x-pack/plugins/monitoring/public/components/kibana/instances/instances.tsx index 4e939682b1db..3766a09f91b8 100644 --- a/x-pack/plugins/monitoring/public/components/kibana/instances/instances.tsx +++ b/x-pack/plugins/monitoring/public/components/kibana/instances/instances.tsx @@ -25,8 +25,7 @@ import { capitalize, get } from 'lodash'; import { ClusterStatus } from '../cluster_status'; // @ts-ignore import { EuiMonitoringTable } from '../../table'; -// @ts-ignore -import { StatusIcon } from '../../status_icon'; +import { STATUS_ICON_TYPES } from '../../status_icon'; // @ts-ignore import { formatMetric, formatNumber } from '../../../lib/format_number'; import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; @@ -205,7 +204,7 @@ export const KibanaInstances: React.FC = (props: Props) => { _instances.push({ kibana: { ...(instance as any).instance.kibana, - status: StatusIcon.TYPES.GRAY, + status: STATUS_ICON_TYPES.GRAY, }, }); } diff --git a/x-pack/plugins/monitoring/public/components/kibana/status_icon.js b/x-pack/plugins/monitoring/public/components/kibana/status_icon.js index e5b501b1e15e..976b3ff992e3 100644 --- a/x-pack/plugins/monitoring/public/components/kibana/status_icon.js +++ b/x-pack/plugins/monitoring/public/components/kibana/status_icon.js @@ -6,17 +6,17 @@ */ import React from 'react'; -import { StatusIcon } from '../status_icon'; +import { StatusIcon, STATUS_ICON_TYPES } from '../status_icon'; import { i18n } from '@kbn/i18n'; export function KibanaStatusIcon({ status, availability = true }) { const type = (() => { if (!availability) { - return StatusIcon.TYPES.GRAY; + return STATUS_ICON_TYPES.GRAY; } const statusKey = status.toUpperCase(); - return StatusIcon.TYPES[statusKey] || StatusIcon.TYPES.YELLOW; + return STATUS_ICON_TYPES[statusKey] || STATUS_ICON_TYPES.YELLOW; })(); return ( diff --git a/x-pack/plugins/monitoring/public/components/license/index.js b/x-pack/plugins/monitoring/public/components/license/index.tsx similarity index 86% rename from x-pack/plugins/monitoring/public/components/license/index.js rename to x-pack/plugins/monitoring/public/components/license/index.tsx index ad16663c88ea..766f0af3bccc 100644 --- a/x-pack/plugins/monitoring/public/components/license/index.js +++ b/x-pack/plugins/monitoring/public/components/license/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { Fragment } from 'react'; +import React, { Fragment, FunctionComponent } from 'react'; import { EuiPage, EuiPageBody, @@ -27,7 +27,10 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { Legacy } from '../../legacy_shims'; -export const AddLicense = ({ uploadPath }) => { +interface AddLicenseProps { + uploadPath?: string; +} +const AddLicense: FunctionComponent = ({ uploadPath }) => { return ( { ); }; -export class LicenseStatus extends React.PureComponent { +export interface LicenseStatusProps { + isExpired: boolean; + status: string; + type: string; + expiryDate: string | Date; +} + +class LicenseStatus extends React.PureComponent { render() { const { isExpired, status, type, expiryDate } = this.props; const typeTitleCase = type.charAt(0).toUpperCase() + type.substr(1).toLowerCase(); @@ -133,7 +143,15 @@ export class LicenseStatus extends React.PureComponent { } } -const LicenseUpdateInfoForPrimary = ({ isPrimaryCluster, uploadLicensePath }) => { +export interface LicenseUpdateInfoProps { + isPrimaryCluster: boolean; + uploadLicensePath?: string; +} + +const LicenseUpdateInfoForPrimary: FunctionComponent = ({ + isPrimaryCluster, + uploadLicensePath, +}) => { if (!isPrimaryCluster) { return null; } @@ -142,7 +160,9 @@ const LicenseUpdateInfoForPrimary = ({ isPrimaryCluster, uploadLicensePath }) => return ; }; -const LicenseUpdateInfoForRemote = ({ isPrimaryCluster }) => { +const LicenseUpdateInfoForRemote: FunctionComponent = ({ + isPrimaryCluster, +}) => { if (isPrimaryCluster) { return null; } @@ -168,7 +188,8 @@ const LicenseUpdateInfoForRemote = ({ isPrimaryCluster }) => { ); }; -export function License(props) { +export interface LicenseProps extends LicenseStatusProps, LicenseUpdateInfoProps {} +export const License: FunctionComponent = (props) => { const { status, type, isExpired, expiryDate } = props; const licenseManagement = `${Legacy.shims.getBasePath()}/app/management/stack/license_management`; return ( @@ -199,4 +220,4 @@ export function License(props) {
); -} +}; diff --git a/x-pack/plugins/monitoring/public/components/no_data/index.js b/x-pack/plugins/monitoring/public/components/no_data/index.ts similarity index 100% rename from x-pack/plugins/monitoring/public/components/no_data/index.js rename to x-pack/plugins/monitoring/public/components/no_data/index.ts diff --git a/x-pack/plugins/monitoring/public/components/index.d.ts b/x-pack/plugins/monitoring/public/components/no_data/no_data.d.ts similarity index 71% rename from x-pack/plugins/monitoring/public/components/index.d.ts rename to x-pack/plugins/monitoring/public/components/no_data/no_data.d.ts index fc1a81cc4dba..b87d326e834a 100644 --- a/x-pack/plugins/monitoring/public/components/index.d.ts +++ b/x-pack/plugins/monitoring/public/components/no_data/no_data.d.ts @@ -5,5 +5,6 @@ * 2.0. */ -export const PageLoading: FunctionComponent; -export const License: FunctionComponent; +import { FunctionComponent } from 'react'; + +export const NoData: FunctionComponent>; diff --git a/x-pack/plugins/monitoring/public/components/page_loading/index.js b/x-pack/plugins/monitoring/public/components/page_loading/index.tsx similarity index 89% rename from x-pack/plugins/monitoring/public/components/page_loading/index.js rename to x-pack/plugins/monitoring/public/components/page_loading/index.tsx index fd4aa9d84815..e7535fc3dc85 100644 --- a/x-pack/plugins/monitoring/public/components/page_loading/index.js +++ b/x-pack/plugins/monitoring/public/components/page_loading/index.tsx @@ -48,17 +48,21 @@ function PageLoadingUI() { ); } -function PageLoadingTracking({ pageViewTitle }) { +const PageLoadingTracking: React.FunctionComponent<{ pageViewTitle: string }> = ({ + pageViewTitle, +}) => { const path = pageViewTitle.toLowerCase().replace(/-/g, '').replace(/\s+/g, '_'); useTrackPageview({ app: 'stack_monitoring', path }); useTrackPageview({ app: 'stack_monitoring', path, delay: 15000 }); return ; -} +}; -export function PageLoading({ pageViewTitle }) { +export const PageLoading: React.FunctionComponent<{ pageViewTitle?: string }> = ({ + pageViewTitle, +}) => { if (pageViewTitle) { return ; } return ; -} +}; diff --git a/x-pack/plugins/monitoring/public/components/setup_mode/formatting.js b/x-pack/plugins/monitoring/public/components/setup_mode/formatting.ts similarity index 93% rename from x-pack/plugins/monitoring/public/components/setup_mode/formatting.js rename to x-pack/plugins/monitoring/public/components/setup_mode/formatting.ts index 11e8ca48719f..06eb029fcc4a 100644 --- a/x-pack/plugins/monitoring/public/components/setup_mode/formatting.js +++ b/x-pack/plugins/monitoring/public/components/setup_mode/formatting.ts @@ -34,7 +34,7 @@ const SERVER_IDENTIFIER_PLURAL = i18n.translate('xpack.monitoring.setupMode.serv defaultMessage: `servers`, }); -export function formatProductName(productName) { +export function formatProductName(productName: string) { if (productName === APM_SYSTEM_ID) { return productName.toUpperCase(); } @@ -43,7 +43,7 @@ export function formatProductName(productName) { const PRODUCTS_THAT_USE_NODES = [LOGSTASH_SYSTEM_ID, ELASTICSEARCH_SYSTEM_ID]; const PRODUCTS_THAT_USE_INSTANCES = [KIBANA_SYSTEM_ID, BEATS_SYSTEM_ID]; -export function getIdentifier(productName, usePlural = false) { +export function getIdentifier(productName: string, usePlural = false) { if (PRODUCTS_THAT_USE_INSTANCES.includes(productName)) { return usePlural ? INSTANCE_IDENTIFIER_PLURAL : INSTANCE_IDENTIFIER_SINGULAR; } diff --git a/x-pack/plugins/monitoring/public/components/status_icon/index.js b/x-pack/plugins/monitoring/public/components/status_icon/index.js deleted file mode 100644 index bcd4b58d6912..000000000000 --- a/x-pack/plugins/monitoring/public/components/status_icon/index.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiIcon } from '@elastic/eui'; - -export function StatusIcon({ type, label }) { - const typeToIconMap = { - [StatusIcon.TYPES.RED]: 'danger', - [StatusIcon.TYPES.YELLOW]: 'warning', - [StatusIcon.TYPES.GREEN]: 'success', - [StatusIcon.TYPES.GRAY]: 'subdued', - }; - const icon = typeToIconMap[type]; - - return ; -} - -StatusIcon.TYPES = { - RED: 'RED', - YELLOW: 'YELLOW', - GREEN: 'GREEN', - GRAY: 'GRAY', -}; diff --git a/x-pack/plugins/monitoring/public/components/status_icon/index.tsx b/x-pack/plugins/monitoring/public/components/status_icon/index.tsx new file mode 100644 index 000000000000..59c87866d57d --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/status_icon/index.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiIcon } from '@elastic/eui'; + +export const STATUS_ICON_TYPES = { + RED: 'RED' as const, + YELLOW: 'YELLOW' as const, + GREEN: 'GREEN' as const, + GRAY: 'GRAY' as const, +}; + +const typeToIconMap = { + [STATUS_ICON_TYPES.RED]: 'danger', + [STATUS_ICON_TYPES.YELLOW]: 'warning', + [STATUS_ICON_TYPES.GREEN]: 'success', + [STATUS_ICON_TYPES.GRAY]: 'subdued', +}; + +export interface StatusIconProps { + type: keyof typeof STATUS_ICON_TYPES; + label: string; +} +export const StatusIcon: React.FunctionComponent = ({ type, label }) => { + const icon = typeToIconMap[type]; + + return ( + + ); +}; diff --git a/x-pack/plugins/monitoring/public/components/summary_status/summary_status.js b/x-pack/plugins/monitoring/public/components/summary_status/summary_status.js index 71fcf4e193f2..db4ac9098532 100644 --- a/x-pack/plugins/monitoring/public/components/summary_status/summary_status.js +++ b/x-pack/plugins/monitoring/public/components/summary_status/summary_status.js @@ -9,7 +9,7 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { isEmpty, capitalize } from 'lodash'; import { EuiFlexGroup, EuiFlexItem, EuiStat } from '@elastic/eui'; -import { StatusIcon } from '../status_icon/index.js'; +import { StatusIcon } from '../status_icon'; import { AlertsStatus } from '../../alerts/status'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; diff --git a/x-pack/plugins/monitoring/public/components/table/eui_table.js b/x-pack/plugins/monitoring/public/components/table/eui_table.tsx similarity index 88% rename from x-pack/plugins/monitoring/public/components/table/eui_table.js rename to x-pack/plugins/monitoring/public/components/table/eui_table.tsx index a702fdc03357..a383fcf1cd66 100644 --- a/x-pack/plugins/monitoring/public/components/table/eui_table.js +++ b/x-pack/plugins/monitoring/public/components/table/eui_table.tsx @@ -5,21 +5,21 @@ * 2.0. */ -import React, { Fragment } from 'react'; +import React, { Fragment, FunctionComponent } from 'react'; import { EuiInMemoryTable, EuiButton, EuiSpacer, EuiSearchBar } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { getIdentifier } from '../setup_mode/formatting'; import { isSetupModeFeatureEnabled } from '../../lib/setup_mode'; import { SetupModeFeature } from '../../../common/enums'; -export function EuiMonitoringTable({ +export const EuiMonitoringTable: FunctionComponent> = ({ rows: items, search = {}, columns: _columns, setupMode, productName, ...props -}) { +}) => { const [hasItems, setHasItem] = React.useState(items.length > 0); if (search.box && !search.box['data-test-subj']) { @@ -32,15 +32,17 @@ export function EuiMonitoringTable({ if (search) { const oldOnChange = search.onChange; - search.onChange = (arg) => { + search.onChange = (arg: any) => { const filteredItems = EuiSearchBar.Query.execute(arg.query, items, props.executeQueryOptions); setHasItem(filteredItems.length > 0); - oldOnChange && oldOnChange(arg); + if (oldOnChange) { + oldOnChange(arg); + } return true; }; } - const columns = _columns.map((column) => { + const columns = _columns.map((column: any) => { if (!('sortable' in column)) { column.sortable = true; } @@ -78,4 +80,4 @@ export function EuiMonitoringTable({ {footerContent}

); -} +}; diff --git a/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.d.ts b/x-pack/plugins/monitoring/public/components/table/eui_table_ssp.d.ts similarity index 68% rename from x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.d.ts rename to x-pack/plugins/monitoring/public/components/table/eui_table_ssp.d.ts index 5a310c977efa..bdc8199b3c57 100644 --- a/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.d.ts +++ b/x-pack/plugins/monitoring/public/components/table/eui_table_ssp.d.ts @@ -5,8 +5,6 @@ * 2.0. */ -export const getClusterFromClusters: ( - clusters: any, - globalState: State, - unsetGlobalState: boolean -) => any; +import { FunctionComponent } from 'react'; + +export const EuiMonitoringSSPTable: FunctionComponent>; diff --git a/x-pack/plugins/monitoring/public/components/table/index.d.ts b/x-pack/plugins/monitoring/public/components/table/index.d.ts deleted file mode 100644 index 23406ba9e3a5..000000000000 --- a/x-pack/plugins/monitoring/public/components/table/index.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const euiTableStorageGetter: (string) => any; -export const euiTableStorageSetter: (string) => any; -export const EuiMonitoringTable: FunctionComponent; diff --git a/x-pack/plugins/monitoring/public/components/table/index.js b/x-pack/plugins/monitoring/public/components/table/index.ts similarity index 100% rename from x-pack/plugins/monitoring/public/components/table/index.js rename to x-pack/plugins/monitoring/public/components/table/index.ts diff --git a/x-pack/plugins/monitoring/public/components/table/storage.js b/x-pack/plugins/monitoring/public/components/table/storage.ts similarity index 71% rename from x-pack/plugins/monitoring/public/components/table/storage.js rename to x-pack/plugins/monitoring/public/components/table/storage.ts index b9694dc5db42..411bd0987285 100644 --- a/x-pack/plugins/monitoring/public/components/table/storage.js +++ b/x-pack/plugins/monitoring/public/components/table/storage.ts @@ -8,9 +8,22 @@ import { set } from '@elastic/safer-lodash-set'; import { get } from 'lodash'; import { STORAGE_KEY } from '../../../common/constants'; +import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; -export const tableStorageGetter = (keyPrefix) => { - return (storage) => { +interface TableValues { + filterText: any; + pageIndex: any; + sortKey: any; + sortOrder: any; +} + +interface EuiTableValues { + sort: any; + page: any; +} + +export const tableStorageGetter = (keyPrefix: string) => { + return (storage: Storage): TableValues => { const localStorageData = storage.get(STORAGE_KEY) || {}; const filterText = get(localStorageData, [keyPrefix, 'filterText']); const pageIndex = get(localStorageData, [keyPrefix, 'pageIndex']); @@ -21,8 +34,8 @@ export const tableStorageGetter = (keyPrefix) => { }; }; -export const tableStorageSetter = (keyPrefix) => { - return (storage, { filterText, pageIndex, sortKey, sortOrder }) => { +export const tableStorageSetter = (keyPrefix: string) => { + return (storage: Storage, { filterText, pageIndex, sortKey, sortOrder }: TableValues) => { const localStorageData = storage.get(STORAGE_KEY) || {}; set(localStorageData, [keyPrefix, 'filterText'], filterText || undefined); // don`t store empty data @@ -36,8 +49,8 @@ export const tableStorageSetter = (keyPrefix) => { }; }; -export const euiTableStorageGetter = (keyPrefix) => { - return (storage) => { +export const euiTableStorageGetter = (keyPrefix: string) => { + return (storage: Storage): EuiTableValues => { const localStorageData = storage.get(STORAGE_KEY) || {}; const sort = get(localStorageData, [keyPrefix, 'sort']); const page = get(localStorageData, [keyPrefix, 'page']); @@ -46,8 +59,8 @@ export const euiTableStorageGetter = (keyPrefix) => { }; }; -export const euiTableStorageSetter = (keyPrefix) => { - return (storage, { sort, page }) => { +export const euiTableStorageSetter = (keyPrefix: string) => { + return (storage: Storage, { sort, page }: EuiTableValues) => { const localStorageData = storage.get(STORAGE_KEY) || {}; set(localStorageData, [keyPrefix, 'sort'], sort || undefined); // don`t store empty data diff --git a/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.js b/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.ts similarity index 74% rename from x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.js rename to x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.ts index 94bd39aa769f..837f59aaf7c2 100644 --- a/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.js +++ b/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.ts @@ -6,15 +6,20 @@ */ import { find, first } from 'lodash'; +import { State } from '../application/global_state_context'; -export function getClusterFromClusters(clusters, globalState, unsetGlobalState = false) { +export function getClusterFromClusters( + clusters: any, + globalState: State, + unsetGlobalState = false +) { const cluster = (() => { const existingCurrent = find(clusters, { cluster_uuid: globalState.cluster_uuid }); if (existingCurrent) { return existingCurrent; } - const firstCluster = first(clusters); + const firstCluster: any = first(clusters); if (firstCluster && firstCluster.cluster_uuid) { return firstCluster; } @@ -25,7 +30,9 @@ export function getClusterFromClusters(clusters, globalState, unsetGlobalState = if (cluster && cluster.license) { globalState.cluster_uuid = unsetGlobalState ? undefined : cluster.cluster_uuid; globalState.ccs = unsetGlobalState ? undefined : cluster.ccs; - globalState.save(); + if (globalState.save) { + globalState.save(); + } return cluster; } From 50b3602d7f5eb046fe57e68c5718ab9c2169def5 Mon Sep 17 00:00:00 2001 From: spalger Date: Mon, 11 Oct 2021 17:41:49 +0000 Subject: [PATCH 56/74] fix import --- .../plugins/monitoring/public/lib/get_cluster_from_clusters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.ts b/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.ts index 837f59aaf7c2..93d0c5a6f790 100644 --- a/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.ts +++ b/x-pack/plugins/monitoring/public/lib/get_cluster_from_clusters.ts @@ -6,7 +6,7 @@ */ import { find, first } from 'lodash'; -import { State } from '../application/global_state_context'; +import { State } from '../application/contexts/global_state_context'; export function getClusterFromClusters( clusters: any, From 9d498b962c88680c306722d172ad1841c7ddea27 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Mon, 11 Oct 2021 12:55:06 -0500 Subject: [PATCH 57/74] [APM] Disabling apm e2e test (#114544) [skip-ci] APM Cypress tests are again failing on PRs. Disable temporarily. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- vars/tasks.groovy | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 1842e278282b..5a015bddc8fb 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -146,13 +146,14 @@ def functionalXpack(Map params = [:]) { } } - whenChanged([ - 'x-pack/plugins/apm/', - ]) { - if (githubPr.isPr()) { - task(kibanaPipeline.functionalTestProcess('xpack-APMCypress', './test/scripts/jenkins_apm_cypress.sh')) - } - } + //temporarily disable apm e2e test since it's breaking. + // whenChanged([ + // 'x-pack/plugins/apm/', + // ]) { + // if (githubPr.isPr()) { + // task(kibanaPipeline.functionalTestProcess('xpack-APMCypress', './test/scripts/jenkins_apm_cypress.sh')) + // } + // } whenChanged([ 'x-pack/plugins/uptime/', From e32dd1c493b747345b4d1103d14f61898ad5ccfe Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 11 Oct 2021 14:19:28 -0400 Subject: [PATCH 58/74] [Alerting] Allow rule types to specify a default and minimum interval (#113650) * WIP * Remove test defaults * Fix types * Add tests * Add missing files * Fix issue with using default interval after user manually changed it * PR feedback * Fix types * Remove debug --- x-pack/plugins/alerting/README.md | 2 + x-pack/plugins/alerting/common/alert_type.ts | 2 + .../alerting/server/routes/rule_types.test.ts | 6 + .../alerting/server/routes/rule_types.ts | 4 + .../server/rule_type_registry.test.ts | 57 +++- .../alerting/server/rule_type_registry.ts | 44 +++ .../server/rules_client/rules_client.ts | 22 ++ .../server/rules_client/tests/create.test.ts | 26 ++ .../server/rules_client/tests/update.test.ts | 46 +++ x-pack/plugins/alerting/server/types.ts | 2 + .../public/application/constants/index.ts | 2 + .../components/alert_details.tsx | 1 + .../sections/alert_form/alert_add.test.tsx | 24 +- .../sections/alert_form/alert_add.tsx | 57 +++- .../sections/alert_form/alert_edit.test.tsx | 34 ++- .../sections/alert_form/alert_edit.tsx | 33 +- .../sections/alert_form/alert_errors.test.tsx | 284 ++++++++++++++++++ .../sections/alert_form/alert_errors.ts | 132 ++++++++ .../sections/alert_form/alert_form.tsx | 149 +++------ .../alerts_list/components/alerts_list.tsx | 25 +- .../triggers_actions_ui/public/types.ts | 6 +- 21 files changed, 822 insertions(+), 136 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.ts diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index 58d2ca35dea7..343960aee9df 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -122,6 +122,8 @@ The following table describes the properties of the `options` object. |useSavedObjectReferences.extractReferences|(Optional) When developing a rule type, you can choose to implement hooks for extracting saved object references from rule parameters. This hook will be invoked when a rule is created or updated. Implementing this hook is optional, but if an extract hook is implemented, an inject hook must also be implemented.|Function |useSavedObjectReferences.injectReferences|(Optional) When developing a rule type, you can choose to implement hooks for injecting saved object references into rule parameters. This hook will be invoked when a rule is retrieved (get or find). Implementing this hook is optional, but if an inject hook is implemented, an extract hook must also be implemented.|Function |isExportable|Whether the rule type is exportable from the Saved Objects Management UI.|boolean| +|defaultScheduleInterval|The default interval that will show up in the UI when creating a rule of this rule type.|boolean| +|minimumScheduleInterval|The minimum interval that will be allowed for all rules of this rule type.|boolean| ### Executor diff --git a/x-pack/plugins/alerting/common/alert_type.ts b/x-pack/plugins/alerting/common/alert_type.ts index e56034a4c41f..d71540b4418e 100644 --- a/x-pack/plugins/alerting/common/alert_type.ts +++ b/x-pack/plugins/alerting/common/alert_type.ts @@ -21,6 +21,8 @@ export interface AlertType< producer: string; minimumLicenseRequired: LicenseType; isExportable: boolean; + defaultScheduleInterval?: string; + minimumScheduleInterval?: string; } export interface ActionGroup { diff --git a/x-pack/plugins/alerting/server/routes/rule_types.test.ts b/x-pack/plugins/alerting/server/routes/rule_types.test.ts index 2e8f43508a96..e4247c9de6ca 100644 --- a/x-pack/plugins/alerting/server/routes/rule_types.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule_types.test.ts @@ -57,6 +57,8 @@ describe('ruleTypesRoute', () => { }, producer: 'test', enabledInLicense: true, + minimumScheduleInterval: '1m', + defaultScheduleInterval: '10m', } as RegistryAlertTypeWithAuth, ]; const expectedResult: Array> = [ @@ -70,7 +72,9 @@ describe('ruleTypesRoute', () => { }, ], default_action_group_id: 'default', + default_schedule_interval: '10m', minimum_license_required: 'basic', + minimum_schedule_interval: '1m', is_exportable: true, recovery_action_group: RecoveredActionGroup, authorized_consumers: {}, @@ -102,10 +106,12 @@ describe('ruleTypesRoute', () => { }, "authorized_consumers": Object {}, "default_action_group_id": "default", + "default_schedule_interval": "10m", "enabled_in_license": true, "id": "1", "is_exportable": true, "minimum_license_required": "basic", + "minimum_schedule_interval": "1m", "name": "name", "producer": "test", "recovery_action_group": Object { diff --git a/x-pack/plugins/alerting/server/routes/rule_types.ts b/x-pack/plugins/alerting/server/routes/rule_types.ts index 153ae96ff68e..72502b25e9af 100644 --- a/x-pack/plugins/alerting/server/routes/rule_types.ts +++ b/x-pack/plugins/alerting/server/routes/rule_types.ts @@ -22,6 +22,8 @@ const rewriteBodyRes: RewriteResponseCase = (result isExportable, actionVariables, authorizedConsumers, + minimumScheduleInterval, + defaultScheduleInterval, ...rest }) => ({ ...rest, @@ -33,6 +35,8 @@ const rewriteBodyRes: RewriteResponseCase = (result is_exportable: isExportable, action_variables: actionVariables, authorized_consumers: authorizedConsumers, + minimum_schedule_interval: minimumScheduleInterval, + default_schedule_interval: defaultScheduleInterval, }) ); }; diff --git a/x-pack/plugins/alerting/server/rule_type_registry.test.ts b/x-pack/plugins/alerting/server/rule_type_registry.test.ts index 1c44e862c261..beb5f264eb72 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.test.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.test.ts @@ -114,7 +114,7 @@ describe('register()', () => { test('throws if AlertType ruleTaskTimeout is not a valid duration', () => { const alertType: AlertType = { - id: 123 as unknown as string, + id: '123', name: 'Test', actionGroups: [ { @@ -138,6 +138,59 @@ describe('register()', () => { ); }); + test('throws if defaultScheduleInterval isnt valid', () => { + const alertType: AlertType = { + id: '123', + name: 'Test', + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + executor: jest.fn(), + producer: 'alerts', + defaultScheduleInterval: 'foobar', + }; + const registry = new RuleTypeRegistry(ruleTypeRegistryParams); + + expect(() => registry.register(alertType)).toThrowError( + new Error( + `Rule type \"123\" has invalid default interval: string is not a valid duration: foobar.` + ) + ); + }); + + test('throws if minimumScheduleInterval isnt valid', () => { + const alertType: AlertType = { + id: '123', + name: 'Test', + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + executor: jest.fn(), + producer: 'alerts', + minimumScheduleInterval: 'foobar', + }; + const registry = new RuleTypeRegistry(ruleTypeRegistryParams); + + expect(() => registry.register(alertType)).toThrowError( + new Error( + `Rule type \"123\" has invalid minimum interval: string is not a valid duration: foobar.` + ) + ); + }); + test('throws if RuleType action groups contains reserved group id', () => { const alertType: AlertType = { id: 'test', @@ -465,10 +518,12 @@ describe('list()', () => { "state": Array [], }, "defaultActionGroupId": "testActionGroup", + "defaultScheduleInterval": undefined, "enabledInLicense": false, "id": "test", "isExportable": true, "minimumLicenseRequired": "basic", + "minimumScheduleInterval": undefined, "name": "Test", "producer": "alerts", "recoveryActionGroup": Object { diff --git a/x-pack/plugins/alerting/server/rule_type_registry.ts b/x-pack/plugins/alerting/server/rule_type_registry.ts index dc72b644b2c7..db02edf4d19d 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.ts @@ -48,6 +48,8 @@ export interface RegistryRuleType | 'producer' | 'minimumLicenseRequired' | 'isExportable' + | 'minimumScheduleInterval' + | 'defaultScheduleInterval' > { id: string; enabledInLicense: boolean; @@ -188,6 +190,44 @@ export class RuleTypeRegistry { } alertType.actionVariables = normalizedActionVariables(alertType.actionVariables); + // validate defaultScheduleInterval here + if (alertType.defaultScheduleInterval) { + const invalidDefaultTimeout = validateDurationSchema(alertType.defaultScheduleInterval); + if (invalidDefaultTimeout) { + throw new Error( + i18n.translate( + 'xpack.alerting.ruleTypeRegistry.register.invalidDefaultTimeoutAlertTypeError', + { + defaultMessage: 'Rule type "{id}" has invalid default interval: {errorMessage}.', + values: { + id: alertType.id, + errorMessage: invalidDefaultTimeout, + }, + } + ) + ); + } + } + + // validate minimumScheduleInterval here + if (alertType.minimumScheduleInterval) { + const invalidMinimumTimeout = validateDurationSchema(alertType.minimumScheduleInterval); + if (invalidMinimumTimeout) { + throw new Error( + i18n.translate( + 'xpack.alerting.ruleTypeRegistry.register.invalidMinimumTimeoutAlertTypeError', + { + defaultMessage: 'Rule type "{id}" has invalid minimum interval: {errorMessage}.', + values: { + id: alertType.id, + errorMessage: invalidMinimumTimeout, + }, + } + ) + ); + } + } + const normalizedAlertType = augmentActionGroupsWithReserved< Params, ExtractedParams, @@ -287,6 +327,8 @@ export class RuleTypeRegistry { producer, minimumLicenseRequired, isExportable, + minimumScheduleInterval, + defaultScheduleInterval, }, ]: [string, UntypedNormalizedAlertType]) => ({ id, @@ -298,6 +340,8 @@ export class RuleTypeRegistry { producer, minimumLicenseRequired, isExportable, + minimumScheduleInterval, + defaultScheduleInterval, enabledInLicense: !!this.licenseState.getLicenseCheckForAlertType( id, name, diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 231d19ce9a6f..2228b5d27910 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -296,6 +296,17 @@ export class RulesClient { await this.validateActions(ruleType, data.actions); + // Validate intervals, if configured + if (ruleType.minimumScheduleInterval) { + const intervalInMs = parseDuration(data.schedule.interval); + const minimumScheduleIntervalInMs = parseDuration(ruleType.minimumScheduleInterval); + if (intervalInMs < minimumScheduleIntervalInMs) { + throw Boom.badRequest( + `Error updating rule: the interval is less than the minimum interval of ${ruleType.minimumScheduleInterval}` + ); + } + } + // Extract saved object references for this rule const { references, @@ -847,6 +858,17 @@ export class RulesClient { ); await this.validateActions(ruleType, data.actions); + // Validate intervals, if configured + if (ruleType.minimumScheduleInterval) { + const intervalInMs = parseDuration(data.schedule.interval); + const minimumScheduleIntervalInMs = parseDuration(ruleType.minimumScheduleInterval); + if (intervalInMs < minimumScheduleIntervalInMs) { + throw Boom.badRequest( + `Error updating rule: the interval is less than the minimum interval of ${ruleType.minimumScheduleInterval}` + ); + } + } + // Extract saved object references for this rule const { references, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts index 2bb92046db68..fc8f272702e0 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts @@ -2268,4 +2268,30 @@ describe('create()', () => { expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); expect(taskManager.schedule).not.toHaveBeenCalled(); }); + + test('throws error when updating with an interval less than the minimum configured one', async () => { + ruleTypeRegistry.get.mockImplementation(() => ({ + id: '123', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + async executor() {}, + producer: 'alerts', + minimumScheduleInterval: '5m', + useSavedObjectReferences: { + extractReferences: jest.fn(), + injectReferences: jest.fn(), + }, + })); + + const data = getMockData({ schedule: { interval: '1m' } }); + await expect(rulesClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Error updating rule: the interval is less than the minimum interval of 5m"` + ); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + expect(taskManager.schedule).not.toHaveBeenCalled(); + }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts index 1328b666f96e..55ffc49fd339 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts @@ -140,6 +140,7 @@ describe('update()', () => { recoveryActionGroup: RecoveredActionGroup, async executor() {}, producer: 'alerts', + minimumScheduleInterval: '5s', }); }); @@ -1966,4 +1967,49 @@ describe('update()', () => { ); }); }); + + test('throws error when updating with an interval less than the minimum configured one', async () => { + await expect( + rulesClient.update({ + id: '1', + data: { + schedule: { interval: '1s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + { + group: 'default', + id: '2', + params: { + foo: true, + }, + }, + ], + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Error updating rule: the interval is less than the minimum interval of 5s"` + ); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + expect(taskManager.schedule).not.toHaveBeenCalled(); + }); }); diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index c73ce86acf78..1dc8291d2875 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -157,6 +157,8 @@ export interface AlertType< injectReferences: (params: ExtractedParams, references: SavedObjectReference[]) => Params; }; isExportable: boolean; + defaultScheduleInterval?: string; + minimumScheduleInterval?: string; ruleTaskTimeout?: string; } export type UntypedAlertType = AlertType< diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index bed7b09110d8..c69cbcfe8ac0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -36,3 +36,5 @@ export enum SORT_ORDERS { } export const DEFAULT_SEARCH_PAGE_SIZE: number = 10; + +export const DEFAULT_ALERT_INTERVAL = '1m'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 2b13bdf613d9..3b15295cf7a3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -171,6 +171,7 @@ export const AlertDetails: React.FunctionComponent = ({ }} actionTypeRegistry={actionTypeRegistry} ruleTypeRegistry={ruleTypeRegistry} + ruleType={alertType} onSave={setAlert} /> )} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx index 514594ffb855..4ae570a62f7d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx @@ -69,7 +69,8 @@ describe('alert_add', () => { async function setup( initialValues?: Partial, - onClose: AlertAddProps['onClose'] = jest.fn() + onClose: AlertAddProps['onClose'] = jest.fn(), + defaultScheduleInterval?: string ) { const mocks = coreMock.createSetup(); const { loadAlertTypes } = jest.requireMock('../../lib/alert_api'); @@ -84,6 +85,7 @@ describe('alert_add', () => { }, ], defaultActionGroupId: 'testActionGroup', + defaultScheduleInterval, minimumLicenseRequired: 'basic', recoveryActionGroup: { id: 'recovered', name: 'Recovered' }, producer: ALERTS_FEATURE_ID, @@ -243,6 +245,26 @@ describe('alert_add', () => { expect(onClose).toHaveBeenCalledWith(AlertFlyoutCloseReason.SAVED); }); + + it('should enforce any default inteval', async () => { + await setup({ alertTypeId: 'my-alert-type' }, jest.fn(), '3h'); + await delay(1000); + + // Wait for handlers to fire + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + const intervalInputUnit = wrapper + .find('[data-test-subj="intervalInputUnit"]') + .first() + .getElement().props.value; + const intervalInput = wrapper.find('[data-test-subj="intervalInput"]').first().getElement() + .props.value; + expect(intervalInputUnit).toBe('h'); + expect(intervalInput).toBe(3); + }); }); function mockAlert(overloads: Partial = {}): Alert { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx index 5e4ca42523b3..2b376ea0d0b3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx @@ -17,10 +17,12 @@ import { AlertFlyoutCloseReason, IErrorObject, AlertAddProps, + RuleTypeIndex, } from '../../../types'; -import { AlertForm, getAlertActionErrors, getAlertErrors, isValidAlert } from './alert_form'; +import { AlertForm } from './alert_form'; +import { getAlertActionErrors, getAlertErrors, isValidAlert } from './alert_errors'; import { alertReducer, InitialAlert, InitialAlertReducer } from './alert_reducer'; -import { createAlert } from '../../lib/alert_api'; +import { createAlert, loadAlertTypes } from '../../lib/alert_api'; import { HealthCheck } from '../../components/health_check'; import { ConfirmAlertSave } from './confirm_alert_save'; import { ConfirmAlertClose } from './confirm_alert_close'; @@ -30,6 +32,7 @@ import { HealthContextProvider } from '../../context/health_context'; import { useKibana } from '../../../common/lib/kibana'; import { hasAlertChanged, haveAlertParamsChanged } from './has_alert_changed'; import { getAlertWithInvalidatedFields } from '../../lib/value_validators'; +import { DEFAULT_ALERT_INTERVAL } from '../../constants'; const AlertAdd = ({ consumer, @@ -39,26 +42,28 @@ const AlertAdd = ({ canChangeTrigger, alertTypeId, initialValues, + reloadAlerts, onSave, metadata, + ...props }: AlertAddProps) => { const onSaveHandler = onSave ?? reloadAlerts; - const initialAlert: InitialAlert = useMemo( - () => ({ + + const initialAlert: InitialAlert = useMemo(() => { + return { params: {}, consumer, alertTypeId, schedule: { - interval: '1m', + interval: DEFAULT_ALERT_INTERVAL, }, actions: [], tags: [], notifyWhen: 'onActionGroupChange', ...(initialValues ? initialValues : {}), - }), - [alertTypeId, consumer, initialValues] - ); + }; + }, [alertTypeId, consumer, initialValues]); const [{ alert }, dispatch] = useReducer(alertReducer as InitialAlertReducer, { alert: initialAlert, @@ -67,6 +72,10 @@ const AlertAdd = ({ const [isSaving, setIsSaving] = useState(false); const [isConfirmAlertSaveModalOpen, setIsConfirmAlertSaveModalOpen] = useState(false); const [isConfirmAlertCloseModalOpen, setIsConfirmAlertCloseModalOpen] = useState(false); + const [ruleTypeIndex, setRuleTypeIndex] = useState( + props.ruleTypeIndex + ); + const [changedFromDefaultInterval, setChangedFromDefaultInterval] = useState(false); const setAlert = (value: InitialAlert) => { dispatch({ command: { type: 'setAlert' }, payload: { key: 'alert', value } }); @@ -90,6 +99,19 @@ const AlertAdd = ({ } }, [alertTypeId]); + useEffect(() => { + if (!props.ruleTypeIndex) { + (async () => { + const alertTypes = await loadAlertTypes({ http }); + const index: RuleTypeIndex = new Map(); + for (const alertType of alertTypes) { + index.set(alertType.id, alertType); + } + setRuleTypeIndex(index); + })(); + } + }, [props.ruleTypeIndex, http]); + useEffect(() => { if (isEmpty(alert.params) && !isEmpty(initialAlertParams)) { // alert params are explicitly cleared when the alert type is cleared. @@ -115,6 +137,21 @@ const AlertAdd = ({ })(); }, [alert, actionTypeRegistry]); + useEffect(() => { + if (alert.alertTypeId && ruleTypeIndex) { + const type = ruleTypeIndex.get(alert.alertTypeId); + if (type?.defaultScheduleInterval && !changedFromDefaultInterval) { + setAlertProperty('schedule', { interval: type.defaultScheduleInterval }); + } + } + }, [alert.alertTypeId, ruleTypeIndex, alert.schedule.interval, changedFromDefaultInterval]); + + useEffect(() => { + if (alert.schedule.interval !== DEFAULT_ALERT_INTERVAL && !changedFromDefaultInterval) { + setChangedFromDefaultInterval(true); + } + }, [alert.schedule.interval, changedFromDefaultInterval]); + const checkForChangesAndCloseFlyout = () => { if ( hasAlertChanged(alert, initialAlert, false) || @@ -138,9 +175,11 @@ const AlertAdd = ({ }; const alertType = alert.alertTypeId ? ruleTypeRegistry.get(alert.alertTypeId) : null; + const { alertBaseErrors, alertErrors, alertParamsErrors } = getAlertErrors( alert as Alert, - alertType + alertType, + alert.alertTypeId ? ruleTypeIndex?.get(alert.alertTypeId) : undefined ); // Confirm before saving if user is able to add actions but hasn't added any to this alert diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx index 1aea6c68acbf..467f2af6ed70 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx @@ -35,6 +35,23 @@ jest.mock('../../lib/alert_api', () => ({ })), })); +jest.mock('./alert_errors', () => ({ + getAlertActionErrors: jest.fn().mockImplementation(() => { + return []; + }), + getAlertErrors: jest.fn().mockImplementation(() => ({ + alertParamsErrors: {}, + alertBaseErrors: {}, + alertErrors: { + name: new Array(), + interval: new Array(), + alertTypeId: new Array(), + actionConnectors: new Array(), + }, + })), + isValidAlert: jest.fn(), +})); + jest.mock('../../../common/lib/health_api', () => ({ triggersActionsUiHealth: jest.fn(() => ({ isAlertsAvailable: true })), })); @@ -47,7 +64,7 @@ describe('alert_edit', () => { mockedCoreSetup = coreMock.createSetup(); }); - async function setup() { + async function setup(initialAlertFields = {}) { const [ { application: { capabilities }, @@ -154,6 +171,7 @@ describe('alert_edit', () => { status: 'unknown', lastExecutionDate: new Date('2020-08-20T19:23:38Z'), }, + ...initialAlertFields, }; actionTypeRegistry.get.mockReturnValueOnce(actionTypeModel); actionTypeRegistry.has.mockReturnValue(true); @@ -188,7 +206,11 @@ describe('alert_edit', () => { }); it('displays a toast message on save for server errors', async () => { - await setup(); + const { isValidAlert } = jest.requireMock('./alert_errors'); + (isValidAlert as jest.Mock).mockImplementation(() => { + return true; + }); + await setup({ name: undefined }); await act(async () => { wrapper.find('[data-test-subj="saveEditedAlertButton"]').first().simulate('click'); @@ -197,4 +219,12 @@ describe('alert_edit', () => { 'Fail message' ); }); + + it('should pass in the server alert type into `getAlertErrors`', async () => { + const { getAlertErrors } = jest.requireMock('./alert_errors'); + await setup(); + const lastCall = getAlertErrors.mock.calls[getAlertErrors.mock.calls.length - 1]; + expect(lastCall[2]).toBeDefined(); + expect(lastCall[2].id).toBe('my-alert-type'); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index 8b79950d03ac..f8c506fa1e8b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -24,10 +24,17 @@ import { } from '@elastic/eui'; import { cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { Alert, AlertFlyoutCloseReason, AlertEditProps, IErrorObject } from '../../../types'; -import { AlertForm, getAlertActionErrors, getAlertErrors, isValidAlert } from './alert_form'; +import { + Alert, + AlertFlyoutCloseReason, + AlertEditProps, + IErrorObject, + AlertType, +} from '../../../types'; +import { AlertForm } from './alert_form'; +import { getAlertActionErrors, getAlertErrors, isValidAlert } from './alert_errors'; import { alertReducer, ConcreteAlertReducer } from './alert_reducer'; -import { updateAlert } from '../../lib/alert_api'; +import { updateAlert, loadAlertTypes } from '../../lib/alert_api'; import { HealthCheck } from '../../components/health_check'; import { HealthContextProvider } from '../../context/health_context'; import { useKibana } from '../../../common/lib/kibana'; @@ -43,6 +50,7 @@ export const AlertEdit = ({ ruleTypeRegistry, actionTypeRegistry, metadata, + ...props }: AlertEditProps) => { const onSaveHandler = onSave ?? reloadAlerts; const [{ alert }, dispatch] = useReducer(alertReducer as ConcreteAlertReducer, { @@ -55,6 +63,9 @@ export const AlertEdit = ({ const [isConfirmAlertCloseModalOpen, setIsConfirmAlertCloseModalOpen] = useState(false); const [alertActionsErrors, setAlertActionsErrors] = useState([]); const [isLoading, setIsLoading] = useState(false); + const [serverRuleType, setServerRuleType] = useState | undefined>( + props.ruleType + ); const { http, @@ -75,9 +86,23 @@ export const AlertEdit = ({ })(); }, [alert, actionTypeRegistry]); + useEffect(() => { + if (!props.ruleType && !serverRuleType) { + (async () => { + const serverRuleTypes = await loadAlertTypes({ http }); + for (const _serverRuleType of serverRuleTypes) { + if (alertType.id === _serverRuleType.id) { + setServerRuleType(_serverRuleType); + } + } + })(); + } + }, [props.ruleType, alertType.id, serverRuleType, http]); + const { alertBaseErrors, alertErrors, alertParamsErrors } = getAlertErrors( alert as Alert, - alertType + alertType, + serverRuleType ); const checkForChangesAndCloseFlyout = () => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.test.tsx new file mode 100644 index 000000000000..5ca0fd9dafc1 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.test.tsx @@ -0,0 +1,284 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import uuid from 'uuid'; +import React, { Fragment } from 'react'; +import { + validateBaseProperties, + getAlertErrors, + getAlertActionErrors, + hasObjectErrors, + isValidAlert, +} from './alert_errors'; +import { Alert, AlertType, AlertTypeModel } from '../../../types'; +import { actionTypeRegistryMock } from '../../action_type_registry.mock'; + +describe('alert_errors', () => { + describe('validateBaseProperties()', () => { + it('should validate the name', () => { + const alert = mockAlert(); + alert.name = ''; + const result = validateBaseProperties(alert); + expect(result.errors).toStrictEqual({ + name: ['Name is required.'], + interval: [], + alertTypeId: [], + actionConnectors: [], + }); + }); + + it('should validate the interval', () => { + const alert = mockAlert(); + alert.schedule.interval = ''; + const result = validateBaseProperties(alert); + expect(result.errors).toStrictEqual({ + name: [], + interval: ['Check interval is required.'], + alertTypeId: [], + actionConnectors: [], + }); + }); + + it('should validate the minimumScheduleInterval', () => { + const alert = mockAlert(); + alert.schedule.interval = '2m'; + const result = validateBaseProperties( + alert, + mockserverRuleType({ minimumScheduleInterval: '5m' }) + ); + expect(result.errors).toStrictEqual({ + name: [], + interval: ['Interval is below minimum (5m) for this rule type'], + alertTypeId: [], + actionConnectors: [], + }); + }); + + it('should validate the alertTypeId', () => { + const alert = mockAlert(); + alert.alertTypeId = ''; + const result = validateBaseProperties(alert); + expect(result.errors).toStrictEqual({ + name: [], + interval: [], + alertTypeId: ['Rule type is required.'], + actionConnectors: [], + }); + }); + + it('should validate the connectors', () => { + const alert = mockAlert(); + alert.actions = [ + { + id: '1234', + actionTypeId: 'myActionType', + group: '', + params: { + name: 'yes', + }, + }, + ]; + const result = validateBaseProperties(alert); + expect(result.errors).toStrictEqual({ + name: [], + interval: [], + alertTypeId: [], + actionConnectors: ['Action for myActionType connector is required.'], + }); + }); + }); + + describe('getAlertErrors()', () => { + it('should return all errors', () => { + const result = getAlertErrors( + mockAlert({ + name: '', + }), + mockAlertTypeModel({ + validate: () => ({ + errors: { + field: ['This is wrong'], + }, + }), + }), + mockserverRuleType() + ); + expect(result).toStrictEqual({ + alertParamsErrors: { field: ['This is wrong'] }, + alertBaseErrors: { + name: ['Name is required.'], + interval: [], + alertTypeId: [], + actionConnectors: [], + }, + alertErrors: { + name: ['Name is required.'], + field: ['This is wrong'], + interval: [], + alertTypeId: [], + actionConnectors: [], + }, + }); + }); + }); + + describe('getAlertActionErrors()', () => { + it('should return an array of errors', async () => { + const actionTypeRegistry = actionTypeRegistryMock.create(); + actionTypeRegistry.get.mockImplementation((actionTypeId: string) => ({ + ...actionTypeRegistryMock.createMockActionTypeModel(), + validateParams: jest.fn().mockImplementation(() => ({ + errors: { + [actionTypeId]: ['Yes, this failed'], + }, + })), + })); + const result = await getAlertActionErrors( + mockAlert({ + actions: [ + { + id: '1234', + actionTypeId: 'myActionType', + group: '', + params: { + name: 'yes', + }, + }, + { + id: '5678', + actionTypeId: 'myActionType2', + group: '', + params: { + name: 'yes', + }, + }, + ], + }), + actionTypeRegistry + ); + expect(result).toStrictEqual([ + { + myActionType: ['Yes, this failed'], + }, + { + myActionType2: ['Yes, this failed'], + }, + ]); + }); + }); + + describe('hasObjectErrors()', () => { + it('should return true for any errors', () => { + expect( + hasObjectErrors({ + foo: ['1'], + }) + ).toBe(true); + expect( + hasObjectErrors({ + foo: { + foo: ['1'], + }, + }) + ).toBe(true); + }); + it('should return false for no errors', () => { + expect(hasObjectErrors({})).toBe(false); + }); + }); + + describe('isValidAlert()', () => { + it('should return true for a valid alert', () => { + const result = isValidAlert(mockAlert(), {}, []); + expect(result).toBe(true); + }); + it('should return false for an invalid alert', () => { + expect( + isValidAlert( + mockAlert(), + { + name: ['This is wrong'], + }, + [] + ) + ).toBe(false); + expect( + isValidAlert(mockAlert(), {}, [ + { + name: ['This is wrong'], + }, + ]) + ).toBe(false); + }); + }); +}); + +function mockserverRuleType( + overloads: Partial> = {} +): AlertType { + return { + actionGroups: [], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + recoveryActionGroup: { + id: 'recovery', + name: 'doRecovery', + }, + id: 'myAppAlertType', + name: 'myAppAlertType', + producer: 'myApp', + authorizedConsumers: {}, + enabledInLicense: true, + actionVariables: { + context: [], + state: [], + params: [], + }, + ...overloads, + }; +} + +function mockAlertTypeModel(overloads: Partial = {}): AlertTypeModel { + return { + id: 'alertTypeModel', + description: 'some alert', + iconClass: 'something', + documentationUrl: null, + validate: () => ({ errors: {} }), + alertParamsExpression: () => , + requiresAppContext: false, + ...overloads, + }; +} + +function mockAlert(overloads: Partial = {}): Alert { + return { + id: uuid.v4(), + enabled: true, + name: `alert-${uuid.v4()}`, + tags: [], + alertTypeId: '.noop', + consumer: 'consumer', + schedule: { interval: '1m' }, + actions: [], + params: {}, + createdBy: null, + updatedBy: null, + createdAt: new Date(), + updatedAt: new Date(), + apiKeyOwner: null, + throttle: null, + notifyWhen: null, + muteAll: false, + mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, + ...overloads, + }; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.ts new file mode 100644 index 000000000000..3ca6a822b2d2 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_errors.ts @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { isObject } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { parseDuration } from '../../../../../alerting/common/parse_duration'; +import { + AlertTypeModel, + Alert, + IErrorObject, + AlertAction, + AlertType, + ValidationResult, + ActionTypeRegistryContract, +} from '../../../types'; +import { InitialAlert } from './alert_reducer'; + +export function validateBaseProperties( + alertObject: InitialAlert, + serverRuleType?: AlertType +): ValidationResult { + const validationResult = { errors: {} }; + const errors = { + name: new Array(), + interval: new Array(), + alertTypeId: new Array(), + actionConnectors: new Array(), + }; + validationResult.errors = errors; + if (!alertObject.name) { + errors.name.push( + i18n.translate('xpack.triggersActionsUI.sections.alertForm.error.requiredNameText', { + defaultMessage: 'Name is required.', + }) + ); + } + if (alertObject.schedule.interval.length < 2) { + errors.interval.push( + i18n.translate('xpack.triggersActionsUI.sections.alertForm.error.requiredIntervalText', { + defaultMessage: 'Check interval is required.', + }) + ); + } else if (serverRuleType?.minimumScheduleInterval) { + const duration = parseDuration(alertObject.schedule.interval); + const minimumDuration = parseDuration(serverRuleType.minimumScheduleInterval); + if (duration < minimumDuration) { + errors.interval.push( + i18n.translate('xpack.triggersActionsUI.sections.alertForm.error.belowMinimumText', { + defaultMessage: 'Interval is below minimum ({minimum}) for this rule type', + values: { + minimum: serverRuleType.minimumScheduleInterval, + }, + }) + ); + } + } + + if (!alertObject.alertTypeId) { + errors.alertTypeId.push( + i18n.translate('xpack.triggersActionsUI.sections.alertForm.error.requiredRuleTypeIdText', { + defaultMessage: 'Rule type is required.', + }) + ); + } + const emptyConnectorActions = alertObject.actions.find( + (actionItem) => /^\d+$/.test(actionItem.id) && Object.keys(actionItem.params).length > 0 + ); + if (emptyConnectorActions !== undefined) { + errors.actionConnectors.push( + i18n.translate('xpack.triggersActionsUI.sections.alertForm.error.requiredActionConnector', { + defaultMessage: 'Action for {actionTypeId} connector is required.', + values: { actionTypeId: emptyConnectorActions.actionTypeId }, + }) + ); + } + return validationResult; +} + +export function getAlertErrors( + alert: Alert, + alertTypeModel: AlertTypeModel | null, + serverRuleType?: AlertType +) { + const alertParamsErrors: IErrorObject = alertTypeModel + ? alertTypeModel.validate(alert.params).errors + : []; + const alertBaseErrors = validateBaseProperties(alert, serverRuleType).errors as IErrorObject; + const alertErrors = { + ...alertParamsErrors, + ...alertBaseErrors, + } as IErrorObject; + + return { + alertParamsErrors, + alertBaseErrors, + alertErrors, + }; +} + +export async function getAlertActionErrors( + alert: Alert, + actionTypeRegistry: ActionTypeRegistryContract +): Promise { + return await Promise.all( + alert.actions.map( + async (alertAction: AlertAction) => + ( + await actionTypeRegistry.get(alertAction.actionTypeId)?.validateParams(alertAction.params) + ).errors + ) + ); +} + +export const hasObjectErrors: (errors: IErrorObject) => boolean = (errors) => + !!Object.values(errors).find((errorList) => { + if (isObject(errorList)) return hasObjectErrors(errorList as IErrorObject); + return errorList.length >= 1; + }); + +export function isValidAlert( + alertObject: InitialAlert | Alert, + validationResult: IErrorObject, + actionsErrors: IErrorObject[] +): alertObject is Alert { + return ( + !hasObjectErrors(validationResult) && + actionsErrors.every((error: IErrorObject) => !hasObjectErrors(error)) + ); +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx index 5facda85e6c8..3f6bf3c955e8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx @@ -35,7 +35,7 @@ import { EuiToolTip, EuiCallOut, } from '@elastic/eui'; -import { capitalize, isObject } from 'lodash'; +import { capitalize } from 'lodash'; import { KibanaFeature } from '../../../../../features/public'; import { getDurationNumberInItsUnit, @@ -48,9 +48,8 @@ import { Alert, IErrorObject, AlertAction, - AlertTypeIndex, + RuleTypeIndex, AlertType, - ValidationResult, RuleTypeRegistryContract, ActionTypeRegistryContract, } from '../../../types'; @@ -74,101 +73,10 @@ import { checkAlertTypeEnabled } from '../../lib/check_alert_type_enabled'; import { alertTypeCompare, alertTypeGroupCompare } from '../../lib/alert_type_compare'; import { VIEW_LICENSE_OPTIONS_LINK } from '../../../common/constants'; import { SectionLoading } from '../../components/section_loading'; +import { DEFAULT_ALERT_INTERVAL } from '../../constants'; const ENTER_KEY = 13; -export function validateBaseProperties(alertObject: InitialAlert): ValidationResult { - const validationResult = { errors: {} }; - const errors = { - name: new Array(), - interval: new Array(), - alertTypeId: new Array(), - actionConnectors: new Array(), - }; - validationResult.errors = errors; - if (!alertObject.name) { - errors.name.push( - i18n.translate('xpack.triggersActionsUI.sections.alertForm.error.requiredNameText', { - defaultMessage: 'Name is required.', - }) - ); - } - if (alertObject.schedule.interval.length < 2) { - errors.interval.push( - i18n.translate('xpack.triggersActionsUI.sections.alertForm.error.requiredIntervalText', { - defaultMessage: 'Check interval is required.', - }) - ); - } - if (!alertObject.alertTypeId) { - errors.alertTypeId.push( - i18n.translate('xpack.triggersActionsUI.sections.alertForm.error.requiredRuleTypeIdText', { - defaultMessage: 'Rule type is required.', - }) - ); - } - const emptyConnectorActions = alertObject.actions.find( - (actionItem) => /^\d+$/.test(actionItem.id) && Object.keys(actionItem.params).length > 0 - ); - if (emptyConnectorActions !== undefined) { - errors.actionConnectors.push( - i18n.translate('xpack.triggersActionsUI.sections.alertForm.error.requiredActionConnector', { - defaultMessage: 'Action for {actionTypeId} connector is required.', - values: { actionTypeId: emptyConnectorActions.actionTypeId }, - }) - ); - } - return validationResult; -} - -export function getAlertErrors(alert: Alert, alertTypeModel: AlertTypeModel | null) { - const alertParamsErrors: IErrorObject = alertTypeModel - ? alertTypeModel.validate(alert.params).errors - : []; - const alertBaseErrors = validateBaseProperties(alert).errors as IErrorObject; - const alertErrors = { - ...alertParamsErrors, - ...alertBaseErrors, - } as IErrorObject; - - return { - alertParamsErrors, - alertBaseErrors, - alertErrors, - }; -} - -export async function getAlertActionErrors( - alert: Alert, - actionTypeRegistry: ActionTypeRegistryContract -): Promise { - return await Promise.all( - alert.actions.map( - async (alertAction: AlertAction) => - ( - await actionTypeRegistry.get(alertAction.actionTypeId)?.validateParams(alertAction.params) - ).errors - ) - ); -} - -export const hasObjectErrors: (errors: IErrorObject) => boolean = (errors) => - !!Object.values(errors).find((errorList) => { - if (isObject(errorList)) return hasObjectErrors(errorList as IErrorObject); - return errorList.length >= 1; - }); - -export function isValidAlert( - alertObject: InitialAlert | Alert, - validationResult: IErrorObject, - actionsErrors: IErrorObject[] -): alertObject is Alert { - return ( - !hasObjectErrors(validationResult) && - actionsErrors.every((error: IErrorObject) => !hasObjectErrors(error)) - ); -} - function getProducerFeatureName(producer: string, kibanaFeatures: KibanaFeature[]) { return kibanaFeatures.find((featureItem) => featureItem.id === producer)?.name; } @@ -186,6 +94,9 @@ interface AlertFormProps> { metadata?: MetaData; } +const defaultScheduleInterval = getDurationNumberInItsUnit(DEFAULT_ALERT_INTERVAL); +const defaultScheduleIntervalUnit = getDurationUnitValue(DEFAULT_ALERT_INTERVAL); + export const AlertForm = ({ alert, canChangeTrigger = true, @@ -212,10 +123,14 @@ export const AlertForm = ({ const [alertTypeModel, setAlertTypeModel] = useState(null); const [alertInterval, setAlertInterval] = useState( - alert.schedule.interval ? getDurationNumberInItsUnit(alert.schedule.interval) : undefined + alert.schedule.interval + ? getDurationNumberInItsUnit(alert.schedule.interval) + : defaultScheduleInterval ); const [alertIntervalUnit, setAlertIntervalUnit] = useState( - alert.schedule.interval ? getDurationUnitValue(alert.schedule.interval) : 'm' + alert.schedule.interval + ? getDurationUnitValue(alert.schedule.interval) + : defaultScheduleIntervalUnit ); const [alertThrottle, setAlertThrottle] = useState( alert.throttle ? getDurationNumberInItsUnit(alert.throttle) : null @@ -224,7 +139,7 @@ export const AlertForm = ({ alert.throttle ? getDurationUnitValue(alert.throttle) : 'h' ); const [defaultActionGroupId, setDefaultActionGroupId] = useState(undefined); - const [alertTypesIndex, setAlertTypesIndex] = useState(null); + const [ruleTypeIndex, setRuleTypeIndex] = useState(null); const [availableAlertTypes, setAvailableAlertTypes] = useState< Array<{ alertTypeModel: AlertTypeModel; alertType: AlertType }> @@ -243,14 +158,14 @@ export const AlertForm = ({ (async () => { try { const alertTypesResult = await loadAlertTypes({ http }); - const index: AlertTypeIndex = new Map(); + const index: RuleTypeIndex = new Map(); for (const alertTypeItem of alertTypesResult) { index.set(alertTypeItem.id, alertTypeItem); } if (alert.alertTypeId && index.has(alert.alertTypeId)) { setDefaultActionGroupId(index.get(alert.alertTypeId)!.defaultActionGroupId); } - setAlertTypesIndex(index); + setRuleTypeIndex(index); const availableAlertTypesResult = getAvailableAlertTypes(alertTypesResult); setAvailableAlertTypes(availableAlertTypesResult); @@ -287,10 +202,24 @@ export const AlertForm = ({ useEffect(() => { setAlertTypeModel(alert.alertTypeId ? ruleTypeRegistry.get(alert.alertTypeId) : null); - if (alert.alertTypeId && alertTypesIndex && alertTypesIndex.has(alert.alertTypeId)) { - setDefaultActionGroupId(alertTypesIndex.get(alert.alertTypeId)!.defaultActionGroupId); + if (alert.alertTypeId && ruleTypeIndex && ruleTypeIndex.has(alert.alertTypeId)) { + setDefaultActionGroupId(ruleTypeIndex.get(alert.alertTypeId)!.defaultActionGroupId); + } + }, [alert, alert.alertTypeId, ruleTypeIndex, ruleTypeRegistry]); + + useEffect(() => { + if (alert.schedule.interval) { + const interval = getDurationNumberInItsUnit(alert.schedule.interval); + const intervalUnit = getDurationUnitValue(alert.schedule.interval); + + if (interval !== defaultScheduleInterval) { + setAlertInterval(interval); + } + if (intervalUnit !== defaultScheduleIntervalUnit) { + setAlertIntervalUnit(intervalUnit); + } } - }, [alert, alert.alertTypeId, alertTypesIndex, ruleTypeRegistry]); + }, [alert.schedule.interval]); const setAlertProperty = useCallback( (key: Key, value: Alert[Key] | null) => { @@ -372,9 +301,7 @@ export const AlertForm = ({ ? !item.alertTypeModel.requiresAppContext : item.alertType!.producer === alert.consumer ); - const selectedAlertType = alert?.alertTypeId - ? alertTypesIndex?.get(alert?.alertTypeId) - : undefined; + const selectedAlertType = alert?.alertTypeId ? ruleTypeIndex?.get(alert?.alertTypeId) : undefined; const recoveryActionGroup = selectedAlertType?.recoveryActionGroup?.id; const getDefaultActionParams = useCallback( (actionTypeId: string, actionGroupId: string): Record | undefined => @@ -499,8 +426,8 @@ export const AlertForm = ({ setActions([]); setAlertTypeModel(item.alertTypeItem); setAlertProperty('params', {}); - if (alertTypesIndex && alertTypesIndex.has(item.id)) { - setDefaultActionGroupId(alertTypesIndex.get(item.id)!.defaultActionGroupId); + if (ruleTypeIndex && ruleTypeIndex.has(item.id)) { + setDefaultActionGroupId(ruleTypeIndex.get(item.id)!.defaultActionGroupId); } }} /> @@ -518,8 +445,8 @@ export const AlertForm = ({
- {alert.alertTypeId && alertTypesIndex && alertTypesIndex.has(alert.alertTypeId) - ? alertTypesIndex.get(alert.alertTypeId)!.name + {alert.alertTypeId && ruleTypeIndex && ruleTypeIndex.has(alert.alertTypeId) + ? ruleTypeIndex.get(alert.alertTypeId)!.name : ''}
@@ -870,7 +797,7 @@ export const AlertForm = ({ ) : null} {alertTypeNodes} - ) : alertTypesIndex ? ( + ) : ruleTypeIndex ? ( ) : ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index 941d40010408..1daaf3b99612 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -33,7 +33,14 @@ import { import { useHistory } from 'react-router-dom'; import { isEmpty } from 'lodash'; -import { ActionType, Alert, AlertTableItem, AlertTypeIndex, Pagination } from '../../../../types'; +import { + ActionType, + Alert, + AlertTableItem, + AlertType, + RuleTypeIndex, + Pagination, +} from '../../../../types'; import { AlertAdd, AlertEdit } from '../../alert_form'; import { BulkOperationPopover } from '../../common/components/bulk_operation_popover'; import { AlertQuickEditButtonsWithApi as AlertQuickEditButtons } from '../../common/components/alert_quick_edit_buttons'; @@ -74,7 +81,7 @@ const ENTER_KEY = 13; interface AlertTypeState { isLoading: boolean; isInitialized: boolean; - data: AlertTypeIndex; + data: RuleTypeIndex; } interface AlertState { isLoading: boolean; @@ -161,7 +168,7 @@ export const AlertsList: React.FunctionComponent = () => { try { setAlertTypesState({ ...alertTypesState, isLoading: true }); const alertTypes = await loadAlertTypes({ http }); - const index: AlertTypeIndex = new Map(); + const index: RuleTypeIndex = new Map(); for (const alertType of alertTypes) { index.set(alertType.id, alertType); } @@ -895,6 +902,7 @@ export const AlertsList: React.FunctionComponent = () => { }} actionTypeRegistry={actionTypeRegistry} ruleTypeRegistry={ruleTypeRegistry} + ruleTypeIndex={alertTypesState.data} onSave={loadAlertsData} /> )} @@ -906,6 +914,9 @@ export const AlertsList: React.FunctionComponent = () => { }} actionTypeRegistry={actionTypeRegistry} ruleTypeRegistry={ruleTypeRegistry} + ruleType={ + alertTypesState.data.get(currentRuleToEdit.alertTypeId) as AlertType + } onSave={loadAlertsData} /> )} @@ -944,17 +955,17 @@ function filterAlertsById(alerts: Alert[], ids: string[]): Alert[] { function convertAlertsToTableItems( alerts: Alert[], - alertTypesIndex: AlertTypeIndex, + ruleTypeIndex: RuleTypeIndex, canExecuteActions: boolean ) { return alerts.map((alert) => ({ ...alert, actionsCount: alert.actions.length, tagsText: alert.tags.join(', '), - alertType: alertTypesIndex.get(alert.alertTypeId)?.name ?? alert.alertTypeId, + alertType: ruleTypeIndex.get(alert.alertTypeId)?.name ?? alert.alertTypeId, isEditable: - hasAllPrivilege(alert, alertTypesIndex.get(alert.alertTypeId)) && + hasAllPrivilege(alert, ruleTypeIndex.get(alert.alertTypeId)) && (canExecuteActions || (!canExecuteActions && !alert.actions.length)), - enabledInLicense: !!alertTypesIndex.get(alert.alertTypeId)?.enabledInLicense, + enabledInLicense: !!ruleTypeIndex.get(alert.alertTypeId)?.enabledInLicense, })); } diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index ae4fd5152794..2ef20f36b7ca 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -66,7 +66,7 @@ export { }; export type ActionTypeIndex = Record; -export type AlertTypeIndex = Map; +export type RuleTypeIndex = Map; export type ActionTypeRegistryContract< ActionConnector = unknown, ActionParams = unknown @@ -197,6 +197,8 @@ export interface AlertType< | 'minimumLicenseRequired' | 'recoveryActionGroup' | 'defaultActionGroupId' + | 'defaultScheduleInterval' + | 'minimumScheduleInterval' > { actionVariables: ActionVariables; authorizedConsumers: Record; @@ -285,6 +287,7 @@ export interface AlertEditProps> { reloadAlerts?: () => Promise; onSave?: () => Promise; metadata?: MetaData; + ruleType?: AlertType; } export interface AlertAddProps> { @@ -299,4 +302,5 @@ export interface AlertAddProps> { reloadAlerts?: () => Promise; onSave?: () => Promise; metadata?: MetaData; + ruleTypeIndex?: RuleTypeIndex; } From a3895208498e835a692fee918191b4b20802bb59 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 11 Oct 2021 20:27:46 +0200 Subject: [PATCH 59/74] [Utpime] Remove unnecessary usememo in url params hooks (#114252) --- x-pack/plugins/uptime/public/hooks/use_url_params.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/uptime/public/hooks/use_url_params.ts b/x-pack/plugins/uptime/public/hooks/use_url_params.ts index 329e0ccef4d9..1318b635693c 100644 --- a/x-pack/plugins/uptime/public/hooks/use_url_params.ts +++ b/x-pack/plugins/uptime/public/hooks/use_url_params.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useCallback, useEffect, useMemo } from 'react'; +import { useCallback, useEffect } from 'react'; import { parse, stringify } from 'query-string'; import { useLocation, useHistory } from 'react-router-dom'; import { useDispatch, useSelector } from 'react-redux'; @@ -28,7 +28,7 @@ const getParsedParams = (search: string) => { export const useGetUrlParams: GetUrlParams = () => { const { search } = useLocation(); - return useMemo(() => getSupportedUrlParams(getParsedParams(search)), [search]); + return getSupportedUrlParams(getParsedParams(search)); }; const getMapFromFilters = (value: any): Map | undefined => { From e8d16cddca6003383c972a01229f9a284799a2ed Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 11 Oct 2021 13:30:33 -0500 Subject: [PATCH 60/74] skip flaky suite. #114541, #114542 --- x-pack/test/accessibility/apps/index_lifecycle_management.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/test/accessibility/apps/index_lifecycle_management.ts b/x-pack/test/accessibility/apps/index_lifecycle_management.ts index da56cfd702ab..65faa77fc497 100644 --- a/x-pack/test/accessibility/apps/index_lifecycle_management.ts +++ b/x-pack/test/accessibility/apps/index_lifecycle_management.ts @@ -57,7 +57,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { throw new Error(`Could not find ${policyName} in policy table`); }; - describe('Index Lifecycle Management', async () => { + // FLAKY + // https://github.com/elastic/kibana/issues/114541 + // https://github.com/elastic/kibana/issues/114542 + describe.skip('Index Lifecycle Management', async () => { before(async () => { await esClient.ilm.putLifecycle({ policy: POLICY_NAME, body: POLICY_ALL_PHASES }); await esClient.indices.putIndexTemplate({ From 328da6f720f6988cd307bccdd4e4be5a0f783ba8 Mon Sep 17 00:00:00 2001 From: Sandra G Date: Mon, 11 Oct 2021 16:18:10 -0400 Subject: [PATCH 61/74] add default sort props (#114294) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/monitoring/public/application/hooks/use_table.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/monitoring/public/application/hooks/use_table.ts b/x-pack/plugins/monitoring/public/application/hooks/use_table.ts index 45d1f717f5d4..be1983c373c2 100644 --- a/x-pack/plugins/monitoring/public/application/hooks/use_table.ts +++ b/x-pack/plugins/monitoring/public/application/hooks/use_table.ts @@ -9,6 +9,7 @@ import { useState, useCallback } from 'react'; import { EuiTableSortingType } from '@elastic/eui'; import { euiTableStorageGetter, euiTableStorageSetter } from '../../components/table'; import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; +import { EUI_SORT_ASCENDING } from '../../../common/constants'; interface Pagination { pageSize: number; @@ -77,7 +78,9 @@ export function useTable(storageKey: string) { ); // get initial state from localStorage - const [sorting, setSorting] = useState(storageData.sort || { sort: {} }); + const [sorting, setSorting] = useState( + storageData.sort || { sort: { field: 'name', direction: EUI_SORT_ASCENDING } } + ); const [query, setQuery] = useState(''); From de42e5329f8b76bcc7bb07e0fceb4739a74fe7ea Mon Sep 17 00:00:00 2001 From: Sandra G Date: Mon, 11 Oct 2021 16:18:29 -0400 Subject: [PATCH 62/74] [Stack Monitoring] add test subject to tab (#114319) * pass test subject to advanced link and change name in angular version to be generic * change name in functional test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/application/pages/elasticsearch/item_template.tsx | 1 + x-pack/plugins/monitoring/public/directives/main/index.html | 2 +- .../functional/services/monitoring/elasticsearch_node_detail.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/item_template.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/item_template.tsx index 1f06ba18bf10..05d567a9b3ff 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/item_template.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/item_template.tsx @@ -25,6 +25,7 @@ export const ItemTemplate: React.FC = (props) => { }, { id: 'advanced', + testSubj: 'esItemDetailAdvancedLink', label: i18n.translate('xpack.monitoring.esItemNavigation.advancedLinkText', { defaultMessage: 'Advanced', }), diff --git a/x-pack/plugins/monitoring/public/directives/main/index.html b/x-pack/plugins/monitoring/public/directives/main/index.html index fb24d9e678d5..558ed5e874cd 100644 --- a/x-pack/plugins/monitoring/public/directives/main/index.html +++ b/x-pack/plugins/monitoring/public/directives/main/index.html @@ -103,7 +103,7 @@

{{pageTitle || monitoringMain.instance}} Date: Mon, 11 Oct 2021 20:30:54 +0000 Subject: [PATCH 63/74] auto-upgrade polyfills --- renovate.json5 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/renovate.json5 b/renovate.json5 index dea7d311bae1..b08d7e0bcec1 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -75,6 +75,16 @@ labels: ['Team:Operations', 'release_note:skip'], enabled: true, }, + { + groupName: 'polyfills', + packageNames: ['core-js'], + matchPackagePatterns: ["polyfill"], + excludePackageNames: ['@loaders.gl/polyfills'], + reviewers: ['team:kibana-operations'], + matchBaseBranches: ['master'], + labels: ['Team:Operations', 'release_note:skip'], + enabled: true, + }, { groupName: 'vega related modules', packageNames: ['vega', 'vega-lite', 'vega-schema-url-parser', 'vega-tooltip'], From a03aa7b7faa0bef0ef4d3c0043c7cdf7f8e3890f Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 11 Oct 2021 13:44:50 +0100 Subject: [PATCH 64/74] skip flaky suites (#114418) --- .../functional/apps/maps/documents_source/docvalue_fields.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js b/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js index 3479f292374d..64c6c2b6749e 100644 --- a/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js +++ b/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js @@ -11,7 +11,8 @@ export default function ({ getPageObjects, getService }) { const PageObjects = getPageObjects(['maps']); const security = getService('security'); - describe('docvalue_fields', () => { + // FLAKY: https://github.com/elastic/kibana/issues/114418 + describe.skip('docvalue_fields', () => { before(async () => { await security.testUser.setRoles(['global_maps_read', 'test_logstash_reader'], false); await PageObjects.maps.loadSavedMap('document example'); From f88901c6ac37440beff1629685055e0bca64221b Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Mon, 11 Oct 2021 23:50:35 +0200 Subject: [PATCH 65/74] Log Unhandled rejection stack (#113614) * Refactor state types * Log the stack trace of unhandled promise rejections * Revert "Refactor state types" This reverts commit 6a5123a1b2d2b40089c94109619c471b3c33acf2. * Fix types * code review feedback --- .../environment/environment_service.test.ts | 26 +++++++++++++++++++ .../server/environment/environment_service.ts | 5 ++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/core/server/environment/environment_service.test.ts b/src/core/server/environment/environment_service.test.ts index 34647d090b99..4b074482248b 100644 --- a/src/core/server/environment/environment_service.test.ts +++ b/src/core/server/environment/environment_service.test.ts @@ -135,6 +135,32 @@ describe('UuidService', () => { expect(logger.get('process').warn).not.toHaveBeenCalled(); }); }); + + describe('unhandledRejection warnings', () => { + it('logs warn for an unhandeld promise rejected with an Error', async () => { + await service.preboot(); + + const err = new Error('something went wrong'); + process.emit('unhandledRejection', err, new Promise((res, rej) => rej(err))); + + expect(logger.get('process').warn).toHaveBeenCalledTimes(1); + expect(loggingSystemMock.collect(logger).warn[0][0]).toMatch( + /Detected an unhandled Promise rejection: Error: something went wrong\n.*at / + ); + }); + + it('logs warn for an unhandeld promise rejected with a string', async () => { + await service.preboot(); + + const err = 'something went wrong'; + process.emit('unhandledRejection', err, new Promise((res, rej) => rej(err))); + + expect(logger.get('process').warn).toHaveBeenCalledTimes(1); + expect(loggingSystemMock.collect(logger).warn[0][0]).toMatch( + /Detected an unhandled Promise rejection: "something went wrong"/ + ); + }); + }); }); describe('#setup()', () => { diff --git a/src/core/server/environment/environment_service.ts b/src/core/server/environment/environment_service.ts index f96b61625657..472883c1f482 100644 --- a/src/core/server/environment/environment_service.ts +++ b/src/core/server/environment/environment_service.ts @@ -54,9 +54,10 @@ export class EnvironmentService { this.configService.atPath(pidConfigDef.path).pipe(take(1)).toPromise(), ]); - // was present in the legacy `pid` file. + // Log unhandled rejections so that we can fix them in preparation for https://github.com/elastic/kibana/issues/77469 process.on('unhandledRejection', (reason) => { - this.log.warn(`Detected an unhandled Promise rejection.\n${reason}`); + const message = (reason as Error)?.stack ?? JSON.stringify(reason); + this.log.warn(`Detected an unhandled Promise rejection: ${message}`); }); process.on('warning', (warning) => { From c6b5136c3a5a95c9027e64c01a03ee52f30d264d Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 11 Oct 2021 16:26:52 -0600 Subject: [PATCH 66/74] =?UTF-8?q?[Maps]=20fix=20docvalue=5Ffields=C2=B7js?= =?UTF-8?q?=20functional=20test=20(#114527)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Maps] fix docvalue_fields·js functional test * remove unused * eslint * remove _type from check list, copied from 7.x branch and is not in 8.0 Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../apps/maps/documents_source/docvalue_fields.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js b/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js index 64c6c2b6749e..d1da193ac50a 100644 --- a/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js +++ b/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js @@ -15,7 +15,6 @@ export default function ({ getPageObjects, getService }) { describe.skip('docvalue_fields', () => { before(async () => { await security.testUser.setRoles(['global_maps_read', 'test_logstash_reader'], false); - await PageObjects.maps.loadSavedMap('document example'); }); after(async () => { @@ -46,11 +45,14 @@ export default function ({ getPageObjects, getService }) { it('should format date fields as epoch_millis when data driven styling is applied to a date field', async () => { await PageObjects.maps.loadSavedMap('document example with data driven styles on date field'); const { rawResponse: response } = await PageObjects.maps.getResponse(); - const firstHit = response.hits.hits[0]; - expect(firstHit).to.only.have.keys(['_id', '_index', '_score', 'fields']); - expect(firstHit.fields).to.only.have.keys(['@timestamp', 'bytes', 'geo.coordinates']); - expect(firstHit.fields['@timestamp']).to.be.an('array'); - expect(firstHit.fields['@timestamp'][0]).to.eql('1442709321445'); + const targetHit = response.hits.hits.find((hit) => { + return hit._id === 'AU_x3_g4GFA8no6QjkSR'; + }); + expect(targetHit).not.to.be(undefined); + expect(targetHit).to.only.have.keys(['_id', '_index', '_score', 'fields']); + expect(targetHit.fields).to.only.have.keys(['@timestamp', 'bytes', 'geo.coordinates']); + expect(targetHit.fields['@timestamp']).to.be.an('array'); + expect(targetHit.fields['@timestamp'][0]).to.eql('1442709321445'); }); }); } From a0b55b316e23a80de5d755864c7acd656b44441f Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Mon, 11 Oct 2021 18:29:50 -0400 Subject: [PATCH 67/74] [Core][Deprecations] omit deprecationDetails if needed it in the reques (#114399) * omit deprecationDetails if needed it * review I * doc update Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...r.deprecationsdetails.correctiveactions.md | 1 + ...-plugin-core-server.deprecationsdetails.md | 2 +- .../deprecations/deprecations_client.test.ts | 33 +++++++++++++++++++ .../deprecations/deprecations_client.ts | 4 +-- src/core/server/deprecations/README.mdx | 2 +- src/core/server/deprecations/types.ts | 2 ++ src/core/server/server.api.md | 1 + 7 files changed, 41 insertions(+), 4 deletions(-) diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.correctiveactions.md b/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.correctiveactions.md index 657c62a21c58..d7d10651033b 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.correctiveactions.md +++ b/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.correctiveactions.md @@ -16,6 +16,7 @@ correctiveActions: { body?: { [key: string]: any; }; + omitContextFromBody?: boolean; }; manualSteps: string[]; }; diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.md b/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.md index 86418a1d0c1c..2ff9f4b792f5 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.md +++ b/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.md @@ -15,7 +15,7 @@ export interface DeprecationsDetails | Property | Type | Description | | --- | --- | --- | -| [correctiveActions](./kibana-plugin-core-server.deprecationsdetails.correctiveactions.md) | {
api?: {
path: string;
method: 'POST' | 'PUT';
body?: {
[key: string]: any;
};
};
manualSteps: string[];
} | corrective action needed to fix this deprecation. | +| [correctiveActions](./kibana-plugin-core-server.deprecationsdetails.correctiveactions.md) | {
api?: {
path: string;
method: 'POST' | 'PUT';
body?: {
[key: string]: any;
};
omitContextFromBody?: boolean;
};
manualSteps: string[];
} | corrective action needed to fix this deprecation. | | [deprecationType](./kibana-plugin-core-server.deprecationsdetails.deprecationtype.md) | 'config' | 'feature' | (optional) Used to identify between different deprecation types. Example use case: in Upgrade Assistant, we may want to allow the user to sort by deprecation type or show each type in a separate tab.Feel free to add new types if necessary. Predefined types are necessary to reduce having similar definitions with different keywords across kibana deprecations. | | [documentationUrl](./kibana-plugin-core-server.deprecationsdetails.documentationurl.md) | string | (optional) link to the documentation for more details on the deprecation. | | [level](./kibana-plugin-core-server.deprecationsdetails.level.md) | 'warning' | 'critical' | 'fetch_error' | levels: - warning: will not break deployment upon upgrade - critical: needs to be addressed before upgrade. - fetch\_error: Deprecations service failed to grab the deprecation details for the domain. | diff --git a/src/core/public/deprecations/deprecations_client.test.ts b/src/core/public/deprecations/deprecations_client.test.ts index cca81f4687a9..3c45b76cfecd 100644 --- a/src/core/public/deprecations/deprecations_client.test.ts +++ b/src/core/public/deprecations/deprecations_client.test.ts @@ -197,5 +197,38 @@ describe('DeprecationsClient', () => { expect(result).toEqual({ status: 'fail', reason: mockResponse }); }); + + it('omit deprecationDetails in the request of the body', async () => { + const deprecationsClient = new DeprecationsClient({ http }); + const mockDeprecationDetails: DomainDeprecationDetails = { + title: 'some-title', + domainId: 'testPluginId-1', + message: 'some-message', + level: 'warning', + correctiveActions: { + api: { + path: 'some-path', + method: 'POST', + body: { + extra_param: 123, + }, + omitContextFromBody: true, + }, + manualSteps: ['manual-step'], + }, + }; + const result = await deprecationsClient.resolveDeprecation(mockDeprecationDetails); + + expect(http.fetch).toBeCalledTimes(1); + expect(http.fetch).toBeCalledWith({ + path: 'some-path', + method: 'POST', + asSystemRequest: true, + body: JSON.stringify({ + extra_param: 123, + }), + }); + expect(result).toEqual({ status: 'ok' }); + }); }); }); diff --git a/src/core/public/deprecations/deprecations_client.ts b/src/core/public/deprecations/deprecations_client.ts index 4b9cfca1986b..01906c930d73 100644 --- a/src/core/public/deprecations/deprecations_client.ts +++ b/src/core/public/deprecations/deprecations_client.ts @@ -59,7 +59,7 @@ export class DeprecationsClient { }; } - const { body, method, path } = correctiveActions.api; + const { body, method, path, omitContextFromBody = false } = correctiveActions.api; try { await this.http.fetch({ path, @@ -67,7 +67,7 @@ export class DeprecationsClient { asSystemRequest: true, body: JSON.stringify({ ...body, - deprecationDetails: { domainId }, + ...(omitContextFromBody ? {} : { deprecationDetails: { domainId } }), }), }); return { status: 'ok' }; diff --git a/src/core/server/deprecations/README.mdx b/src/core/server/deprecations/README.mdx index 82a01995502e..ed542610e753 100644 --- a/src/core/server/deprecations/README.mdx +++ b/src/core/server/deprecations/README.mdx @@ -212,7 +212,7 @@ async function getDeprecations({ esClient, savedObjectsClient }: GetDeprecations The deprecations API allows plugins to provide an API call that can be used to automatically fix specific deprecations. To do so create a `PUT` or `POST` route in your plugin and specify data you want to be passed in the payload for the deprecation. -In the example above, `/internal/security/users/test_dashboard_user` will be called when users click on `Quick Resolve` in the UA. The service will automatically pass the body provided in the api corrective action to provide context to the route for fixing the deprecation. +In the example above, `/internal/security/users/test_dashboard_user` will be called when users click on `Quick Resolve` in the UA. The service will automatically pass the body provided in the api corrective action to provide context to the route for fixing the deprecation. If you need to omit the deprecation details context in the request of the body, you can use the property `omitContextFromBody`. The deprecations service expects a `200` status code to recognize the corrective action as a success. diff --git a/src/core/server/deprecations/types.ts b/src/core/server/deprecations/types.ts index 7e276514a64d..e24c6a13fcee 100644 --- a/src/core/server/deprecations/types.ts +++ b/src/core/server/deprecations/types.ts @@ -69,6 +69,8 @@ export interface DeprecationsDetails { body?: { [key: string]: any; }; + /* Allow to omit context in the request of the body */ + omitContextFromBody?: boolean; }; /** * Specify a list of manual steps users need to follow to diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index c92f767ce891..a1fd69f5e1c7 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -812,6 +812,7 @@ export interface DeprecationsDetails { body?: { [key: string]: any; }; + omitContextFromBody?: boolean; }; manualSteps: string[]; }; From 912eb0d9373ef17c27c2a1308ff8151dd8382e5a Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 11 Oct 2021 18:29:10 -0500 Subject: [PATCH 68/74] [ftr] ensure indentation is reset between configs (#114359) Co-authored-by: spalger Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../src/tooling_log/tooling_log.test.ts | 28 ++++++++++ .../src/tooling_log/tooling_log.ts | 37 +++++++++++-- packages/kbn-pm/dist/index.js | 40 ++++++++++++-- .../src/functional_test_runner/cli.ts | 2 +- .../kbn-test/src/functional_tests/tasks.ts | 54 +++++++++---------- 5 files changed, 121 insertions(+), 40 deletions(-) diff --git a/packages/kbn-dev-utils/src/tooling_log/tooling_log.test.ts b/packages/kbn-dev-utils/src/tooling_log/tooling_log.test.ts index 2a1099587d0c..ec63a9fb7e6f 100644 --- a/packages/kbn-dev-utils/src/tooling_log/tooling_log.test.ts +++ b/packages/kbn-dev-utils/src/tooling_log/tooling_log.test.ts @@ -12,6 +12,10 @@ import { toArray, takeUntil } from 'rxjs/operators'; import { ToolingLog } from './tooling_log'; import { Writer } from './writer'; import { ToolingLogTextWriter } from './tooling_log_text_writer'; +import { ToolingLogCollectingWriter } from './tooling_log_collecting_writer'; +import { createStripAnsiSerializer } from '../serializers/strip_ansi_serializer'; + +expect.addSnapshotSerializer(createStripAnsiSerializer()); it('creates zero writers without a config', () => { const log = new ToolingLog(); @@ -67,6 +71,30 @@ describe('#indent()', () => { expect(write.mock.calls).toMatchSnapshot(); }); + + it('resets the indentation after block executes and promise resolves', async () => { + const log = new ToolingLog(); + const writer = new ToolingLogCollectingWriter(); + log.setWriters([writer]); + + log.info('base'); + await log.indent(2, async () => { + log.indent(2); + log.info('hello'); + log.indent(2); + log.info('world'); + }); + log.info('back to base'); + + expect(writer.messages).toMatchInlineSnapshot(` + Array [ + " info base", + " │ info hello", + " │ info world", + " info back to base", + ] + `); + }); }); (['verbose', 'debug', 'info', 'success', 'warning', 'error', 'write'] as const).forEach( diff --git a/packages/kbn-dev-utils/src/tooling_log/tooling_log.ts b/packages/kbn-dev-utils/src/tooling_log/tooling_log.ts index d60bf9bcb1d7..e9fd15afefe4 100644 --- a/packages/kbn-dev-utils/src/tooling_log/tooling_log.ts +++ b/packages/kbn-dev-utils/src/tooling_log/tooling_log.ts @@ -13,7 +13,7 @@ import { Writer } from './writer'; import { Message, MessageTypes } from './message'; export class ToolingLog { - private identWidth = 0; + private indentWidth = 0; private writers: Writer[]; private readonly written$: Rx.Subject; @@ -22,9 +22,36 @@ export class ToolingLog { this.written$ = new Rx.Subject(); } - public indent(delta = 0) { - this.identWidth = Math.max(this.identWidth + delta, 0); - return this.identWidth; + /** + * Get the current indentation level of the ToolingLog + */ + public getIndent() { + return this.indentWidth; + } + + /** + * Indent the output of the ToolingLog by some character (4 is a good choice usually). + * + * If provided, the `block` function will be executed and once it's promise is resolved + * or rejected the indentation will be reset to its original state. + * + * @param delta the number of spaces to increase/decrease the indentation + * @param block a function to run and reset any indentation changes after + */ + public indent(delta = 0, block?: () => Promise) { + const originalWidth = this.indentWidth; + this.indentWidth = Math.max(this.indentWidth + delta, 0); + if (!block) { + return; + } + + return (async () => { + try { + return await block(); + } finally { + this.indentWidth = originalWidth; + } + })(); } public verbose(...args: any[]) { @@ -70,7 +97,7 @@ export class ToolingLog { private sendToWriters(type: MessageTypes, args: any[]) { const msg = { type, - indent: this.identWidth, + indent: this.indentWidth, args, }; diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index f39563637914..a231fe21ea83 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -626,16 +626,46 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && */ class ToolingLog { constructor(writerConfig) { - (0, _defineProperty2.default)(this, "identWidth", 0); + (0, _defineProperty2.default)(this, "indentWidth", 0); (0, _defineProperty2.default)(this, "writers", void 0); (0, _defineProperty2.default)(this, "written$", void 0); this.writers = writerConfig ? [new _tooling_log_text_writer.ToolingLogTextWriter(writerConfig)] : []; this.written$ = new Rx.Subject(); } + /** + * Get the current indentation level of the ToolingLog + */ + + + getIndent() { + return this.indentWidth; + } + /** + * Indent the output of the ToolingLog by some character (4 is a good choice usually). + * + * If provided, the `block` function will be executed and once it's promise is resolved + * or rejected the indentation will be reset to its original state. + * + * @param delta the number of spaces to increase/decrease the indentation + * @param block a function to run and reset any indentation changes after + */ - indent(delta = 0) { - this.identWidth = Math.max(this.identWidth + delta, 0); - return this.identWidth; + + indent(delta = 0, block) { + const originalWidth = this.indentWidth; + this.indentWidth = Math.max(this.indentWidth + delta, 0); + + if (!block) { + return; + } + + return (async () => { + try { + return await block(); + } finally { + this.indentWidth = originalWidth; + } + })(); } verbose(...args) { @@ -681,7 +711,7 @@ class ToolingLog { sendToWriters(type, args) { const msg = { type, - indent: this.identWidth, + indent: this.indentWidth, args }; let written = false; diff --git a/packages/kbn-test/src/functional_test_runner/cli.ts b/packages/kbn-test/src/functional_test_runner/cli.ts index 3ad365a028b6..d9938bebea5b 100644 --- a/packages/kbn-test/src/functional_test_runner/cli.ts +++ b/packages/kbn-test/src/functional_test_runner/cli.ts @@ -79,7 +79,7 @@ export function runFtrCli() { err: err.message, ...flags, }); - log.indent(-log.indent()); + log.indent(-log.getIndent()); log.error(err); process.exitCode = 1; } else { diff --git a/packages/kbn-test/src/functional_tests/tasks.ts b/packages/kbn-test/src/functional_tests/tasks.ts index 3bc697c143f4..c8265c032cbc 100644 --- a/packages/kbn-test/src/functional_tests/tasks.ts +++ b/packages/kbn-test/src/functional_tests/tasks.ts @@ -78,12 +78,9 @@ export async function runTests(options: RunTestsParams) { log.write('--- asserting that all tests belong to a ciGroup'); for (const configPath of options.configs) { log.info('loading', configPath); - log.indent(4); - try { + await log.indent(4, async () => { await assertNoneExcluded({ configPath, options: { ...options, log } }); - } finally { - log.indent(-4); - } + }); continue; } @@ -94,42 +91,41 @@ export async function runTests(options: RunTestsParams) { const configPathsWithTests: string[] = []; for (const configPath of options.configs) { log.info('testing', configPath); - log.indent(4); - try { + await log.indent(4, async () => { if (await hasTests({ configPath, options: { ...options, log } })) { configPathsWithTests.push(configPath); } - } finally { - log.indent(-4); - } + }); } for (const configPath of configPathsWithTests) { - log.write(`--- Running ${relative(REPO_ROOT, configPath)}`); + await log.indent(0, async () => { + log.write(`--- Running ${relative(REPO_ROOT, configPath)}`); - await withProcRunner(log, async (procs) => { - const config = await readConfigFile(log, configPath); + await withProcRunner(log, async (procs) => { + const config = await readConfigFile(log, configPath); - let es; - try { - es = await runElasticsearch({ config, options: { ...options, log } }); - await runKibanaServer({ procs, config, options }); - await runFtr({ configPath, options: { ...options, log } }); - } finally { + let es; try { - const delay = config.get('kbnTestServer.delayShutdown'); - if (typeof delay === 'number') { - log.info('Delaying shutdown of Kibana for', delay, 'ms'); - await new Promise((r) => setTimeout(r, delay)); - } - - await procs.stop('kibana'); + es = await runElasticsearch({ config, options: { ...options, log } }); + await runKibanaServer({ procs, config, options }); + await runFtr({ configPath, options: { ...options, log } }); } finally { - if (es) { - await es.cleanup(); + try { + const delay = config.get('kbnTestServer.delayShutdown'); + if (typeof delay === 'number') { + log.info('Delaying shutdown of Kibana for', delay, 'ms'); + await new Promise((r) => setTimeout(r, delay)); + } + + await procs.stop('kibana'); + } finally { + if (es) { + await es.cleanup(); + } } } - } + }); }); } } From 8e72e17648dbfd4d6c3af527edc0daea9e6c6b36 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Mon, 11 Oct 2021 19:46:01 -0400 Subject: [PATCH 69/74] api docs (#114565) --- api_docs/apm.json | 290 +++--- api_docs/apm.mdx | 2 +- api_docs/charts.json | 945 +++++++++++++++++- api_docs/charts.mdx | 8 +- api_docs/core.json | 262 ++++- api_docs/core.mdx | 2 +- api_docs/core_application.json | 2 +- api_docs/core_application.mdx | 2 +- api_docs/core_chrome.mdx | 2 +- api_docs/core_http.json | 2 +- api_docs/core_http.mdx | 2 +- api_docs/core_saved_objects.json | 8 +- api_docs/core_saved_objects.mdx | 2 +- api_docs/dashboard.json | 6 +- api_docs/dashboard_enhanced.json | 10 +- api_docs/data.json | 735 ++++++++------ api_docs/data.mdx | 2 +- api_docs/data_autocomplete.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.json | 10 +- api_docs/data_search.mdx | 2 +- api_docs/data_ui.mdx | 2 +- api_docs/data_views.json | 345 +++++-- api_docs/data_views.mdx | 2 +- api_docs/deprecations_by_api.mdx | 4 +- api_docs/deprecations_by_plugin.mdx | 28 +- api_docs/elastic_apm_generator.json | 257 +++++ api_docs/elastic_apm_generator.mdx | 27 + api_docs/event_log.json | 22 +- api_docs/expression_metric_vis.json | 837 ++++++++++++++++ api_docs/expression_metric_vis.mdx | 33 + api_docs/fleet.json | 816 +++++++++++++-- api_docs/fleet.mdx | 2 +- api_docs/interactive_setup.json | 98 ++ api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_config.json | 212 +++- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_es_query.json | 370 ++++--- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_logging.json | 2 +- api_docs/kbn_monaco.json | 166 ++- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_optimizer.json | 55 + api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.json | 4 +- api_docs/kbn_typed_react_router_config.json | 125 ++- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kibana_utils.json | 48 +- api_docs/kibana_utils.mdx | 2 +- api_docs/lens.json | 13 + api_docs/lens.mdx | 2 +- api_docs/maps.json | 4 +- api_docs/observability.json | 238 ++++- api_docs/observability.mdx | 2 +- api_docs/plugin_directory.mdx | 47 +- api_docs/saved_objects.json | 8 - api_docs/security_solution.json | 269 +++-- api_docs/security_solution.mdx | 2 +- api_docs/share.json | 111 +- api_docs/share.mdx | 2 +- api_docs/spaces.json | 91 +- api_docs/spaces.mdx | 2 +- api_docs/timelines.json | 106 +- api_docs/transform.json | 101 ++ api_docs/transform.mdx | 37 + api_docs/vis_default_editor.json | 8 +- api_docs/vis_type_vislib.json | 4 +- api_docs/vis_type_xy.json | 8 +- api_docs/visualizations.json | 500 ++++++++- api_docs/visualizations.mdx | 2 +- .../tests/snapshots/plugin_a.foo.json | 76 -- .../api_docs/tests/snapshots/plugin_a.foo.mdx | 35 - 72 files changed, 6206 insertions(+), 1229 deletions(-) create mode 100644 api_docs/elastic_apm_generator.json create mode 100644 api_docs/elastic_apm_generator.mdx create mode 100644 api_docs/expression_metric_vis.json create mode 100644 api_docs/expression_metric_vis.mdx create mode 100644 api_docs/transform.json create mode 100644 api_docs/transform.mdx delete mode 100644 packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.foo.json delete mode 100644 packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.foo.mdx diff --git a/api_docs/apm.json b/api_docs/apm.json index c2300232f9b8..8b4337009709 100644 --- a/api_docs/apm.json +++ b/api_docs/apm.json @@ -184,7 +184,7 @@ "APMPluginStartDependencies", ", unknown>, plugins: Pick<", "APMPluginSetupDependencies", - ", \"data\" | \"cloud\" | \"security\" | \"home\" | \"features\" | \"fleet\" | \"ml\" | \"actions\" | \"usageCollection\" | \"apmOss\" | \"licensing\" | \"observability\" | \"ruleRegistry\" | \"spaces\" | \"taskManager\" | \"alerting\">) => { config$: ", + ", \"data\" | \"cloud\" | \"security\" | \"home\" | \"features\" | \"fleet\" | \"ml\" | \"actions\" | \"usageCollection\" | \"spaces\" | \"apmOss\" | \"licensing\" | \"observability\" | \"ruleRegistry\" | \"taskManager\" | \"alerting\">) => { config$: ", "Observable", "<{ 'apm_oss.transactionIndices': string; 'apm_oss.spanIndices': string; 'apm_oss.errorIndices': string; 'apm_oss.metricsIndices': string; 'apm_oss.sourcemapIndices': string; 'apm_oss.onboardingIndices': string; 'xpack.apm.serviceMapEnabled': boolean; 'xpack.apm.serviceMapFingerprintBucketSize': number; 'xpack.apm.serviceMapTraceIdBucketSize': number; 'xpack.apm.serviceMapFingerprintGlobalBucketSize': number; 'xpack.apm.serviceMapTraceIdGlobalBucketSize': number; 'xpack.apm.serviceMapMaxTracesPerRequest': number; 'xpack.apm.ui.enabled': boolean; 'xpack.apm.ui.maxTraceItems': number; 'xpack.apm.ui.transactionGroupBucketSize': number; 'xpack.apm.autocreateApmIndexPattern': boolean; 'xpack.apm.telemetryCollectionEnabled': boolean; 'xpack.apm.searchAggregatedTransactions': ", "SearchAggregatedTransactionSetting", @@ -248,7 +248,7 @@ "signature": [ "Pick<", "APMPluginSetupDependencies", - ", \"data\" | \"cloud\" | \"security\" | \"home\" | \"features\" | \"fleet\" | \"ml\" | \"actions\" | \"usageCollection\" | \"apmOss\" | \"licensing\" | \"observability\" | \"ruleRegistry\" | \"spaces\" | \"taskManager\" | \"alerting\">" + ", \"data\" | \"cloud\" | \"security\" | \"home\" | \"features\" | \"fleet\" | \"ml\" | \"actions\" | \"usageCollection\" | \"spaces\" | \"apmOss\" | \"licensing\" | \"observability\" | \"ruleRegistry\" | \"taskManager\" | \"alerting\">" ], "path": "x-pack/plugins/apm/server/plugin.ts", "deprecated": false, @@ -797,7 +797,7 @@ "label": "APIEndpoint", "description": [], "signature": [ - "\"POST /api/apm/index_pattern/static\" | \"GET /api/apm/index_pattern/dynamic\" | \"GET /api/apm/environments\" | \"GET /api/apm/services/{serviceName}/errors\" | \"GET /api/apm/services/{serviceName}/errors/{groupId}\" | \"GET /api/apm/services/{serviceName}/errors/distribution\" | \"GET /api/apm/services/{serviceName}/metrics/charts\" | \"GET /api/apm/observability_overview\" | \"GET /api/apm/observability_overview/has_data\" | \"GET /api/apm/rum/client-metrics\" | \"GET /api/apm/rum-client/page-load-distribution\" | \"GET /api/apm/rum-client/page-load-distribution/breakdown\" | \"GET /api/apm/rum-client/page-view-trends\" | \"GET /api/apm/rum-client/services\" | \"GET /api/apm/rum-client/visitor-breakdown\" | \"GET /api/apm/rum-client/web-core-vitals\" | \"GET /api/apm/rum-client/long-task-metrics\" | \"GET /api/apm/rum-client/url-search\" | \"GET /api/apm/rum-client/js-errors\" | \"GET /api/apm/observability_overview/has_rum_data\" | \"GET /api/apm/service-map\" | \"GET /api/apm/service-map/service/{serviceName}\" | \"GET /api/apm/service-map/backend/{backendName}\" | \"GET /api/apm/services/{serviceName}/serviceNodes\" | \"GET /api/apm/services\" | \"GET /api/apm/services/detailed_statistics\" | \"GET /api/apm/services/{serviceName}/metadata/details\" | \"GET /api/apm/services/{serviceName}/metadata/icons\" | \"GET /api/apm/services/{serviceName}/agent\" | \"GET /api/apm/services/{serviceName}/transaction_types\" | \"GET /api/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search\" | \"POST /api/apm/services/{serviceName}/annotation\" | \"GET /api/apm/services/{serviceName}/error_groups/main_statistics\" | \"GET /api/apm/services/{serviceName}/error_groups/detailed_statistics\" | \"GET /api/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /api/apm/services/{serviceName}/throughput\" | \"GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /api/apm/services/{serviceName}/dependencies\" | \"GET /api/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /api/apm/services/{serviceName}/profiling/timeline\" | \"GET /api/apm/services/{serviceName}/profiling/statistics\" | \"GET /api/apm/services/{serviceName}/alerts\" | \"GET /api/apm/services/{serviceName}/infrastructure\" | \"GET /internal/apm/suggestions\" | \"GET /api/apm/traces/{traceId}\" | \"GET /api/apm/traces\" | \"GET /api/apm/traces/{traceId}/root_transaction\" | \"GET /api/apm/transactions/{transactionId}\" | \"GET /api/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /api/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /api/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /api/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /api/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /api/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /api/apm/alerts/chart_preview/transaction_error_rate\" | \"GET /api/apm/alerts/chart_preview/transaction_duration\" | \"GET /api/apm/alerts/chart_preview/transaction_error_count\" | \"GET /api/apm/settings/agent-configuration\" | \"GET /api/apm/settings/agent-configuration/view\" | \"DELETE /api/apm/settings/agent-configuration\" | \"PUT /api/apm/settings/agent-configuration\" | \"POST /api/apm/settings/agent-configuration/search\" | \"GET /api/apm/settings/agent-configuration/services\" | \"GET /api/apm/settings/agent-configuration/environments\" | \"GET /api/apm/settings/agent-configuration/agent_name\" | \"GET /api/apm/settings/anomaly-detection/jobs\" | \"POST /api/apm/settings/anomaly-detection/jobs\" | \"GET /api/apm/settings/anomaly-detection/environments\" | \"GET /api/apm/settings/apm-index-settings\" | \"GET /api/apm/settings/apm-indices\" | \"POST /api/apm/settings/apm-indices/save\" | \"GET /api/apm/settings/custom_links/transaction\" | \"GET /api/apm/settings/custom_links\" | \"POST /api/apm/settings/custom_links\" | \"PUT /api/apm/settings/custom_links/{id}\" | \"DELETE /api/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps\" | \"POST /api/apm/sourcemaps\" | \"DELETE /api/apm/sourcemaps/{id}\" | \"GET /api/apm/fleet/has_data\" | \"GET /api/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema\" | \"GET /api/apm/fleet/apm_server_schema/unsupported\" | \"GET /api/apm/fleet/migration_check\" | \"POST /api/apm/fleet/cloud_apm_package_policy\" | \"GET /api/apm/backends/top_backends\" | \"GET /api/apm/backends/{backendName}/upstream_services\" | \"GET /api/apm/backends/{backendName}/metadata\" | \"GET /api/apm/backends/{backendName}/charts/latency\" | \"GET /api/apm/backends/{backendName}/charts/throughput\" | \"GET /api/apm/backends/{backendName}/charts/error_rate\" | \"GET /api/apm/fallback_to_transactions\" | \"GET /api/apm/has_data\" | \"GET /api/apm/event_metadata/{processorEvent}/{id}\"" + "\"POST /internal/apm/index_pattern/static\" | \"GET /internal/apm/index_pattern/dynamic\" | \"GET /internal/apm/environments\" | \"GET /internal/apm/services/{serviceName}/errors\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}\" | \"GET /internal/apm/services/{serviceName}/errors/distribution\" | \"GET /internal/apm/services/{serviceName}/metrics/charts\" | \"GET /internal/apm/observability_overview\" | \"GET /internal/apm/observability_overview/has_data\" | \"GET /api/apm/rum/client-metrics\" | \"GET /api/apm/rum-client/page-load-distribution\" | \"GET /api/apm/rum-client/page-load-distribution/breakdown\" | \"GET /api/apm/rum-client/page-view-trends\" | \"GET /api/apm/rum-client/services\" | \"GET /api/apm/rum-client/visitor-breakdown\" | \"GET /api/apm/rum-client/web-core-vitals\" | \"GET /api/apm/rum-client/long-task-metrics\" | \"GET /api/apm/rum-client/url-search\" | \"GET /api/apm/rum-client/js-errors\" | \"GET /api/apm/observability_overview/has_rum_data\" | \"GET /internal/apm/service-map\" | \"GET /internal/apm/service-map/service/{serviceName}\" | \"GET /internal/apm/service-map/backend/{backendName}\" | \"GET /internal/apm/services/{serviceName}/serviceNodes\" | \"GET /internal/apm/services\" | \"GET /internal/apm/services/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/metadata/details\" | \"GET /internal/apm/services/{serviceName}/metadata/icons\" | \"GET /internal/apm/services/{serviceName}/agent\" | \"GET /internal/apm/services/{serviceName}/transaction_types\" | \"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search\" | \"POST /api/apm/services/{serviceName}/annotation\" | \"GET /internal/apm/services/{serviceName}/error_groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /internal/apm/services/{serviceName}/throughput\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/dependencies\" | \"GET /internal/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /internal/apm/services/{serviceName}/profiling/timeline\" | \"GET /internal/apm/services/{serviceName}/profiling/statistics\" | \"GET /internal/apm/services/{serviceName}/alerts\" | \"GET /internal/apm/services/{serviceName}/infrastructure\" | \"GET /internal/apm/suggestions\" | \"GET /internal/apm/traces/{traceId}\" | \"GET /internal/apm/traces\" | \"GET /internal/apm/traces/{traceId}/root_transaction\" | \"GET /internal/apm/transactions/{transactionId}\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /internal/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /internal/apm/alerts/chart_preview/transaction_error_rate\" | \"GET /internal/apm/alerts/chart_preview/transaction_duration\" | \"GET /internal/apm/alerts/chart_preview/transaction_error_count\" | \"GET /api/apm/settings/agent-configuration\" | \"GET /api/apm/settings/agent-configuration/view\" | \"DELETE /api/apm/settings/agent-configuration\" | \"PUT /api/apm/settings/agent-configuration\" | \"POST /api/apm/settings/agent-configuration/search\" | \"GET /api/apm/settings/agent-configuration/services\" | \"GET /api/apm/settings/agent-configuration/environments\" | \"GET /api/apm/settings/agent-configuration/agent_name\" | \"GET /internal/apm/settings/anomaly-detection/jobs\" | \"POST /internal/apm/settings/anomaly-detection/jobs\" | \"GET /internal/apm/settings/anomaly-detection/environments\" | \"GET /internal/apm/settings/apm-index-settings\" | \"GET /internal/apm/settings/apm-indices\" | \"POST /internal/apm/settings/apm-indices/save\" | \"GET /internal/apm/settings/custom_links/transaction\" | \"GET /internal/apm/settings/custom_links\" | \"POST /internal/apm/settings/custom_links\" | \"PUT /internal/apm/settings/custom_links/{id}\" | \"DELETE /internal/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps\" | \"POST /api/apm/sourcemaps\" | \"DELETE /api/apm/sourcemaps/{id}\" | \"GET /internal/apm/fleet/has_data\" | \"GET /internal/apm/fleet/agents\" | \"POST /internal/apm/fleet/apm_server_schema\" | \"GET /internal/apm/fleet/apm_server_schema/unsupported\" | \"GET /internal/apm/fleet/migration_check\" | \"POST /internal/apm/fleet/cloud_apm_package_policy\" | \"GET /internal/apm/backends/top_backends\" | \"GET /internal/apm/backends/{backendName}/upstream_services\" | \"GET /internal/apm/backends/{backendName}/metadata\" | \"GET /internal/apm/backends/{backendName}/charts/latency\" | \"GET /internal/apm/backends/{backendName}/charts/throughput\" | \"GET /internal/apm/backends/{backendName}/charts/error_rate\" | \"GET /internal/apm/fallback_to_transactions\" | \"GET /internal/apm/has_data\" | \"GET /internal/apm/event_metadata/{processorEvent}/{id}\"" ], "path": "x-pack/plugins/apm/server/routes/get_global_apm_server_route_repository.ts", "deprecated": false, @@ -858,7 +858,7 @@ }, ", ", "APMRouteCreateOptions", - ", { \"POST /api/apm/index_pattern/static\": ", + ", { \"POST /internal/apm/index_pattern/static\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -866,7 +866,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"POST /api/apm/index_pattern/static\", undefined, ", + "<\"POST /internal/apm/index_pattern/static\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -876,7 +876,7 @@ }, ", { created: boolean; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/index_pattern/dynamic\": ", + ">; } & { \"GET /internal/apm/index_pattern/dynamic\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -884,7 +884,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/index_pattern/dynamic\", undefined, ", + "<\"GET /internal/apm/index_pattern/dynamic\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -896,7 +896,7 @@ "IndexPatternTitleAndFields", " | undefined; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/environments\": ", + ">; } & { \"GET /internal/apm/environments\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -904,7 +904,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/environments\", ", + "<\"GET /internal/apm/environments\", ", "TypeC", "<{ query: ", "IntersectionC", @@ -928,7 +928,7 @@ }, ", { environments: string[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/errors\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/errors\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -936,7 +936,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/errors\", ", + "<\"GET /internal/apm/services/{serviceName}/errors\", ", "TypeC", "<{ path: ", "TypeC", @@ -988,7 +988,7 @@ }, ", { errorGroups: { message: string; occurrenceCount: number; culprit: string | undefined; groupId: string; latestOccurrenceAt: string; handled: boolean | undefined; type: string | undefined; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/errors/{groupId}\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/errors/{groupId}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -996,7 +996,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/errors/{groupId}\", ", + "<\"GET /internal/apm/services/{serviceName}/errors/{groupId}\", ", "TypeC", "<{ path: ", "TypeC", @@ -1044,7 +1044,7 @@ "APMError", "; occurrencesCount: number; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/errors/distribution\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/errors/distribution\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -1052,7 +1052,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/errors/distribution\", ", + "<\"GET /internal/apm/services/{serviceName}/errors/distribution\", ", "TypeC", "<{ path: ", "TypeC", @@ -1098,7 +1098,7 @@ }, ", { noHits: boolean; buckets: { key: number; count: number; }[]; bucketSize: number; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/metrics/charts\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/metrics/charts\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -1106,7 +1106,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/metrics/charts\", ", + "<\"GET /internal/apm/services/{serviceName}/metrics/charts\", ", "TypeC", "<{ path: ", "TypeC", @@ -1158,7 +1158,7 @@ "MetricsChartsByAgentAPIResponse", ", ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/observability_overview\": ", + ">; } & { \"GET /internal/apm/observability_overview\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -1166,7 +1166,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/observability_overview\", ", + "<\"GET /internal/apm/observability_overview\", ", "TypeC", "<{ query: ", "IntersectionC", @@ -1190,7 +1190,7 @@ }, ", { serviceCount: number; transactionPerMinute: { value: undefined; timeseries: never[]; } | { value: number; timeseries: { x: number; y: number | null; }[]; }; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/observability_overview/has_data\": ", + ">; } & { \"GET /internal/apm/observability_overview/has_data\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -1198,7 +1198,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/observability_overview/has_data\", undefined, ", + "<\"GET /internal/apm/observability_overview/has_data\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -1642,7 +1642,7 @@ }, ", { indices: string; hasData: boolean; serviceName: string | number | undefined; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/service-map\": ", + ">; } & { \"GET /internal/apm/service-map\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -1650,7 +1650,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/service-map\", ", + "<\"GET /internal/apm/service-map\", ", "TypeC", "<{ query: ", "IntersectionC", @@ -1692,7 +1692,7 @@ "ServiceAnomalyStats", " | undefined; label: string | undefined; id?: string | undefined; parent?: string | undefined; position?: cytoscape.Position | undefined; } | { 'span.destination.service.resource': string; 'span.type': string; 'span.subtype': string; label: string | undefined; id?: string | undefined; parent?: string | undefined; position?: cytoscape.Position | undefined; } | { id: string; source: string | undefined; target: string | undefined; label: string | undefined; bidirectional?: boolean | undefined; isInverseEdge?: boolean | undefined; } | undefined)[]; }; } | { data: { id: string; source: string; target: string; }; })[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/service-map/service/{serviceName}\": ", + ">; } & { \"GET /internal/apm/service-map/service/{serviceName}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -1700,7 +1700,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/service-map/service/{serviceName}\", ", + "<\"GET /internal/apm/service-map/service/{serviceName}\", ", "TypeC", "<{ path: ", "TypeC", @@ -1738,7 +1738,7 @@ }, ", { avgMemoryUsage: number | null; avgCpuUsage: number | null; transactionStats: { avgTransactionDuration: number | null; avgRequestsPerMinute: number | null; }; avgErrorRate: number | null; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/service-map/backend/{backendName}\": ", + ">; } & { \"GET /internal/apm/service-map/backend/{backendName}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -1746,7 +1746,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/service-map/backend/{backendName}\", ", + "<\"GET /internal/apm/service-map/backend/{backendName}\", ", "TypeC", "<{ path: ", "TypeC", @@ -1784,7 +1784,7 @@ }, ", { avgErrorRate: null; transactionStats: { avgRequestsPerMinute: null; avgTransactionDuration: null; }; } | { avgErrorRate: number; transactionStats: { avgRequestsPerMinute: number; avgTransactionDuration: number; }; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/serviceNodes\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/serviceNodes\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -1792,7 +1792,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/serviceNodes\", ", + "<\"GET /internal/apm/services/{serviceName}/serviceNodes\", ", "TypeC", "<{ path: ", "TypeC", @@ -1834,7 +1834,7 @@ }, ", { serviceNodes: { name: string; cpu: number | null; heapMemory: number | null; hostName: string | null | undefined; nonHeapMemory: number | null; threadCount: number | null; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services\": ", + ">; } & { \"GET /internal/apm/services\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -1842,7 +1842,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services\", ", + "<\"GET /internal/apm/services\", ", "TypeC", "<{ query: ", "IntersectionC", @@ -1892,7 +1892,7 @@ "ServiceHealthStatus", "; }>; hasLegacyData: boolean; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/detailed_statistics\": ", + ">; } & { \"GET /internal/apm/services/detailed_statistics\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -1900,7 +1900,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/detailed_statistics\", ", + "<\"GET /internal/apm/services/detailed_statistics\", ", "TypeC", "<{ query: ", "IntersectionC", @@ -1946,7 +1946,7 @@ }, ", { currentPeriod: _.Dictionary<{ serviceName: string; latency: { x: number; y: number | null; }[]; transactionErrorRate: { x: number; y: number; }[]; throughput: { x: number; y: number; }[]; }>; previousPeriod: _.Dictionary<{ serviceName: string; latency: { x: number; y: number | null; }[]; transactionErrorRate: { x: number; y: number; }[]; throughput: { x: number; y: number; }[]; }>; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/metadata/details\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/metadata/details\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -1954,7 +1954,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/metadata/details\", ", + "<\"GET /internal/apm/services/{serviceName}/metadata/details\", ", "TypeC", "<{ path: ", "TypeC", @@ -1978,7 +1978,7 @@ "ServiceMetadataDetails", ", ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/metadata/icons\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/metadata/icons\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -1986,7 +1986,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/metadata/icons\", ", + "<\"GET /internal/apm/services/{serviceName}/metadata/icons\", ", "TypeC", "<{ path: ", "TypeC", @@ -2010,7 +2010,7 @@ "ServiceMetadataIcons", ", ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/agent\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/agent\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2018,7 +2018,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/agent\", ", + "<\"GET /internal/apm/services/{serviceName}/agent\", ", "TypeC", "<{ path: ", "TypeC", @@ -2040,7 +2040,7 @@ }, ", { agentName?: undefined; runtimeName?: undefined; } | { agentName: string | undefined; runtimeName: string | undefined; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/transaction_types\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/transaction_types\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2048,7 +2048,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/transaction_types\", ", + "<\"GET /internal/apm/services/{serviceName}/transaction_types\", ", "TypeC", "<{ path: ", "TypeC", @@ -2070,7 +2070,7 @@ }, ", { transactionTypes: string[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/node/{serviceNodeName}/metadata\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2078,7 +2078,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/node/{serviceNodeName}/metadata\", ", + "<\"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\", ", "TypeC", "<{ path: ", "TypeC", @@ -2206,7 +2206,7 @@ "Annotation", "; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/error_groups/main_statistics\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/error_groups/main_statistics\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2214,7 +2214,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/error_groups/main_statistics\", ", + "<\"GET /internal/apm/services/{serviceName}/error_groups/main_statistics\", ", "TypeC", "<{ path: ", "TypeC", @@ -2260,7 +2260,7 @@ }, ", { is_aggregation_accurate: boolean; error_groups: { group_id: string; name: string; lastSeen: number; occurrences: number; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/error_groups/detailed_statistics\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2268,7 +2268,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/error_groups/detailed_statistics\", ", + "<\"GET /internal/apm/services/{serviceName}/error_groups/detailed_statistics\", ", "TypeC", "<{ path: ", "TypeC", @@ -2326,7 +2326,7 @@ "Coordinate", "[]; }>; previousPeriod: _.Dictionary<{ timeseries: { x: number; y: number | null | undefined; }[]; groupId: string; }>; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2334,7 +2334,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\", ", + "<\"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\", ", "TypeC", "<{ path: ", "TypeC", @@ -2380,7 +2380,7 @@ "Cloud", " | undefined; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/throughput\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/throughput\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2388,7 +2388,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/throughput\", ", + "<\"GET /internal/apm/services/{serviceName}/throughput\", ", "TypeC", "<{ path: ", "TypeC", @@ -2448,7 +2448,7 @@ "ThroughputUnit", "; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2456,7 +2456,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics\", ", + "<\"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\", ", "TypeC", "<{ path: ", "TypeC", @@ -2522,7 +2522,7 @@ }, ", { currentPeriod: { serviceNodeName: string; errorRate?: number | undefined; latency?: number | undefined; throughput?: number | undefined; cpuUsage?: number | null | undefined; memoryUsage?: number | null | undefined; }[]; previousPeriod: { serviceNodeName: string; errorRate?: number | undefined; latency?: number | undefined; throughput?: number | undefined; cpuUsage?: number | null | undefined; memoryUsage?: number | null | undefined; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2530,7 +2530,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics\", ", + "<\"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\", ", "TypeC", "<{ path: ", "TypeC", @@ -2610,7 +2610,7 @@ "Coordinate", "[] | undefined; }>; previousPeriod: _.Dictionary<{ cpuUsage: { x: number; y: number | null | undefined; }[]; errorRate: { x: number; y: number | null | undefined; }[]; latency: { x: number; y: number | null | undefined; }[]; memoryUsage: { x: number; y: number | null | undefined; }[]; throughput: { x: number; y: number | null | undefined; }[]; serviceNodeName: string; }>; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/dependencies\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/dependencies\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2618,7 +2618,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/dependencies\", ", + "<\"GET /internal/apm/services/{serviceName}/dependencies\", ", "TypeC", "<{ path: ", "TypeC", @@ -2682,7 +2682,7 @@ "Node", "; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/dependencies/breakdown\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/dependencies/breakdown\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2690,7 +2690,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/dependencies/breakdown\", ", + "<\"GET /internal/apm/services/{serviceName}/dependencies/breakdown\", ", "TypeC", "<{ path: ", "TypeC", @@ -2732,7 +2732,7 @@ }, ", { breakdown: { title: string; data: { x: number; y: number; }[]; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/profiling/timeline\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/profiling/timeline\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2740,7 +2740,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/profiling/timeline\", ", + "<\"GET /internal/apm/services/{serviceName}/profiling/timeline\", ", "TypeC", "<{ path: ", "TypeC", @@ -2782,7 +2782,7 @@ }, ", { profilingTimeline: { x: number; valueTypes: { wall_time: number; cpu_time: number; samples: number; alloc_objects: number; alloc_space: number; inuse_objects: number; inuse_space: number; unknown: number; }; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/profiling/statistics\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/profiling/statistics\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2790,7 +2790,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/profiling/statistics\", ", + "<\"GET /internal/apm/services/{serviceName}/profiling/statistics\", ", "TypeC", "<{ path: ", "TypeC", @@ -2866,7 +2866,7 @@ "ProfileNode", ">; rootNodes: string[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/alerts\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/alerts\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2874,7 +2874,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/alerts\", ", + "<\"GET /internal/apm/services/{serviceName}/alerts\", ", "TypeC", "<{ path: ", "TypeC", @@ -2916,7 +2916,7 @@ }, ", { alerts: Partial>[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/infrastructure\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/infrastructure\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -2924,7 +2924,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/infrastructure\", ", + "<\"GET /internal/apm/services/{serviceName}/infrastructure\", ", "TypeC", "<{ path: ", "TypeC", @@ -2992,7 +2992,7 @@ }, ", { terms: string[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/traces/{traceId}\": ", + ">; } & { \"GET /internal/apm/traces/{traceId}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3000,7 +3000,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/traces/{traceId}\", ", + "<\"GET /internal/apm/traces/{traceId}\", ", "TypeC", "<{ path: ", "TypeC", @@ -3028,7 +3028,7 @@ "APMError", "[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/traces\": ", + ">; } & { \"GET /internal/apm/traces\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3036,7 +3036,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/traces\", ", + "<\"GET /internal/apm/traces\", ", "TypeC", "<{ query: ", "IntersectionC", @@ -3076,7 +3076,7 @@ "TransactionGroup", "[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/traces/{traceId}/root_transaction\": ", + ">; } & { \"GET /internal/apm/traces/{traceId}/root_transaction\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3084,7 +3084,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/traces/{traceId}/root_transaction\", ", + "<\"GET /internal/apm/traces/{traceId}/root_transaction\", ", "TypeC", "<{ path: ", "TypeC", @@ -3102,7 +3102,7 @@ "Transaction", "; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/transactions/{transactionId}\": ", + ">; } & { \"GET /internal/apm/transactions/{transactionId}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3110,7 +3110,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/transactions/{transactionId}\", ", + "<\"GET /internal/apm/transactions/{transactionId}\", ", "TypeC", "<{ path: ", "TypeC", @@ -3128,7 +3128,7 @@ "Transaction", "; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/transactions/groups/main_statistics\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3136,7 +3136,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/transactions/groups/main_statistics\", ", + "<\"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\", ", "TypeC", "<{ path: ", "TypeC", @@ -3196,7 +3196,7 @@ }, ", { transactionGroups: { transactionType: string; name: string; latency: number | null; throughput: number; errorRate: number; impact: number; }[]; isAggregationAccurate: boolean; bucketSize: number; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/transactions/groups/detailed_statistics\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3204,7 +3204,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/transactions/groups/detailed_statistics\", ", + "<\"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\", ", "TypeC", "<{ path: ", "TypeC", @@ -3280,7 +3280,7 @@ "Coordinate", "[]; impact: number; }>; previousPeriod: _.Dictionary<{ errorRate: { x: number; y: number | null | undefined; }[]; throughput: { x: number; y: number | null | undefined; }[]; latency: { x: number; y: number | null | undefined; }[]; transactionName: string; impact: number; }>; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/transactions/charts/latency\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/transactions/charts/latency\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3288,7 +3288,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/transactions/charts/latency\", ", + "<\"GET /internal/apm/services/{serviceName}/transactions/charts/latency\", ", "TypeC", "<{ path: ", "TypeC", @@ -3360,7 +3360,7 @@ }, ", { currentPeriod: { overallAvgDuration: number | null; latencyTimeseries: { x: number; y: number | null; }[]; }; previousPeriod: { latencyTimeseries: { x: number; y: number | null | undefined; }[]; overallAvgDuration: number | null; }; anomalyTimeseries: { jobId: string; anomalyScore: { x0: number; x: number; y: number; }[]; anomalyBoundaries: { x: number; y0: number; y: number; }[]; } | undefined; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/transactions/traces/samples\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/transactions/traces/samples\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3368,7 +3368,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/transactions/traces/samples\", ", + "<\"GET /internal/apm/services/{serviceName}/transactions/traces/samples\", ", "TypeC", "<{ path: ", "TypeC", @@ -3426,7 +3426,7 @@ }, ", { noHits: boolean; traceSamples: { transactionId: string; traceId: string; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/transaction/charts/breakdown\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3434,7 +3434,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/transaction/charts/breakdown\", ", + "<\"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\", ", "TypeC", "<{ path: ", "TypeC", @@ -3484,7 +3484,7 @@ }, ", { timeseries: { title: string; color: string; type: string; data: { x: number; y: number | null; }[]; hideLegend: boolean; legendValue: string; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/services/{serviceName}/transactions/charts/error_rate\": ", + ">; } & { \"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3492,7 +3492,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/services/{serviceName}/transactions/charts/error_rate\", ", + "<\"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\", ", "TypeC", "<{ path: ", "TypeC", @@ -3552,7 +3552,7 @@ "Coordinate", "[]; average: number | null; }; previousPeriod: { transactionErrorRate: { x: number; y: number | null | undefined; }[]; noHits: boolean; average: number | null; }; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/alerts/chart_preview/transaction_error_rate\": ", + ">; } & { \"GET /internal/apm/alerts/chart_preview/transaction_error_rate\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3560,7 +3560,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/alerts/chart_preview/transaction_error_rate\", ", + "<\"GET /internal/apm/alerts/chart_preview/transaction_error_rate\", ", "TypeC", "<{ query: ", "IntersectionC", @@ -3612,7 +3612,7 @@ }, ", { errorRateChartPreview: { x: number; y: number; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/alerts/chart_preview/transaction_duration\": ", + ">; } & { \"GET /internal/apm/alerts/chart_preview/transaction_duration\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3620,7 +3620,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/alerts/chart_preview/transaction_duration\", ", + "<\"GET /internal/apm/alerts/chart_preview/transaction_duration\", ", "TypeC", "<{ query: ", "IntersectionC", @@ -3672,7 +3672,7 @@ }, ", { latencyChartPreview: { x: number; y: number | null; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/alerts/chart_preview/transaction_error_count\": ", + ">; } & { \"GET /internal/apm/alerts/chart_preview/transaction_error_count\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3680,7 +3680,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/alerts/chart_preview/transaction_error_count\", ", + "<\"GET /internal/apm/alerts/chart_preview/transaction_error_count\", ", "TypeC", "<{ query: ", "IntersectionC", @@ -3968,7 +3968,7 @@ }, ", { agentName: string | undefined; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/settings/anomaly-detection/jobs\": ", + ">; } & { \"GET /internal/apm/settings/anomaly-detection/jobs\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3976,7 +3976,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/settings/anomaly-detection/jobs\", undefined, ", + "<\"GET /internal/apm/settings/anomaly-detection/jobs\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -3986,7 +3986,7 @@ }, ", { jobs: { job_id: string; environment: string; }[]; hasLegacyJobs: boolean; }, ", "APMRouteCreateOptions", - ">; } & { \"POST /api/apm/settings/anomaly-detection/jobs\": ", + ">; } & { \"POST /internal/apm/settings/anomaly-detection/jobs\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -3994,7 +3994,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"POST /api/apm/settings/anomaly-detection/jobs\", ", + "<\"POST /internal/apm/settings/anomaly-detection/jobs\", ", "TypeC", "<{ body: ", "TypeC", @@ -4012,7 +4012,7 @@ }, ", { jobCreated: boolean; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/settings/anomaly-detection/environments\": ", + ">; } & { \"GET /internal/apm/settings/anomaly-detection/environments\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4020,7 +4020,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/settings/anomaly-detection/environments\", undefined, ", + "<\"GET /internal/apm/settings/anomaly-detection/environments\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -4030,7 +4030,7 @@ }, ", { environments: string[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/settings/apm-index-settings\": ", + ">; } & { \"GET /internal/apm/settings/apm-index-settings\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4038,7 +4038,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/settings/apm-index-settings\", undefined, ", + "<\"GET /internal/apm/settings/apm-index-settings\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -4048,7 +4048,7 @@ }, ", { apmIndexSettings: { configurationName: \"apm_oss.sourcemapIndices\" | \"apm_oss.errorIndices\" | \"apm_oss.onboardingIndices\" | \"apm_oss.spanIndices\" | \"apm_oss.transactionIndices\" | \"apm_oss.metricsIndices\" | \"apmAgentConfigurationIndex\" | \"apmCustomLinkIndex\"; defaultValue: string; savedValue: string | undefined; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/settings/apm-indices\": ", + ">; } & { \"GET /internal/apm/settings/apm-indices\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4056,7 +4056,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/settings/apm-indices\", undefined, ", + "<\"GET /internal/apm/settings/apm-indices\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -4068,7 +4068,7 @@ "ApmIndicesConfig", ", ", "APMRouteCreateOptions", - ">; } & { \"POST /api/apm/settings/apm-indices/save\": ", + ">; } & { \"POST /internal/apm/settings/apm-indices/save\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4076,7 +4076,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"POST /api/apm/settings/apm-indices/save\", ", + "<\"POST /internal/apm/settings/apm-indices/save\", ", "TypeC", "<{ body: ", "PartialC", @@ -4104,7 +4104,7 @@ "SavedObject", "<{}>, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/settings/custom_links/transaction\": ", + ">; } & { \"GET /internal/apm/settings/custom_links/transaction\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4112,7 +4112,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/settings/custom_links/transaction\", ", + "<\"GET /internal/apm/settings/custom_links/transaction\", ", "PartialC", "<{ query: ", "PartialC", @@ -4136,7 +4136,7 @@ "Transaction", ", ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/settings/custom_links\": ", + ">; } & { \"GET /internal/apm/settings/custom_links\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4144,7 +4144,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/settings/custom_links\", ", + "<\"GET /internal/apm/settings/custom_links\", ", "PartialC", "<{ query: ", "PartialC", @@ -4168,7 +4168,7 @@ "CustomLink", "[]; }, ", "APMRouteCreateOptions", - ">; } & { \"POST /api/apm/settings/custom_links\": ", + ">; } & { \"POST /internal/apm/settings/custom_links\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4176,7 +4176,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"POST /api/apm/settings/custom_links\", ", + "<\"POST /internal/apm/settings/custom_links\", ", "TypeC", "<{ body: ", "IntersectionC", @@ -4220,7 +4220,7 @@ }, ", void, ", "APMRouteCreateOptions", - ">; } & { \"PUT /api/apm/settings/custom_links/{id}\": ", + ">; } & { \"PUT /internal/apm/settings/custom_links/{id}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4228,7 +4228,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"PUT /api/apm/settings/custom_links/{id}\", ", + "<\"PUT /internal/apm/settings/custom_links/{id}\", ", "TypeC", "<{ path: ", "TypeC", @@ -4276,7 +4276,7 @@ }, ", void, ", "APMRouteCreateOptions", - ">; } & { \"DELETE /api/apm/settings/custom_links/{id}\": ", + ">; } & { \"DELETE /internal/apm/settings/custom_links/{id}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4284,7 +4284,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"DELETE /api/apm/settings/custom_links/{id}\", ", + "<\"DELETE /internal/apm/settings/custom_links/{id}\", ", "TypeC", "<{ path: ", "TypeC", @@ -4382,7 +4382,7 @@ }, ", void, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/fleet/has_data\": ", + ">; } & { \"GET /internal/apm/fleet/has_data\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4390,7 +4390,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/fleet/has_data\", undefined, ", + "<\"GET /internal/apm/fleet/has_data\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -4400,7 +4400,7 @@ }, ", { hasData: boolean; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/fleet/agents\": ", + ">; } & { \"GET /internal/apm/fleet/agents\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4408,7 +4408,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/fleet/agents\", undefined, ", + "<\"GET /internal/apm/fleet/agents\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -4418,7 +4418,7 @@ }, ", { cloudStandaloneSetup: { apmServerUrl: string | undefined; secretToken: string | undefined; } | undefined; isFleetEnabled: boolean; fleetAgents: { id: string; name: string; apmServerUrl: any; secretToken: any; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"POST /api/apm/fleet/apm_server_schema\": ", + ">; } & { \"POST /internal/apm/fleet/apm_server_schema\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4426,7 +4426,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"POST /api/apm/fleet/apm_server_schema\", ", + "<\"POST /internal/apm/fleet/apm_server_schema\", ", "TypeC", "<{ body: ", "TypeC", @@ -4446,7 +4446,7 @@ }, ", void, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/fleet/apm_server_schema/unsupported\": ", + ">; } & { \"GET /internal/apm/fleet/apm_server_schema/unsupported\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4454,7 +4454,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/fleet/apm_server_schema/unsupported\", undefined, ", + "<\"GET /internal/apm/fleet/apm_server_schema/unsupported\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -4464,7 +4464,7 @@ }, ", { unsupported: { key: string; value: any; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/fleet/migration_check\": ", + ">; } & { \"GET /internal/apm/fleet/migration_check\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4472,7 +4472,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/fleet/migration_check\", undefined, ", + "<\"GET /internal/apm/fleet/migration_check\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -4482,7 +4482,7 @@ }, ", { has_cloud_agent_policy: boolean; has_cloud_apm_package_policy: boolean; cloud_apm_migration_enabled: boolean; has_required_role: boolean | undefined; }, ", "APMRouteCreateOptions", - ">; } & { \"POST /api/apm/fleet/cloud_apm_package_policy\": ", + ">; } & { \"POST /internal/apm/fleet/cloud_apm_package_policy\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4490,7 +4490,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"POST /api/apm/fleet/cloud_apm_package_policy\", undefined, ", + "<\"POST /internal/apm/fleet/cloud_apm_package_policy\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -4508,7 +4508,7 @@ }, "; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/backends/top_backends\": ", + ">; } & { \"GET /internal/apm/backends/top_backends\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4516,7 +4516,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/backends/top_backends\", ", + "<\"GET /internal/apm/backends/top_backends\", ", "IntersectionC", "<[", "TypeC", @@ -4584,7 +4584,7 @@ "Node", "; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/backends/{backendName}/upstream_services\": ", + ">; } & { \"GET /internal/apm/backends/{backendName}/upstream_services\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4592,7 +4592,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/backends/{backendName}/upstream_services\", ", + "<\"GET /internal/apm/backends/{backendName}/upstream_services\", ", "IntersectionC", "<[", "TypeC", @@ -4666,7 +4666,7 @@ "Node", "; }[]; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/backends/{backendName}/metadata\": ", + ">; } & { \"GET /internal/apm/backends/{backendName}/metadata\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4674,7 +4674,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/backends/{backendName}/metadata\", ", + "<\"GET /internal/apm/backends/{backendName}/metadata\", ", "TypeC", "<{ path: ", "TypeC", @@ -4696,7 +4696,7 @@ }, ", { metadata: { spanType: string | undefined; spanSubtype: string | undefined; }; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/backends/{backendName}/charts/latency\": ", + ">; } & { \"GET /internal/apm/backends/{backendName}/charts/latency\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4704,7 +4704,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/backends/{backendName}/charts/latency\", ", + "<\"GET /internal/apm/backends/{backendName}/charts/latency\", ", "TypeC", "<{ path: ", "TypeC", @@ -4750,7 +4750,7 @@ }, ", { currentTimeseries: { x: number; y: number; }[]; comparisonTimeseries: { x: number; y: number; }[] | null; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/backends/{backendName}/charts/throughput\": ", + ">; } & { \"GET /internal/apm/backends/{backendName}/charts/throughput\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4758,7 +4758,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/backends/{backendName}/charts/throughput\", ", + "<\"GET /internal/apm/backends/{backendName}/charts/throughput\", ", "TypeC", "<{ path: ", "TypeC", @@ -4804,7 +4804,7 @@ }, ", { currentTimeseries: { x: number; y: number | null; }[]; comparisonTimeseries: { x: number; y: number | null; }[] | null; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/backends/{backendName}/charts/error_rate\": ", + ">; } & { \"GET /internal/apm/backends/{backendName}/charts/error_rate\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4812,7 +4812,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/backends/{backendName}/charts/error_rate\", ", + "<\"GET /internal/apm/backends/{backendName}/charts/error_rate\", ", "TypeC", "<{ path: ", "TypeC", @@ -4858,7 +4858,7 @@ }, ", { currentTimeseries: { x: number; y: number; }[]; comparisonTimeseries: { x: number; y: number; }[] | null; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/fallback_to_transactions\": ", + ">; } & { \"GET /internal/apm/fallback_to_transactions\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4866,7 +4866,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/fallback_to_transactions\", ", + "<\"GET /internal/apm/fallback_to_transactions\", ", "PartialC", "<{ query: ", "IntersectionC", @@ -4890,7 +4890,7 @@ }, ", { fallbackToTransactions: boolean; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/has_data\": ", + ">; } & { \"GET /internal/apm/has_data\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4898,7 +4898,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/has_data\", undefined, ", + "<\"GET /internal/apm/has_data\", undefined, ", { "pluginId": "apm", "scope": "server", @@ -4908,7 +4908,7 @@ }, ", { hasData: boolean; }, ", "APMRouteCreateOptions", - ">; } & { \"GET /api/apm/event_metadata/{processorEvent}/{id}\": ", + ">; } & { \"GET /internal/apm/event_metadata/{processorEvent}/{id}\": ", { "pluginId": "@kbn/server-route-repository", "scope": "server", @@ -4916,7 +4916,7 @@ "section": "def-server.ServerRoute", "text": "ServerRoute" }, - "<\"GET /api/apm/event_metadata/{processorEvent}/{id}\", ", + "<\"GET /internal/apm/event_metadata/{processorEvent}/{id}\", ", "TypeC", "<{ path: ", "TypeC", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index a3fb1df512fa..d9e53e6ec9df 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -10,7 +10,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex --- import apmObj from './apm.json'; - +The user interface for Elastic APM Contact [APM UI](https://github.com/orgs/elastic/teams/apm-ui) for questions regarding this plugin. diff --git a/api_docs/charts.json b/api_docs/charts.json index 9f6d07287eba..5d4f047a247e 100644 --- a/api_docs/charts.json +++ b/api_docs/charts.json @@ -975,9 +975,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchemas", + "section": "def-common.ColorSchemas", "text": "ColorSchemas" } ], @@ -1004,7 +1004,7 @@ "tags": [], "label": "ColorSchemaParams", "description": [], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false, "children": [ { @@ -1017,13 +1017,13 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchemas", + "section": "def-common.ColorSchemas", "text": "ColorSchemas" } ], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false }, { @@ -1033,7 +1033,7 @@ "tags": [], "label": "invertColors", "description": [], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false } ], @@ -1255,7 +1255,7 @@ "tags": [], "label": "Labels", "description": [], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false, "children": [ { @@ -1268,7 +1268,7 @@ "signature": [ "string | undefined" ], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false }, { @@ -1281,7 +1281,7 @@ "signature": [ "boolean | undefined" ], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false }, { @@ -1294,7 +1294,7 @@ "signature": [ "boolean | undefined" ], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false }, { @@ -1307,7 +1307,7 @@ "signature": [ "number | undefined" ], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false }, { @@ -1320,7 +1320,7 @@ "signature": [ "boolean | undefined" ], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false }, { @@ -1333,7 +1333,7 @@ "signature": [ "number | null | undefined" ], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false } ], @@ -1830,9 +1830,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchemas", + "section": "def-common.ColorSchemas", "text": "ColorSchemas" } ], @@ -1923,7 +1923,7 @@ "tags": [], "label": "Style", "description": [], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false, "children": [ { @@ -1933,7 +1933,7 @@ "tags": [], "label": "bgFill", "description": [], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false }, { @@ -1943,7 +1943,7 @@ "tags": [], "label": "bgColor", "description": [], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false }, { @@ -1953,7 +1953,7 @@ "tags": [], "label": "labelColor", "description": [], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false }, { @@ -1963,7 +1963,7 @@ "tags": [], "label": "subText", "description": [], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false }, { @@ -1973,7 +1973,7 @@ "tags": [], "label": "fontSize", "description": [], - "path": "src/plugins/charts/public/static/components/types.ts", + "path": "src/plugins/charts/common/types.ts", "deprecated": false } ], @@ -2041,9 +2041,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchema", + "section": "def-common.ColorSchema", "text": "ColorSchema" }, "[]" @@ -2117,9 +2117,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchema", + "section": "def-common.ColorSchema", "text": "ColorSchema" }, "[]" @@ -2202,9 +2202,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchemas", + "section": "def-common.ColorSchemas", "text": "ColorSchemas" }, ".Blues" @@ -2257,9 +2257,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchemas", + "section": "def-common.ColorSchemas", "text": "ColorSchemas" }, ".Greens" @@ -2312,9 +2312,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchemas", + "section": "def-common.ColorSchemas", "text": "ColorSchemas" }, ".Greys" @@ -2367,9 +2367,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchemas", + "section": "def-common.ColorSchemas", "text": "ColorSchemas" }, ".Reds" @@ -2422,9 +2422,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchemas", + "section": "def-common.ColorSchemas", "text": "ColorSchemas" }, ".YellowToRed" @@ -2477,9 +2477,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchemas", + "section": "def-common.ColorSchemas", "text": "ColorSchemas" }, ".GreenToRed" @@ -2946,6 +2946,51 @@ "common": { "classes": [], "functions": [ + { + "parentPluginId": "charts", + "id": "def-common.getHeatmapColors", + "type": "Function", + "tags": [], + "label": "getHeatmapColors", + "description": [], + "signature": [ + "(value: any, colorSchemaName: string) => string" + ], + "path": "src/plugins/charts/common/static/color_maps/heatmap_color.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.getHeatmapColors.$1", + "type": "Any", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "any" + ], + "path": "src/plugins/charts/common/static/color_maps/heatmap_color.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "charts", + "id": "def-common.getHeatmapColors.$2", + "type": "string", + "tags": [], + "label": "colorSchemaName", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/charts/common/static/color_maps/heatmap_color.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "charts", "id": "def-common.palette", @@ -3084,6 +3129,116 @@ } ], "interfaces": [ + { + "parentPluginId": "charts", + "id": "def-common.ColorMap", + "type": "Interface", + "tags": [], + "label": "ColorMap", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.ColorMap.Unnamed", + "type": "Any", + "tags": [], + "label": "Unnamed", + "description": [], + "signature": [ + "any" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-common.ColorSchema", + "type": "Interface", + "tags": [], + "label": "ColorSchema", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.ColorSchema.value", + "type": "Enum", + "tags": [], + "label": "value", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchemas", + "text": "ColorSchemas" + } + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.ColorSchema.text", + "type": "string", + "tags": [], + "label": "text", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-common.ColorSchemaParams", + "type": "Interface", + "tags": [], + "label": "ColorSchemaParams", + "description": [], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.ColorSchemaParams.colorSchema", + "type": "Enum", + "tags": [], + "label": "colorSchema", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchemas", + "text": "ColorSchemas" + } + ], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.ColorSchemaParams.invertColors", + "type": "boolean", + "tags": [], + "label": "invertColors", + "description": [], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "charts", "id": "def-common.CustomPaletteArguments", @@ -3293,6 +3448,97 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "charts", + "id": "def-common.Labels", + "type": "Interface", + "tags": [], + "label": "Labels", + "description": [], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.Labels.color", + "type": "string", + "tags": [], + "label": "color", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.Labels.filter", + "type": "CompoundType", + "tags": [], + "label": "filter", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.Labels.overwriteColor", + "type": "CompoundType", + "tags": [], + "label": "overwriteColor", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.Labels.rotate", + "type": "number", + "tags": [], + "label": "rotate", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.Labels.show", + "type": "CompoundType", + "tags": [], + "label": "show", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.Labels.truncate", + "type": "CompoundType", + "tags": [], + "label": "truncate", + "description": [], + "signature": [ + "number | null | undefined" + ], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "charts", "id": "def-common.PaletteOutput", @@ -3354,29 +3600,159 @@ }, { "parentPluginId": "charts", - "id": "def-common.SystemPaletteArguments", + "id": "def-common.RawColorSchema", "type": "Interface", "tags": [], - "label": "SystemPaletteArguments", + "label": "RawColorSchema", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false, "children": [ { "parentPluginId": "charts", - "id": "def-common.SystemPaletteArguments.name", - "type": "string", + "id": "def-common.RawColorSchema.id", + "type": "Enum", "tags": [], - "label": "name", + "label": "id", "description": [], - "path": "src/plugins/charts/common/palette.ts", + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchemas", + "text": "ColorSchemas" + } + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", "deprecated": false - } - ], - "initialIsOpen": false - } + }, + { + "parentPluginId": "charts", + "id": "def-common.RawColorSchema.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.RawColorSchema.value", + "type": "Array", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "[number, number[]][]" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-common.Style", + "type": "Interface", + "tags": [], + "label": "Style", + "description": [], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.Style.bgFill", + "type": "string", + "tags": [], + "label": "bgFill", + "description": [], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.Style.bgColor", + "type": "boolean", + "tags": [], + "label": "bgColor", + "description": [], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.Style.labelColor", + "type": "boolean", + "tags": [], + "label": "labelColor", + "description": [], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.Style.subText", + "type": "string", + "tags": [], + "label": "subText", + "description": [], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.Style.fontSize", + "type": "number", + "tags": [], + "label": "fontSize", + "description": [], + "path": "src/plugins/charts/common/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-common.SystemPaletteArguments", + "type": "Interface", + "tags": [], + "label": "SystemPaletteArguments", + "description": [], + "path": "src/plugins/charts/common/palette.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.SystemPaletteArguments.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "src/plugins/charts/common/palette.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [ + { + "parentPluginId": "charts", + "id": "def-common.ColorSchemas", + "type": "Enum", + "tags": [], + "label": "ColorSchemas", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false, + "initialIsOpen": false + } ], - "enums": [], "misc": [ { "parentPluginId": "charts", @@ -3392,6 +3768,52 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "charts", + "id": "def-common.ColorMode", + "type": "Type", + "tags": [], + "label": "ColorMode", + "description": [], + "signature": [ + "\"Background\" | \"Labels\" | \"None\"" + ], + "path": "src/plugins/charts/common/static/components/collections.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-common.colorSchemas", + "type": "Array", + "tags": [], + "label": "colorSchemas", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchema", + "text": "ColorSchema" + }, + "[]" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-common.defaultCountLabel", + "type": "string", + "tags": [], + "label": "defaultCountLabel", + "description": [], + "path": "src/plugins/charts/common/static/components/collections.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "charts", "id": "def-common.defaultCustomColors", @@ -3406,6 +3828,20 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "charts", + "id": "def-common.LabelRotation", + "type": "Type", + "tags": [], + "label": "LabelRotation", + "description": [], + "signature": [ + "number" + ], + "path": "src/plugins/charts/common/static/components/collections.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "charts", "id": "def-common.paletteIds", @@ -3419,8 +3855,415 @@ "path": "src/plugins/charts/common/constants.ts", "deprecated": false, "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-common.truncatedColorSchemas", + "type": "Array", + "tags": [], + "label": "truncatedColorSchemas", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchema", + "text": "ColorSchema" + }, + "[]" + ], + "path": "src/plugins/charts/common/static/color_maps/truncated_color_maps.ts", + "deprecated": false, + "initialIsOpen": false } ], - "objects": [] + "objects": [ + { + "parentPluginId": "charts", + "id": "def-common.ColorMode", + "type": "Object", + "tags": [], + "label": "ColorMode", + "description": [], + "signature": [ + "{ readonly Background: \"Background\"; readonly Labels: \"Labels\"; readonly None: \"None\"; }" + ], + "path": "src/plugins/charts/common/static/components/collections.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-common.LabelRotation", + "type": "Object", + "tags": [], + "label": "LabelRotation", + "description": [], + "signature": [ + "{ readonly Horizontal: number; readonly Vertical: number; readonly Angled: number; }" + ], + "path": "src/plugins/charts/common/static/components/collections.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-common.truncatedColorMaps", + "type": "Object", + "tags": [], + "label": "truncatedColorMaps", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/truncated_color_maps.ts", + "deprecated": false, + "children": [], + "initialIsOpen": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps", + "type": "Object", + "tags": [], + "label": "vislibColorMaps", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Blues", + "type": "Object", + "tags": [], + "label": "[ColorSchemas.Blues]", + "description": [ + "// Sequential" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Blues.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchemas", + "text": "ColorSchemas" + }, + ".Blues" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Blues.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Blues.value", + "type": "Array", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "[number, number[]][]" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Greens", + "type": "Object", + "tags": [], + "label": "[ColorSchemas.Greens]", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Greens.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchemas", + "text": "ColorSchemas" + }, + ".Greens" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Greens.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Greens.value", + "type": "Array", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "[number, number[]][]" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Greys", + "type": "Object", + "tags": [], + "label": "[ColorSchemas.Greys]", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Greys.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchemas", + "text": "ColorSchemas" + }, + ".Greys" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Greys.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Greys.value", + "type": "Array", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "[number, number[]][]" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Reds", + "type": "Object", + "tags": [], + "label": "[ColorSchemas.Reds]", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Reds.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchemas", + "text": "ColorSchemas" + }, + ".Reds" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Reds.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.Reds.value", + "type": "Array", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "[number, number[]][]" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.YellowToRed", + "type": "Object", + "tags": [], + "label": "[ColorSchemas.YellowToRed]", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.YellowToRed.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchemas", + "text": "ColorSchemas" + }, + ".YellowToRed" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.YellowToRed.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.YellowToRed.value", + "type": "Array", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "[number, number[]][]" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.GreenToRed", + "type": "Object", + "tags": [], + "label": "[ColorSchemas.GreenToRed]", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.GreenToRed.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchemas", + "text": "ColorSchemas" + }, + ".GreenToRed" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.GreenToRed.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + }, + { + "parentPluginId": "charts", + "id": "def-common.vislibColorMaps.ColorSchemas.GreenToRed.value", + "type": "Array", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "[number, number[]][]" + ], + "path": "src/plugins/charts/common/static/color_maps/color_maps.ts", + "deprecated": false + } + ] + } + ], + "initialIsOpen": false + } + ] } } \ No newline at end of file diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index c8159a3bc0df..8d31f6ad1d64 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -18,7 +18,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 223 | 2 | 192 | 3 | +| 285 | 4 | 253 | 3 | ## Client @@ -53,12 +53,18 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) ## Common +### Objects + + ### Functions ### Interfaces +### Enums + + ### Consts, variables and types diff --git a/api_docs/core.json b/api_docs/core.json index 8b64ef86dbf1..c288037b4486 100644 --- a/api_docs/core.json +++ b/api_docs/core.json @@ -1603,7 +1603,7 @@ "label": "links", "description": [], "signature": [ - "{ readonly settings: string; readonly apm: { readonly kibanaSettings: string; readonly supportedServiceMaps: string; readonly customLinks: string; readonly droppedTransactionSpans: string; readonly upgrading: string; readonly metaData: string; }; readonly canvas: { readonly guide: string; }; readonly dashboard: { readonly guide: string; readonly drilldowns: string; readonly drilldownsTriggerPicker: string; readonly urlDrilldownTemplateSyntax: string; readonly urlDrilldownVariables: string; }; readonly discover: Record; readonly filebeat: { readonly base: string; readonly installation: string; readonly configuration: string; readonly elasticsearchOutput: string; readonly elasticsearchModule: string; readonly startup: string; readonly exportedFields: string; readonly suricataModule: string; readonly zeekModule: string; }; readonly auditbeat: { readonly base: string; readonly auditdModule: string; readonly systemModule: string; }; readonly metricbeat: { readonly base: string; readonly configure: string; readonly httpEndpoint: string; readonly install: string; readonly start: string; }; readonly enterpriseSearch: { readonly base: string; readonly appSearchBase: string; readonly workplaceSearchBase: string; }; readonly heartbeat: { readonly base: string; }; readonly libbeat: { readonly getStarted: string; }; readonly logstash: { readonly base: string; }; readonly functionbeat: { readonly base: string; }; readonly winlogbeat: { readonly base: string; }; readonly aggs: { readonly composite: string; readonly composite_missing_bucket: string; readonly date_histogram: string; readonly date_range: string; readonly date_format_pattern: string; readonly filter: string; readonly filters: string; readonly geohash_grid: string; readonly histogram: string; readonly ip_range: string; readonly range: string; readonly significant_terms: string; readonly terms: string; readonly avg: string; readonly avg_bucket: string; readonly max_bucket: string; readonly min_bucket: string; readonly sum_bucket: string; readonly cardinality: string; readonly count: string; readonly cumulative_sum: string; readonly derivative: string; readonly geo_bounds: string; readonly geo_centroid: string; readonly max: string; readonly median: string; readonly min: string; readonly moving_avg: string; readonly percentile_ranks: string; readonly serial_diff: string; readonly std_dev: string; readonly sum: string; readonly top_hits: string; }; readonly runtimeFields: { readonly overview: string; readonly mapping: string; }; readonly scriptedFields: { readonly scriptFields: string; readonly scriptAggs: string; readonly painless: string; readonly painlessApi: string; readonly painlessLangSpec: string; readonly painlessSyntax: string; readonly painlessWalkthrough: string; readonly luceneExpressions: string; }; readonly search: { readonly sessions: string; readonly sessionLimits: string; }; readonly indexPatterns: { readonly introduction: string; readonly fieldFormattersNumber: string; readonly fieldFormattersString: string; readonly runtimeFields: string; }; readonly addData: string; readonly kibana: string; readonly upgradeAssistant: string; readonly rollupJobs: string; readonly elasticsearch: Record; readonly siem: { readonly privileges: string; readonly guide: string; readonly gettingStarted: string; readonly ml: string; readonly ruleChangeLog: string; readonly detectionsReq: string; readonly networkMap: string; }; readonly query: { readonly eql: string; readonly kueryQuerySyntax: string; readonly luceneQuerySyntax: string; readonly percolate: string; readonly queryDsl: string; readonly autocompleteChanges: string; }; readonly date: { readonly dateMath: string; readonly dateMathIndexNames: string; }; readonly management: Record; readonly ml: Record; readonly transforms: Record; readonly visualize: Record; readonly apis: Readonly<{ bulkIndexAlias: string; byteSizeUnits: string; createAutoFollowPattern: string; createFollower: string; createIndex: string; createSnapshotLifecyclePolicy: string; createRoleMapping: string; createRoleMappingTemplates: string; createRollupJobsRequest: string; createApiKey: string; createPipeline: string; createTransformRequest: string; cronExpressions: string; executeWatchActionModes: string; indexExists: string; openIndex: string; putComponentTemplate: string; painlessExecute: string; painlessExecuteAPIContexts: string; putComponentTemplateMetadata: string; putSnapshotLifecyclePolicy: string; putIndexTemplateV1: string; putWatch: string; simulatePipeline: string; timeUnits: string; updateTransform: string; }>; readonly observability: Readonly<{ guide: string; infrastructureThreshold: string; logsThreshold: string; metricsThreshold: string; monitorStatus: string; monitorUptime: string; tlsCertificate: string; uptimeDurationAnomaly: string; }>; readonly alerting: Record; readonly maps: Record; readonly monitoring: Record; readonly security: Readonly<{ apiKeyServiceSettings: string; clusterPrivileges: string; elasticsearchSettings: string; elasticsearchEnableSecurity: string; indicesPrivileges: string; kibanaTLS: string; kibanaPrivileges: string; mappingRoles: string; mappingRolesFieldRules: string; runAsPrivilege: string; }>; readonly watcher: Record; readonly ccs: Record; readonly plugins: Record; readonly snapshotRestore: Record; readonly ingest: Record; readonly fleet: Readonly<{ guide: string; fleetServer: string; fleetServerAddFleetServer: string; settings: string; settingsFleetServerHostSettings: string; troubleshooting: string; elasticAgent: string; datastreams: string; datastreamsNamingScheme: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; }>; readonly ecs: { readonly guide: string; }; }" + "{ readonly settings: string; readonly apm: { readonly kibanaSettings: string; readonly supportedServiceMaps: string; readonly customLinks: string; readonly droppedTransactionSpans: string; readonly upgrading: string; readonly metaData: string; }; readonly canvas: { readonly guide: string; }; readonly dashboard: { readonly guide: string; readonly drilldowns: string; readonly drilldownsTriggerPicker: string; readonly urlDrilldownTemplateSyntax: string; readonly urlDrilldownVariables: string; }; readonly discover: Record; readonly filebeat: { readonly base: string; readonly installation: string; readonly configuration: string; readonly elasticsearchOutput: string; readonly elasticsearchModule: string; readonly startup: string; readonly exportedFields: string; readonly suricataModule: string; readonly zeekModule: string; }; readonly auditbeat: { readonly base: string; readonly auditdModule: string; readonly systemModule: string; }; readonly metricbeat: { readonly base: string; readonly configure: string; readonly httpEndpoint: string; readonly install: string; readonly start: string; }; readonly enterpriseSearch: { readonly base: string; readonly appSearchBase: string; readonly workplaceSearchBase: string; }; readonly heartbeat: { readonly base: string; }; readonly libbeat: { readonly getStarted: string; }; readonly logstash: { readonly base: string; }; readonly functionbeat: { readonly base: string; }; readonly winlogbeat: { readonly base: string; }; readonly aggs: { readonly composite: string; readonly composite_missing_bucket: string; readonly date_histogram: string; readonly date_range: string; readonly date_format_pattern: string; readonly filter: string; readonly filters: string; readonly geohash_grid: string; readonly histogram: string; readonly ip_range: string; readonly range: string; readonly significant_terms: string; readonly terms: string; readonly avg: string; readonly avg_bucket: string; readonly max_bucket: string; readonly min_bucket: string; readonly sum_bucket: string; readonly cardinality: string; readonly count: string; readonly cumulative_sum: string; readonly derivative: string; readonly geo_bounds: string; readonly geo_centroid: string; readonly max: string; readonly median: string; readonly min: string; readonly moving_avg: string; readonly percentile_ranks: string; readonly serial_diff: string; readonly std_dev: string; readonly sum: string; readonly top_hits: string; }; readonly runtimeFields: { readonly overview: string; readonly mapping: string; }; readonly scriptedFields: { readonly scriptFields: string; readonly scriptAggs: string; readonly painless: string; readonly painlessApi: string; readonly painlessLangSpec: string; readonly painlessSyntax: string; readonly painlessWalkthrough: string; readonly luceneExpressions: string; }; readonly search: { readonly sessions: string; readonly sessionLimits: string; }; readonly indexPatterns: { readonly introduction: string; readonly fieldFormattersNumber: string; readonly fieldFormattersString: string; readonly runtimeFields: string; }; readonly addData: string; readonly kibana: string; readonly upgradeAssistant: string; readonly rollupJobs: string; readonly elasticsearch: Record; readonly siem: { readonly privileges: string; readonly guide: string; readonly gettingStarted: string; readonly ml: string; readonly ruleChangeLog: string; readonly detectionsReq: string; readonly networkMap: string; readonly troubleshootGaps: string; }; readonly query: { readonly eql: string; readonly kueryQuerySyntax: string; readonly luceneQuerySyntax: string; readonly percolate: string; readonly queryDsl: string; readonly autocompleteChanges: string; }; readonly date: { readonly dateMath: string; readonly dateMathIndexNames: string; }; readonly management: Record; readonly ml: Record; readonly transforms: Record; readonly visualize: Record; readonly apis: Readonly<{ bulkIndexAlias: string; byteSizeUnits: string; createAutoFollowPattern: string; createFollower: string; createIndex: string; createSnapshotLifecyclePolicy: string; createRoleMapping: string; createRoleMappingTemplates: string; createRollupJobsRequest: string; createApiKey: string; createPipeline: string; createTransformRequest: string; cronExpressions: string; executeWatchActionModes: string; indexExists: string; openIndex: string; putComponentTemplate: string; painlessExecute: string; painlessExecuteAPIContexts: string; putComponentTemplateMetadata: string; putSnapshotLifecyclePolicy: string; putIndexTemplateV1: string; putWatch: string; simulatePipeline: string; timeUnits: string; updateTransform: string; }>; readonly observability: Readonly<{ guide: string; infrastructureThreshold: string; logsThreshold: string; metricsThreshold: string; monitorStatus: string; monitorUptime: string; tlsCertificate: string; uptimeDurationAnomaly: string; }>; readonly alerting: Record; readonly maps: Record; readonly monitoring: Record; readonly security: Readonly<{ apiKeyServiceSettings: string; clusterPrivileges: string; elasticsearchSettings: string; elasticsearchEnableSecurity: string; indicesPrivileges: string; kibanaTLS: string; kibanaPrivileges: string; mappingRoles: string; mappingRolesFieldRules: string; runAsPrivilege: string; }>; readonly spaces: Readonly<{ kibanaLegacyUrlAliases: string; kibanaDisableLegacyUrlAliasesApi: string; }>; readonly watcher: Record; readonly ccs: Record; readonly plugins: Record; readonly snapshotRestore: Record; readonly ingest: Record; readonly fleet: Readonly<{ guide: string; fleetServer: string; fleetServerAddFleetServer: string; settings: string; settingsFleetServerHostSettings: string; troubleshooting: string; elasticAgent: string; datastreams: string; datastreamsNamingScheme: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; learnMoreBlog: string; }>; readonly ecs: { readonly guide: string; }; readonly clients: { readonly guide: string; readonly goOverview: string; readonly javaIndex: string; readonly jsIntro: string; readonly netGuide: string; readonly perlGuide: string; readonly phpGuide: string; readonly pythonGuide: string; readonly rubyOverview: string; readonly rustGuide: string; }; }" ], "path": "src/core/public/doc_links/doc_links_service.ts", "deprecated": false @@ -1611,40 +1611,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "core", - "id": "def-public.DomainDeprecationDetails", - "type": "Interface", - "tags": [], - "label": "DomainDeprecationDetails", - "description": [], - "signature": [ - "DomainDeprecationDetails", - " extends ", - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.DeprecationsDetails", - "text": "DeprecationsDetails" - } - ], - "path": "src/core/server/deprecations/types.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "core", - "id": "def-public.DomainDeprecationDetails.domainId", - "type": "string", - "tags": [], - "label": "domainId", - "description": [], - "path": "src/core/server/deprecations/types.ts", - "deprecated": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "core", "id": "def-public.EnvironmentMode", @@ -8281,6 +8247,45 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "core", + "id": "def-server.ConfigDeprecationContext", + "type": "Interface", + "tags": [], + "label": "ConfigDeprecationContext", + "description": [ + "\nDeprecation context provided to {@link ConfigDeprecation | config deprecations}\n" + ], + "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "core", + "id": "def-server.ConfigDeprecationContext.version", + "type": "string", + "tags": [], + "label": "version", + "description": [ + "The current Kibana version, e.g `7.16.1`, `8.0.0`" + ], + "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.ConfigDeprecationContext.branch", + "type": "string", + "tags": [], + "label": "branch", + "description": [ + "The current Kibana branch, e.g `7.x`, `7.16`, `master`" + ], + "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "core", "id": "def-server.ConfigDeprecationFactory", @@ -8306,7 +8311,13 @@ "(deprecatedKey: string, removeBy: string, details?: Partial<", "DeprecatedConfigDetails", "> | undefined) => ", - "ConfigDeprecation" + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + } ], "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", "deprecated": false, @@ -8371,7 +8382,13 @@ "(deprecatedKey: string, removeBy: string, details?: Partial<", "DeprecatedConfigDetails", "> | undefined) => ", - "ConfigDeprecation" + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + } ], "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", "deprecated": false, @@ -8436,7 +8453,13 @@ "(oldKey: string, newKey: string, details?: Partial<", "DeprecatedConfigDetails", "> | undefined) => ", - "ConfigDeprecation" + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + } ], "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", "deprecated": false, @@ -8501,7 +8524,13 @@ "(oldKey: string, newKey: string, details?: Partial<", "DeprecatedConfigDetails", "> | undefined) => ", - "ConfigDeprecation" + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + } ], "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", "deprecated": false, @@ -8566,7 +8595,13 @@ "(unusedKey: string, details?: Partial<", "DeprecatedConfigDetails", "> | undefined) => ", - "ConfigDeprecation" + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + } ], "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", "deprecated": false, @@ -8617,7 +8652,13 @@ "(unusedKey: string, details?: Partial<", "DeprecatedConfigDetails", "> | undefined) => ", - "ConfigDeprecation" + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + } ], "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", "deprecated": false, @@ -9552,7 +9593,9 @@ "type": "string", "tags": [], "label": "documentationUrl", - "description": [], + "description": [ + "(optional) link to the documentation for more details on the deprecation." + ], "signature": [ "string | undefined" ], @@ -9565,7 +9608,9 @@ "type": "CompoundType", "tags": [], "label": "requireRestart", - "description": [], + "description": [ + "(optional) specify the fix for this deprecation requires a full kibana restart." + ], "signature": [ "boolean | undefined" ], @@ -9578,7 +9623,9 @@ "type": "Object", "tags": [], "label": "correctiveActions", - "description": [], + "description": [ + "corrective action needed to fix this deprecation." + ], "signature": [ "{ api?: { path: string; method: \"PUT\" | \"POST\"; body?: { [key: string]: any; } | undefined; } | undefined; manualSteps: string[]; }" ], @@ -16830,6 +16877,123 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "core", + "id": "def-server.ConfigDeprecation", + "type": "Type", + "tags": [], + "label": "ConfigDeprecation", + "description": [ + "\nConfiguration deprecation returned from {@link ConfigDeprecationProvider} that handles a single deprecation from the configuration.\n" + ], + "signature": [ + "(config: Readonly<{ [x: string]: any; }>, fromPath: string, addDeprecation: ", + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.AddConfigDeprecation", + "text": "AddConfigDeprecation" + }, + ", context: ", + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecationContext", + "text": "ConfigDeprecationContext" + }, + ") => void | ", + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecationCommand", + "text": "ConfigDeprecationCommand" + } + ], + "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "core", + "id": "def-server.ConfigDeprecation.$1", + "type": "Object", + "tags": [], + "label": "config", + "description": [ + "must not be mutated, return {@link ConfigDeprecationCommand} to change config shape." + ], + "signature": [ + "{ readonly [x: string]: any; }" + ], + "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.ConfigDeprecation.$2", + "type": "string", + "tags": [], + "label": "fromPath", + "description": [], + "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", + "deprecated": false + }, + { + "parentPluginId": "core", + "id": "def-server.ConfigDeprecation.$3", + "type": "Function", + "tags": [], + "label": "addDeprecation", + "description": [], + "signature": [ + "(details: ", + "DeprecatedConfigDetails", + ") => void" + ], + "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "core", + "id": "def-server.ConfigDeprecation.$3.$1", + "type": "Object", + "tags": [], + "label": "details", + "description": [], + "signature": [ + "DeprecatedConfigDetails" + ], + "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "core", + "id": "def-server.ConfigDeprecation.$4", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecationContext", + "text": "ConfigDeprecationContext" + } + ], + "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "core", "id": "def-server.ConfigDeprecationProvider", @@ -16849,7 +17013,13 @@ "text": "ConfigDeprecationFactory" }, ") => ", - "ConfigDeprecation", + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + }, "[]" ], "path": "node_modules/@kbn/config/target_types/deprecation/types.d.ts", @@ -17974,7 +18144,7 @@ "EcsHttp", " | undefined; log?: Pick<", "EcsLog", - ", \"origin\" | \"original\" | \"file\" | \"syslog\"> | undefined; network?: ", + ", \"origin\" | \"file\" | \"syslog\"> | undefined; network?: ", "EcsNetwork", " | undefined; observer?: ", "EcsObserver", diff --git a/api_docs/core.mdx b/api_docs/core.mdx index 856db3cf8871..018a9f1beda6 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2293 | 27 | 1020 | 29 | +| 2300 | 27 | 1019 | 29 | ## Client diff --git a/api_docs/core_application.json b/api_docs/core_application.json index 71e4244af778..52f50c364fdb 100644 --- a/api_docs/core_application.json +++ b/api_docs/core_application.json @@ -1664,7 +1664,7 @@ "tags": [], "label": "euiIconType", "description": [ - "\nA EUI iconType that will be used for the app's icon. This icon\ntakes precendence over the `icon` property." + "\nA EUI iconType that will be used for the app's icon. This icon\ntakes precedence over the `icon` property." ], "signature": [ "string | undefined" diff --git a/api_docs/core_application.mdx b/api_docs/core_application.mdx index bf9e610a24b7..1c01073421f6 100644 --- a/api_docs/core_application.mdx +++ b/api_docs/core_application.mdx @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2293 | 27 | 1020 | 29 | +| 2300 | 27 | 1019 | 29 | ## Client diff --git a/api_docs/core_chrome.mdx b/api_docs/core_chrome.mdx index c217d2ae66f7..18244445385c 100644 --- a/api_docs/core_chrome.mdx +++ b/api_docs/core_chrome.mdx @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2293 | 27 | 1020 | 29 | +| 2300 | 27 | 1019 | 29 | ## Client diff --git a/api_docs/core_http.json b/api_docs/core_http.json index f6345d6f6e93..94ee961f265b 100644 --- a/api_docs/core_http.json +++ b/api_docs/core_http.json @@ -1249,7 +1249,7 @@ "tags": [], "label": "fetch", "description": [ - "Makes an HTTP request. Defaults to a GET request unless overriden. See {@link HttpHandler} for options." + "Makes an HTTP request. Defaults to a GET request unless overridden. See {@link HttpHandler} for options." ], "signature": [ { diff --git a/api_docs/core_http.mdx b/api_docs/core_http.mdx index 739916c56ecf..ae5747c711b9 100644 --- a/api_docs/core_http.mdx +++ b/api_docs/core_http.mdx @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2293 | 27 | 1020 | 29 | +| 2300 | 27 | 1019 | 29 | ## Client diff --git a/api_docs/core_saved_objects.json b/api_docs/core_saved_objects.json index f5b601e346de..91af1d2465c3 100644 --- a/api_docs/core_saved_objects.json +++ b/api_docs/core_saved_objects.json @@ -5600,7 +5600,7 @@ "tags": [], "label": "objectTransformError", "description": [ - "\nError returned when a {@link SavedObjectsExportTransform | export tranform} threw an error" + "\nError returned when a {@link SavedObjectsExportTransform | export transform} threw an error" ], "signature": [ "(objects: ", @@ -5656,7 +5656,7 @@ "tags": [], "label": "invalidTransformError", "description": [ - "\nError returned when a {@link SavedObjectsExportTransform | export tranform} performed an invalid operation\nduring the transform, such as removing objects from the export, or changing an object's type or id." + "\nError returned when a {@link SavedObjectsExportTransform | export transform} performed an invalid operation\nduring the transform, such as removing objects from the export, or changing an object's type or id." ], "signature": [ "(objectKeys: string[]) => ", @@ -6599,7 +6599,7 @@ ], "label": "resolveImportErrors", "description": [ - "\nResolve and return saved object import errors.\nSee the {@link SavedObjectsResolveImportErrorsOptions | options} for more detailed informations.\n" + "\nResolve and return saved object import errors.\nSee the {@link SavedObjectsResolveImportErrorsOptions | options} for more detailed information.\n" ], "signature": [ "({ readStream, createNewCopies, namespace, retries, }: ", @@ -16148,7 +16148,7 @@ "tags": [], "label": "SavedObjectsClientContract", "description": [ - "\nSaved Objects is Kibana's data persisentence mechanism allowing plugins to\nuse Elasticsearch for storing plugin state.\n\n## SavedObjectsClient errors\n\nSince the SavedObjectsClient has its hands in everything we\nare a little paranoid about the way we present errors back to\nto application code. Ideally, all errors will be either:\n\n 1. Caused by bad implementation (ie. undefined is not a function) and\n as such unpredictable\n 2. An error that has been classified and decorated appropriately\n by the decorators in {@link SavedObjectsErrorHelpers}\n\nType 1 errors are inevitable, but since all expected/handle-able errors\nshould be Type 2 the `isXYZError()` helpers exposed at\n`SavedObjectsErrorHelpers` should be used to understand and manage error\nresponses from the `SavedObjectsClient`.\n\nType 2 errors are decorated versions of the source error, so if\nthe elasticsearch client threw an error it will be decorated based\non its type. That means that rather than looking for `error.body.error.type` or\ndoing substring checks on `error.body.error.reason`, just use the helpers to\nunderstand the meaning of the error:\n\n ```js\n if (SavedObjectsErrorHelpers.isNotFoundError(error)) {\n // handle 404\n }\n\n if (SavedObjectsErrorHelpers.isNotAuthorizedError(error)) {\n // 401 handling should be automatic, but in case you wanted to know\n }\n\n // always rethrow the error unless you handle it\n throw error;\n ```\n\n### 404s from missing index\n\nFrom the perspective of application code and APIs the SavedObjectsClient is\na black box that persists objects. One of the internal details that users have\nno control over is that we use an elasticsearch index for persistance and that\nindex might be missing.\n\nAt the time of writing we are in the process of transitioning away from the\noperating assumption that the SavedObjects index is always available. Part of\nthis transition is handling errors resulting from an index missing. These used\nto trigger a 500 error in most cases, and in others cause 404s with different\nerror messages.\n\nFrom my (Spencer) perspective, a 404 from the SavedObjectsApi is a 404; The\nobject the request/call was targeting could not be found. This is why #14141\ntakes special care to ensure that 404 errors are generic and don't distinguish\nbetween index missing or document missing.\n\nSee {@link SavedObjectsClient}\nSee {@link SavedObjectsErrorHelpers}\n" + "\nSaved Objects is Kibana's data persisentence mechanism allowing plugins to\nuse Elasticsearch for storing plugin state.\n\n## SavedObjectsClient errors\n\nSince the SavedObjectsClient has its hands in everything we\nare a little paranoid about the way we present errors back to\nto application code. Ideally, all errors will be either:\n\n 1. Caused by bad implementation (ie. undefined is not a function) and\n as such unpredictable\n 2. An error that has been classified and decorated appropriately\n by the decorators in {@link SavedObjectsErrorHelpers}\n\nType 1 errors are inevitable, but since all expected/handle-able errors\nshould be Type 2 the `isXYZError()` helpers exposed at\n`SavedObjectsErrorHelpers` should be used to understand and manage error\nresponses from the `SavedObjectsClient`.\n\nType 2 errors are decorated versions of the source error, so if\nthe elasticsearch client threw an error it will be decorated based\non its type. That means that rather than looking for `error.body.error.type` or\ndoing substring checks on `error.body.error.reason`, just use the helpers to\nunderstand the meaning of the error:\n\n ```js\n if (SavedObjectsErrorHelpers.isNotFoundError(error)) {\n // handle 404\n }\n\n if (SavedObjectsErrorHelpers.isNotAuthorizedError(error)) {\n // 401 handling should be automatic, but in case you wanted to know\n }\n\n // always rethrow the error unless you handle it\n throw error;\n ```\n\n### 404s from missing index\n\nFrom the perspective of application code and APIs the SavedObjectsClient is\na black box that persists objects. One of the internal details that users have\nno control over is that we use an elasticsearch index for persistence and that\nindex might be missing.\n\nAt the time of writing we are in the process of transitioning away from the\noperating assumption that the SavedObjects index is always available. Part of\nthis transition is handling errors resulting from an index missing. These used\nto trigger a 500 error in most cases, and in others cause 404s with different\nerror messages.\n\nFrom my (Spencer) perspective, a 404 from the SavedObjectsApi is a 404; The\nobject the request/call was targeting could not be found. This is why #14141\ntakes special care to ensure that 404 errors are generic and don't distinguish\nbetween index missing or document missing.\n\nSee {@link SavedObjectsClient}\nSee {@link SavedObjectsErrorHelpers}\n" ], "signature": [ "{ get: (type: string, id: string, options?: ", diff --git a/api_docs/core_saved_objects.mdx b/api_docs/core_saved_objects.mdx index 66d66b3d41c1..5fc7bc63466b 100644 --- a/api_docs/core_saved_objects.mdx +++ b/api_docs/core_saved_objects.mdx @@ -18,7 +18,7 @@ Contact [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) for que | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2293 | 27 | 1020 | 29 | +| 2300 | 27 | 1019 | 29 | ## Client diff --git a/api_docs/dashboard.json b/api_docs/dashboard.json index f43e6e3923ca..b42846c747bf 100644 --- a/api_docs/dashboard.json +++ b/api_docs/dashboard.json @@ -2426,15 +2426,15 @@ "references": [ { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx" + "path": "x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href.ts" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href.ts" + "path": "x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_links.tsx" }, { "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_links.tsx" + "path": "x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx" } ] }, diff --git a/api_docs/dashboard_enhanced.json b/api_docs/dashboard_enhanced.json index 9712b25f7377..92bdfbc11b3f 100644 --- a/api_docs/dashboard_enhanced.json +++ b/api_docs/dashboard_enhanced.json @@ -617,7 +617,15 @@ "section": "def-server.SerializableRecord", "text": "SerializableRecord" }, - ">): void; }" + ">): void; setAnonymousAccessServiceProvider: (provider: () => ", + { + "pluginId": "share", + "scope": "common", + "docId": "kibSharePluginApi", + "section": "def-common.AnonymousAccessServiceContract", + "text": "AnonymousAccessServiceContract" + }, + ") => void; }" ], "path": "x-pack/plugins/dashboard_enhanced/public/plugin.ts", "deprecated": false diff --git a/api_docs/data.json b/api_docs/data.json index 31d0a919b2d4..ab02b81539cf 100644 --- a/api_docs/data.json +++ b/api_docs/data.json @@ -4170,35 +4170,35 @@ }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", @@ -12311,10 +12311,6 @@ "plugin": "dataViews", "path": "src/plugins/data_views/common/fields/utils.ts" }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/utils.ts" - }, { "plugin": "dataViews", "path": "src/plugins/data_views/common/fields/data_view_field.ts" @@ -13309,14 +13305,6 @@ "plugin": "monitoring", "path": "x-pack/plugins/monitoring/public/lib/kuery.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts" @@ -13529,14 +13517,6 @@ "plugin": "timelines", "path": "x-pack/plugins/timelines/public/mock/index_pattern.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/target/types/common/search_strategy/index_fields/index.d.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/target/types/common/search_strategy/index_fields/index.d.ts" - }, { "plugin": "infra", "path": "x-pack/plugins/infra/target/types/public/containers/with_kuery_autocompletion.d.ts" @@ -13801,6 +13781,14 @@ "plugin": "infra", "path": "x-pack/plugins/infra/public/pages/metrics/index.tsx" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts" @@ -15902,7 +15890,7 @@ "section": "def-common.FilterMeta", "text": "FilterMeta" }, - "; exists?: { field: string; } | undefined; }" + "; query: { exists?: { field: string; } | undefined; }; }" ], "path": "src/plugins/data/common/es_query/index.ts", "deprecated": true, @@ -18097,6 +18085,11 @@ ], "label": "IFieldSubType", "description": [], + "signature": [ + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional" + ], "path": "src/plugins/data/common/es_query/index.ts", "deprecated": true, "removeBy": "8.1", @@ -19798,9 +19791,9 @@ }, " & { meta: ", "MatchAllFilterMeta", - "; match_all: ", + "; query: { match_all: ", "QueryDslMatchAllQuery", - "; }" + "; }; }" ], "path": "src/plugins/data/common/es_query/index.ts", "deprecated": true, @@ -19929,7 +19922,7 @@ "section": "def-common.RangeFilterMeta", "text": "RangeFilterMeta" }, - "; range: { [key: string]: ", + "; query: { range: { [key: string]: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -19937,7 +19930,7 @@ "section": "def-common.RangeFilterParams", "text": "RangeFilterParams" }, - "; }; }" + "; }; }; }" ], "path": "src/plugins/data/common/es_query/index.ts", "deprecated": true, @@ -20319,35 +20312,35 @@ }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts" }, { "plugin": "maps", @@ -20489,10 +20482,6 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx" @@ -21515,66 +21504,6 @@ } ] }, - { - "parentPluginId": "data", - "id": "def-public.esFilters.isMissingFilter", - "type": "Function", - "tags": [], - "label": "isMissingFilter", - "description": [], - "signature": [ - "(filter: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" - }, - ") => filter is ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.MissingFilter", - "text": "MissingFilter" - } - ], - "path": "src/plugins/data/public/deprecated.ts", - "deprecated": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "data", - "id": "def-public.esFilters.isMissingFilter.$1", - "type": "Object", - "tags": [], - "label": "filter", - "description": [], - "signature": [ - "{ $state?: { store: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterStateStore", - "text": "FilterStateStore" - }, - "; } | undefined; meta: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterMeta", - "text": "FilterMeta" - }, - "; query?: Record | undefined; }" - ], - "path": "node_modules/@kbn/es-query/target_types/filters/build_filters/missing_filter.d.ts", - "deprecated": false - } - ] - }, { "parentPluginId": "data", "id": "def-public.esFilters.isQueryStringFilter", @@ -22563,7 +22492,7 @@ "section": "def-common.RangeFilterMeta", "text": "RangeFilterMeta" }, - "; range: { [key: string]: ", + "; query: { range: { [key: string]: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -22571,7 +22500,7 @@ "section": "def-common.RangeFilterParams", "text": "RangeFilterParams" }, - "; }; }" + "; }; }; }" ], "path": "src/plugins/data/public/query/timefilter/lib/change_time_filter.ts", "deprecated": false @@ -22630,7 +22559,7 @@ "section": "def-common.RangeFilterMeta", "text": "RangeFilterMeta" }, - "; range: { [key: string]: ", + "; query: { range: { [key: string]: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -22638,7 +22567,7 @@ "section": "def-common.RangeFilterParams", "text": "RangeFilterParams" }, - "; }; }" + "; }; }; }" ], "path": "src/plugins/data/public/query/timefilter/lib/change_time_filter.ts", "deprecated": false @@ -24148,15 +24077,15 @@ "label": "isNestedField", "description": [], "signature": [ - "(field: ", + "(field: Pick<", { - "pluginId": "dataViews", + "pluginId": "@kbn/es-query", "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.IFieldType", - "text": "IFieldType" + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" }, - ") => boolean" + ", \"subType\">) => boolean" ], "path": "src/plugins/data/public/index.ts", "deprecated": false, @@ -24170,13 +24099,150 @@ "label": "field", "description": [], "signature": [ - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.IFieldType", - "text": "IFieldType" - } + "{ subType?: ", + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined; }" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "data", + "id": "def-public.indexPatterns.isMultiField", + "type": "Function", + "tags": [], + "label": "isMultiField", + "description": [], + "signature": [ + "(field: Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">) => boolean" + ], + "path": "src/plugins/data/public/index.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "data", + "id": "def-public.indexPatterns.isMultiField.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "{ subType?: ", + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined; }" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "data", + "id": "def-public.indexPatterns.getFieldSubtypeMulti", + "type": "Function", + "tags": [], + "label": "getFieldSubtypeMulti", + "description": [], + "signature": [ + "(field: Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubTypeMulti", + "text": "IFieldSubTypeMulti" + }, + " | undefined" + ], + "path": "src/plugins/data/public/index.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "data", + "id": "def-public.indexPatterns.getFieldSubtypeMulti.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "{ subType?: ", + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined; }" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "data", + "id": "def-public.indexPatterns.getFieldSubtypeNested", + "type": "Function", + "tags": [], + "label": "getFieldSubtypeNested", + "description": [], + "signature": [ + "(field: Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubTypeNested", + "text": "IFieldSubTypeNested" + }, + " | undefined" + ], + "path": "src/plugins/data/public/index.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "data", + "id": "def-public.indexPatterns.getFieldSubtypeNested.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "{ subType?: ", + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined; }" ], "path": "src/plugins/data_views/common/fields/utils.ts", "deprecated": false @@ -26318,10 +26384,6 @@ "plugin": "indexPatternManagement", "path": "src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx" }, - { - "plugin": "visTypeMetric", - "path": "src/plugins/vis_types/metric/public/plugin.ts" - }, { "plugin": "visTypePie", "path": "src/plugins/vis_types/pie/public/pie_component.tsx" @@ -29774,35 +29836,35 @@ }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", @@ -33985,10 +34047,6 @@ "plugin": "dataViews", "path": "src/plugins/data_views/common/fields/utils.ts" }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/utils.ts" - }, { "plugin": "dataViews", "path": "src/plugins/data_views/common/fields/data_view_field.ts" @@ -36961,6 +37019,11 @@ ], "label": "IFieldSubType", "description": [], + "signature": [ + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional" + ], "path": "src/plugins/data/common/es_query/index.ts", "deprecated": true, "removeBy": "8.1", @@ -41065,18 +41128,14 @@ { "parentPluginId": "data", "id": "def-common.DataViewField.subType", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "subType", "description": [], "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined" ], "path": "src/plugins/data_views/common/fields/data_view_field.ts", @@ -41127,6 +41186,82 @@ "path": "src/plugins/data_views/common/fields/data_view_field.ts", "deprecated": false }, + { + "parentPluginId": "data", + "id": "def-common.DataViewField.isSubtypeNested", + "type": "Function", + "tags": [], + "label": "isSubtypeNested", + "description": [], + "signature": [ + "() => boolean" + ], + "path": "src/plugins/data_views/common/fields/data_view_field.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-common.DataViewField.isSubtypeMulti", + "type": "Function", + "tags": [], + "label": "isSubtypeMulti", + "description": [], + "signature": [ + "() => boolean" + ], + "path": "src/plugins/data_views/common/fields/data_view_field.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-common.DataViewField.getSubtypeNested", + "type": "Function", + "tags": [], + "label": "getSubtypeNested", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubTypeNested", + "text": "IFieldSubTypeNested" + }, + " | undefined" + ], + "path": "src/plugins/data_views/common/fields/data_view_field.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "data", + "id": "def-common.DataViewField.getSubtypeMulti", + "type": "Function", + "tags": [], + "label": "getSubtypeMulti", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubTypeMulti", + "text": "IFieldSubTypeMulti" + }, + " | undefined" + ], + "path": "src/plugins/data_views/common/fields/data_view_field.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "data", "id": "def-common.DataViewField.deleteCount", @@ -41151,13 +41286,9 @@ "description": [], "signature": [ "() => { count: number; script: string | undefined; lang: \"painless\" | \"expression\" | \"mustache\" | \"java\" | undefined; conflictDescriptions: Record | undefined; name: string; type: string; esTypes: string[] | undefined; scripted: boolean; searchable: boolean; aggregatable: boolean; readFromDocValues: boolean; subType: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined; customLabel: string | undefined; }" ], "path": "src/plugins/data_views/common/fields/data_view_field.ts", @@ -43916,35 +44047,35 @@ }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", @@ -49135,6 +49266,106 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "data", + "id": "def-common.getFieldSubtypeMulti", + "type": "Function", + "tags": [], + "label": "getFieldSubtypeMulti", + "description": [], + "signature": [ + "(field: Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubTypeMulti", + "text": "IFieldSubTypeMulti" + }, + " | undefined" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "data", + "id": "def-common.getFieldSubtypeMulti.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "{ subType?: ", + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined; }" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "data", + "id": "def-common.getFieldSubtypeNested", + "type": "Function", + "tags": [], + "label": "getFieldSubtypeNested", + "description": [], + "signature": [ + "(field: Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubTypeNested", + "text": "IFieldSubTypeNested" + }, + " | undefined" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "data", + "id": "def-common.getFieldSubtypeNested.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "{ subType?: ", + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined; }" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "data", "id": "def-common.getFilterableKbnTypeNames", @@ -49764,64 +49995,41 @@ }, { "parentPluginId": "data", - "id": "def-common.isMissingFilter", + "id": "def-common.isMultiField", "type": "Function", - "tags": [ - "deprecated" - ], - "label": "isMissingFilter", + "tags": [], + "label": "isMultiField", "description": [], "signature": [ - "(filter: ", + "(field: Pick<", { "pluginId": "@kbn/es-query", "scope": "common", "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" }, - ") => filter is ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.MissingFilter", - "text": "MissingFilter" - } + ", \"subType\">) => boolean" ], - "path": "src/plugins/data/common/es_query/index.ts", - "deprecated": true, - "removeBy": "8.1", - "references": [], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false, "returnComment": [], "children": [ { "parentPluginId": "data", - "id": "def-common.isMissingFilter.$1", + "id": "def-common.isMultiField.$1", "type": "Object", "tags": [], - "label": "filter", + "label": "field", "description": [], "signature": [ - "{ $state?: { store: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterStateStore", - "text": "FilterStateStore" - }, - "; } | undefined; meta: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterMeta", - "text": "FilterMeta" - }, - "; query?: Record | undefined; }" + "{ subType?: ", + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined; }" ], - "path": "node_modules/@kbn/es-query/target_types/filters/build_filters/missing_filter.d.ts", + "path": "src/plugins/data_views/common/fields/utils.ts", "deprecated": false } ], @@ -49835,18 +50043,19 @@ "label": "isNestedField", "description": [], "signature": [ - "(field: ", + "(field: Pick<", { - "pluginId": "dataViews", + "pluginId": "@kbn/es-query", "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.IFieldType", - "text": "IFieldType" + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" }, - ") => boolean" + ", \"subType\">) => boolean" ], "path": "src/plugins/data_views/common/fields/utils.ts", "deprecated": false, + "returnComment": [], "children": [ { "parentPluginId": "data", @@ -49856,20 +50065,16 @@ "label": "field", "description": [], "signature": [ - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.IFieldType", - "text": "IFieldType" - } + "{ subType?: ", + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined; }" ], "path": "src/plugins/data_views/common/fields/utils.ts", - "deprecated": false, - "isRequired": true + "deprecated": false } ], - "returnComment": [], "initialIsOpen": false }, { @@ -51582,18 +51787,14 @@ { "parentPluginId": "data", "id": "def-common.FieldSpecExportFmt.subType", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "subType", "description": [], "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined" ], "path": "src/plugins/data_views/common/types.ts", @@ -52005,10 +52206,6 @@ "plugin": "dataViews", "path": "src/plugins/data_views/common/fields/utils.ts" }, - { - "plugin": "dataViews", - "path": "src/plugins/data_views/common/fields/utils.ts" - }, { "plugin": "dataViews", "path": "src/plugins/data_views/common/fields/data_view_field.ts" @@ -53003,14 +53200,6 @@ "plugin": "monitoring", "path": "x-pack/plugins/monitoring/public/lib/kuery.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts" @@ -53223,14 +53412,6 @@ "plugin": "timelines", "path": "x-pack/plugins/timelines/public/mock/index_pattern.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/target/types/common/search_strategy/index_fields/index.d.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/target/types/common/search_strategy/index_fields/index.d.ts" - }, { "plugin": "infra", "path": "x-pack/plugins/infra/target/types/public/containers/with_kuery_autocompletion.d.ts" @@ -53495,6 +53676,14 @@ "plugin": "infra", "path": "x-pack/plugins/infra/public/pages/metrics/index.tsx" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts" @@ -55604,7 +55793,7 @@ "section": "def-common.FilterMeta", "text": "FilterMeta" }, - "; exists?: { field: string; } | undefined; }" + "; query: { exists?: { field: string; } | undefined; }; }" ], "path": "src/plugins/data/common/es_query/index.ts", "deprecated": true, @@ -57641,6 +57830,11 @@ ], "label": "IFieldSubType", "description": [], + "signature": [ + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional" + ], "path": "src/plugins/data/common/es_query/index.ts", "deprecated": true, "removeBy": "8.1", @@ -57713,19 +57907,19 @@ }, { "plugin": "visualizations", - "path": "src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/controls_references.ts" + "path": "src/plugins/visualizations/public/utils/saved_visualization_references/controls_references.ts" }, { "plugin": "visualizations", - "path": "src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/controls_references.ts" + "path": "src/plugins/visualizations/public/utils/saved_visualization_references/controls_references.ts" }, { "plugin": "visualizations", - "path": "src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/timeseries_references.ts" + "path": "src/plugins/visualizations/public/utils/saved_visualization_references/timeseries_references.ts" }, { "plugin": "visualizations", - "path": "src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/timeseries_references.ts" + "path": "src/plugins/visualizations/public/utils/saved_visualization_references/timeseries_references.ts" }, { "plugin": "dashboard", @@ -59166,9 +59360,9 @@ }, " & { meta: ", "MatchAllFilterMeta", - "; match_all: ", + "; query: { match_all: ", "QueryDslMatchAllQuery", - "; }" + "; }; }" ], "path": "src/plugins/data/common/es_query/index.ts", "deprecated": true, @@ -59190,39 +59384,6 @@ "deprecated": false, "initialIsOpen": false }, - { - "parentPluginId": "data", - "id": "def-common.MissingFilter", - "type": "Type", - "tags": [ - "deprecated" - ], - "label": "MissingFilter", - "description": [], - "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" - }, - " & { meta: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterMeta", - "text": "FilterMeta" - }, - "; missing: { field: string; }; }" - ], - "path": "src/plugins/data/common/es_query/index.ts", - "deprecated": true, - "removeBy": "8.1", - "references": [], - "initialIsOpen": false - }, { "parentPluginId": "data", "id": "def-common.OnError", @@ -59435,7 +59596,7 @@ "section": "def-common.RangeFilterMeta", "text": "RangeFilterMeta" }, - "; range: { [key: string]: ", + "; query: { range: { [key: string]: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -59443,7 +59604,7 @@ "section": "def-common.RangeFilterParams", "text": "RangeFilterParams" }, - "; }; }" + "; }; }; }" ], "path": "src/plugins/data/common/es_query/index.ts", "deprecated": true, diff --git a/api_docs/data.mdx b/api_docs/data.mdx index a8e983c6a6b2..0734f19d6c3e 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3181 | 43 | 2796 | 48 | +| 3192 | 43 | 2807 | 48 | ## Client diff --git a/api_docs/data_autocomplete.mdx b/api_docs/data_autocomplete.mdx index 61c786d94839..1eef9ef1c693 100644 --- a/api_docs/data_autocomplete.mdx +++ b/api_docs/data_autocomplete.mdx @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3181 | 43 | 2796 | 48 | +| 3192 | 43 | 2807 | 48 | ## Client diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index cc5a593ab216..bb88e5868b60 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3181 | 43 | 2796 | 48 | +| 3192 | 43 | 2807 | 48 | ## Client diff --git a/api_docs/data_search.json b/api_docs/data_search.json index 836984073c0e..25ed62f473bd 100644 --- a/api_docs/data_search.json +++ b/api_docs/data_search.json @@ -2277,13 +2277,7 @@ "label": "uiSettingsClient", "description": [], "signature": [ - { - "pluginId": "core", - "scope": "server", - "docId": "kibCorePluginApi", - "section": "def-server.IUiSettingsClient", - "text": "IUiSettingsClient" - } + "{ get: (key: string) => Promise; }" ], "path": "src/plugins/data/server/search/types.ts", "deprecated": false @@ -28577,7 +28571,7 @@ "label": "fn", "description": [], "signature": [ - "(input: null, args: Arguments) => any" + "(input: null, args: Arguments) => { type: \"kibana_filter\"; meta: { negate: boolean; alias: string; disabled: boolean; }; query: any; }" ], "path": "src/plugins/data/common/search/expressions/kibana_filter.ts", "deprecated": false, diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 8b89cd491c16..c7256296f163 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3181 | 43 | 2796 | 48 | +| 3192 | 43 | 2807 | 48 | ## Client diff --git a/api_docs/data_ui.mdx b/api_docs/data_ui.mdx index 87c9d2ce08af..39a1948d15c2 100644 --- a/api_docs/data_ui.mdx +++ b/api_docs/data_ui.mdx @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3181 | 43 | 2796 | 48 | +| 3192 | 43 | 2807 | 48 | ## Client diff --git a/api_docs/data_views.json b/api_docs/data_views.json index d3160ea10871..c77aa02425e2 100644 --- a/api_docs/data_views.json +++ b/api_docs/data_views.json @@ -4736,35 +4736,35 @@ }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", @@ -13245,18 +13245,14 @@ { "parentPluginId": "dataViews", "id": "def-common.DataViewField.subType", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "subType", "description": [], "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined" ], "path": "src/plugins/data_views/common/fields/data_view_field.ts", @@ -13307,6 +13303,82 @@ "path": "src/plugins/data_views/common/fields/data_view_field.ts", "deprecated": false }, + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewField.isSubtypeNested", + "type": "Function", + "tags": [], + "label": "isSubtypeNested", + "description": [], + "signature": [ + "() => boolean" + ], + "path": "src/plugins/data_views/common/fields/data_view_field.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewField.isSubtypeMulti", + "type": "Function", + "tags": [], + "label": "isSubtypeMulti", + "description": [], + "signature": [ + "() => boolean" + ], + "path": "src/plugins/data_views/common/fields/data_view_field.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewField.getSubtypeNested", + "type": "Function", + "tags": [], + "label": "getSubtypeNested", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubTypeNested", + "text": "IFieldSubTypeNested" + }, + " | undefined" + ], + "path": "src/plugins/data_views/common/fields/data_view_field.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewField.getSubtypeMulti", + "type": "Function", + "tags": [], + "label": "getSubtypeMulti", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubTypeMulti", + "text": "IFieldSubTypeMulti" + }, + " | undefined" + ], + "path": "src/plugins/data_views/common/fields/data_view_field.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "dataViews", "id": "def-common.DataViewField.deleteCount", @@ -13331,13 +13403,9 @@ "description": [], "signature": [ "() => { count: number; script: string | undefined; lang: \"painless\" | \"expression\" | \"mustache\" | \"java\" | undefined; conflictDescriptions: Record | undefined; name: string; type: string; esTypes: string[] | undefined; scripted: boolean; searchable: boolean; aggregatable: boolean; readFromDocValues: boolean; subType: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined; customLabel: string | undefined; }" ], "path": "src/plugins/data_views/common/fields/data_view_field.ts", @@ -16472,35 +16540,35 @@ }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx" + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts" }, { "plugin": "observability", @@ -19769,6 +19837,106 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "dataViews", + "id": "def-common.getFieldSubtypeMulti", + "type": "Function", + "tags": [], + "label": "getFieldSubtypeMulti", + "description": [], + "signature": [ + "(field: Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubTypeMulti", + "text": "IFieldSubTypeMulti" + }, + " | undefined" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-common.getFieldSubtypeMulti.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "{ subType?: ", + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined; }" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "dataViews", + "id": "def-common.getFieldSubtypeNested", + "type": "Function", + "tags": [], + "label": "getFieldSubtypeNested", + "description": [], + "signature": [ + "(field: Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubTypeNested", + "text": "IFieldSubTypeNested" + }, + " | undefined" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-common.getFieldSubtypeNested.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "{ subType?: ", + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined; }" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "dataViews", "id": "def-common.getIndexPatternLoadMeta", @@ -19838,6 +20006,48 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "dataViews", + "id": "def-common.isMultiField", + "type": "Function", + "tags": [], + "label": "isMultiField", + "description": [], + "signature": [ + "(field: Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">) => boolean" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-common.isMultiField.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "{ subType?: ", + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined; }" + ], + "path": "src/plugins/data_views/common/fields/utils.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "dataViews", "id": "def-common.isNestedField", @@ -19846,18 +20056,19 @@ "label": "isNestedField", "description": [], "signature": [ - "(field: ", + "(field: Pick<", { - "pluginId": "dataViews", + "pluginId": "@kbn/es-query", "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.IFieldType", - "text": "IFieldType" + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" }, - ") => boolean" + ", \"subType\">) => boolean" ], "path": "src/plugins/data_views/common/fields/utils.ts", "deprecated": false, + "returnComment": [], "children": [ { "parentPluginId": "dataViews", @@ -19867,20 +20078,16 @@ "label": "field", "description": [], "signature": [ - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.IFieldType", - "text": "IFieldType" - } + "{ subType?: ", + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined; }" ], "path": "src/plugins/data_views/common/fields/utils.ts", - "deprecated": false, - "isRequired": true + "deprecated": false } ], - "returnComment": [], "initialIsOpen": false } ], @@ -20754,18 +20961,14 @@ { "parentPluginId": "dataViews", "id": "def-common.FieldSpecExportFmt.subType", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "subType", "description": [], "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined" ], "path": "src/plugins/data_views/common/types.ts", @@ -22411,14 +22614,6 @@ "plugin": "monitoring", "path": "x-pack/plugins/monitoring/public/lib/kuery.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts" - }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts" @@ -22631,14 +22826,6 @@ "plugin": "timelines", "path": "x-pack/plugins/timelines/public/mock/index_pattern.ts" }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/target/types/common/search_strategy/index_fields/index.d.ts" - }, - { - "plugin": "securitySolution", - "path": "x-pack/plugins/security_solution/target/types/common/search_strategy/index_fields/index.d.ts" - }, { "plugin": "infra", "path": "x-pack/plugins/infra/target/types/public/containers/with_kuery_autocompletion.d.ts" @@ -22903,6 +23090,14 @@ "plugin": "infra", "path": "x-pack/plugins/infra/public/pages/metrics/index.tsx" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts" @@ -24924,19 +25119,19 @@ }, { "plugin": "visualizations", - "path": "src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/controls_references.ts" + "path": "src/plugins/visualizations/public/utils/saved_visualization_references/controls_references.ts" }, { "plugin": "visualizations", - "path": "src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/controls_references.ts" + "path": "src/plugins/visualizations/public/utils/saved_visualization_references/controls_references.ts" }, { "plugin": "visualizations", - "path": "src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/timeseries_references.ts" + "path": "src/plugins/visualizations/public/utils/saved_visualization_references/timeseries_references.ts" }, { "plugin": "visualizations", - "path": "src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/timeseries_references.ts" + "path": "src/plugins/visualizations/public/utils/saved_visualization_references/timeseries_references.ts" }, { "plugin": "dashboard", diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 373587de6f28..e907c075b9a8 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 671 | 6 | 531 | 5 | +| 681 | 6 | 541 | 5 | ## Client diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 5e3c38eb354c..a083c82e69d0 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -46,7 +46,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | dataViews, indexPatternManagement, data | - | | | dataViews, discover, ml, transform, canvas | - | | | dataViews, visTypeTimeseries, maps, lens, discover | - | -| | fleet, indexPatternFieldEditor, discover, dashboard, lens, ml, stackAlerts, indexPatternManagement, visTypeMetric, visTypePie, visTypeTable, visTypeTimeseries, visTypeXy, visTypeVislib | - | +| | fleet, indexPatternFieldEditor, discover, dashboard, lens, ml, stackAlerts, indexPatternManagement, visTypePie, visTypeTable, visTypeTimeseries, visTypeXy, visTypeVislib | - | | | reporting, visTypeTimeseries | - | | | data, lens, visTypeTimeseries, infra, maps, visTypeTimelion | - | | | dashboard, maps, graph, visualize | - | @@ -178,7 +178,6 @@ Safe to remove. | | | | | | -| | | | | | | | @@ -203,7 +202,6 @@ Safe to remove. | | | | | | -| | | | | | | | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 3714161b6c42..b33ab317ca88 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -205,12 +205,12 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPattern), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPattern), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/public/index.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternField), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/public/index.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPatternField), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPatternField)+ 2 more | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IIndexPattern), [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=IIndexPattern), [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=IIndexPattern) | - | -| | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 14 more | 8.1 | +| | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 13 more | 8.1 | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternAttributes), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/utils.ts#:~:text=IndexPatternAttributes), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/utils.ts#:~:text=IndexPatternAttributes), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/utils.ts#:~:text=IndexPatternAttributes), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/utils.ts#:~:text=IndexPatternAttributes), [scripted_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/deprecations/scripted_fields.ts#:~:text=IndexPatternAttributes), [scripted_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/deprecations/scripted_fields.ts#:~:text=IndexPatternAttributes) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternSpec), [create_index_pattern.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/routes/create_index_pattern.ts#:~:text=IndexPatternSpec), [create_index_pattern.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/routes/create_index_pattern.ts#:~:text=IndexPatternSpec) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternType) | - | | | [data_views.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_views.ts#:~:text=IndexPatternListItem), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternListItem) | - | -| | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 14 more | 8.1 | +| | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 13 more | 8.1 | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IIndexPattern), [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=IIndexPattern), [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=IIndexPattern) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternAttributes), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/utils.ts#:~:text=IndexPatternAttributes), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/utils.ts#:~:text=IndexPatternAttributes), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/utils.ts#:~:text=IndexPatternAttributes), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/utils.ts#:~:text=IndexPatternAttributes), [scripted_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/deprecations/scripted_fields.ts#:~:text=IndexPatternAttributes), [scripted_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/deprecations/scripted_fields.ts#:~:text=IndexPatternAttributes) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IIndexPatternsApiClient), [index_patterns_api_client.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/index_patterns_api_client.ts#:~:text=IIndexPatternsApiClient), [index_patterns_api_client.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/index_patterns_api_client.ts#:~:text=IIndexPatternsApiClient) | - | @@ -230,7 +230,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=getNonScriptedFields) | 8.1 | | | [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=getScriptedFields), [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=getScriptedFields), [data_view.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.ts#:~:text=getScriptedFields), [data_views.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_views.ts#:~:text=getScriptedFields), [register_index_pattern_usage_collection.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/register_index_pattern_usage_collection.ts#:~:text=getScriptedFields), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=getScriptedFields), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=getScriptedFields), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=getScriptedFields), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=getScriptedFields), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=getScriptedFields)+ 1 more | 8.1 | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternField), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/public/index.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPatternField), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPatternField), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPatternField)+ 2 more | - | -| | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 14 more | 8.1 | +| | [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/utils.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [data_view_field.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [field_list.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/field_list.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/types.ts#:~:text=IFieldType)+ 13 more | 8.1 | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternAttributes), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/utils.ts#:~:text=IndexPatternAttributes), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/utils.ts#:~:text=IndexPatternAttributes), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/utils.ts#:~:text=IndexPatternAttributes), [utils.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/utils.ts#:~:text=IndexPatternAttributes), [scripted_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/deprecations/scripted_fields.ts#:~:text=IndexPatternAttributes), [scripted_fields.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/server/deprecations/scripted_fields.ts#:~:text=IndexPatternAttributes) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPattern), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPattern), [data_view_field.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/fields/data_view_field.test.ts#:~:text=IndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/public/index.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern), [data_view.test.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/data_views/data_view.test.ts#:~:text=IndexPattern) | - | | | [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternsService), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/public/index.ts#:~:text=IndexPatternsService), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/common/index.ts#:~:text=IndexPatternsService), [index.ts](https://github.com/elastic/kibana/tree/master/src/plugins/data_views/public/index.ts#:~:text=IndexPatternsService) | - | @@ -678,7 +678,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=IndexPattern), [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=IndexPattern), [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=IndexPattern), [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=IndexPattern), [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=IndexPattern), [lens_attributes.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts#:~:text=IndexPattern), [lens_attributes.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts#:~:text=IndexPattern), [lens_attributes.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts#:~:text=IndexPattern), [default_configs.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts#:~:text=IndexPattern), [default_configs.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/default_configs.ts#:~:text=IndexPattern)+ 38 more | - | | | [observability_index_patterns.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts#:~:text=IndexPatternSpec), [observability_index_patterns.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts#:~:text=IndexPatternSpec) | - | | | [observability_index_patterns.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts#:~:text=indexPatterns), [observability_index_patterns.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts#:~:text=indexPatterns), [observability_index_patterns.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts#:~:text=indexPatterns), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/pages/alerts/index.tsx#:~:text=indexPatterns), [observability_index_patterns.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.test.ts#:~:text=indexPatterns), [observability_index_patterns.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.test.ts#:~:text=indexPatterns), [observability_index_patterns.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.test.ts#:~:text=indexPatterns), [observability_index_patterns.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.test.ts#:~:text=indexPatterns), [observability_index_patterns.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.test.ts#:~:text=indexPatterns), [observability_index_patterns.test.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.test.ts#:~:text=indexPatterns)+ 5 more | - | -| | [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=esFilters), [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=esFilters), [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=esFilters), [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=esFilters), [filter_value_label.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx#:~:text=esFilters), [filter_value_label.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx#:~:text=esFilters), [filter_value_label.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx#:~:text=esFilters), [filter_value_label.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx#:~:text=esFilters) | 8.1 | +| | [filter_value_label.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx#:~:text=esFilters), [filter_value_label.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx#:~:text=esFilters), [filter_value_label.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx#:~:text=esFilters), [filter_value_label.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx#:~:text=esFilters), [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=esFilters), [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=esFilters), [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=esFilters), [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=esFilters) | 8.1 | | | [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=ExistsFilter), [utils.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts#:~:text=ExistsFilter) | 8.1 | | | [filter_value_label.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx#:~:text=Filter), [filter_value_label.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx#:~:text=Filter) | 8.1 | | | [observability_index_patterns.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts#:~:text=IndexPatternSpec), [observability_index_patterns.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts#:~:text=IndexPatternSpec), [observability_index_patterns.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts#:~:text=IndexPatternSpec), [observability_index_patterns.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts#:~:text=IndexPatternSpec) | - | @@ -836,15 +836,15 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx#:~:text=dashboardUrlGenerator), [use_risky_hosts_dashboard_button_href.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href.ts#:~:text=dashboardUrlGenerator), [use_risky_hosts_dashboard_links.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_links.tsx#:~:text=dashboardUrlGenerator) | - | +| | [use_risky_hosts_dashboard_button_href.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_button_href.ts#:~:text=dashboardUrlGenerator), [use_risky_hosts_dashboard_links.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_risky_hosts_dashboard_links.tsx#:~:text=dashboardUrlGenerator), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/index.tsx#:~:text=dashboardUrlGenerator) | - | | | [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [entry_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx#:~:text=IndexPattern), [entry_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx#:~:text=IndexPattern), [entry_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx#:~:text=IndexPattern), [list_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.tsx#:~:text=IndexPattern), [list_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.tsx#:~:text=IndexPattern)+ 30 more | - | | | [field_name_cell.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx#:~:text=IndexPatternField), [field_name_cell.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx#:~:text=IndexPatternField), [field_name_cell.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx#:~:text=IndexPatternField), [field_name_cell.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx#:~:text=IndexPatternField) | - | -| | [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts#:~:text=IIndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts#:~:text=IIndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts#:~:text=IIndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts#:~:text=IIndexPattern), [action.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts#:~:text=IIndexPattern), [action.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx#:~:text=IIndexPattern)+ 76 more | - | +| | [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts#:~:text=IIndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts#:~:text=IIndexPattern), [action.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts#:~:text=IIndexPattern), [action.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx#:~:text=IIndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx#:~:text=IIndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/url_state/types.ts#:~:text=IIndexPattern)+ 74 more | - | | | [middleware.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts#:~:text=indexPatterns), [plugin.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/plugin.tsx#:~:text=indexPatterns), [dependencies_start_mock.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/mock/endpoint/dependencies_start_mock.ts#:~:text=indexPatterns) | - | -| | [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx#:~:text=esFilters), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters)+ 15 more | 8.1 | +| | [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx#:~:text=esFilters), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters), [epic.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts#:~:text=esFilters)+ 14 more | 8.1 | | | [expandable_network.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx#:~:text=esQuery), [expandable_network.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx#:~:text=esQuery), [events_viewer.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx#:~:text=esQuery), [events_viewer.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx#:~:text=esQuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx#:~:text=esQuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx#:~:text=esQuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx#:~:text=esQuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx#:~:text=esQuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx#:~:text=esQuery), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx#:~:text=esQuery)+ 30 more | 8.1 | | | [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter)+ 163 more | 8.1 | -| | [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts#:~:text=IIndexPattern), [index.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts#:~:text=IIndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts#:~:text=IIndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts#:~:text=IIndexPattern), [action.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts#:~:text=IIndexPattern), [action.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx#:~:text=IIndexPattern)+ 162 more | - | +| | [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts#:~:text=IIndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts#:~:text=IIndexPattern), [action.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts#:~:text=IIndexPattern), [action.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [index.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/containers/source/index.tsx#:~:text=IIndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx#:~:text=IIndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx#:~:text=IIndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/url_state/types.ts#:~:text=IIndexPattern)+ 158 more | - | | | [field_name_cell.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx#:~:text=IndexPatternField), [field_name_cell.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx#:~:text=IndexPatternField), [field_name_cell.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx#:~:text=IndexPatternField), [field_name_cell.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/event_details/table/field_name_cell.tsx#:~:text=IndexPatternField) | - | | | [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [helpers.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx#:~:text=IndexPattern), [entry_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx#:~:text=IndexPattern), [entry_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx#:~:text=IndexPattern), [entry_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx#:~:text=IndexPattern), [list_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.tsx#:~:text=IndexPattern), [list_item.tsx](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.tsx#:~:text=IndexPattern)+ 30 more | - | | | [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [store.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/common/types/timeline/store.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [model.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/model.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/actions.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [selectors.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/common/store/inputs/selectors.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter), [actions.ts](https://github.com/elastic/kibana/tree/master/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts#:~:text=Filter)+ 163 more | 8.1 | @@ -983,14 +983,6 @@ warning: This document is auto-generated and is meant to be viewed inside our ex -## visTypeMetric - -| Deprecated API | Reference location(s) | Remove By | -| ---------------|-----------|-----------| -| | [plugin.ts](https://github.com/elastic/kibana/tree/master/src/plugins/vis_types/metric/public/plugin.ts#:~:text=fieldFormats) | - | - - - ## visTypePie | Deprecated API | Reference location(s) | Remove By | @@ -1101,7 +1093,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [plugin.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/plugin.ts#:~:text=indexPatterns) | - | | | [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=esFilters), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=esFilters) | 8.1 | | | [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=Filter), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=Filter), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=Filter) | 8.1 | -| | [controls_references.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/controls_references.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [controls_references.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/controls_references.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [timeseries_references.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/timeseries_references.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [timeseries_references.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/timeseries_references.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [visualization_saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [visualization_saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [visualization_saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [visualization_saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [visualization_saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [controls_references.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/controls_references.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE)+ 8 more | - | +| | [controls_references.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_visualization_references/controls_references.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [controls_references.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_visualization_references/controls_references.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [timeseries_references.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_visualization_references/timeseries_references.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [timeseries_references.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_visualization_references/timeseries_references.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [visualization_saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [visualization_saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [visualization_saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [visualization_saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [visualization_saved_object_migrations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/server/migrations/visualization_saved_object_migrations.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE), [controls_references.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/utils/saved_visualization_references/controls_references.ts#:~:text=INDEX_PATTERN_SAVED_OBJECT_TYPE)+ 8 more | - | | | [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=IndexPatternsContract), [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=IndexPatternsContract), [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=IndexPatternsContract), [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=IndexPatternsContract) | - | | | [vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/vis.ts#:~:text=IndexPattern), [vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/vis.ts#:~:text=IndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/vis_types/types.ts#:~:text=IndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/vis_types/types.ts#:~:text=IndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/vis_types/types.ts#:~:text=IndexPattern), [create_vis_embeddable_from_object.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts#:~:text=IndexPattern), [create_vis_embeddable_from_object.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts#:~:text=IndexPattern), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=IndexPattern), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=IndexPattern), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=IndexPattern)+ 10 more | - | | | [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=Filter), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=Filter), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=Filter) | 8.1 | @@ -1109,7 +1101,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/vis.ts#:~:text=IndexPattern), [vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/vis.ts#:~:text=IndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/vis_types/types.ts#:~:text=IndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/vis_types/types.ts#:~:text=IndexPattern), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/vis_types/types.ts#:~:text=IndexPattern), [create_vis_embeddable_from_object.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts#:~:text=IndexPattern), [create_vis_embeddable_from_object.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts#:~:text=IndexPattern), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=IndexPattern), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=IndexPattern), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=IndexPattern) | - | | | [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=Filter), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=Filter), [visualize_embeddable.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts#:~:text=Filter) | 8.1 | | | [find_list_items.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/find_list_items.ts#:~:text=SavedObjectLoader), [find_list_items.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/find_list_items.ts#:~:text=SavedObjectLoader), [saved_visualizations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/saved_visualizations.ts#:~:text=SavedObjectLoader), [saved_visualizations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/saved_visualizations.ts#:~:text=SavedObjectLoader), [saved_visualizations.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/saved_visualizations.ts#:~:text=SavedObjectLoader), [services.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/services.ts#:~:text=SavedObjectLoader), [services.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/services.ts#:~:text=SavedObjectLoader) | - | -| | [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/types.ts#:~:text=SavedObject), [types.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/types.ts#:~:text=SavedObject), [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=SavedObject), [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=SavedObject), [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=SavedObject), [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=SavedObject) | - | +| | [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=SavedObject), [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=SavedObject), [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=SavedObject), [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=SavedObject) | - | | | [_saved_vis.ts](https://github.com/elastic/kibana/tree/master/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts#:~:text=SavedObjectClass) | - | diff --git a/api_docs/elastic_apm_generator.json b/api_docs/elastic_apm_generator.json new file mode 100644 index 000000000000..dc69c08bba5b --- /dev/null +++ b/api_docs/elastic_apm_generator.json @@ -0,0 +1,257 @@ +{ + "id": "@elastic/apm-generator", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.getObserverDefaults", + "type": "Function", + "tags": [], + "label": "getObserverDefaults", + "description": [], + "signature": [ + "() => Partial<{ '@timestamp': number; 'agent.name': string; 'agent.version': string; 'ecs.version': string; 'event.outcome': string; 'event.ingested': number; 'metricset.name': string; 'observer.version': string; 'observer.version_major': number; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'trace.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.id': string; 'transaction.duration.us': number; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'transaction.sampled': true; 'service.name': string; 'service.environment': string; 'service.node.name': string; 'span.id': string; 'span.name': string; 'span.type': string; 'span.subtype': string; 'span.duration.us': number; 'span.destination.service.name': string; 'span.destination.service.resource': string; 'span.destination.service.type': string; 'span.destination.service.response_time.sum.us': number; 'span.destination.service.response_time.count': number; }>" + ], + "path": "packages/elastic-apm-generator/src/lib/defaults/get_observer_defaults.ts", + "deprecated": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.getSpanDestinationMetrics", + "type": "Function", + "tags": [], + "label": "getSpanDestinationMetrics", + "description": [], + "signature": [ + "(events: Partial<{ '@timestamp': number; 'agent.name': string; 'agent.version': string; 'ecs.version': string; 'event.outcome': string; 'event.ingested': number; 'metricset.name': string; 'observer.version': string; 'observer.version_major': number; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'trace.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.id': string; 'transaction.duration.us': number; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'transaction.sampled': true; 'service.name': string; 'service.environment': string; 'service.node.name': string; 'span.id': string; 'span.name': string; 'span.type': string; 'span.subtype': string; 'span.duration.us': number; 'span.destination.service.name': string; 'span.destination.service.resource': string; 'span.destination.service.type': string; 'span.destination.service.response_time.sum.us': number; 'span.destination.service.response_time.count': number; }>[]) => Partial<{ '@timestamp': number; 'agent.name': string; 'agent.version': string; 'ecs.version': string; 'event.outcome': string; 'event.ingested': number; 'metricset.name': string; 'observer.version': string; 'observer.version_major': number; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'trace.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.id': string; 'transaction.duration.us': number; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'transaction.sampled': true; 'service.name': string; 'service.environment': string; 'service.node.name': string; 'span.id': string; 'span.name': string; 'span.type': string; 'span.subtype': string; 'span.duration.us': number; 'span.destination.service.name': string; 'span.destination.service.resource': string; 'span.destination.service.type': string; 'span.destination.service.response_time.sum.us': number; 'span.destination.service.response_time.count': number; }>[]" + ], + "path": "packages/elastic-apm-generator/src/lib/utils/get_span_destination_metrics.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.getSpanDestinationMetrics.$1", + "type": "Array", + "tags": [], + "label": "events", + "description": [], + "signature": [ + "Partial<{ '@timestamp': number; 'agent.name': string; 'agent.version': string; 'ecs.version': string; 'event.outcome': string; 'event.ingested': number; 'metricset.name': string; 'observer.version': string; 'observer.version_major': number; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'trace.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.id': string; 'transaction.duration.us': number; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'transaction.sampled': true; 'service.name': string; 'service.environment': string; 'service.node.name': string; 'span.id': string; 'span.name': string; 'span.type': string; 'span.subtype': string; 'span.duration.us': number; 'span.destination.service.name': string; 'span.destination.service.resource': string; 'span.destination.service.type': string; 'span.destination.service.response_time.sum.us': number; 'span.destination.service.response_time.count': number; }>[]" + ], + "path": "packages/elastic-apm-generator/src/lib/utils/get_span_destination_metrics.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.getTransactionMetrics", + "type": "Function", + "tags": [], + "label": "getTransactionMetrics", + "description": [], + "signature": [ + "(events: Partial<{ '@timestamp': number; 'agent.name': string; 'agent.version': string; 'ecs.version': string; 'event.outcome': string; 'event.ingested': number; 'metricset.name': string; 'observer.version': string; 'observer.version_major': number; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'trace.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.id': string; 'transaction.duration.us': number; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'transaction.sampled': true; 'service.name': string; 'service.environment': string; 'service.node.name': string; 'span.id': string; 'span.name': string; 'span.type': string; 'span.subtype': string; 'span.duration.us': number; 'span.destination.service.name': string; 'span.destination.service.resource': string; 'span.destination.service.type': string; 'span.destination.service.response_time.sum.us': number; 'span.destination.service.response_time.count': number; }>[]) => { \"transaction.duration.histogram\": { values: number[]; counts: number[]; }; _doc_count: number; '@timestamp'?: number | undefined; 'agent.name'?: string | undefined; 'agent.version'?: string | undefined; 'ecs.version'?: string | undefined; 'event.outcome'?: string | undefined; 'event.ingested'?: number | undefined; 'metricset.name'?: string | undefined; 'observer.version'?: string | undefined; 'observer.version_major'?: number | undefined; 'parent.id'?: string | undefined; 'processor.event'?: string | undefined; 'processor.name'?: string | undefined; 'trace.id'?: string | undefined; 'transaction.name'?: string | undefined; 'transaction.type'?: string | undefined; 'transaction.id'?: string | undefined; 'transaction.duration.us'?: number | undefined; 'transaction.sampled'?: true | undefined; 'service.name'?: string | undefined; 'service.environment'?: string | undefined; 'service.node.name'?: string | undefined; 'span.id'?: string | undefined; 'span.name'?: string | undefined; 'span.type'?: string | undefined; 'span.subtype'?: string | undefined; 'span.duration.us'?: number | undefined; 'span.destination.service.name'?: string | undefined; 'span.destination.service.resource'?: string | undefined; 'span.destination.service.type'?: string | undefined; 'span.destination.service.response_time.sum.us'?: number | undefined; 'span.destination.service.response_time.count'?: number | undefined; }[]" + ], + "path": "packages/elastic-apm-generator/src/lib/utils/get_transaction_metrics.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.getTransactionMetrics.$1", + "type": "Array", + "tags": [], + "label": "events", + "description": [], + "signature": [ + "Partial<{ '@timestamp': number; 'agent.name': string; 'agent.version': string; 'ecs.version': string; 'event.outcome': string; 'event.ingested': number; 'metricset.name': string; 'observer.version': string; 'observer.version_major': number; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'trace.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.id': string; 'transaction.duration.us': number; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'transaction.sampled': true; 'service.name': string; 'service.environment': string; 'service.node.name': string; 'span.id': string; 'span.name': string; 'span.type': string; 'span.subtype': string; 'span.duration.us': number; 'span.destination.service.name': string; 'span.destination.service.resource': string; 'span.destination.service.type': string; 'span.destination.service.response_time.sum.us': number; 'span.destination.service.response_time.count': number; }>[]" + ], + "path": "packages/elastic-apm-generator/src/lib/utils/get_transaction_metrics.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.service", + "type": "Function", + "tags": [], + "label": "service", + "description": [], + "signature": [ + "(name: string, environment: string, agentName: string) => ", + "Service" + ], + "path": "packages/elastic-apm-generator/src/lib/service.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.service.$1", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "signature": [ + "string" + ], + "path": "packages/elastic-apm-generator/src/lib/service.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.service.$2", + "type": "string", + "tags": [], + "label": "environment", + "description": [], + "signature": [ + "string" + ], + "path": "packages/elastic-apm-generator/src/lib/service.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.service.$3", + "type": "string", + "tags": [], + "label": "agentName", + "description": [], + "signature": [ + "string" + ], + "path": "packages/elastic-apm-generator/src/lib/service.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.timerange", + "type": "Function", + "tags": [], + "label": "timerange", + "description": [], + "signature": [ + "(from: number, to: number) => ", + "Timerange" + ], + "path": "packages/elastic-apm-generator/src/lib/timerange.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.timerange.$1", + "type": "number", + "tags": [], + "label": "from", + "description": [], + "signature": [ + "number" + ], + "path": "packages/elastic-apm-generator/src/lib/timerange.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.timerange.$2", + "type": "number", + "tags": [], + "label": "to", + "description": [], + "signature": [ + "number" + ], + "path": "packages/elastic-apm-generator/src/lib/timerange.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.toElasticsearchOutput", + "type": "Function", + "tags": [], + "label": "toElasticsearchOutput", + "description": [], + "signature": [ + "(events: Partial<{ '@timestamp': number; 'agent.name': string; 'agent.version': string; 'ecs.version': string; 'event.outcome': string; 'event.ingested': number; 'metricset.name': string; 'observer.version': string; 'observer.version_major': number; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'trace.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.id': string; 'transaction.duration.us': number; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'transaction.sampled': true; 'service.name': string; 'service.environment': string; 'service.node.name': string; 'span.id': string; 'span.name': string; 'span.type': string; 'span.subtype': string; 'span.duration.us': number; 'span.destination.service.name': string; 'span.destination.service.resource': string; 'span.destination.service.type': string; 'span.destination.service.response_time.sum.us': number; 'span.destination.service.response_time.count': number; }>[], versionOverride: string | undefined) => { _index: string; _source: {}; }[]" + ], + "path": "packages/elastic-apm-generator/src/lib/output/to_elasticsearch_output.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.toElasticsearchOutput.$1", + "type": "Array", + "tags": [], + "label": "events", + "description": [], + "signature": [ + "Partial<{ '@timestamp': number; 'agent.name': string; 'agent.version': string; 'ecs.version': string; 'event.outcome': string; 'event.ingested': number; 'metricset.name': string; 'observer.version': string; 'observer.version_major': number; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'trace.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.id': string; 'transaction.duration.us': number; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'transaction.sampled': true; 'service.name': string; 'service.environment': string; 'service.node.name': string; 'span.id': string; 'span.name': string; 'span.type': string; 'span.subtype': string; 'span.duration.us': number; 'span.destination.service.name': string; 'span.destination.service.resource': string; 'span.destination.service.type': string; 'span.destination.service.response_time.sum.us': number; 'span.destination.service.response_time.count': number; }>[]" + ], + "path": "packages/elastic-apm-generator/src/lib/output/to_elasticsearch_output.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "@elastic/apm-generator", + "id": "def-server.toElasticsearchOutput.$2", + "type": "string", + "tags": [], + "label": "versionOverride", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/elastic-apm-generator/src/lib/output/to_elasticsearch_output.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/elastic_apm_generator.mdx b/api_docs/elastic_apm_generator.mdx new file mode 100644 index 000000000000..3b7667a6837b --- /dev/null +++ b/api_docs/elastic_apm_generator.mdx @@ -0,0 +1,27 @@ +--- +id: kibElasticApmGeneratorPluginApi +slug: /kibana-dev-docs/api/elastic-apm-generator +title: "@elastic/apm-generator" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @elastic/apm-generator plugin +date: 2020-11-16 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@elastic/apm-generator'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import elasticApmGeneratorObj from './elastic_apm_generator.json'; + +Elastic APM trace data generator + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 15 | 0 | 15 | 2 | + +## Server + +### Functions + + diff --git a/api_docs/event_log.json b/api_docs/event_log.json index 95ab6b473a0d..1991fbff2589 100644 --- a/api_docs/event_log.json +++ b/api_docs/event_log.json @@ -753,7 +753,7 @@ "label": "logEvent", "description": [], "signature": [ - "(properties: DeepPartial[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined) => void" + "(properties: DeepPartial | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; } & {}> | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined) => void" ], "path": "x-pack/plugins/event_log/server/types.ts", "deprecated": false, @@ -766,7 +766,7 @@ "label": "properties", "description": [], "signature": [ - "DeepPartial[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" + "DeepPartial | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; } & {}> | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" ], "path": "x-pack/plugins/event_log/server/types.ts", "deprecated": false, @@ -783,7 +783,7 @@ "label": "startTiming", "description": [], "signature": [ - "(event: DeepPartial[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined) => void" + "(event: DeepPartial | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; } & {}> | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined) => void" ], "path": "x-pack/plugins/event_log/server/types.ts", "deprecated": false, @@ -796,7 +796,7 @@ "label": "event", "description": [], "signature": [ - "DeepPartial[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" + "DeepPartial | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; } & {}> | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" ], "path": "x-pack/plugins/event_log/server/types.ts", "deprecated": false, @@ -813,7 +813,7 @@ "label": "stopTiming", "description": [], "signature": [ - "(event: DeepPartial[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined) => void" + "(event: DeepPartial | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; } & {}> | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined) => void" ], "path": "x-pack/plugins/event_log/server/types.ts", "deprecated": false, @@ -826,7 +826,7 @@ "label": "event", "description": [], "signature": [ - "DeepPartial[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" + "DeepPartial | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; } & {}> | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" ], "path": "x-pack/plugins/event_log/server/types.ts", "deprecated": false, @@ -886,7 +886,7 @@ "label": "data", "description": [], "signature": [ - "(Readonly<{ tags?: string[] | undefined; kibana?: Readonly<{ version?: string | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}> | undefined)[]" + "(Readonly<{ tags?: string[] | undefined; kibana?: Readonly<{ version?: string | undefined; alert?: Readonly<{ rule?: Readonly<{ execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ total_indexing_duration_ms?: number | undefined; total_search_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; } & {}> | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}> | undefined)[]" ], "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", "deprecated": false @@ -905,7 +905,7 @@ "label": "IEvent", "description": [], "signature": [ - "DeepPartial[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" + "DeepPartial | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; } & {}> | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, @@ -919,7 +919,7 @@ "label": "IValidatedEvent", "description": [], "signature": [ - "Readonly<{ tags?: string[] | undefined; kibana?: Readonly<{ version?: string | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}> | undefined" + "Readonly<{ tags?: string[] | undefined; kibana?: Readonly<{ version?: string | undefined; alert?: Readonly<{ rule?: Readonly<{ execution?: Readonly<{ status?: string | undefined; metrics?: Readonly<{ total_indexing_duration_ms?: number | undefined; total_search_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; } & {}> | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; } & {}> | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, @@ -1138,7 +1138,7 @@ "label": "getLogger", "description": [], "signature": [ - "(properties: DeepPartial[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined) => ", + "(properties: DeepPartial | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; } & {}> | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined) => ", { "pluginId": "eventLog", "scope": "server", @@ -1158,7 +1158,7 @@ "label": "properties", "description": [], "signature": [ - "DeepPartial[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" + "DeepPartial | undefined; uuid?: string | undefined; status_order?: number | undefined; } & {}> | undefined; } & {}> | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; type_id?: string | undefined; } & {}>[] | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; server_uuid?: string | undefined; task?: Readonly<{ scheduled?: string | undefined; schedule_delay?: number | undefined; } & {}> | undefined; space_ids?: string[] | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; message?: string | undefined; event?: Readonly<{ start?: string | undefined; type?: string[] | undefined; id?: string | undefined; end?: string | undefined; category?: string[] | undefined; url?: string | undefined; code?: string | undefined; original?: string | undefined; action?: string | undefined; kind?: string | undefined; severity?: number | undefined; outcome?: string | undefined; created?: string | undefined; dataset?: string | undefined; duration?: number | undefined; hash?: string | undefined; ingested?: string | undefined; module?: string | undefined; provider?: string | undefined; reason?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: number | undefined; timezone?: string | undefined; } & {}> | undefined; rule?: Readonly<{ id?: string | undefined; description?: string | undefined; name?: string | undefined; version?: string | undefined; license?: string | undefined; category?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; uuid?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" ], "path": "x-pack/plugins/event_log/server/types.ts", "deprecated": false, diff --git a/api_docs/expression_metric_vis.json b/api_docs/expression_metric_vis.json new file mode 100644 index 000000000000..251e916797e7 --- /dev/null +++ b/api_docs/expression_metric_vis.json @@ -0,0 +1,837 @@ +{ + "id": "expressionMetricVis", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.metricVisFunction", + "type": "Function", + "tags": [], + "label": "metricVisFunction", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "expressionMetricVis", + "scope": "common", + "docId": "kibExpressionMetricVisPluginApi", + "section": "def-common.MetricVisExpressionFunctionDefinition", + "text": "MetricVisExpressionFunctionDefinition" + } + ], + "path": "src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_vis_function.ts", + "deprecated": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.DimensionsVisParam", + "type": "Interface", + "tags": [], + "label": "DimensionsVisParam", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.DimensionsVisParam.metrics", + "type": "Array", + "tags": [], + "label": "metrics", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionValueBoxed", + "text": "ExpressionValueBoxed" + }, + "<\"vis_dimension\", { accessor: number | ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.DatatableColumn", + "text": "DatatableColumn" + }, + "; format: { id?: string | undefined; params: Record; }; }>[]" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.DimensionsVisParam.bucket", + "type": "CompoundType", + "tags": [], + "label": "bucket", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionValueBoxed", + "text": "ExpressionValueBoxed" + }, + "<\"vis_dimension\", { accessor: number | ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.DatatableColumn", + "text": "DatatableColumn" + }, + "; format: { id?: string | undefined; params: Record; }; }> | undefined" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments", + "type": "Interface", + "tags": [], + "label": "MetricArguments", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.percentageMode", + "type": "boolean", + "tags": [], + "label": "percentageMode", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.colorSchema", + "type": "Enum", + "tags": [], + "label": "colorSchema", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchemas", + "text": "ColorSchemas" + } + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.colorMode", + "type": "CompoundType", + "tags": [], + "label": "colorMode", + "description": [], + "signature": [ + "\"Background\" | \"Labels\" | \"None\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.useRanges", + "type": "boolean", + "tags": [], + "label": "useRanges", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.invertColors", + "type": "boolean", + "tags": [], + "label": "invertColors", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.showLabels", + "type": "boolean", + "tags": [], + "label": "showLabels", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.bgFill", + "type": "string", + "tags": [], + "label": "bgFill", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.subText", + "type": "string", + "tags": [], + "label": "subText", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.colorRange", + "type": "Array", + "tags": [], + "label": "colorRange", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.Range", + "text": "Range" + }, + "[]" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.font", + "type": "Object", + "tags": [], + "label": "font", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionTypeStyle", + "text": "ExpressionTypeStyle" + } + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.metric", + "type": "Array", + "tags": [], + "label": "metric", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionValueBoxed", + "text": "ExpressionValueBoxed" + }, + "<\"vis_dimension\", { accessor: number | ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.DatatableColumn", + "text": "DatatableColumn" + }, + "; format: { id?: string | undefined; params: Record; }; }>[]" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricArguments.bucket", + "type": "CompoundType", + "tags": [], + "label": "bucket", + "description": [], + "signature": [ + "{ type: \"vis_dimension\"; } & { accessor: number | ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.DatatableColumn", + "text": "DatatableColumn" + }, + "; format: { id?: string | undefined; params: Record; }; }" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricOptions", + "type": "Interface", + "tags": [], + "label": "MetricOptions", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricOptions.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricOptions.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricOptions.color", + "type": "string", + "tags": [], + "label": "color", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricOptions.bgColor", + "type": "string", + "tags": [], + "label": "bgColor", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricOptions.lightText", + "type": "boolean", + "tags": [], + "label": "lightText", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricOptions.rowIndex", + "type": "number", + "tags": [], + "label": "rowIndex", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam", + "type": "Interface", + "tags": [], + "label": "MetricVisParam", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.percentageMode", + "type": "boolean", + "tags": [], + "label": "percentageMode", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.percentageFormatPattern", + "type": "string", + "tags": [], + "label": "percentageFormatPattern", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.useRanges", + "type": "boolean", + "tags": [], + "label": "useRanges", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.colorSchema", + "type": "Enum", + "tags": [], + "label": "colorSchema", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.ColorSchemas", + "text": "ColorSchemas" + } + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.metricColorMode", + "type": "CompoundType", + "tags": [], + "label": "metricColorMode", + "description": [], + "signature": [ + "\"Background\" | \"Labels\" | \"None\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.colorsRange", + "type": "Array", + "tags": [], + "label": "colorsRange", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.Range", + "text": "Range" + }, + "[]" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.labels", + "type": "Object", + "tags": [], + "label": "labels", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.Labels", + "text": "Labels" + } + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.invertColors", + "type": "boolean", + "tags": [], + "label": "invertColors", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisParam.style", + "type": "Object", + "tags": [], + "label": "style", + "description": [], + "signature": [ + { + "pluginId": "charts", + "scope": "common", + "docId": "kibChartsPluginApi", + "section": "def-common.Style", + "text": "Style" + } + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisRenderConfig", + "type": "Interface", + "tags": [], + "label": "MetricVisRenderConfig", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisRenderConfig.visType", + "type": "string", + "tags": [], + "label": "visType", + "description": [], + "signature": [ + "\"metric\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisRenderConfig.visData", + "type": "Object", + "tags": [], + "label": "visData", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.Datatable", + "text": "Datatable" + } + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisRenderConfig.visConfig", + "type": "Object", + "tags": [], + "label": "visConfig", + "description": [], + "signature": [ + "{ metric: ", + { + "pluginId": "expressionMetricVis", + "scope": "common", + "docId": "kibExpressionMetricVisPluginApi", + "section": "def-common.MetricVisParam", + "text": "MetricVisParam" + }, + "; dimensions: ", + { + "pluginId": "expressionMetricVis", + "scope": "common", + "docId": "kibExpressionMetricVisPluginApi", + "section": "def-common.DimensionsVisParam", + "text": "DimensionsVisParam" + }, + "; }" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.VisParams", + "type": "Interface", + "tags": [], + "label": "VisParams", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.VisParams.addTooltip", + "type": "boolean", + "tags": [], + "label": "addTooltip", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.VisParams.addLegend", + "type": "boolean", + "tags": [], + "label": "addLegend", + "description": [], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.VisParams.dimensions", + "type": "Object", + "tags": [], + "label": "dimensions", + "description": [], + "signature": [ + { + "pluginId": "expressionMetricVis", + "scope": "common", + "docId": "kibExpressionMetricVisPluginApi", + "section": "def-common.DimensionsVisParam", + "text": "DimensionsVisParam" + } + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.VisParams.metric", + "type": "Object", + "tags": [], + "label": "metric", + "description": [], + "signature": [ + { + "pluginId": "expressionMetricVis", + "scope": "common", + "docId": "kibExpressionMetricVisPluginApi", + "section": "def-common.MetricVisParam", + "text": "MetricVisParam" + } + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.VisParams.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "\"metric\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_renderers.ts", + "deprecated": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.EXPRESSION_METRIC_NAME", + "type": "string", + "tags": [], + "label": "EXPRESSION_METRIC_NAME", + "description": [], + "signature": [ + "\"metricVis\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricInput", + "type": "Type", + "tags": [], + "label": "MetricInput", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.Datatable", + "text": "Datatable" + } + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.MetricVisExpressionFunctionDefinition", + "type": "Type", + "tags": [], + "label": "MetricVisExpressionFunctionDefinition", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionFunctionDefinition", + "text": "ExpressionFunctionDefinition" + }, + "<\"metricVis\", ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.Datatable", + "text": "Datatable" + }, + ", ", + { + "pluginId": "expressionMetricVis", + "scope": "common", + "docId": "kibExpressionMetricVisPluginApi", + "section": "def-common.MetricArguments", + "text": "MetricArguments" + }, + ", ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionValueBoxed", + "text": "ExpressionValueBoxed" + }, + "<\"render\", { as: string; value: ", + { + "pluginId": "expressionMetricVis", + "scope": "common", + "docId": "kibExpressionMetricVisPluginApi", + "section": "def-common.MetricVisRenderConfig", + "text": "MetricVisRenderConfig" + }, + "; }>, ", + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExecutionContext", + "text": "ExecutionContext" + }, + "<", + { + "pluginId": "inspector", + "scope": "common", + "docId": "kibInspectorPluginApi", + "section": "def-common.Adapters", + "text": "Adapters" + }, + ", ", + { + "pluginId": "@kbn/utility-types", + "scope": "server", + "docId": "kibKbnUtilityTypesPluginApi", + "section": "def-server.SerializableRecord", + "text": "SerializableRecord" + }, + ">>" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.PLUGIN_ID", + "type": "string", + "tags": [], + "label": "PLUGIN_ID", + "description": [], + "signature": [ + "\"expressionMetricVis\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/index.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "expressionMetricVis", + "id": "def-common.PLUGIN_NAME", + "type": "string", + "tags": [], + "label": "PLUGIN_NAME", + "description": [], + "signature": [ + "\"expressionMetricVis\"" + ], + "path": "src/plugins/chart_expressions/expression_metric/common/index.ts", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx new file mode 100644 index 000000000000..891d4d3f022d --- /dev/null +++ b/api_docs/expression_metric_vis.mdx @@ -0,0 +1,33 @@ +--- +id: kibExpressionMetricVisPluginApi +slug: /kibana-dev-docs/api/expressionMetricVis +title: "expressionMetricVis" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the expressionMetricVis plugin +date: 2020-11-16 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import expressionMetricVisObj from './expression_metric_vis.json'; + +Expression MetricVis plugin adds a `metric` renderer and function to the expression plugin. The renderer will display the `metric` chart. + +Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 49 | 0 | 49 | 0 | + +## Common + +### Functions + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/fleet.json b/api_docs/fleet.json index b1a9d1bf8948..0bdb078d6f70 100644 --- a/api_docs/fleet.json +++ b/api_docs/fleet.json @@ -731,6 +731,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installing", + "text": "Installing" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -755,6 +779,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallFailed", + "text": "InstallFailed" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -779,6 +827,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installing", + "text": "Installing" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -802,6 +874,30 @@ "section": "def-common.EpmPackageAdditions", "text": "EpmPackageAdditions" }, + ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallFailed", + "text": "InstallFailed" + }, + ">" ], "path": "x-pack/plugins/fleet/public/types/ui_extensions.ts", @@ -2064,40 +2160,6 @@ ], "returnComment": [] }, - { - "parentPluginId": "fleet", - "id": "def-public.pagePathGetters.add_integration_from_policy", - "type": "Function", - "tags": [], - "label": "add_integration_from_policy", - "description": [ - "// TODO: This might need to be removed because we do not have a way to pick an integration in line anymore" - ], - "signature": [ - "({ policyId }: ", - "DynamicPagePathValues", - ") => [string, string]" - ], - "path": "x-pack/plugins/fleet/public/constants/page_paths.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "fleet", - "id": "def-public.pagePathGetters.add_integration_from_policy.$1", - "type": "Object", - "tags": [], - "label": "{ policyId }", - "description": [], - "signature": [ - "DynamicPagePathValues" - ], - "path": "x-pack/plugins/fleet/public/constants/page_paths.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [] - }, { "parentPluginId": "fleet", "id": "def-public.pagePathGetters.add_integration_to_policy", @@ -6342,6 +6404,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installing", + "text": "Installing" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -6366,6 +6452,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallFailed", + "text": "InstallFailed" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -6390,6 +6500,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installing", + "text": "Installing" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -6413,6 +6547,30 @@ "section": "def-common.EpmPackageAdditions", "text": "EpmPackageAdditions" }, + ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallFailed", + "text": "InstallFailed" + }, + "> | (Pick<", { "pluginId": "fleet", @@ -6439,7 +6597,33 @@ "section": "def-common.RegistryPackage", "text": "RegistryPackage" }, - ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"not_installed\"; } & { integration?: string | undefined; id: string; })) => boolean" + ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"installing\"; savedObject: ", + "SavedObject", + "<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installation", + "text": "Installation" + }, + ">; } & { integration?: string | undefined; id: string; }) | (Pick<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.RegistryPackage", + "text": "RegistryPackage" + }, + ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"not_installed\"; } & { integration?: string | undefined; id: string; }) | (Pick<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.RegistryPackage", + "text": "RegistryPackage" + }, + ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"install_failed\"; } & { integration?: string | undefined; id: string; })) => boolean" ], "path": "x-pack/plugins/fleet/common/services/packages_with_integrations.ts", "deprecated": false, @@ -6480,8 +6664,8 @@ "pluginId": "fleet", "scope": "common", "docId": "kibFleetPluginApi", - "section": "def-common.NotInstalled", - "text": "NotInstalled" + "section": "def-common.Installing", + "text": "Installing" }, "> | (Pick<", + ">> | ", { "pluginId": "fleet", "scope": "common", "docId": "kibFleetPluginApi", - "section": "def-common.RegistryPackage", - "text": "RegistryPackage" + "section": "def-common.Installed", + "text": "Installed" }, - ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"installed\"; savedObject: ", - "SavedObject", - "<", + "> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installing", + "text": "Installing" + }, + "> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.NotInstalled", + "text": "NotInstalled" + }, + "> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallFailed", + "text": "InstallFailed" + }, + "> | (Pick<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.RegistryPackage", + "text": "RegistryPackage" + }, + ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"installed\"; savedObject: ", + "SavedObject", + "<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installation", + "text": "Installation" }, ">; } & { integration?: string | undefined; id: string; }) | (Pick<", { @@ -6573,7 +6853,33 @@ "section": "def-common.RegistryPackage", "text": "RegistryPackage" }, - ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"not_installed\"; } & { integration?: string | undefined; id: string; })" + ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"installing\"; savedObject: ", + "SavedObject", + "<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installation", + "text": "Installation" + }, + ">; } & { integration?: string | undefined; id: string; }) | (Pick<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.RegistryPackage", + "text": "RegistryPackage" + }, + ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"not_installed\"; } & { integration?: string | undefined; id: string; }) | (Pick<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.RegistryPackage", + "text": "RegistryPackage" + }, + ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"install_failed\"; } & { integration?: string | undefined; id: string; })" ], "path": "x-pack/plugins/fleet/common/services/packages_with_integrations.ts", "deprecated": false, @@ -11069,6 +11375,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installing", + "text": "Installing" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -11093,6 +11423,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallFailed", + "text": "InstallFailed" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -11117,6 +11471,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installing", + "text": "Installing" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -11140,6 +11518,30 @@ "section": "def-common.EpmPackageAdditions", "text": "EpmPackageAdditions" }, + ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallFailed", + "text": "InstallFailed" + }, + ">" ], "path": "x-pack/plugins/fleet/common/types/rest_spec/epm.ts", @@ -12090,7 +12492,7 @@ "label": "install_status", "description": [], "signature": [ - "\"installed\" | \"installing\"" + "\"installed\" | \"installing\" | \"install_failed\"" ], "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false @@ -15771,6 +16173,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installing", + "text": "Installing" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -15795,6 +16221,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallFailed", + "text": "InstallFailed" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -15819,6 +16269,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installing", + "text": "Installing" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -15842,6 +16316,30 @@ "section": "def-common.EpmPackageAdditions", "text": "EpmPackageAdditions" }, + ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallFailed", + "text": "InstallFailed" + }, + ">" ], "path": "x-pack/plugins/fleet/common/types/rest_spec/epm.ts", @@ -17120,7 +17618,7 @@ "label": "EpmPackageInstallStatus", "description": [], "signature": [ - "\"installed\" | \"installing\"" + "\"installed\" | \"installing\" | \"install_failed\"" ], "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false, @@ -17390,6 +17888,14 @@ "text": "Installed" }, " | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installing", + "text": "Installing" + }, + " | ", { "pluginId": "fleet", "scope": "common", @@ -17397,6 +17903,14 @@ "section": "def-common.NotInstalled", "text": "NotInstalled" }, + " | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallFailed", + "text": "InstallFailed" + }, "" ], "path": "x-pack/plugins/fleet/common/types/models/epm.ts", @@ -17439,7 +17953,7 @@ "label": "InstallationStatus", "description": [], "signature": [ - "{ readonly Installed: \"installed\"; readonly NotInstalled: \"not_installed\"; }" + "{ readonly Installed: \"installed\"; readonly Installing: \"installing\"; readonly InstallFailed: \"install_failed\"; readonly NotInstalled: \"not_installed\"; }" ], "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false, @@ -17469,6 +17983,44 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "fleet", + "id": "def-common.InstallFailed", + "type": "Type", + "tags": [], + "label": "InstallFailed", + "description": [], + "signature": [ + "T & { status: \"install_failed\"; }" + ], + "path": "x-pack/plugins/fleet/common/types/models/epm.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "fleet", + "id": "def-common.Installing", + "type": "Type", + "tags": [], + "label": "Installing", + "description": [], + "signature": [ + "T & { status: \"installing\"; savedObject: ", + "SavedObject", + "<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installation", + "text": "Installation" + }, + ">; }" + ], + "path": "x-pack/plugins/fleet/common/types/models/epm.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "fleet", "id": "def-common.InstallSource", @@ -17636,6 +18188,14 @@ "section": "def-common.KibanaAssetParts", "text": "KibanaAssetParts" }, + "[]; tag: ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.KibanaAssetParts", + "text": "KibanaAssetParts" + }, "[]; }" ], "path": "x-pack/plugins/fleet/common/types/models/epm.ts", @@ -17869,6 +18429,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installing", + "text": "Installing" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -17893,6 +18477,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallFailed", + "text": "InstallFailed" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -17917,6 +18525,30 @@ "text": "EpmPackageAdditions" }, ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installing", + "text": "Installing" + }, + "> | ", { "pluginId": "fleet", "scope": "common", @@ -17940,6 +18572,30 @@ "section": "def-common.EpmPackageAdditions", "text": "EpmPackageAdditions" }, + ">> | ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallFailed", + "text": "InstallFailed" + }, + ">" ], "path": "x-pack/plugins/fleet/common/types/models/epm.ts", @@ -18001,7 +18657,33 @@ "section": "def-common.RegistryPackage", "text": "RegistryPackage" }, - ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"not_installed\"; } & { integration?: string | undefined; id: string; })" + ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"installing\"; savedObject: ", + "SavedObject", + "<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.Installation", + "text": "Installation" + }, + ">; } & { integration?: string | undefined; id: string; }) | (Pick<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.RegistryPackage", + "text": "RegistryPackage" + }, + ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"not_installed\"; } & { integration?: string | undefined; id: string; }) | (Pick<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.RegistryPackage", + "text": "RegistryPackage" + }, + ", \"type\" | \"title\" | \"description\" | \"icons\" | \"name\" | \"version\" | \"path\" | \"download\" | \"internal\" | \"data_streams\" | \"release\" | \"policy_templates\"> & { status: \"install_failed\"; } & { integration?: string | undefined; id: string; })" ], "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false, @@ -18155,6 +18837,22 @@ "section": "def-common.PackageList", "text": "PackageList" }, + "; installing: ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.PackageList", + "text": "PackageList" + }, + "; install_failed: ", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.PackageList", + "text": "PackageList" + }, "; not_installed: ", { "pluginId": "fleet", @@ -20496,7 +21194,7 @@ "label": "installationStatuses", "description": [], "signature": [ - "{ readonly Installed: \"installed\"; readonly NotInstalled: \"not_installed\"; }" + "{ readonly Installed: \"installed\"; readonly Installing: \"installing\"; readonly InstallFailed: \"install_failed\"; readonly NotInstalled: \"not_installed\"; }" ], "path": "x-pack/plugins/fleet/common/constants/epm.ts", "deprecated": false, diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index bd467d94391f..88d006982d0f 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -18,7 +18,7 @@ Contact [Fleet](https://github.com/orgs/elastic/teams/fleet) for questions regar | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1207 | 15 | 1106 | 10 | +| 1207 | 15 | 1107 | 10 | ## Client diff --git a/api_docs/interactive_setup.json b/api_docs/interactive_setup.json index b10a3bd9f149..bfccfd28388c 100644 --- a/api_docs/interactive_setup.json +++ b/api_docs/interactive_setup.json @@ -265,6 +265,104 @@ } ], "misc": [ + { + "parentPluginId": "interactiveSetup", + "id": "def-common.ERROR_CONFIGURE_FAILURE", + "type": "string", + "tags": [], + "label": "ERROR_CONFIGURE_FAILURE", + "description": [], + "signature": [ + "\"configure_failure\"" + ], + "path": "src/plugins/interactive_setup/common/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "interactiveSetup", + "id": "def-common.ERROR_ELASTICSEARCH_CONNECTION_CONFIGURED", + "type": "string", + "tags": [], + "label": "ERROR_ELASTICSEARCH_CONNECTION_CONFIGURED", + "description": [], + "signature": [ + "\"elasticsearch_connection_configured\"" + ], + "path": "src/plugins/interactive_setup/common/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "interactiveSetup", + "id": "def-common.ERROR_ENROLL_FAILURE", + "type": "string", + "tags": [], + "label": "ERROR_ENROLL_FAILURE", + "description": [], + "signature": [ + "\"enroll_failure\"" + ], + "path": "src/plugins/interactive_setup/common/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "interactiveSetup", + "id": "def-common.ERROR_KIBANA_CONFIG_FAILURE", + "type": "string", + "tags": [], + "label": "ERROR_KIBANA_CONFIG_FAILURE", + "description": [], + "signature": [ + "\"kibana_config_failure\"" + ], + "path": "src/plugins/interactive_setup/common/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "interactiveSetup", + "id": "def-common.ERROR_KIBANA_CONFIG_NOT_WRITABLE", + "type": "string", + "tags": [], + "label": "ERROR_KIBANA_CONFIG_NOT_WRITABLE", + "description": [], + "signature": [ + "\"kibana_config_not_writable\"" + ], + "path": "src/plugins/interactive_setup/common/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "interactiveSetup", + "id": "def-common.ERROR_OUTSIDE_PREBOOT_STAGE", + "type": "string", + "tags": [], + "label": "ERROR_OUTSIDE_PREBOOT_STAGE", + "description": [], + "signature": [ + "\"outside_preboot_stage\"" + ], + "path": "src/plugins/interactive_setup/common/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "interactiveSetup", + "id": "def-common.ERROR_PING_FAILURE", + "type": "string", + "tags": [], + "label": "ERROR_PING_FAILURE", + "description": [], + "signature": [ + "\"ping_failure\"" + ], + "path": "src/plugins/interactive_setup/common/constants.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "interactiveSetup", "id": "def-common.VERIFICATION_CODE_LENGTH", diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index afc6ca7bb7e5..b954d2c42354 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -18,7 +18,7 @@ Contact [Platform Security](https://github.com/orgs/elastic/teams/kibana-securit | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 19 | 0 | 9 | 0 | +| 26 | 0 | 16 | 0 | ## Common diff --git a/api_docs/kbn_config.json b/api_docs/kbn_config.json index 71827efb5cea..f8a49fd35886 100644 --- a/api_docs/kbn_config.json +++ b/api_docs/kbn_config.json @@ -250,6 +250,45 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/config", + "id": "def-server.ConfigDeprecationContext", + "type": "Interface", + "tags": [], + "label": "ConfigDeprecationContext", + "description": [ + "\nDeprecation context provided to {@link ConfigDeprecation | config deprecations}\n" + ], + "path": "packages/kbn-config/src/deprecation/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/config", + "id": "def-server.ConfigDeprecationContext.version", + "type": "string", + "tags": [], + "label": "version", + "description": [ + "The current Kibana version, e.g `7.16.1`, `8.0.0`" + ], + "path": "packages/kbn-config/src/deprecation/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/config", + "id": "def-server.ConfigDeprecationContext.branch", + "type": "string", + "tags": [], + "label": "branch", + "description": [ + "The current Kibana branch, e.g `7.x`, `7.16`, `master`" + ], + "path": "packages/kbn-config/src/deprecation/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/config", "id": "def-server.ConfigDeprecationFactory", @@ -275,7 +314,13 @@ "(deprecatedKey: string, removeBy: string, details?: Partial<", "DeprecatedConfigDetails", "> | undefined) => ", - "ConfigDeprecation" + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + } ], "path": "packages/kbn-config/src/deprecation/types.ts", "deprecated": false, @@ -340,7 +385,13 @@ "(deprecatedKey: string, removeBy: string, details?: Partial<", "DeprecatedConfigDetails", "> | undefined) => ", - "ConfigDeprecation" + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + } ], "path": "packages/kbn-config/src/deprecation/types.ts", "deprecated": false, @@ -405,7 +456,13 @@ "(oldKey: string, newKey: string, details?: Partial<", "DeprecatedConfigDetails", "> | undefined) => ", - "ConfigDeprecation" + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + } ], "path": "packages/kbn-config/src/deprecation/types.ts", "deprecated": false, @@ -470,7 +527,13 @@ "(oldKey: string, newKey: string, details?: Partial<", "DeprecatedConfigDetails", "> | undefined) => ", - "ConfigDeprecation" + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + } ], "path": "packages/kbn-config/src/deprecation/types.ts", "deprecated": false, @@ -535,7 +598,13 @@ "(unusedKey: string, details?: Partial<", "DeprecatedConfigDetails", "> | undefined) => ", - "ConfigDeprecation" + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + } ], "path": "packages/kbn-config/src/deprecation/types.ts", "deprecated": false, @@ -586,7 +655,13 @@ "(unusedKey: string, details?: Partial<", "DeprecatedConfigDetails", "> | undefined) => ", - "ConfigDeprecation" + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + } ], "path": "packages/kbn-config/src/deprecation/types.ts", "deprecated": false, @@ -773,6 +848,123 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/config", + "id": "def-server.ConfigDeprecation", + "type": "Type", + "tags": [], + "label": "ConfigDeprecation", + "description": [ + "\nConfiguration deprecation returned from {@link ConfigDeprecationProvider} that handles a single deprecation from the configuration.\n" + ], + "signature": [ + "(config: Readonly<{ [x: string]: any; }>, fromPath: string, addDeprecation: ", + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.AddConfigDeprecation", + "text": "AddConfigDeprecation" + }, + ", context: ", + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecationContext", + "text": "ConfigDeprecationContext" + }, + ") => void | ", + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecationCommand", + "text": "ConfigDeprecationCommand" + } + ], + "path": "packages/kbn-config/src/deprecation/types.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/config", + "id": "def-server.ConfigDeprecation.$1", + "type": "Object", + "tags": [], + "label": "config", + "description": [ + "must not be mutated, return {@link ConfigDeprecationCommand} to change config shape." + ], + "signature": [ + "{ readonly [x: string]: any; }" + ], + "path": "packages/kbn-config/src/deprecation/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/config", + "id": "def-server.ConfigDeprecation.$2", + "type": "string", + "tags": [], + "label": "fromPath", + "description": [], + "path": "packages/kbn-config/src/deprecation/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/config", + "id": "def-server.ConfigDeprecation.$3", + "type": "Function", + "tags": [], + "label": "addDeprecation", + "description": [], + "signature": [ + "(details: ", + "DeprecatedConfigDetails", + ") => void" + ], + "path": "packages/kbn-config/src/deprecation/types.ts", + "deprecated": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/config", + "id": "def-server.ConfigDeprecation.$3.$1", + "type": "Object", + "tags": [], + "label": "details", + "description": [], + "signature": [ + "DeprecatedConfigDetails" + ], + "path": "packages/kbn-config/src/deprecation/types.ts", + "deprecated": false + } + ] + }, + { + "parentPluginId": "@kbn/config", + "id": "def-server.ConfigDeprecation.$4", + "type": "Object", + "tags": [], + "label": "context", + "description": [], + "signature": [ + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecationContext", + "text": "ConfigDeprecationContext" + } + ], + "path": "packages/kbn-config/src/deprecation/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/config", "id": "def-server.ConfigDeprecationProvider", @@ -792,7 +984,13 @@ "text": "ConfigDeprecationFactory" }, ") => ", - "ConfigDeprecation", + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.ConfigDeprecation", + "text": "ConfigDeprecation" + }, "[]" ], "path": "packages/kbn-config/src/deprecation/types.ts", diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index 94939da9e8d7..4f4058ccd450 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 57 | 0 | 42 | 2 | +| 66 | 0 | 46 | 1 | ## Server diff --git a/api_docs/kbn_es_query.json b/api_docs/kbn_es_query.json index f86c6a78eeb2..0533bd775684 100644 --- a/api_docs/kbn_es_query.json +++ b/api_docs/kbn_es_query.json @@ -1725,6 +1725,210 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.getDataViewFieldSubtypeMulti", + "type": "Function", + "tags": [], + "label": "getDataViewFieldSubtypeMulti", + "description": [], + "signature": [ + "(field: Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubTypeMulti", + "text": "IFieldSubTypeMulti" + }, + " | undefined" + ], + "path": "packages/kbn-es-query/src/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.getDataViewFieldSubtypeMulti.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">" + ], + "path": "packages/kbn-es-query/src/utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.getDataViewFieldSubtypeNested", + "type": "Function", + "tags": [], + "label": "getDataViewFieldSubtypeNested", + "description": [], + "signature": [ + "(field: Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.IFieldSubTypeNested", + "text": "IFieldSubTypeNested" + }, + " | undefined" + ], + "path": "packages/kbn-es-query/src/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.getDataViewFieldSubtypeNested.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">" + ], + "path": "packages/kbn-es-query/src/utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.isDataViewFieldSubtypeMulti", + "type": "Function", + "tags": [], + "label": "isDataViewFieldSubtypeMulti", + "description": [], + "signature": [ + "(field: Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">) => boolean" + ], + "path": "packages/kbn-es-query/src/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.isDataViewFieldSubtypeMulti.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">" + ], + "path": "packages/kbn-es-query/src/utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.isDataViewFieldSubtypeNested", + "type": "Function", + "tags": [], + "label": "isDataViewFieldSubtypeNested", + "description": [], + "signature": [ + "(field: Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">) => boolean" + ], + "path": "packages/kbn-es-query/src/utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.isDataViewFieldSubtypeNested.$1", + "type": "Object", + "tags": [], + "label": "field", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.DataViewFieldBase", + "text": "DataViewFieldBase" + }, + ", \"subType\">" + ], + "path": "packages/kbn-es-query/src/utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/es-query", "id": "def-common.isExistsFilter", @@ -2010,60 +2214,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/es-query", - "id": "def-common.isMissingFilter", - "type": "Function", - "tags": [], - "label": "isMissingFilter", - "description": [], - "signature": [ - "(filter: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" - }, - ") => filter is ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.MissingFilter", - "text": "MissingFilter" - } - ], - "path": "packages/kbn-es-query/src/filters/build_filters/missing_filter.ts", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/es-query", - "id": "def-common.isMissingFilter.$1", - "type": "Object", - "tags": [], - "label": "filter", - "description": [], - "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" - } - ], - "path": "packages/kbn-es-query/src/filters/build_filters/missing_filter.ts", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [ - "`true` if a filter is an `MissingFilter`" - ], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/es-query", "id": "def-common.isPhraseFilter", @@ -3136,18 +3286,14 @@ { "parentPluginId": "@kbn/es-query", "id": "def-common.DataViewFieldBase.subType", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "subType", "description": [], "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined" ], "path": "packages/kbn-es-query/src/es_query/types.ts", @@ -3279,38 +3425,49 @@ }, { "parentPluginId": "@kbn/es-query", - "id": "def-common.IFieldSubType", + "id": "def-common.IFieldSubTypeMulti", "type": "Interface", "tags": [], - "label": "IFieldSubType", - "description": [ - "\nA field's sub type" - ], + "label": "IFieldSubTypeMulti", + "description": [], "path": "packages/kbn-es-query/src/es_query/types.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/es-query", - "id": "def-common.IFieldSubType.multi", + "id": "def-common.IFieldSubTypeMulti.multi", "type": "Object", "tags": [], "label": "multi", "description": [], "signature": [ - "{ parent: string; } | undefined" + "{ parent: string; }" ], "path": "packages/kbn-es-query/src/es_query/types.ts", "deprecated": false - }, + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.IFieldSubTypeNested", + "type": "Interface", + "tags": [], + "label": "IFieldSubTypeNested", + "description": [], + "path": "packages/kbn-es-query/src/es_query/types.ts", + "deprecated": false, + "children": [ { "parentPluginId": "@kbn/es-query", - "id": "def-common.IFieldSubType.nested", + "id": "def-common.IFieldSubTypeNested.nested", "type": "Object", "tags": [], "label": "nested", "description": [], "signature": [ - "{ path: string; } | undefined" + "{ path: string; }" ], "path": "packages/kbn-es-query/src/es_query/types.ts", "deprecated": false @@ -3665,7 +3822,7 @@ "section": "def-common.FilterMeta", "text": "FilterMeta" }, - "; exists?: { field: string; } | undefined; }" + "; query: { exists?: { field: string; } | undefined; }; }" ], "path": "packages/kbn-es-query/src/filters/build_filters/exists_filter.ts", "deprecated": false, @@ -3697,14 +3854,6 @@ "text": "MatchAllFilter" }, " | ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.MissingFilter", - "text": "MissingFilter" - }, - " | ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -3777,6 +3926,24 @@ "deprecated": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.IFieldSubType", + "type": "Type", + "tags": [], + "label": "IFieldSubType", + "description": [ + "\nA field's sub type" + ], + "signature": [ + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional" + ], + "path": "packages/kbn-es-query/src/es_query/types.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/es-query", "id": "def-common.IndexPatternBase", @@ -3840,43 +4007,14 @@ }, " & { meta: ", "MatchAllFilterMeta", - "; match_all: ", + "; query: { match_all: ", "QueryDslMatchAllQuery", - "; }" + "; }; }" ], "path": "packages/kbn-es-query/src/filters/build_filters/match_all_filter.ts", "deprecated": false, "initialIsOpen": false }, - { - "parentPluginId": "@kbn/es-query", - "id": "def-common.MissingFilter", - "type": "Type", - "tags": [], - "label": "MissingFilter", - "description": [], - "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" - }, - " & { meta: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.FilterMeta", - "text": "FilterMeta" - }, - "; missing: { field: string; }; }" - ], - "path": "packages/kbn-es-query/src/filters/build_filters/missing_filter.ts", - "deprecated": false, - "initialIsOpen": false - }, { "parentPluginId": "@kbn/es-query", "id": "def-common.PhraseFilter", @@ -3995,7 +4133,7 @@ "section": "def-common.RangeFilterMeta", "text": "RangeFilterMeta" }, - "; range: { [key: string]: ", + "; query: { range: { [key: string]: ", { "pluginId": "@kbn/es-query", "scope": "common", @@ -4003,7 +4141,7 @@ "section": "def-common.RangeFilterParams", "text": "RangeFilterParams" }, - "; }; }" + "; }; }; }" ], "path": "packages/kbn-es-query/src/filters/build_filters/range_filter.ts", "deprecated": false, @@ -4086,9 +4224,9 @@ "section": "def-common.RangeFilterMeta", "text": "RangeFilterMeta" }, - "; script: { script: ", + "; query: { script: { script: ", "InlineScript", - "; }; }" + "; }; }; }" ], "path": "packages/kbn-es-query/src/filters/build_filters/range_filter.ts", "deprecated": false, diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 0ba50032b363..be44bd1e5a9a 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 198 | 2 | 146 | 12 | +| 205 | 2 | 153 | 14 | ## Common diff --git a/api_docs/kbn_logging.json b/api_docs/kbn_logging.json index 32ab4bac1ffa..efb4d2783119 100644 --- a/api_docs/kbn_logging.json +++ b/api_docs/kbn_logging.json @@ -706,7 +706,7 @@ "EcsHttp", " | undefined; log?: Pick<", "EcsLog", - ", \"origin\" | \"original\" | \"file\" | \"syslog\"> | undefined; network?: ", + ", \"origin\" | \"file\" | \"syslog\"> | undefined; network?: ", "EcsNetwork", " | undefined; observer?: ", "EcsObserver", diff --git a/api_docs/kbn_monaco.json b/api_docs/kbn_monaco.json index fd755fda853e..b87619f90045 100644 --- a/api_docs/kbn_monaco.json +++ b/api_docs/kbn_monaco.json @@ -32,8 +32,8 @@ "pluginId": "@kbn/monaco", "scope": "common", "docId": "kibKbnMonacoPluginApi", - "section": "def-common.LangModule", - "text": "LangModule" + "section": "def-common.LangModuleType", + "text": "LangModuleType" }, ") => void" ], @@ -52,8 +52,8 @@ "pluginId": "@kbn/monaco", "scope": "common", "docId": "kibKbnMonacoPluginApi", - "section": "def-common.LangModule", - "text": "LangModule" + "section": "def-common.LangModuleType", + "text": "LangModuleType" } ], "path": "packages/kbn-monaco/src/helpers.ts", @@ -68,26 +68,26 @@ "interfaces": [ { "parentPluginId": "@kbn/monaco", - "id": "def-common.CompleteLangModule", + "id": "def-common.CompleteLangModuleType", "type": "Interface", "tags": [], - "label": "CompleteLangModule", + "label": "CompleteLangModuleType", "description": [], "signature": [ { "pluginId": "@kbn/monaco", "scope": "common", "docId": "kibKbnMonacoPluginApi", - "section": "def-common.CompleteLangModule", - "text": "CompleteLangModule" + "section": "def-common.CompleteLangModuleType", + "text": "CompleteLangModuleType" }, " extends ", { "pluginId": "@kbn/monaco", "scope": "common", "docId": "kibKbnMonacoPluginApi", - "section": "def-common.LangModule", - "text": "LangModule" + "section": "def-common.LangModuleType", + "text": "LangModuleType" } ], "path": "packages/kbn-monaco/src/types.ts", @@ -95,7 +95,7 @@ "children": [ { "parentPluginId": "@kbn/monaco", - "id": "def-common.CompleteLangModule.languageConfiguration", + "id": "def-common.CompleteLangModuleType.languageConfiguration", "type": "Object", "tags": [], "label": "languageConfiguration", @@ -109,7 +109,7 @@ }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.CompleteLangModule.getSuggestionProvider", + "id": "def-common.CompleteLangModuleType.getSuggestionProvider", "type": "Object", "tags": [], "label": "getSuggestionProvider", @@ -122,7 +122,7 @@ }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.CompleteLangModule.getSyntaxErrors", + "id": "def-common.CompleteLangModuleType.getSyntaxErrors", "type": "Object", "tags": [], "label": "getSyntaxErrors", @@ -138,17 +138,17 @@ }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.LangModule", + "id": "def-common.LangModuleType", "type": "Interface", "tags": [], - "label": "LangModule", + "label": "LangModuleType", "description": [], "path": "packages/kbn-monaco/src/types.ts", "deprecated": false, "children": [ { "parentPluginId": "@kbn/monaco", - "id": "def-common.LangModule.ID", + "id": "def-common.LangModuleType.ID", "type": "string", "tags": [], "label": "ID", @@ -158,7 +158,7 @@ }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.LangModule.lexerRules", + "id": "def-common.LangModuleType.lexerRules", "type": "Object", "tags": [], "label": "lexerRules", @@ -172,7 +172,7 @@ }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.LangModule.languageConfiguration", + "id": "def-common.LangModuleType.languageConfiguration", "type": "Object", "tags": [], "label": "languageConfiguration", @@ -186,7 +186,7 @@ }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.LangModule.getSuggestionProvider", + "id": "def-common.LangModuleType.getSuggestionProvider", "type": "Object", "tags": [], "label": "getSuggestionProvider", @@ -199,7 +199,7 @@ }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.LangModule.getSyntaxErrors", + "id": "def-common.LangModuleType.getSyntaxErrors", "type": "Object", "tags": [], "label": "getSyntaxErrors", @@ -245,10 +245,136 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.PainlessCompletionItem", + "type": "Interface", + "tags": [], + "label": "PainlessCompletionItem", + "description": [], + "path": "packages/kbn-monaco/src/painless/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.PainlessCompletionItem.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-monaco/src/painless/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.PainlessCompletionItem.kind", + "type": "CompoundType", + "tags": [], + "label": "kind", + "description": [], + "signature": [ + "\"type\" | \"keyword\" | \"field\" | \"property\" | \"method\" | \"class\" | \"constructor\"" + ], + "path": "packages/kbn-monaco/src/painless/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.PainlessCompletionItem.documentation", + "type": "string", + "tags": [], + "label": "documentation", + "description": [], + "path": "packages/kbn-monaco/src/painless/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.PainlessCompletionItem.insertText", + "type": "string", + "tags": [], + "label": "insertText", + "description": [], + "path": "packages/kbn-monaco/src/painless/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.PainlessCompletionItem.insertTextAsSnippet", + "type": "CompoundType", + "tags": [], + "label": "insertTextAsSnippet", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-monaco/src/painless/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.PainlessCompletionResult", + "type": "Interface", + "tags": [], + "label": "PainlessCompletionResult", + "description": [], + "path": "packages/kbn-monaco/src/painless/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.PainlessCompletionResult.isIncomplete", + "type": "boolean", + "tags": [], + "label": "isIncomplete", + "description": [], + "path": "packages/kbn-monaco/src/painless/types.ts", + "deprecated": false + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.PainlessCompletionResult.suggestions", + "type": "Array", + "tags": [], + "label": "suggestions", + "description": [], + "signature": [ + { + "pluginId": "@kbn/monaco", + "scope": "common", + "docId": "kibKbnMonacoPluginApi", + "section": "def-common.PainlessCompletionItem", + "text": "PainlessCompletionItem" + }, + "[]" + ], + "path": "packages/kbn-monaco/src/painless/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false } ], "enums": [], "misc": [ + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.PainlessCompletionKind", + "type": "Type", + "tags": [], + "label": "PainlessCompletionKind", + "description": [], + "signature": [ + "\"type\" | \"keyword\" | \"field\" | \"property\" | \"method\" | \"class\" | \"constructor\"" + ], + "path": "packages/kbn-monaco/src/painless/types.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/monaco", "id": "def-common.PainlessContext", diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 4d205f38a760..08c7f95ee4f4 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 28 | 0 | 28 | 3 | +| 38 | 0 | 38 | 3 | ## Common diff --git a/api_docs/kbn_optimizer.json b/api_docs/kbn_optimizer.json index 2e0fe2fb179b..e03d89f200ba 100644 --- a/api_docs/kbn_optimizer.json +++ b/api_docs/kbn_optimizer.json @@ -314,6 +314,61 @@ } ], "functions": [ + { + "parentPluginId": "@kbn/optimizer", + "id": "def-server.logOptimizerProgress", + "type": "Function", + "tags": [], + "label": "logOptimizerProgress", + "description": [], + "signature": [ + "(log: ", + { + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.ToolingLog", + "text": "ToolingLog" + }, + ") => ", + "MonoTypeOperatorFunction", + "<", + { + "pluginId": "@kbn/optimizer", + "scope": "server", + "docId": "kibKbnOptimizerPluginApi", + "section": "def-server.OptimizerUpdate", + "text": "OptimizerUpdate" + }, + ">" + ], + "path": "packages/kbn-optimizer/src/log_optimizer_progress.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/optimizer", + "id": "def-server.logOptimizerProgress.$1", + "type": "Object", + "tags": [], + "label": "log", + "description": [], + "signature": [ + { + "pluginId": "@kbn/dev-utils", + "scope": "server", + "docId": "kibKbnDevUtilsPluginApi", + "section": "def-server.ToolingLog", + "text": "ToolingLog" + } + ], + "path": "packages/kbn-optimizer/src/log_optimizer_progress.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/optimizer", "id": "def-server.logOptimizerState", diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index e59e36212189..e4af57faa790 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 42 | 0 | 42 | 9 | +| 44 | 0 | 44 | 9 | ## Server diff --git a/api_docs/kbn_securitysolution_list_utils.json b/api_docs/kbn_securitysolution_list_utils.json index bfc009f17df0..5b956218f5ac 100644 --- a/api_docs/kbn_securitysolution_list_utils.json +++ b/api_docs/kbn_securitysolution_list_utils.json @@ -2774,7 +2774,7 @@ "label": "bool", "description": [], "signature": [ - "{ must?: unknown; must_not?: unknown; should?: unknown[] | undefined; filter?: unknown; minimum_should_match?: number | undefined; }" + "QueryDslBoolQuery" ], "path": "packages/kbn-securitysolution-list-utils/src/build_exception_filter/index.ts", "deprecated": false @@ -3265,7 +3265,7 @@ "label": "nested", "description": [], "signature": [ - "{ path: string; query: unknown; score_mode: string; }" + "QueryDslNestedQuery" ], "path": "packages/kbn-securitysolution-list-utils/src/build_exception_filter/index.ts", "deprecated": false diff --git a/api_docs/kbn_typed_react_router_config.json b/api_docs/kbn_typed_react_router_config.json index aaa1c025910c..3f9fa9c74d72 100644 --- a/api_docs/kbn_typed_react_router_config.json +++ b/api_docs/kbn_typed_react_router_config.json @@ -165,7 +165,7 @@ "label": "Outlet", "description": [], "signature": [ - "() => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" + "() => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)> | null" ], "path": "packages/kbn-typed-react-router-config/src/outlet.tsx", "deprecated": false, @@ -173,6 +173,61 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/typed-react-router-config", + "id": "def-common.OutletContextProvider", + "type": "Function", + "tags": [], + "label": "OutletContextProvider", + "description": [], + "signature": [ + "({\n element,\n children,\n}: { element: React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>; children: React.ReactNode; }) => JSX.Element" + ], + "path": "packages/kbn-typed-react-router-config/src/outlet.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/typed-react-router-config", + "id": "def-common.OutletContextProvider.$1", + "type": "Object", + "tags": [], + "label": "{\n element,\n children,\n}", + "description": [], + "path": "packages/kbn-typed-react-router-config/src/outlet.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/typed-react-router-config", + "id": "def-common.OutletContextProvider.$1.element", + "type": "Object", + "tags": [], + "label": "element", + "description": [], + "signature": [ + "React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" + ], + "path": "packages/kbn-typed-react-router-config/src/outlet.tsx", + "deprecated": false + }, + { + "parentPluginId": "@kbn/typed-react-router-config", + "id": "def-common.OutletContextProvider.$1.children", + "type": "CompoundType", + "tags": [], + "label": "children", + "description": [], + "signature": [ + "string | number | boolean | {} | React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)> | React.ReactNodeArray | React.ReactPortal | null | undefined" + ], + "path": "packages/kbn-typed-react-router-config/src/outlet.tsx", + "deprecated": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/typed-react-router-config", "id": "def-common.route", @@ -236,7 +291,7 @@ "section": "def-common.Route", "text": "Route" }, - "[]>; children: React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>; }) => JSX.Element" + "[]>; children: React.ReactNode; }) => JSX.Element" ], "path": "packages/kbn-typed-react-router-config/src/use_router.tsx", "deprecated": false, @@ -282,12 +337,12 @@ { "parentPluginId": "@kbn/typed-react-router-config", "id": "def-common.RouterContextProvider.$1.children", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "children", "description": [], "signature": [ - "React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" + "string | number | boolean | {} | React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)> | React.ReactNodeArray | React.ReactPortal | null | undefined" ], "path": "packages/kbn-typed-react-router-config/src/use_router.tsx", "deprecated": false @@ -340,7 +395,7 @@ }, "[]>; history: ", "History", - "; children: React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>; }) => JSX.Element" + "; children: React.ReactNode; }) => JSX.Element" ], "path": "packages/kbn-typed-react-router-config/src/router_provider.tsx", "deprecated": false, @@ -400,12 +455,12 @@ { "parentPluginId": "@kbn/typed-react-router-config", "id": "def-common.RouterProvider.$1.children", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "children", "description": [], "signature": [ - "React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" + "string | number | boolean | {} | React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)> | React.ReactNodeArray | React.ReactPortal | null | undefined" ], "path": "packages/kbn-typed-react-router-config/src/router_provider.tsx", "deprecated": false @@ -2027,6 +2082,44 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "@kbn/typed-react-router-config", + "id": "def-common.Router.getRoutesToMatch", + "type": "Function", + "tags": [], + "label": "getRoutesToMatch", + "description": [], + "signature": [ + "(path: string) => ", + { + "pluginId": "@kbn/typed-react-router-config", + "scope": "common", + "docId": "kibKbnTypedReactRouterConfigPluginApi", + "section": "def-common.FlattenRoutesOf", + "text": "FlattenRoutesOf" + }, + "" + ], + "path": "packages/kbn-typed-react-router-config/src/types/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/typed-react-router-config", + "id": "def-common.Router.getRoutesToMatch.$1", + "type": "string", + "tags": [], + "label": "path", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-typed-react-router-config/src/types/index.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false @@ -2034,6 +2127,24 @@ ], "enums": [], "misc": [ + { + "parentPluginId": "@kbn/typed-react-router-config", + "id": "def-common.FlattenRoutesOf", + "type": "Type", + "tags": [], + "label": "FlattenRoutesOf", + "description": [], + "signature": [ + "Pick<", + "ValuesType", + ">, Exclude>, \"parents\">>[]" + ], + "path": "packages/kbn-typed-react-router-config/src/types/index.ts", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/typed-react-router-config", "id": "def-common.Match", diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 9a665ee8e676..a25fa737996c 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 71 | 0 | 71 | 1 | +| 78 | 0 | 78 | 1 | ## Common diff --git a/api_docs/kibana_utils.json b/api_docs/kibana_utils.json index 6411b8b7e1ce..744c735e94d6 100644 --- a/api_docs/kibana_utils.json +++ b/api_docs/kibana_utils.json @@ -1486,7 +1486,7 @@ "label": "createGetterSetter", "description": [], "signature": [ - "(name: string) => [", + "(name: string, isValueRequired?: boolean) => [", { "pluginId": "kibanaUtils", "scope": "common", @@ -1520,6 +1520,20 @@ "path": "src/plugins/kibana_utils/common/create_getter_setter.ts", "deprecated": false, "isRequired": true + }, + { + "parentPluginId": "kibanaUtils", + "id": "def-public.createGetterSetter.$2", + "type": "boolean", + "tags": [], + "label": "isValueRequired", + "description": [], + "signature": [ + "boolean" + ], + "path": "src/plugins/kibana_utils/common/create_getter_setter.ts", + "deprecated": false, + "isRequired": true } ], "returnComment": [], @@ -7113,7 +7127,7 @@ "label": "createGetterSetter", "description": [], "signature": [ - "(name: string) => [", + "(name: string, isValueRequired?: boolean) => [", { "pluginId": "kibanaUtils", "scope": "common", @@ -7147,6 +7161,20 @@ "path": "src/plugins/kibana_utils/common/create_getter_setter.ts", "deprecated": false, "isRequired": true + }, + { + "parentPluginId": "kibanaUtils", + "id": "def-server.createGetterSetter.$2", + "type": "boolean", + "tags": [], + "label": "isValueRequired", + "description": [], + "signature": [ + "boolean" + ], + "path": "src/plugins/kibana_utils/common/create_getter_setter.ts", + "deprecated": false, + "isRequired": true } ], "returnComment": [], @@ -8686,7 +8714,7 @@ "label": "createGetterSetter", "description": [], "signature": [ - "(name: string) => [", + "(name: string, isValueRequired?: boolean) => [", { "pluginId": "kibanaUtils", "scope": "common", @@ -8720,6 +8748,20 @@ "path": "src/plugins/kibana_utils/common/create_getter_setter.ts", "deprecated": false, "isRequired": true + }, + { + "parentPluginId": "kibanaUtils", + "id": "def-common.createGetterSetter.$2", + "type": "boolean", + "tags": [], + "label": "isValueRequired", + "description": [], + "signature": [ + "boolean" + ], + "path": "src/plugins/kibana_utils/common/create_getter_setter.ts", + "deprecated": false, + "isRequired": true } ], "returnComment": [], diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index b428f08aca7e..807999db2f4e 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 603 | 3 | 410 | 8 | +| 606 | 3 | 413 | 8 | ## Client diff --git a/api_docs/lens.json b/api_docs/lens.json index 9d9591af006a..18f6f3320f62 100644 --- a/api_docs/lens.json +++ b/api_docs/lens.json @@ -1807,6 +1807,19 @@ ], "path": "x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts", "deprecated": false + }, + { + "parentPluginId": "lens", + "id": "def-public.YConfig.textVisibility", + "type": "CompoundType", + "tags": [], + "label": "textVisibility", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/lens/common/expressions/xy_chart/axis_config.ts", + "deprecated": false } ], "initialIsOpen": false diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 6165c3ec65d5..9232e5da0942 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -18,7 +18,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 252 | 0 | 234 | 24 | +| 253 | 0 | 235 | 24 | ## Client diff --git a/api_docs/maps.json b/api_docs/maps.json index b5ff309ccbbb..3b4551b099af 100644 --- a/api_docs/maps.json +++ b/api_docs/maps.json @@ -2962,7 +2962,7 @@ "section": "def-public.LayerWizard", "text": "LayerWizard" }, - ") => Promise" + ") => void" ], "path": "x-pack/plugins/maps/public/api/setup_api.ts", "deprecated": false, @@ -3000,7 +3000,7 @@ "signature": [ "(entry: ", "SourceRegistryEntry", - ") => Promise" + ") => void" ], "path": "x-pack/plugins/maps/public/api/setup_api.ts", "deprecated": false, diff --git a/api_docs/observability.json b/api_docs/observability.json index 5887895476e3..aa66c500c05e 100644 --- a/api_docs/observability.json +++ b/api_docs/observability.json @@ -238,9 +238,11 @@ "label": "FilterValueLabel", "description": [], "signature": [ - "({\n label,\n field,\n value,\n negate,\n indexPattern,\n invertFilter,\n removeFilter,\n allowExclusion = true,\n}: Props) => JSX.Element | null" + "(props: ", + "FilterValueLabelProps", + ") => JSX.Element" ], - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx", + "path": "x-pack/plugins/observability/public/components/shared/index.tsx", "deprecated": false, "children": [ { @@ -248,12 +250,12 @@ "id": "def-public.FilterValueLabel.$1", "type": "Object", "tags": [], - "label": "{\n label,\n field,\n value,\n negate,\n indexPattern,\n invertFilter,\n removeFilter,\n allowExclusion = true,\n}", + "label": "props", "description": [], "signature": [ - "Props" + "FilterValueLabelProps" ], - "path": "x-pack/plugins/observability/public/components/shared/filter_value_label/filter_value_label.tsx", + "path": "x-pack/plugins/observability/public/components/shared/index.tsx", "deprecated": false, "isRequired": true } @@ -386,6 +388,48 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-public.InspectorContextProvider", + "type": "Function", + "tags": [], + "label": "InspectorContextProvider", + "description": [], + "signature": [ + "({ children }: { children: React.ReactNode; }) => JSX.Element" + ], + "path": "x-pack/plugins/observability/public/context/inspector/inspector_context.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "observability", + "id": "def-public.InspectorContextProvider.$1", + "type": "Object", + "tags": [], + "label": "{ children }", + "description": [], + "path": "x-pack/plugins/observability/public/context/inspector/inspector_context.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "observability", + "id": "def-public.InspectorContextProvider.$1.children", + "type": "CompoundType", + "tags": [], + "label": "children", + "description": [], + "signature": [ + "string | number | boolean | {} | React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)> | React.ReactNodeArray | React.ReactPortal | null | undefined" + ], + "path": "x-pack/plugins/observability/public/context/inspector/inspector_context.tsx", + "deprecated": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-public.LazyAlertsFlyout", @@ -833,6 +877,23 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-public.useInspectorContext", + "type": "Function", + "tags": [], + "label": "useInspectorContext", + "description": [], + "signature": [ + "() => ", + "InspectorContextValue" + ], + "path": "x-pack/plugins/observability/public/context/inspector/use_inspector_context.tsx", + "deprecated": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-public.useTheme", @@ -3660,6 +3721,173 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-server.getInspectResponse", + "type": "Function", + "tags": [], + "label": "getInspectResponse", + "description": [ + "\nCreate a formatted response to be sent in the _inspect key for use in the\ninspector." + ], + "signature": [ + "({\n esError,\n esRequestParams,\n esRequestStatus,\n esResponse,\n kibanaRequest,\n operationName,\n startTime,\n}: { esError: ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.WrappedElasticsearchClientError", + "text": "WrappedElasticsearchClientError" + }, + " | null; esRequestParams: Record; esRequestStatus: ", + { + "pluginId": "inspector", + "scope": "common", + "docId": "kibInspectorPluginApi", + "section": "def-common.RequestStatus", + "text": "RequestStatus" + }, + "; esResponse: any; kibanaRequest: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreHttpPluginApi", + "section": "def-server.KibanaRequest", + "text": "KibanaRequest" + }, + "; operationName: string; startTime: number; }) => ", + { + "pluginId": "inspector", + "scope": "common", + "docId": "kibInspectorPluginApi", + "section": "def-common.Request", + "text": "Request" + } + ], + "path": "x-pack/plugins/observability/server/utils/get_inspect_response.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "observability", + "id": "def-server.getInspectResponse.$1", + "type": "Object", + "tags": [], + "label": "{\n esError,\n esRequestParams,\n esRequestStatus,\n esResponse,\n kibanaRequest,\n operationName,\n startTime,\n}", + "description": [], + "path": "x-pack/plugins/observability/server/utils/get_inspect_response.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "observability", + "id": "def-server.getInspectResponse.$1.esError", + "type": "CompoundType", + "tags": [], + "label": "esError", + "description": [], + "signature": [ + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.WrappedElasticsearchClientError", + "text": "WrappedElasticsearchClientError" + }, + " | null" + ], + "path": "x-pack/plugins/observability/server/utils/get_inspect_response.ts", + "deprecated": false + }, + { + "parentPluginId": "observability", + "id": "def-server.getInspectResponse.$1.esRequestParams", + "type": "Object", + "tags": [], + "label": "esRequestParams", + "description": [], + "signature": [ + "{ [x: string]: any; }" + ], + "path": "x-pack/plugins/observability/server/utils/get_inspect_response.ts", + "deprecated": false + }, + { + "parentPluginId": "observability", + "id": "def-server.getInspectResponse.$1.esRequestStatus", + "type": "Enum", + "tags": [], + "label": "esRequestStatus", + "description": [], + "signature": [ + { + "pluginId": "inspector", + "scope": "common", + "docId": "kibInspectorPluginApi", + "section": "def-common.RequestStatus", + "text": "RequestStatus" + } + ], + "path": "x-pack/plugins/observability/server/utils/get_inspect_response.ts", + "deprecated": false + }, + { + "parentPluginId": "observability", + "id": "def-server.getInspectResponse.$1.esResponse", + "type": "Any", + "tags": [], + "label": "esResponse", + "description": [], + "signature": [ + "any" + ], + "path": "x-pack/plugins/observability/server/utils/get_inspect_response.ts", + "deprecated": false + }, + { + "parentPluginId": "observability", + "id": "def-server.getInspectResponse.$1.kibanaRequest", + "type": "Object", + "tags": [], + "label": "kibanaRequest", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreHttpPluginApi", + "section": "def-server.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "x-pack/plugins/observability/server/utils/get_inspect_response.ts", + "deprecated": false + }, + { + "parentPluginId": "observability", + "id": "def-server.getInspectResponse.$1.operationName", + "type": "string", + "tags": [], + "label": "operationName", + "description": [], + "path": "x-pack/plugins/observability/server/utils/get_inspect_response.ts", + "deprecated": false + }, + { + "parentPluginId": "observability", + "id": "def-server.getInspectResponse.$1.startTime", + "type": "number", + "tags": [], + "label": "startTime", + "description": [], + "path": "x-pack/plugins/observability/server/utils/get_inspect_response.ts", + "deprecated": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-server.kqlQuery", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 4b7ac03a70e1..57228c938c38 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -18,7 +18,7 @@ Contact [Observability UI](https://github.com/orgs/elastic/teams/observability-u | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 245 | 0 | 245 | 10 | +| 258 | 1 | 257 | 12 | ## Client diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index ab85e0291910..533a86b9e806 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -12,13 +12,13 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 201 | 156 | 32 | +| 202 | 158 | 32 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 24158 | 274 | 19590 | 1579 | +| 24409 | 277 | 19821 | 1585 | ## Plugin Directory @@ -27,23 +27,23 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Kibana Alerting](https://github.com/orgs/elastic/teams/kibana-alerting-services) | - | 125 | 0 | 125 | 8 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 23 | 0 | 22 | 1 | | | [Kibana Alerting](https://github.com/orgs/elastic/teams/kibana-alerting-services) | - | 249 | 0 | 241 | 17 | -| | [APM UI](https://github.com/orgs/elastic/teams/apm-ui) | - | 42 | 0 | 42 | 37 | +| | [APM UI](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 42 | 0 | 42 | 37 | | | [APM UI](https://github.com/orgs/elastic/teams/apm-ui) | - | 6 | 0 | 6 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 9 | 0 | 9 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 77 | 1 | 66 | 2 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | | | [Security Solution Threat Hunting](https://github.com/orgs/elastic/teams/security-threat-hunting) | The Case management system in Kibana | 475 | 0 | 431 | 14 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 223 | 2 | 192 | 3 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 285 | 4 | 253 | 3 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 22 | 0 | 22 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 9 | 0 | 9 | 1 | -| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2293 | 27 | 1020 | 29 | +| | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 2300 | 27 | 1019 | 29 | | crossClusterReplication | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 85 | 1 | 78 | 1 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 145 | 1 | 132 | 10 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 51 | 0 | 50 | 0 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3181 | 43 | 2796 | 48 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3192 | 43 | 2807 | 48 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Enhanced data plugin. (See src/plugins/data.) Enhances the main data plugin with a search session management UI. Includes a reusable search session indicator component to use in other applications. Exposes routes for managing search sessions. Includes a service that monitors, updates, and cleans up search session saved objects. | 16 | 0 | 16 | 2 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 671 | 6 | 531 | 5 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 681 | 6 | 541 | 5 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | The Data Visualizer tools help you understand your data, by analyzing the metrics and fields in a log file or an existing Elasticsearch index. | 80 | 5 | 80 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 10 | 0 | 8 | 2 | | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 82 | 0 | 56 | 6 | @@ -57,6 +57,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'error' renderer to expressions | 12 | 0 | 12 | 2 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'image' function and renderer to expressions | 24 | 0 | 24 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'metric' function and renderer to expressions | 30 | 0 | 25 | 0 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Expression MetricVis plugin adds a `metric` renderer and function to the expression plugin. The renderer will display the `metric` chart. | 49 | 0 | 49 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'repeatImage' function and renderer to expressions | 30 | 0 | 30 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'revealImage' function and renderer to expressions | 12 | 0 | 12 | 3 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'shape' function and renderer to expressions | 143 | 0 | 143 | 0 | @@ -65,7 +66,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 216 | 0 | 98 | 2 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Index pattern fields and ambiguous values formatters | 288 | 7 | 250 | 3 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 129 | 4 | 129 | 1 | -| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1207 | 15 | 1106 | 10 | +| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1207 | 15 | 1107 | 10 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 68 | 0 | 14 | 5 | | globalSearchBar | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | globalSearchProviders | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | @@ -81,13 +82,13 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | ingestPipelines | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | inputControlVis | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Input Control visualization to Kibana | 0 | 0 | 0 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 123 | 6 | 96 | 4 | -| | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides UI and APIs for the interactive setup mode. | 19 | 0 | 9 | 0 | +| | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides UI and APIs for the interactive setup mode. | 26 | 0 | 16 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 48 | 1 | 45 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 6 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 297 | 8 | 260 | 5 | | kibanaUsageCollection | [Kibana Telemtry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 0 | 0 | 0 | 0 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 603 | 3 | 410 | 8 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 252 | 0 | 234 | 24 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 606 | 3 | 413 | 8 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 253 | 0 | 235 | 24 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 8 | 0 | 8 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 3 | 0 | 3 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 8 | @@ -101,7 +102,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Stack Monitoring](https://github.com/orgs/elastic/teams/stack-monitoring-ui) | - | 10 | 0 | 10 | 2 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 31 | 0 | 31 | 2 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | -| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 245 | 0 | 245 | 10 | +| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 258 | 1 | 257 | 12 | | | [Security asset management](https://github.com/orgs/elastic/teams/security-asset-management) | - | 11 | 0 | 11 | 0 | | painlessLab | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). | 178 | 3 | 151 | 5 | @@ -117,11 +118,10 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 22 | 0 | 17 | 1 | | searchprofiler | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 113 | 0 | 51 | 7 | -| | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin exposes a limited set of security functionality to OSS plugins. | 12 | 0 | 9 | 3 | -| | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 1353 | 8 | 1299 | 29 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds URL Service and sharing capabilities to Kibana | 137 | 1 | 90 | 10 | +| | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 1361 | 8 | 1307 | 30 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds URL Service and sharing capabilities to Kibana | 143 | 1 | 90 | 10 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 23 | 1 | 22 | 1 | -| | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 205 | 0 | 21 | 2 | +| | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 208 | 0 | 21 | 1 | | | [Kibana Alerting](https://github.com/orgs/elastic/teams/kibana-alerting-services) | - | 4 | 0 | 4 | 0 | | | [Kibana Alerting](https://github.com/orgs/elastic/teams/kibana-alerting-services) | - | 70 | 0 | 32 | 7 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 41 | 0 | 0 | 0 | @@ -129,7 +129,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 1 | 0 | 1 | 0 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 14 | 0 | 13 | 0 | | | [Security solution](https://github.com/orgs/elastic/teams/security-solution) | - | 968 | 6 | 847 | 25 | -| transform | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 0 | 0 | 0 | 0 | +| | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [Kibana Localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | | | [Kibana Alerting](https://github.com/orgs/elastic/teams/kibana-alerting-services) | - | 239 | 1 | 230 | 18 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds UI Actions service to Kibana | 127 | 0 | 88 | 11 | @@ -150,7 +150,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Registers the vega visualization. Is the elastic version of vega and vega-lite libraries. | 2 | 0 | 2 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the vislib visualizations. These are the classical area/line/bar, pie, gauge/goal and heatmap charts. We want to replace them with elastic-charts. | 26 | 0 | 25 | 1 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the new xy-axis chart using the elastic-charts library, which will eventually replace the vislib xy-axis charts including bar, area, and line. | 57 | 0 | 51 | 5 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 275 | 13 | 257 | 15 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the shared architecture among all the legacy visualizations, e.g. the visualization type registry or the visualization embeddable. | 304 | 13 | 286 | 16 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Contains the visualize application which includes the listing page and the app frame, which will load the visualization's editor. | 24 | 0 | 23 | 1 | | watcher | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | xpackLegacy | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | @@ -159,6 +159,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Package name           | Maintaining team | Description | API Cnt | Any Cnt | Missing
comments | Missing
exports | |--------------|----------------|-----------|--------------|----------|---------------|--------| +| | [Owner missing] | Elastic APM trace data generator | 15 | 0 | 15 | 2 | | | [Owner missing] | elasticsearch datemath parser, used in kibana | 44 | 0 | 43 | 0 | | | [Owner missing] | - | 11 | 5 | 11 | 0 | | | [Owner missing] | Alerts components and hooks | 9 | 1 | 9 | 0 | @@ -166,20 +167,20 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Owner missing] | - | 14 | 0 | 14 | 0 | | | [Owner missing] | - | 11 | 0 | 11 | 0 | | | [Owner missing] | - | 2 | 0 | 2 | 0 | -| | [Owner missing] | - | 57 | 0 | 42 | 2 | +| | [Owner missing] | - | 66 | 0 | 46 | 1 | | | [Owner missing] | - | 109 | 3 | 107 | 18 | | | [Owner missing] | - | 13 | 0 | 7 | 0 | | | [Owner missing] | - | 258 | 7 | 231 | 4 | | | [Owner missing] | - | 1 | 0 | 1 | 0 | | | [Owner missing] | - | 25 | 0 | 12 | 1 | -| | [Owner missing] | - | 198 | 2 | 146 | 12 | +| | [Owner missing] | - | 205 | 2 | 153 | 14 | | | [Owner missing] | - | 20 | 0 | 16 | 0 | | | [Owner missing] | - | 2 | 0 | 2 | 2 | | | [Owner missing] | - | 18 | 0 | 18 | 2 | | | [Owner missing] | - | 30 | 0 | 5 | 37 | | | [Owner missing] | - | 467 | 9 | 378 | 0 | -| | [Owner missing] | - | 28 | 0 | 28 | 3 | -| | [Owner missing] | - | 42 | 0 | 42 | 9 | +| | [Owner missing] | - | 38 | 0 | 38 | 3 | +| | [Owner missing] | - | 44 | 0 | 44 | 9 | | | [Owner missing] | - | 1 | 0 | 1 | 0 | | | [Owner missing] | Just some helpers for kibana plugin devs. | 1 | 0 | 1 | 0 | | | [Owner missing] | - | 63 | 0 | 49 | 5 | @@ -203,7 +204,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Owner missing] | - | 18 | 1 | 18 | 0 | | | [Owner missing] | - | 2 | 0 | 2 | 0 | | | [Owner missing] | - | 200 | 5 | 177 | 9 | -| | [Owner missing] | - | 71 | 0 | 71 | 1 | +| | [Owner missing] | - | 78 | 0 | 78 | 1 | | | [Owner missing] | - | 29 | 1 | 10 | 1 | | | [Owner missing] | - | 31 | 1 | 21 | 0 | diff --git a/api_docs/saved_objects.json b/api_docs/saved_objects.json index b4e8c07d33ca..af63ad120a8c 100644 --- a/api_docs/saved_objects.json +++ b/api_docs/saved_objects.json @@ -2118,14 +2118,6 @@ "plugin": "discover", "path": "src/plugins/discover/public/application/apps/main/discover_main_route.tsx" }, - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/types.ts" - }, - { - "plugin": "visualizations", - "path": "src/plugins/visualizations/public/types.ts" - }, { "plugin": "visualizations", "path": "src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts" diff --git a/api_docs/security_solution.json b/api_docs/security_solution.json index fe6a5cbb9858..ebff5a94fbe2 100644 --- a/api_docs/security_solution.json +++ b/api_docs/security_solution.json @@ -412,7 +412,9 @@ "label": "config", "description": [], "signature": [ - "Readonly<{} & { enabled: boolean; signalsIndex: string; maxRuleImportExportSize: number; maxRuleImportPayloadBytes: number; maxTimelineImportExportSize: number; maxTimelineImportPayloadBytes: number; alertMergeStrategy: \"allFields\" | \"missingFields\" | \"noFields\"; alertIgnoreFields: string[]; enableExperimental: string[]; endpointResultListDefaultFirstPageIndex: number; endpointResultListDefaultPageSize: number; packagerTaskInterval: string; prebuiltRulesFromFileSystem: boolean; prebuiltRulesFromSavedObjects: boolean; }>" + "Readonly<{} & { enabled: boolean; signalsIndex: string; maxRuleImportExportSize: number; maxRuleImportPayloadBytes: number; maxTimelineImportExportSize: number; maxTimelineImportPayloadBytes: number; alertMergeStrategy: \"allFields\" | \"missingFields\" | \"noFields\"; alertIgnoreFields: string[]; enableExperimental: string[]; ruleExecutionLog: Readonly<{} & { underlyingClient: ", + "UnderlyingLogClient", + "; }>; endpointResultListDefaultFirstPageIndex: number; endpointResultListDefaultPageSize: number; packagerTaskInterval: string; prebuiltRulesFromFileSystem: boolean; prebuiltRulesFromSavedObjects: boolean; }>" ], "path": "x-pack/plugins/security_solution/server/client/client.ts", "deprecated": false, @@ -845,7 +847,9 @@ "label": "ConfigType", "description": [], "signature": [ - "{ readonly enabled: boolean; readonly signalsIndex: string; readonly maxRuleImportExportSize: number; readonly maxRuleImportPayloadBytes: number; readonly maxTimelineImportExportSize: number; readonly maxTimelineImportPayloadBytes: number; readonly alertMergeStrategy: \"allFields\" | \"missingFields\" | \"noFields\"; readonly alertIgnoreFields: string[]; readonly enableExperimental: string[]; readonly endpointResultListDefaultFirstPageIndex: number; readonly endpointResultListDefaultPageSize: number; readonly packagerTaskInterval: string; readonly prebuiltRulesFromFileSystem: boolean; readonly prebuiltRulesFromSavedObjects: boolean; }" + "{ readonly enabled: boolean; readonly signalsIndex: string; readonly maxRuleImportExportSize: number; readonly maxRuleImportPayloadBytes: number; readonly maxTimelineImportExportSize: number; readonly maxTimelineImportPayloadBytes: number; readonly alertMergeStrategy: \"allFields\" | \"missingFields\" | \"noFields\"; readonly alertIgnoreFields: string[]; readonly enableExperimental: string[]; readonly ruleExecutionLog: Readonly<{} & { underlyingClient: ", + "UnderlyingLogClient", + "; }>; readonly endpointResultListDefaultFirstPageIndex: number; readonly endpointResultListDefaultPageSize: number; readonly packagerTaskInterval: string; readonly prebuiltRulesFromFileSystem: boolean; readonly prebuiltRulesFromSavedObjects: boolean; }" ], "path": "x-pack/plugins/security_solution/server/config.ts", "deprecated": false, @@ -2437,12 +2441,15 @@ { "parentPluginId": "securitySolution", "id": "def-common.BrowserField.subType", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "subType", "description": [], "signature": [ - "{ [key: string]: unknown; nested?: { path: string; } | undefined; } | undefined" + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined" ], "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts", "deprecated": false @@ -2942,13 +2949,9 @@ "text": "ColumnHeaderType" }, "; description?: string | undefined; example?: string | undefined; format?: string | undefined; linkField?: string | undefined; placeholder?: string | undefined; subType?: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined; type?: string | undefined; }" ], "path": "x-pack/plugins/timelines/common/types/timeline/columns/index.tsx", @@ -7725,18 +7728,194 @@ }, { "parentPluginId": "securitySolution", - "id": "def-common.HostsRiskyHostsStrategyResponse", + "id": "def-common.HostsRiskScore", + "type": "Interface", + "tags": [], + "label": "HostsRiskScore", + "description": [], + "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "securitySolution", + "id": "def-common.HostsRiskScore.host", + "type": "Object", + "tags": [], + "label": "host", + "description": [], + "signature": [ + "{ name: string; }" + ], + "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts", + "deprecated": false + }, + { + "parentPluginId": "securitySolution", + "id": "def-common.HostsRiskScore.risk_score", + "type": "number", + "tags": [], + "label": "risk_score", + "description": [], + "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts", + "deprecated": false + }, + { + "parentPluginId": "securitySolution", + "id": "def-common.HostsRiskScore.risk", + "type": "string", + "tags": [], + "label": "risk", + "description": [], + "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "securitySolution", + "id": "def-common.HostsRiskScoreRequestOptions", + "type": "Interface", + "tags": [], + "label": "HostsRiskScoreRequestOptions", + "description": [], + "signature": [ + { + "pluginId": "securitySolution", + "scope": "common", + "docId": "kibSecuritySolutionPluginApi", + "section": "def-common.HostsRiskScoreRequestOptions", + "text": "HostsRiskScoreRequestOptions" + }, + " extends ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.IEsSearchRequest", + "text": "IEsSearchRequest" + } + ], + "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "securitySolution", + "id": "def-common.HostsRiskScoreRequestOptions.defaultIndex", + "type": "Array", + "tags": [], + "label": "defaultIndex", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts", + "deprecated": false + }, + { + "parentPluginId": "securitySolution", + "id": "def-common.HostsRiskScoreRequestOptions.factoryQueryType", + "type": "CompoundType", + "tags": [], + "label": "factoryQueryType", + "description": [], + "signature": [ + { + "pluginId": "securitySolution", + "scope": "common", + "docId": "kibSecuritySolutionPluginApi", + "section": "def-common.HostsQueries", + "text": "HostsQueries" + }, + " | ", + { + "pluginId": "securitySolution", + "scope": "common", + "docId": "kibSecuritySolutionPluginApi", + "section": "def-common.HostsKpiQueries", + "text": "HostsKpiQueries" + }, + " | ", + { + "pluginId": "securitySolution", + "scope": "common", + "docId": "kibSecuritySolutionPluginApi", + "section": "def-common.UebaQueries", + "text": "UebaQueries" + }, + " | ", + { + "pluginId": "securitySolution", + "scope": "common", + "docId": "kibSecuritySolutionPluginApi", + "section": "def-common.NetworkQueries", + "text": "NetworkQueries" + }, + " | ", + { + "pluginId": "securitySolution", + "scope": "common", + "docId": "kibSecuritySolutionPluginApi", + "section": "def-common.NetworkKpiQueries", + "text": "NetworkKpiQueries" + }, + " | ", + "CtiQueries", + " | \"matrixHistogram\" | \"matrixHistogramEntities\" | undefined" + ], + "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts", + "deprecated": false + }, + { + "parentPluginId": "securitySolution", + "id": "def-common.HostsRiskScoreRequestOptions.hostName", + "type": "string", + "tags": [], + "label": "hostName", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts", + "deprecated": false + }, + { + "parentPluginId": "securitySolution", + "id": "def-common.HostsRiskScoreRequestOptions.timerange", + "type": "Object", + "tags": [], + "label": "timerange", + "description": [], + "signature": [ + { + "pluginId": "timelines", + "scope": "common", + "docId": "kibTimelinesPluginApi", + "section": "def-common.TimerangeInput", + "text": "TimerangeInput" + }, + " | undefined" + ], + "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "securitySolution", + "id": "def-common.HostsRiskScoreStrategyResponse", "type": "Interface", "tags": [], - "label": "HostsRiskyHostsStrategyResponse", + "label": "HostsRiskScoreStrategyResponse", "description": [], "signature": [ { "pluginId": "securitySolution", "scope": "common", "docId": "kibSecuritySolutionPluginApi", - "section": "def-common.HostsRiskyHostsStrategyResponse", - "text": "HostsRiskyHostsStrategyResponse" + "section": "def-common.HostsRiskScoreStrategyResponse", + "text": "HostsRiskScoreStrategyResponse" }, " extends ", { @@ -7748,12 +7927,12 @@ }, "" ], - "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risky_hosts/index.ts", + "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts", "deprecated": false, "children": [ { "parentPluginId": "securitySolution", - "id": "def-common.HostsRiskyHostsStrategyResponse.inspect", + "id": "def-common.HostsRiskScoreStrategyResponse.inspect", "type": "CompoundType", "tags": [], "label": "inspect", @@ -7768,7 +7947,7 @@ }, " | null | undefined" ], - "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risky_hosts/index.ts", + "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risk_score/index.ts", "deprecated": false } ], @@ -8937,18 +9116,14 @@ { "parentPluginId": "securitySolution", "id": "def-common.IndexField.subType", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "subType", "description": [], "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined" ], "path": "x-pack/plugins/security_solution/common/search_strategy/index_fields/index.ts", @@ -19263,13 +19438,9 @@ "text": "ColumnHeaderType" }, "; description?: string | undefined; example?: string | undefined; format?: string | undefined; linkField?: string | undefined; placeholder?: string | undefined; subType?: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined; type?: string | undefined; }" ], "path": "x-pack/plugins/timelines/common/types/timeline/columns/index.tsx", @@ -19832,26 +20003,6 @@ "deprecated": false, "initialIsOpen": false }, - { - "parentPluginId": "securitySolution", - "id": "def-common.HostsRiskyHostsRequestOptions", - "type": "Type", - "tags": [], - "label": "HostsRiskyHostsRequestOptions", - "description": [], - "signature": [ - { - "pluginId": "securitySolution", - "scope": "common", - "docId": "kibSecuritySolutionPluginApi", - "section": "def-common.RequestBasicOptions", - "text": "RequestBasicOptions" - } - ], - "path": "x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/risky_hosts/index.ts", - "deprecated": false, - "initialIsOpen": false - }, { "parentPluginId": "securitySolution", "id": "def-common.HostTacticsSortField", @@ -21393,13 +21544,13 @@ "section": "def-common.HostsQueries", "text": "HostsQueries" }, - ".riskyHosts ? ", + ".hostsRiskScore ? ", { "pluginId": "securitySolution", "scope": "common", "docId": "kibSecuritySolutionPluginApi", - "section": "def-common.RequestBasicOptions", - "text": "RequestBasicOptions" + "section": "def-common.HostsRiskScoreRequestOptions", + "text": "HostsRiskScoreRequestOptions" }, " : T extends ", { @@ -21883,13 +22034,13 @@ "section": "def-common.HostsQueries", "text": "HostsQueries" }, - ".riskyHosts ? ", + ".hostsRiskScore ? ", { "pluginId": "securitySolution", "scope": "common", "docId": "kibSecuritySolutionPluginApi", - "section": "def-common.HostsRiskyHostsStrategyResponse", - "text": "HostsRiskyHostsStrategyResponse" + "section": "def-common.HostsRiskScoreStrategyResponse", + "text": "HostsRiskScoreStrategyResponse" }, " : T extends ", { diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index e3920a4a3685..fec34843bbcd 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -18,7 +18,7 @@ Contact [Security solution](https://github.com/orgs/elastic/teams/security-solut | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1353 | 8 | 1299 | 29 | +| 1361 | 8 | 1307 | 30 | ## Client diff --git a/api_docs/share.json b/api_docs/share.json index df192ba86bec..992fcb9a0d43 100644 --- a/api_docs/share.json +++ b/api_docs/share.json @@ -1912,7 +1912,15 @@ "section": "def-server.SerializableRecord", "text": "SerializableRecord" }, - ">): void; }" + ">): void; setAnonymousAccessServiceProvider: (provider: () => ", + { + "pluginId": "share", + "scope": "common", + "docId": "kibSharePluginApi", + "section": "def-common.AnonymousAccessServiceContract", + "text": "AnonymousAccessServiceContract" + }, + ") => void; }" ], "path": "src/plugins/share/public/plugin.ts", "deprecated": false, @@ -2224,6 +2232,107 @@ } ], "interfaces": [ + { + "parentPluginId": "share", + "id": "def-common.AnonymousAccessServiceContract", + "type": "Interface", + "tags": [], + "label": "AnonymousAccessServiceContract", + "description": [ + "\nThe contract that is used to check anonymous access for the purposes of sharing public links. The implementation is intended to be\nprovided by the security plugin." + ], + "path": "src/plugins/share/common/anonymous_access/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "share", + "id": "def-common.AnonymousAccessServiceContract.getState", + "type": "Function", + "tags": [], + "label": "getState", + "description": [ + "\nThis function returns the current state of anonymous access." + ], + "signature": [ + "() => Promise<", + { + "pluginId": "share", + "scope": "common", + "docId": "kibSharePluginApi", + "section": "def-common.AnonymousAccessState", + "text": "AnonymousAccessState" + }, + ">" + ], + "path": "src/plugins/share/common/anonymous_access/types.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "share", + "id": "def-common.AnonymousAccessServiceContract.getCapabilities", + "type": "Function", + "tags": [], + "label": "getCapabilities", + "description": [ + "\nThis function returns the capabilities of the anonymous access user." + ], + "signature": [ + "() => Promise<", + "Capabilities", + ">" + ], + "path": "src/plugins/share/common/anonymous_access/types.ts", + "deprecated": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "share", + "id": "def-common.AnonymousAccessState", + "type": "Interface", + "tags": [], + "label": "AnonymousAccessState", + "description": [ + "\nThe state of anonymous access." + ], + "path": "src/plugins/share/common/anonymous_access/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "share", + "id": "def-common.AnonymousAccessState.isEnabled", + "type": "boolean", + "tags": [], + "label": "isEnabled", + "description": [ + "\nWhether anonymous access is enabled or not." + ], + "path": "src/plugins/share/common/anonymous_access/types.ts", + "deprecated": false + }, + { + "parentPluginId": "share", + "id": "def-common.AnonymousAccessState.accessURLParameters", + "type": "CompoundType", + "tags": [], + "label": "accessURLParameters", + "description": [ + "\nIf anonymous access is enabled, this reflects what URL parameters need to be added to a Kibana link to make it publicly accessible.\nNote that if anonymous access is the only authentication method, this will be null." + ], + "signature": [ + "Record | null" + ], + "path": "src/plugins/share/common/anonymous_access/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "share", "id": "def-common.LocatorDefinition", diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 38fb5dc1a848..61c0e26f2447 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 137 | 1 | 90 | 10 | +| 143 | 1 | 90 | 10 | ## Client diff --git a/api_docs/spaces.json b/api_docs/spaces.json index e8195a0a47fc..b391ca6ea0db 100644 --- a/api_docs/spaces.json +++ b/api_docs/spaces.json @@ -295,6 +295,45 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "spaces", + "id": "def-public.EmbeddableLegacyUrlConflictProps", + "type": "Interface", + "tags": [], + "label": "EmbeddableLegacyUrlConflictProps", + "description": [ + "\nProperties for the EmbeddableLegacyUrlConflict component." + ], + "path": "x-pack/plugins/spaces/public/legacy_urls/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "spaces", + "id": "def-public.EmbeddableLegacyUrlConflictProps.targetType", + "type": "string", + "tags": [], + "label": "targetType", + "description": [ + "\nThe target type of the legacy URL alias." + ], + "path": "x-pack/plugins/spaces/public/legacy_urls/types.ts", + "deprecated": false + }, + { + "parentPluginId": "spaces", + "id": "def-public.EmbeddableLegacyUrlConflictProps.sourceId", + "type": "string", + "tags": [], + "label": "sourceId", + "description": [ + "\nThe source ID of the legacy URL alias." + ], + "path": "x-pack/plugins/spaces/public/legacy_urls/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "spaces", "id": "def-public.GetSpaceResult", @@ -359,7 +398,7 @@ "description": [ "\nProperties for the LegacyUrlConflict component." ], - "path": "x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts", + "path": "x-pack/plugins/spaces/public/legacy_urls/types.ts", "deprecated": false, "children": [ { @@ -374,7 +413,7 @@ "signature": [ "string | undefined" ], - "path": "x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts", + "path": "x-pack/plugins/spaces/public/legacy_urls/types.ts", "deprecated": false }, { @@ -386,7 +425,7 @@ "description": [ "\nThe ID of the object that is currently shown on the page." ], - "path": "x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts", + "path": "x-pack/plugins/spaces/public/legacy_urls/types.ts", "deprecated": false }, { @@ -398,7 +437,7 @@ "description": [ "\nThe ID of the other object that the legacy URL alias points to." ], - "path": "x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts", + "path": "x-pack/plugins/spaces/public/legacy_urls/types.ts", "deprecated": false }, { @@ -408,9 +447,9 @@ "tags": [], "label": "otherObjectPath", "description": [ - "\nThe path to use for the new URL, optionally including `search` and/or `hash` URL components." + "\n The path within your application to use for the new URL, optionally including `search` and/or `hash` URL components. Do not include\n `/app/my-app` or the current base path." ], - "path": "x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts", + "path": "x-pack/plugins/spaces/public/legacy_urls/types.ts", "deprecated": false } ], @@ -1305,12 +1344,12 @@ }, { "parentPluginId": "spaces", - "id": "def-public.SpacesApiUiComponent.getLegacyUrlConflict", + "id": "def-public.SpacesApiUiComponent.getEmbeddableLegacyUrlConflict", "type": "Function", "tags": [], - "label": "getLegacyUrlConflict", + "label": "getEmbeddableLegacyUrlConflict", "description": [ - "\nDisplays a callout that needs to be used if a call to `SavedObjectsClient.resolve()` results in an `\"conflict\"` outcome, which\nindicates that the user has loaded the page which is associated directly with one object (A), *and* with a legacy URL that points to a\ndifferent object (B).\n\nIn this case, `SavedObjectsClient.resolve()` has returned object A. This component displays a warning callout to the user explaining\nthat there is a conflict, and it includes a button that will redirect the user to object B when clicked.\n\nConsumers need to determine the local path for the new URL on their own, based on the object ID that was used to call\n`SavedObjectsClient.resolve()` (A) and the `alias_target_id` value in the response (B). For example...\n\nA is `workpad-123` and B is `workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e`.\n\nFull legacy URL: `https://localhost:5601/app/canvas#/workpad/workpad-123/page/1`\n\nNew URL path: `#/workpad/workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e/page/1`" + "\nDisplays a callout that needs to be used if an embeddable component call to `SavedObjectsClient.resolve()` results in an `\"conflict\"`\noutcome, which indicates that the user has loaded an embeddable which is associated directly with one object (A), *and* with a legacy\nURL that points to a different object (B)." ], "signature": [ "(props: ", @@ -1318,8 +1357,8 @@ "pluginId": "spaces", "scope": "public", "docId": "kibSpacesPluginApi", - "section": "def-public.LegacyUrlConflictProps", - "text": "LegacyUrlConflictProps" + "section": "def-public.EmbeddableLegacyUrlConflictProps", + "text": "EmbeddableLegacyUrlConflictProps" }, ") => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" ], @@ -1329,7 +1368,7 @@ "children": [ { "parentPluginId": "spaces", - "id": "def-public.SpacesApiUiComponent.getLegacyUrlConflict.$1", + "id": "def-public.SpacesApiUiComponent.getEmbeddableLegacyUrlConflict.$1", "type": "Uncategorized", "tags": [], "label": "props", @@ -1344,12 +1383,12 @@ }, { "parentPluginId": "spaces", - "id": "def-public.SpacesApiUiComponent.getSpaceAvatar", + "id": "def-public.SpacesApiUiComponent.getLegacyUrlConflict", "type": "Function", "tags": [], - "label": "getSpaceAvatar", + "label": "getLegacyUrlConflict", "description": [ - "\nDisplays an avatar for the given space." + "\nDisplays a callout that needs to be used if a call to `SavedObjectsClient.resolve()` results in an `\"conflict\"` outcome, which\nindicates that the user has loaded the page which is associated directly with one object (A), *and* with a legacy URL that points to a\ndifferent object (B).\n\nIn this case, `SavedObjectsClient.resolve()` has returned object A. This component displays a warning callout to the user explaining\nthat there is a conflict, and it includes a button that will redirect the user to object B when clicked.\n\nConsumers need to determine the local path for the new URL on their own, based on the object ID that was used to call\n`SavedObjectsClient.resolve()` (A) and the `alias_target_id` value in the response (B). For example...\n\nA is `workpad-123` and B is `workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e`.\n\nFull legacy URL: `https://localhost:5601/app/canvas#/workpad/workpad-123/page/1`\n\nNew URL path: `#/workpad/workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e/page/1`" ], "signature": [ "(props: ", @@ -1357,8 +1396,8 @@ "pluginId": "spaces", "scope": "public", "docId": "kibSpacesPluginApi", - "section": "def-public.SpaceAvatarProps", - "text": "SpaceAvatarProps" + "section": "def-public.LegacyUrlConflictProps", + "text": "LegacyUrlConflictProps" }, ") => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" ], @@ -1368,7 +1407,7 @@ "children": [ { "parentPluginId": "spaces", - "id": "def-public.SpacesApiUiComponent.getSpaceAvatar.$1", + "id": "def-public.SpacesApiUiComponent.getLegacyUrlConflict.$1", "type": "Uncategorized", "tags": [], "label": "props", @@ -1383,16 +1422,22 @@ }, { "parentPluginId": "spaces", - "id": "def-public.SpacesApiUiComponent.getSavedObjectConflictMessage", + "id": "def-public.SpacesApiUiComponent.getSpaceAvatar", "type": "Function", "tags": [], - "label": "getSavedObjectConflictMessage", + "label": "getSpaceAvatar", "description": [ - "\nDisplays a saved object conflict message that directs user to disable legacy URL alias" + "\nDisplays an avatar for the given space." ], "signature": [ "(props: ", - "SavedObjectConflictMessageProps", + { + "pluginId": "spaces", + "scope": "public", + "docId": "kibSpacesPluginApi", + "section": "def-public.SpaceAvatarProps", + "text": "SpaceAvatarProps" + }, ") => React.ReactElement React.ReactElement React.Component)> | null) | (new (props: any) => React.Component)>" ], "path": "x-pack/plugins/spaces/public/ui_api/types.ts", @@ -1401,7 +1446,7 @@ "children": [ { "parentPluginId": "spaces", - "id": "def-public.SpacesApiUiComponent.getSavedObjectConflictMessage.$1", + "id": "def-public.SpacesApiUiComponent.getSpaceAvatar.$1", "type": "Uncategorized", "tags": [], "label": "props", diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 12e803604ced..9f4bf1d79346 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -18,7 +18,7 @@ Contact [Platform Security](https://github.com/orgs/elastic/teams/kibana-securit | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 205 | 0 | 21 | 2 | +| 208 | 0 | 21 | 1 | ## Client diff --git a/api_docs/timelines.json b/api_docs/timelines.json index eb31eb6849a8..416f62352039 100644 --- a/api_docs/timelines.json +++ b/api_docs/timelines.json @@ -1657,13 +1657,9 @@ "text": "ColumnHeaderType" }, "; description?: string | undefined; example?: string | undefined; format?: string | undefined; linkField?: string | undefined; placeholder?: string | undefined; subType?: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined; type?: string | undefined; })[]; id: string; title: string; filters?: ", { "pluginId": "@kbn/es-query", @@ -1709,13 +1705,9 @@ "text": "ColumnHeaderType" }, "; description?: string | undefined; example?: string | undefined; format?: string | undefined; linkField?: string | undefined; placeholder?: string | undefined; subType?: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined; type?: string | undefined; })[]; savedObjectId: string | null; unit?: ((n: number) => React.ReactNode) | undefined; dataProviders: ", { "pluginId": "timelines", @@ -1732,15 +1724,15 @@ "section": "def-common.RowRendererId", "text": "RowRendererId" }, - "[]; expandedDetail: ", + "[]; expandedDetail: Partial>; footerText?: React.ReactNode; graphEventId?: string | undefined; kqlQuery: { filterQuery: ", { "pluginId": "timelines", "scope": "common", @@ -5582,12 +5574,15 @@ { "parentPluginId": "timelines", "id": "def-common.BrowserField.subType", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "subType", "description": [], "signature": [ - "{ [key: string]: unknown; nested?: { path: string; } | undefined; } | undefined" + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", + " | undefined" ], "path": "x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts", "deprecated": false @@ -6153,13 +6148,9 @@ "text": "ColumnHeaderType" }, "; description?: string | undefined; example?: string | undefined; format?: string | undefined; linkField?: string | undefined; placeholder?: string | undefined; subType?: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined; type?: string | undefined; }" ], "path": "x-pack/plugins/timelines/common/types/timeline/columns/index.tsx", @@ -8418,18 +8409,14 @@ { "parentPluginId": "timelines", "id": "def-common.IndexField.subType", - "type": "Object", + "type": "CompoundType", "tags": [], "label": "subType", "description": [], "signature": [ - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined" ], "path": "x-pack/plugins/timelines/common/search_strategy/index_fields/index.ts", @@ -11273,14 +11260,15 @@ "label": "expandedDetail", "description": [], "signature": [ + "Partial> | undefined" ], "path": "x-pack/plugins/timelines/common/types/timeline/store.ts", "deprecated": false @@ -12716,13 +12704,9 @@ "text": "ColumnHeaderType" }, "; description?: string | undefined; example?: string | undefined; format?: string | undefined; linkField?: string | undefined; placeholder?: string | undefined; subType?: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.IFieldSubType", - "text": "IFieldSubType" - }, + "IFieldSubTypeMultiOptional", + " | ", + "IFieldSubTypeNestedOptional", " | undefined; type?: string | undefined; }" ], "path": "x-pack/plugins/timelines/common/types/timeline/columns/index.tsx", @@ -14383,17 +14367,9 @@ "label": "TimelineExpandedDetail", "description": [], "signature": [ - "{ query?: Record | { panelView?: \"eventDetail\" | undefined; params?: { eventId: string; indexName: string; ecsData?: ", - "Ecs", - " | undefined; } | undefined; } | { panelView?: \"hostDetail\" | undefined; params?: { hostName: string; } | undefined; } | { panelView?: \"networkDetail\" | undefined; params?: { ip: string; flowTarget: FlowTarget; } | undefined; } | undefined; graph?: Record | { panelView?: \"eventDetail\" | undefined; params?: { eventId: string; indexName: string; ecsData?: ", - "Ecs", - " | undefined; } | undefined; } | { panelView?: \"hostDetail\" | undefined; params?: { hostName: string; } | undefined; } | { panelView?: \"networkDetail\" | undefined; params?: { ip: string; flowTarget: FlowTarget; } | undefined; } | undefined; notes?: Record | { panelView?: \"eventDetail\" | undefined; params?: { eventId: string; indexName: string; ecsData?: ", - "Ecs", - " | undefined; } | undefined; } | { panelView?: \"hostDetail\" | undefined; params?: { hostName: string; } | undefined; } | { panelView?: \"networkDetail\" | undefined; params?: { ip: string; flowTarget: FlowTarget; } | undefined; } | undefined; pinned?: Record | { panelView?: \"eventDetail\" | undefined; params?: { eventId: string; indexName: string; ecsData?: ", - "Ecs", - " | undefined; } | undefined; } | { panelView?: \"hostDetail\" | undefined; params?: { hostName: string; } | undefined; } | { panelView?: \"networkDetail\" | undefined; params?: { ip: string; flowTarget: FlowTarget; } | undefined; } | undefined; eql?: Record | { panelView?: \"eventDetail\" | undefined; params?: { eventId: string; indexName: string; ecsData?: ", + "{ [x: string]: { panelView?: \"eventDetail\" | undefined; params?: { eventId: string; indexName: string; ecsData?: ", "Ecs", - " | undefined; } | undefined; } | { panelView?: \"hostDetail\" | undefined; params?: { hostName: string; } | undefined; } | { panelView?: \"networkDetail\" | undefined; params?: { ip: string; flowTarget: FlowTarget; } | undefined; } | undefined; }" + " | undefined; } | undefined; } | Partial> | { panelView?: \"hostDetail\" | undefined; params?: { hostName: string; } | undefined; } | { panelView?: \"networkDetail\" | undefined; params?: { ip: string; flowTarget: FlowTarget; } | undefined; } | undefined; }" ], "path": "x-pack/plugins/timelines/common/types/timeline/index.ts", "deprecated": false, @@ -14407,9 +14383,9 @@ "label": "TimelineExpandedDetailType", "description": [], "signature": [ - "Record | { panelView?: \"eventDetail\" | undefined; params?: { eventId: string; indexName: string; ecsData?: ", + "{ panelView?: \"eventDetail\" | undefined; params?: { eventId: string; indexName: string; ecsData?: ", "Ecs", - " | undefined; } | undefined; } | { panelView?: \"hostDetail\" | undefined; params?: { hostName: string; } | undefined; } | { panelView?: \"networkDetail\" | undefined; params?: { ip: string; flowTarget: FlowTarget; } | undefined; }" + " | undefined; } | undefined; } | Partial> | { panelView?: \"hostDetail\" | undefined; params?: { hostName: string; } | undefined; } | { panelView?: \"networkDetail\" | undefined; params?: { ip: string; flowTarget: FlowTarget; } | undefined; }" ], "path": "x-pack/plugins/timelines/common/types/timeline/index.ts", "deprecated": false, @@ -14423,9 +14399,9 @@ "label": "TimelineExpandedEventType", "description": [], "signature": [ - "Record | { panelView?: \"eventDetail\" | undefined; params?: { eventId: string; indexName: string; ecsData?: ", + "{ panelView?: \"eventDetail\" | undefined; params?: { eventId: string; indexName: string; ecsData?: ", "Ecs", - " | undefined; } | undefined; }" + " | undefined; } | undefined; } | Partial>" ], "path": "x-pack/plugins/timelines/common/types/timeline/index.ts", "deprecated": false, @@ -14439,7 +14415,7 @@ "label": "TimelineExpandedHostType", "description": [], "signature": [ - "Record | { panelView?: \"hostDetail\" | undefined; params?: { hostName: string; } | undefined; }" + "Partial> | { panelView?: \"hostDetail\" | undefined; params?: { hostName: string; } | undefined; }" ], "path": "x-pack/plugins/timelines/common/types/timeline/index.ts", "deprecated": false, @@ -14453,7 +14429,7 @@ "label": "TimelineExpandedNetworkType", "description": [], "signature": [ - "Record | { panelView?: \"networkDetail\" | undefined; params?: { ip: string; flowTarget: FlowTarget; } | undefined; }" + "Partial> | { panelView?: \"networkDetail\" | undefined; params?: { ip: string; flowTarget: FlowTarget; } | undefined; }" ], "path": "x-pack/plugins/timelines/common/types/timeline/index.ts", "deprecated": false, @@ -14894,7 +14870,9 @@ "label": "ToggleDetailPanel", "description": [], "signature": [ - "(Record & { tabType?: ", + "({ panelView?: \"eventDetail\" | undefined; params?: { eventId: string; indexName: string; ecsData?: ", + "Ecs", + " | undefined; } | undefined; } & { tabType?: ", { "pluginId": "timelines", "scope": "common", @@ -14902,9 +14880,7 @@ "section": "def-common.TimelineTabs", "text": "TimelineTabs" }, - " | undefined; timelineId: string; }) | ({ panelView?: \"eventDetail\" | undefined; params?: { eventId: string; indexName: string; ecsData?: ", - "Ecs", - " | undefined; } | undefined; } & { tabType?: ", + " | undefined; timelineId: string; }) | (Partial> & { tabType?: ", { "pluginId": "timelines", "scope": "common", diff --git a/api_docs/transform.json b/api_docs/transform.json new file mode 100644 index 000000000000..cbf3b9dbac34 --- /dev/null +++ b/api_docs/transform.json @@ -0,0 +1,101 @@ +{ + "id": "transform", + "client": { + "classes": [], + "functions": [ + { + "parentPluginId": "transform", + "id": "def-public.getTransformHealthRuleType", + "type": "Function", + "tags": [], + "label": "getTransformHealthRuleType", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "triggersActionsUi", + "scope": "public", + "docId": "kibTriggersActionsUiPluginApi", + "section": "def-public.AlertTypeModel", + "text": "AlertTypeModel" + }, + "<", + "TransformHealthRuleParams", + ">" + ], + "path": "x-pack/plugins/transform/public/alerting/transform_health_rule_type/register_transform_health_rule.ts", + "deprecated": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "transform", + "id": "def-server.registerTransformHealthRuleType", + "type": "Function", + "tags": [], + "label": "registerTransformHealthRuleType", + "description": [], + "signature": [ + "(params: RegisterParams) => void" + ], + "path": "x-pack/plugins/transform/server/lib/alerting/transform_health_rule_type/register_transform_health_rule_type.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "transform", + "id": "def-server.registerTransformHealthRuleType.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "signature": [ + "RegisterParams" + ], + "path": "x-pack/plugins/transform/server/lib/alerting/transform_health_rule_type/register_transform_health_rule_type.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [ + { + "parentPluginId": "transform", + "id": "def-common.TRANSFORM_RULE_TYPE", + "type": "Object", + "tags": [], + "label": "TRANSFORM_RULE_TYPE", + "description": [], + "signature": [ + "{ readonly TRANSFORM_HEALTH: \"transform_health\"; }" + ], + "path": "x-pack/plugins/transform/common/constants.ts", + "deprecated": false, + "initialIsOpen": false + } + ] + } +} \ No newline at end of file diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx new file mode 100644 index 000000000000..21541177b3ce --- /dev/null +++ b/api_docs/transform.mdx @@ -0,0 +1,37 @@ +--- +id: kibTransformPluginApi +slug: /kibana-dev-docs/api/transform +title: "transform" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the transform plugin +date: 2020-11-16 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import transformObj from './transform.json'; + +This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. + +Contact [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 4 | 0 | 4 | 1 | + +## Client + +### Functions + + +## Server + +### Functions + + +## Common + +### Objects + + diff --git a/api_docs/vis_default_editor.json b/api_docs/vis_default_editor.json index 883d846e67bb..619c3734e951 100644 --- a/api_docs/vis_default_editor.json +++ b/api_docs/vis_default_editor.json @@ -943,9 +943,9 @@ "(paramName: T, value: ", { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchemaParams", + "section": "def-common.ColorSchemaParams", "text": "ColorSchemaParams" }, "[T]) => void" @@ -977,9 +977,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.ColorSchemaParams", + "section": "def-common.ColorSchemaParams", "text": "ColorSchemaParams" }, "[T]" diff --git a/api_docs/vis_type_vislib.json b/api_docs/vis_type_vislib.json index f7bac3f7e3e9..3b5d895ac4c8 100644 --- a/api_docs/vis_type_vislib.json +++ b/api_docs/vis_type_vislib.json @@ -107,9 +107,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.Labels", + "section": "def-common.Labels", "text": "Labels" } ], diff --git a/api_docs/vis_type_xy.json b/api_docs/vis_type_xy.json index 92df25763766..0cb9fd390d18 100644 --- a/api_docs/vis_type_xy.json +++ b/api_docs/vis_type_xy.json @@ -141,9 +141,9 @@ "signature": [ { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.Labels", + "section": "def-common.Labels", "text": "Labels" } ], @@ -231,9 +231,9 @@ "Partial<", { "pluginId": "charts", - "scope": "public", + "scope": "common", "docId": "kibChartsPluginApi", - "section": "def-public.Style", + "section": "def-common.Style", "text": "Style" }, "> | undefined" diff --git a/api_docs/visualizations.json b/api_docs/visualizations.json index e93eec8a07ee..d131d522b351 100644 --- a/api_docs/visualizations.json +++ b/api_docs/visualizations.json @@ -1140,6 +1140,37 @@ } ], "functions": [ + { + "parentPluginId": "visualizations", + "id": "def-public.getFullPath", + "type": "Function", + "tags": [], + "label": "getFullPath", + "description": [], + "signature": [ + "(id: string) => string" + ], + "path": "src/plugins/visualizations/public/utils/saved_visualize_utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "visualizations", + "id": "def-public.getFullPath.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/visualizations/public/utils/saved_visualize_utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-public.getVisSchemas", @@ -1360,6 +1391,37 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-public.urlFor", + "type": "Function", + "tags": [], + "label": "urlFor", + "description": [], + "signature": [ + "(id: string) => string" + ], + "path": "src/plugins/visualizations/public/utils/saved_visualize_utils.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "visualizations", + "id": "def-public.urlFor.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/visualizations/public/utils/saved_visualize_utils.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-public.VisualizationContainer", @@ -1494,6 +1556,98 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "visualizations", + "id": "def-public.GetVisOptions", + "type": "Interface", + "tags": [], + "label": "GetVisOptions", + "description": [], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "visualizations", + "id": "def-public.GetVisOptions.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.GetVisOptions.searchSource", + "type": "CompoundType", + "tags": [], + "label": "searchSource", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.GetVisOptions.migrationVersion", + "type": "Object", + "tags": [], + "label": "migrationVersion", + "description": [], + "signature": [ + "SavedObjectsMigrationVersion", + " | undefined" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.GetVisOptions.savedSearchId", + "type": "string", + "tags": [], + "label": "savedSearchId", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.GetVisOptions.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.GetVisOptions.indexPattern", + "type": "string", + "tags": [], + "label": "indexPattern", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "visualizations", "id": "def-public.HistogramParams", @@ -1640,6 +1794,19 @@ ], "path": "src/plugins/visualizations/public/types.ts", "deprecated": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.ISavedVis.sharingSavedObjectProps", + "type": "Object", + "tags": [], + "label": "sharingSavedObjectProps", + "description": [], + "signature": [ + "{ outcome?: \"conflict\" | \"exactMatch\" | \"aliasMatch\" | undefined; aliasTargetId?: string | undefined; errorJSON?: string | undefined; } | undefined" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false } ], "initialIsOpen": false @@ -2531,14 +2698,6 @@ "text": "VisSavedObject" }, " extends ", - { - "pluginId": "savedObjects", - "scope": "public", - "docId": "kibSavedObjectsPluginApi", - "section": "def-public.SavedObject", - "text": "SavedObject" - }, - ",", { "pluginId": "visualizations", "scope": "public", @@ -2549,7 +2708,119 @@ ], "path": "src/plugins/visualizations/public/types.ts", "deprecated": false, - "children": [], + "children": [ + { + "parentPluginId": "visualizations", + "id": "def-public.VisSavedObject.lastSavedTitle", + "type": "string", + "tags": [], + "label": "lastSavedTitle", + "description": [], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisSavedObject.getEsType", + "type": "Function", + "tags": [], + "label": "getEsType", + "description": [], + "signature": [ + "() => string" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisSavedObject.getDisplayName", + "type": "Function", + "tags": [], + "label": "getDisplayName", + "description": [], + "signature": [ + "(() => string) | undefined" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisSavedObject.displayName", + "type": "string", + "tags": [], + "label": "displayName", + "description": [], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisSavedObject.migrationVersion", + "type": "Object", + "tags": [], + "label": "migrationVersion", + "description": [], + "signature": [ + "SavedObjectsMigrationVersion", + " | undefined" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisSavedObject.searchSource", + "type": "Object", + "tags": [], + "label": "searchSource", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.SearchSource", + "text": "SearchSource" + }, + ", \"create\" | \"history\" | \"setPreferredSearchStrategyId\" | \"setField\" | \"removeField\" | \"setFields\" | \"getId\" | \"getFields\" | \"getField\" | \"getOwnField\" | \"createCopy\" | \"createChild\" | \"setParent\" | \"getParent\" | \"fetch$\" | \"fetch\" | \"onRequestStart\" | \"getSearchRequestBody\" | \"destroy\" | \"getSerializedFields\" | \"serialize\"> | undefined" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisSavedObject.version", + "type": "string", + "tags": [], + "label": "version", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisSavedObject.tags", + "type": "Array", + "tags": [], + "label": "tags", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "src/plugins/visualizations/public/types.ts", + "deprecated": false + } + ], "initialIsOpen": false }, { @@ -4788,8 +5059,8 @@ "pluginId": "visualizations", "scope": "public", "docId": "kibVisualizationsPluginApi", - "section": "def-public.ISavedVis", - "text": "ISavedVis" + "section": "def-public.VisSavedObject", + "text": "VisSavedObject" }, ") => ", { @@ -4825,11 +5096,11 @@ "pluginId": "visualizations", "scope": "public", "docId": "kibVisualizationsPluginApi", - "section": "def-public.ISavedVis", - "text": "ISavedVis" + "section": "def-public.VisSavedObject", + "text": "VisSavedObject" } ], - "path": "src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts", + "path": "src/plugins/visualizations/public/utils/saved_visualize_utils.ts", "deprecated": false } ] @@ -4896,7 +5167,7 @@ }, ">" ], - "path": "src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts", + "path": "src/plugins/visualizations/public/utils/saved_visualize_utils.ts", "deprecated": false } ] @@ -4932,6 +5203,193 @@ } ] }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisualizationsStart.getSavedVisualization", + "type": "Function", + "tags": [], + "label": "getSavedVisualization", + "description": [], + "signature": [ + "(opts?: string | ", + { + "pluginId": "visualizations", + "scope": "public", + "docId": "kibVisualizationsPluginApi", + "section": "def-public.GetVisOptions", + "text": "GetVisOptions" + }, + " | undefined) => Promise<", + { + "pluginId": "visualizations", + "scope": "public", + "docId": "kibVisualizationsPluginApi", + "section": "def-public.VisSavedObject", + "text": "VisSavedObject" + }, + ">" + ], + "path": "src/plugins/visualizations/public/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "visualizations", + "id": "def-public.VisualizationsStart.getSavedVisualization.$1", + "type": "CompoundType", + "tags": [], + "label": "opts", + "description": [], + "signature": [ + "string | ", + { + "pluginId": "visualizations", + "scope": "public", + "docId": "kibVisualizationsPluginApi", + "section": "def-public.GetVisOptions", + "text": "GetVisOptions" + }, + " | undefined" + ], + "path": "src/plugins/visualizations/public/plugin.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisualizationsStart.saveVisualization", + "type": "Function", + "tags": [], + "label": "saveVisualization", + "description": [], + "signature": [ + "(savedVis: ", + { + "pluginId": "visualizations", + "scope": "public", + "docId": "kibVisualizationsPluginApi", + "section": "def-public.VisSavedObject", + "text": "VisSavedObject" + }, + ", saveOptions: ", + "SaveVisOptions", + ") => Promise" + ], + "path": "src/plugins/visualizations/public/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "visualizations", + "id": "def-public.VisualizationsStart.saveVisualization.$1", + "type": "Object", + "tags": [], + "label": "savedVis", + "description": [], + "signature": [ + { + "pluginId": "visualizations", + "scope": "public", + "docId": "kibVisualizationsPluginApi", + "section": "def-public.VisSavedObject", + "text": "VisSavedObject" + } + ], + "path": "src/plugins/visualizations/public/plugin.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisualizationsStart.saveVisualization.$2", + "type": "Object", + "tags": [], + "label": "saveOptions", + "description": [], + "signature": [ + "SaveVisOptions" + ], + "path": "src/plugins/visualizations/public/plugin.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisualizationsStart.findListItems", + "type": "Function", + "tags": [], + "label": "findListItems", + "description": [], + "signature": [ + "(searchTerm: string, listingLimit: number, references?: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsFindOptionsReference", + "text": "SavedObjectsFindOptionsReference" + }, + "[] | undefined) => Promise<{ hits: Record[]; total: number; }>" + ], + "path": "src/plugins/visualizations/public/plugin.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "visualizations", + "id": "def-public.VisualizationsStart.findListItems.$1", + "type": "string", + "tags": [], + "label": "searchTerm", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/visualizations/public/plugin.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisualizationsStart.findListItems.$2", + "type": "number", + "tags": [], + "label": "listingLimit", + "description": [], + "signature": [ + "number" + ], + "path": "src/plugins/visualizations/public/plugin.ts", + "deprecated": false, + "isRequired": true + }, + { + "parentPluginId": "visualizations", + "id": "def-public.VisualizationsStart.findListItems.$3", + "type": "Array", + "tags": [], + "label": "references", + "description": [], + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsFindOptionsReference", + "text": "SavedObjectsFindOptionsReference" + }, + "[] | undefined" + ], + "path": "src/plugins/visualizations/public/plugin.ts", + "deprecated": false, + "isRequired": false + } + ], + "returnComment": [] + }, { "parentPluginId": "visualizations", "id": "def-public.VisualizationsStart.__LEGACY", @@ -4964,17 +5422,7 @@ "section": "def-public.VisualizeInput", "text": "VisualizeInput" }, - "> & { id: string; }, savedVisualizationsLoader?: (", - { - "pluginId": "savedObjects", - "scope": "public", - "docId": "kibSavedObjectsPluginApi", - "section": "def-public.SavedObjectLoader", - "text": "SavedObjectLoader" - }, - " & { findListItems: (search: string, sizeOrOptions?: number | ", - "FindListItemsOptions", - " | undefined) => any; }) | undefined, attributeService?: ", + "> & { id: string; }, attributeService?: ", { "pluginId": "embeddable", "scope": "public", diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index a9501b59a1a5..5253bcdcd57e 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -18,7 +18,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 275 | 13 | 257 | 15 | +| 304 | 13 | 286 | 16 | ## Client diff --git a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.foo.json b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.foo.json deleted file mode 100644 index c7b43d9436cb..000000000000 --- a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.foo.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "id": "pluginA.foo", - "client": { - "classes": [], - "functions": [ - { - "parentPluginId": "pluginA", - "id": "def-public.doTheFooFnThing", - "type": "Function", - "tags": [], - "label": "doTheFooFnThing", - "description": [], - "signature": [ - "() => void" - ], - "path": "packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/foo/index.ts", - "deprecated": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - } - ], - "interfaces": [], - "enums": [], - "misc": [ - { - "parentPluginId": "pluginA", - "id": "def-public.FooType", - "type": "Type", - "tags": [], - "label": "FooType", - "description": [], - "signature": [ - "() => \"foo\"" - ], - "path": "packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/public/foo/index.ts", - "deprecated": false, - "returnComment": [], - "children": [], - "initialIsOpen": false - } - ], - "objects": [] - }, - "server": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "common": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [ - { - "parentPluginId": "pluginA", - "id": "def-common.commonFoo", - "type": "string", - "tags": [], - "label": "commonFoo", - "description": [], - "signature": [ - "\"COMMON VAR!\"" - ], - "path": "packages/kbn-docs-utils/src/api_docs/tests/__fixtures__/src/plugin_a/common/foo/index.ts", - "deprecated": false, - "initialIsOpen": false - } - ], - "objects": [] - } -} \ No newline at end of file diff --git a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.foo.mdx b/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.foo.mdx deleted file mode 100644 index 1fd371b585ce..000000000000 --- a/packages/kbn-docs-utils/src/api_docs/tests/snapshots/plugin_a.foo.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -id: kibPluginAFooPluginApi -slug: /kibana-dev-docs/pluginA.fooPluginApi -title: pluginA.foo -image: https://source.unsplash.com/400x175/?github -summary: API docs for the pluginA.foo plugin -date: 2020-11-16 -tags: ['contributor', 'dev', 'apidocs', 'kibana', 'pluginA.foo'] -warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. ---- -import pluginA.fooObj from './plugin_a.foo.json'; - - - -Contact Kibana Core for questions regarding this plugin. - -**Code health stats** - -| Public API count | Any count | Items lacking comments | Missing exports | -|-------------------|-----------|------------------------|-----------------| -| 3 | 0 | 0 | 0 | - -## Client - -### Functions - - -### Consts, variables and types - - -## Common - -### Consts, variables and types - - From 6cb91c472dfbc9ef6c38258407bc8a4b2dca6540 Mon Sep 17 00:00:00 2001 From: James Rucker Date: Mon, 11 Oct 2021 17:08:49 -0700 Subject: [PATCH 70/74] [Enterprise Search] Warn about Kibana access when adding single user role mappings (#114567) * Warn about Kibana access when adding single user role mappings to Enterprise Search * i18n and sentence case for the Kibana access warning title --- .../components/role_mappings/user.tsx | 1 + .../shared/role_mapping/constants.ts | 22 ++++++ .../role_mapping/user_added_info.test.tsx | 67 +++++++++++++++++++ .../shared/role_mapping/user_added_info.tsx | 29 +++++++- .../public/applications/shared/types.ts | 1 + .../views/role_mappings/user.tsx | 1 + 6 files changed, 118 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/user.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/user.tsx index 57f08c22220f..9e041dd83a29 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/user.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/role_mappings/user.tsx @@ -69,6 +69,7 @@ export const User: React.FC = () => { username={singleUserRoleMapping.elasticsearchUser.username} email={singleUserRoleMapping.elasticsearchUser.email as string} roleType={singleUserRoleMapping.roleMapping.roleType} + showKibanaAccessWarning={!singleUserRoleMapping.hasEnterpriseSearchRole} /> ); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts index 25a1e084a3a6..d2229b428932 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/constants.ts @@ -448,3 +448,25 @@ export const SMTP_CALLOUT_LABEL = i18n.translate( export const SMTP_LINK_LABEL = i18n.translate('xpack.enterpriseSearch.roleMapping.smtpLinkLabel', { defaultMessage: 'SMTP configuration is provided', }); + +export const KIBANA_ACCESS_WARNING_TITLE = i18n.translate( + 'xpack.enterpriseSearch.roleMapping.kibanaAccessWarningTitle', + { + defaultMessage: 'Kibana access warning', + } +); + +export const KIBANA_ACCESS_WARNING_ERROR_MESSAGE = i18n.translate( + 'xpack.enterpriseSearch.roleMapping.kibanaAccessWarningErrorMessage', + { + defaultMessage: + 'This Elasticsearch user does not have an Enterprise Search role in Elasticsearch. They may not have access to Kibana.', + } +); + +export const KIBANA_ACCESS_WARNING_DESCRIPTION = i18n.translate( + 'xpack.enterpriseSearch.roleMapping.kibanaAccessWarningDescription', + { + defaultMessage: 'Consider giving them the "enterprise-search-user" role.', + } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.test.tsx index 57200b389591..05ccafbe3e38 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.test.tsx @@ -16,6 +16,7 @@ describe('UserAddedInfo', () => { username: 'user1', email: 'test@test.com', roleType: 'user', + showKibanaAccessWarning: false, }; it('renders with email', () => { @@ -117,4 +118,70 @@ describe('UserAddedInfo', () => { `); }); + + it('renders with the Kibana access warning', () => { + const wrapper = shallow(); + + expect(wrapper).toMatchInlineSnapshot(` + + + + This Elasticsearch user does not have an Enterprise Search role in Elasticsearch. They may not have access to Kibana. + + + + Consider giving them the "enterprise-search-user" role. + + + + + + Username + + + + user1 + + + + + Email + + + + test@test.com + + + + + Role + + + + user + + + + `); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.tsx index 37804414a94a..71fb3d76295f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/role_mapping/user_added_info.tsx @@ -7,22 +7,45 @@ import React from 'react'; -import { EuiSpacer, EuiText, EuiTextColor } from '@elastic/eui'; +import { EuiCallOut, EuiSpacer, EuiText, EuiTextColor } from '@elastic/eui'; import { USERNAME_LABEL, EMAIL_LABEL } from '../constants'; -import { ROLE_LABEL } from './constants'; +import { + KIBANA_ACCESS_WARNING_TITLE, + KIBANA_ACCESS_WARNING_DESCRIPTION, + KIBANA_ACCESS_WARNING_ERROR_MESSAGE, + ROLE_LABEL, +} from './constants'; interface Props { username: string; email: string; roleType: string; + showKibanaAccessWarning: boolean; } +const kibanaAccessWarning = ( + <> + + {KIBANA_ACCESS_WARNING_ERROR_MESSAGE} + + {KIBANA_ACCESS_WARNING_DESCRIPTION} + + + +); + const noItemsPlaceholder = ; -export const UserAddedInfo: React.FC = ({ username, email, roleType }) => ( +export const UserAddedInfo: React.FC = ({ + username, + email, + roleType, + showKibanaAccessWarning, +}) => ( <> + {showKibanaAccessWarning && kibanaAccessWarning} {USERNAME_LABEL} diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts index 4743e808cc6e..51a83cb15cca 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts @@ -55,4 +55,5 @@ export interface SingleUserRoleMapping { invitation: Invitation | null; elasticsearchUser: ElasticsearchUser; roleMapping: T; + hasEnterpriseSearchRole?: boolean; } diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/user.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/user.tsx index 33785b4a4ce0..e6244a0c7b2e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/user.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/role_mappings/user.tsx @@ -66,6 +66,7 @@ export const User: React.FC = () => { username={singleUserRoleMapping.elasticsearchUser.username} email={singleUserRoleMapping.elasticsearchUser.email as string} roleType={singleUserRoleMapping.roleMapping.roleType} + showKibanaAccessWarning={!singleUserRoleMapping.hasEnterpriseSearchRole} /> ); From 44c9611bd9256f069b7dddac4d95c90adcace628 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Mon, 11 Oct 2021 17:49:21 -0700 Subject: [PATCH 71/74] [8.0] Remove support for configuring csp.rules (#114379) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../kibana-plugin-core-server.cspconfig.md | 1 - ...bana-plugin-core-server.cspconfig.rules.md | 11 -- ...core-server.icspconfig.disableembedding.md | 2 +- .../kibana-plugin-core-server.icspconfig.md | 3 +- ...ana-plugin-core-server.icspconfig.rules.md | 13 -- docs/migration/migrate_8_0.asciidoc | 6 + docs/setup/settings.asciidoc | 10 +- .../deprecation/core_deprecations.test.ts | 96 ------------ .../config/deprecation/core_deprecations.ts | 59 +------ src/core/server/csp/config.test.ts | 144 ++--------------- src/core/server/csp/config.ts | 41 ----- src/core/server/csp/csp_config.test.ts | 62 ++------ src/core/server/csp/csp_config.ts | 14 +- src/core/server/csp/csp_directives.test.ts | 147 +----------------- src/core/server/csp/csp_directives.ts | 28 +--- .../http_resources_service.test.ts | 8 +- src/core/server/server.api.md | 3 - .../resources/base/bin/kibana-docker | 1 - .../collectors/csp/csp_collector.test.ts | 8 +- 19 files changed, 54 insertions(+), 603 deletions(-) delete mode 100644 docs/development/core/server/kibana-plugin-core-server.cspconfig.rules.md delete mode 100644 docs/development/core/server/kibana-plugin-core-server.icspconfig.rules.md diff --git a/docs/development/core/server/kibana-plugin-core-server.cspconfig.md b/docs/development/core/server/kibana-plugin-core-server.cspconfig.md index 0337a1f4d330..46f24dfda673 100644 --- a/docs/development/core/server/kibana-plugin-core-server.cspconfig.md +++ b/docs/development/core/server/kibana-plugin-core-server.cspconfig.md @@ -24,7 +24,6 @@ The constructor for this class is marked as internal. Third-party code should no | [DEFAULT](./kibana-plugin-core-server.cspconfig.default.md) | static | CspConfig | | | [disableEmbedding](./kibana-plugin-core-server.cspconfig.disableembedding.md) | | boolean | | | [header](./kibana-plugin-core-server.cspconfig.header.md) | | string | | -| [rules](./kibana-plugin-core-server.cspconfig.rules.md) | | string[] | | | [strict](./kibana-plugin-core-server.cspconfig.strict.md) | | boolean | | | [warnLegacyBrowsers](./kibana-plugin-core-server.cspconfig.warnlegacybrowsers.md) | | boolean | | diff --git a/docs/development/core/server/kibana-plugin-core-server.cspconfig.rules.md b/docs/development/core/server/kibana-plugin-core-server.cspconfig.rules.md deleted file mode 100644 index 2bc73345fe0d..000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.cspconfig.rules.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CspConfig](./kibana-plugin-core-server.cspconfig.md) > [rules](./kibana-plugin-core-server.cspconfig.rules.md) - -## CspConfig.rules property - -Signature: - -```typescript -readonly rules: string[]; -``` diff --git a/docs/development/core/server/kibana-plugin-core-server.icspconfig.disableembedding.md b/docs/development/core/server/kibana-plugin-core-server.icspconfig.disableembedding.md index 2cfd680459fb..42b177c348af 100644 --- a/docs/development/core/server/kibana-plugin-core-server.icspconfig.disableembedding.md +++ b/docs/development/core/server/kibana-plugin-core-server.icspconfig.disableembedding.md @@ -4,7 +4,7 @@ ## ICspConfig.disableEmbedding property -Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled \*and\* no custom rules have been defined, a restrictive 'frame-ancestors' rule will be added to the default CSP rules. +Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled, a restrictive 'frame-ancestors' rule will be added to the default CSP rules. Signature: diff --git a/docs/development/core/server/kibana-plugin-core-server.icspconfig.md b/docs/development/core/server/kibana-plugin-core-server.icspconfig.md index ee49950df076..9da31cdc11e3 100644 --- a/docs/development/core/server/kibana-plugin-core-server.icspconfig.md +++ b/docs/development/core/server/kibana-plugin-core-server.icspconfig.md @@ -16,9 +16,8 @@ export interface ICspConfig | Property | Type | Description | | --- | --- | --- | -| [disableEmbedding](./kibana-plugin-core-server.icspconfig.disableembedding.md) | boolean | Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled \*and\* no custom rules have been defined, a restrictive 'frame-ancestors' rule will be added to the default CSP rules. | +| [disableEmbedding](./kibana-plugin-core-server.icspconfig.disableembedding.md) | boolean | Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled, a restrictive 'frame-ancestors' rule will be added to the default CSP rules. | | [header](./kibana-plugin-core-server.icspconfig.header.md) | string | The CSP rules in a formatted directives string for use in a Content-Security-Policy header. | -| [rules](./kibana-plugin-core-server.icspconfig.rules.md) | string[] | The CSP rules used for Kibana. | | [strict](./kibana-plugin-core-server.icspconfig.strict.md) | boolean | Specify whether browsers that do not support CSP should be able to use Kibana. Use true to block and false to allow. | | [warnLegacyBrowsers](./kibana-plugin-core-server.icspconfig.warnlegacybrowsers.md) | boolean | Specify whether users with legacy browsers should be warned about their lack of Kibana security compliance. | diff --git a/docs/development/core/server/kibana-plugin-core-server.icspconfig.rules.md b/docs/development/core/server/kibana-plugin-core-server.icspconfig.rules.md deleted file mode 100644 index 808eefc30b64..000000000000 --- a/docs/development/core/server/kibana-plugin-core-server.icspconfig.rules.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ICspConfig](./kibana-plugin-core-server.icspconfig.md) > [rules](./kibana-plugin-core-server.icspconfig.rules.md) - -## ICspConfig.rules property - -The CSP rules used for Kibana. - -Signature: - -```typescript -readonly rules: string[]; -``` diff --git a/docs/migration/migrate_8_0.asciidoc b/docs/migration/migrate_8_0.asciidoc index 60a65580501a..dc6754fba1ff 100644 --- a/docs/migration/migrate_8_0.asciidoc +++ b/docs/migration/migrate_8_0.asciidoc @@ -48,6 +48,12 @@ for example, `logstash-*`. *Impact:* To allow Kibana to function for these legacy browsers, set `csp.strict: false`. Since this is about enforcing a security protocol, we *strongly discourage* disabling `csp.strict` unless it is critical that you support Internet Explorer 11. +[float] +==== Configuring content security policy rules is no longer supported +*Details:* Configuring `csp.rules` is removed in favor of per-directive specific configuration. Configuring the default `csp.script_src`, `csp.workers_src` and `csp.style_src` values is not required. + +*Impact:* Configure per-directive sources instead. See https://github.com/elastic/kibana/pull/102059 for more details. + [float] ==== Default logging timezone is now the system's timezone *Details:* In prior releases the timezone used in logs defaulted to UTC. We now use the host machine's timezone by default. diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 9c3d4fc29f13..48bf5fe2cd7b 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -26,13 +26,6 @@ Toggling this causes the server to regenerate assets on the next startup, which may cause a delay before pages start being served. Set to `false` to disable Console. *Default: `true`* -| `csp.rules:` - | deprecated:[7.14.0,"In 8.0 and later, this setting will no longer be supported."] -A https://w3c.github.io/webappsec-csp/[Content Security Policy] template -that disables certain unnecessary and potentially insecure capabilities in -the browser. It is strongly recommended that you keep the default CSP rules -that ship with {kib}. - | `csp.script_src:` | Add sources for the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src[Content Security Policy `script-src` directive]. @@ -502,8 +495,7 @@ To disable, set to `null`. *Default:* `null` | Controls whether the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy[`Content-Security-Policy`] and https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options[`X-Frame-Options`] headers are configured to disable embedding {kib} in other webpages using iframes. When set to `true`, secure headers are used to disable embedding, which adds the `frame-ancestors: -'self'` directive to the `Content-Security-Policy` response header (if you are using the default CSP rules), and adds the `X-Frame-Options: -SAMEORIGIN` response header. *Default:* `false` +'self'` directive to the `Content-Security-Policy` response header and adds the `X-Frame-Options: SAMEORIGIN` response header. *Default:* `false` | `server.customResponseHeaders:` {ess-icon} | Header names and values to diff --git a/src/core/server/config/deprecation/core_deprecations.test.ts b/src/core/server/config/deprecation/core_deprecations.test.ts index 95e23561a937..e08f2216f5cb 100644 --- a/src/core/server/config/deprecation/core_deprecations.test.ts +++ b/src/core/server/config/deprecation/core_deprecations.test.ts @@ -83,100 +83,4 @@ describe('core deprecations', () => { expect(messages).toHaveLength(0); }); }); - - describe('cspRulesDeprecation', () => { - describe('with nonce source', () => { - it('logs a warning', () => { - const settings = { - csp: { - rules: [`script-src 'self' 'nonce-{nonce}'`], - }, - }; - const { messages } = applyCoreDeprecations(settings); - expect(messages).toMatchInlineSnapshot(` - Array [ - "csp.rules no longer supports the {nonce} syntax. Replacing with 'self' in script-src", - ] - `); - }); - - it('replaces a nonce', () => { - expect( - applyCoreDeprecations({ csp: { rules: [`script-src 'nonce-{nonce}'`] } }).migrated.csp - .rules - ).toEqual([`script-src 'self'`]); - expect( - applyCoreDeprecations({ csp: { rules: [`script-src 'unsafe-eval' 'nonce-{nonce}'`] } }) - .migrated.csp.rules - ).toEqual([`script-src 'unsafe-eval' 'self'`]); - }); - - it('removes a quoted nonce', () => { - expect( - applyCoreDeprecations({ csp: { rules: [`script-src 'self' 'nonce-{nonce}'`] } }).migrated - .csp.rules - ).toEqual([`script-src 'self'`]); - expect( - applyCoreDeprecations({ csp: { rules: [`script-src 'nonce-{nonce}' 'self'`] } }).migrated - .csp.rules - ).toEqual([`script-src 'self'`]); - }); - - it('removes a non-quoted nonce', () => { - expect( - applyCoreDeprecations({ csp: { rules: [`script-src 'self' nonce-{nonce}`] } }).migrated - .csp.rules - ).toEqual([`script-src 'self'`]); - expect( - applyCoreDeprecations({ csp: { rules: [`script-src nonce-{nonce} 'self'`] } }).migrated - .csp.rules - ).toEqual([`script-src 'self'`]); - }); - - it('removes a strange nonce', () => { - expect( - applyCoreDeprecations({ csp: { rules: [`script-src 'self' blah-{nonce}-wow`] } }).migrated - .csp.rules - ).toEqual([`script-src 'self'`]); - }); - - it('removes multiple nonces', () => { - expect( - applyCoreDeprecations({ - csp: { - rules: [ - `script-src 'nonce-{nonce}' 'self' blah-{nonce}-wow`, - `style-src 'nonce-{nonce}' 'self'`, - ], - }, - }).migrated.csp.rules - ).toEqual([`script-src 'self'`, `style-src 'self'`]); - }); - }); - - describe('without self source', () => { - it('logs a warning', () => { - const { messages } = applyCoreDeprecations({ - csp: { rules: [`script-src 'unsafe-eval'`] }, - }); - expect(messages).toMatchInlineSnapshot(` - Array [ - "csp.rules must contain the 'self' source. Automatically adding to script-src.", - ] - `); - }); - - it('adds self', () => { - expect( - applyCoreDeprecations({ csp: { rules: [`script-src 'unsafe-eval'`] } }).migrated.csp.rules - ).toEqual([`script-src 'unsafe-eval' 'self'`]); - }); - }); - - it('does not add self to other policies', () => { - expect( - applyCoreDeprecations({ csp: { rules: [`worker-src blob:`] } }).migrated.csp.rules - ).toEqual([`worker-src blob:`]); - }); - }); }); diff --git a/src/core/server/config/deprecation/core_deprecations.ts b/src/core/server/config/deprecation/core_deprecations.ts index 4e5f711fe9f3..5adbb338b42e 100644 --- a/src/core/server/config/deprecation/core_deprecations.ts +++ b/src/core/server/config/deprecation/core_deprecations.ts @@ -45,64 +45,7 @@ const rewriteCorsSettings: ConfigDeprecation = (settings, fromPath, addDeprecati } }; -const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => { - const NONCE_STRING = `{nonce}`; - // Policies that should include the 'self' source - const SELF_POLICIES = Object.freeze(['script-src', 'style-src']); - const SELF_STRING = `'self'`; - - const rules: string[] = settings.csp?.rules; - if (rules) { - const parsed = new Map( - rules.map((ruleStr) => { - const parts = ruleStr.split(/\s+/); - return [parts[0], parts.slice(1)]; - }) - ); - - return { - set: [ - { - path: 'csp.rules', - value: [...parsed].map(([policy, sourceList]) => { - if (sourceList.find((source) => source.includes(NONCE_STRING))) { - addDeprecation({ - message: `csp.rules no longer supports the {nonce} syntax. Replacing with 'self' in ${policy}`, - correctiveActions: { - manualSteps: [`Replace {nonce} syntax with 'self' in ${policy}`], - }, - }); - sourceList = sourceList.filter((source) => !source.includes(NONCE_STRING)); - - // Add 'self' if not present - if (!sourceList.find((source) => source.includes(SELF_STRING))) { - sourceList.push(SELF_STRING); - } - } - - if ( - SELF_POLICIES.includes(policy) && - !sourceList.find((source) => source.includes(SELF_STRING)) - ) { - addDeprecation({ - message: `csp.rules must contain the 'self' source. Automatically adding to ${policy}.`, - correctiveActions: { - manualSteps: [`Add 'self' source to ${policy}.`], - }, - }); - sourceList.push(SELF_STRING); - } - - return `${policy} ${sourceList.join(' ')}`.trim(); - }), - }, - ], - }; - } -}; - -export const coreDeprecationProvider: ConfigDeprecationProvider = ({ rename, unusedFromRoot }) => [ +export const coreDeprecationProvider: ConfigDeprecationProvider = () => [ rewriteCorsSettings, rewriteBasePathDeprecation, - cspRulesDeprecation, ]; diff --git a/src/core/server/csp/config.test.ts b/src/core/server/csp/config.test.ts index 6db93addb7da..346caf488431 100644 --- a/src/core/server/csp/config.test.ts +++ b/src/core/server/csp/config.test.ts @@ -80,21 +80,6 @@ describe('config.validate()', () => { ).not.toThrow(); }); - it(`throws if 'rules' is also specified`, () => { - expect(() => - config.schema.validate({ - rules: [ - `script-src 'unsafe-eval' 'self'`, - `worker-src 'unsafe-eval' 'self'`, - `style-src 'unsafe-eval' 'self'`, - ], - script_src: [`'self'`], - }) - ).toThrowErrorMatchingInlineSnapshot( - `"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""` - ); - }); - it('throws if using an `nonce-*` value', () => { expect(() => config.schema.validate({ @@ -104,6 +89,7 @@ describe('config.validate()', () => { `"[script_src]: using \\"nonce-*\\" is considered insecure and is not allowed"` ); }); + it("throws if using `none` or `'none'`", () => { expect(() => config.schema.validate({ @@ -124,21 +110,6 @@ describe('config.validate()', () => { }); describe(`"worker_src"`, () => { - it(`throws if 'rules' is also specified`, () => { - expect(() => - config.schema.validate({ - rules: [ - `script-src 'unsafe-eval' 'self'`, - `worker-src 'unsafe-eval' 'self'`, - `style-src 'unsafe-eval' 'self'`, - ], - worker_src: [`'self'`], - }) - ).toThrowErrorMatchingInlineSnapshot( - `"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""` - ); - }); - it('throws if using an `nonce-*` value', () => { expect(() => config.schema.validate({ @@ -148,6 +119,7 @@ describe('config.validate()', () => { `"[worker_src]: using \\"nonce-*\\" is considered insecure and is not allowed"` ); }); + it("throws if using `none` or `'none'`", () => { expect(() => config.schema.validate({ @@ -168,21 +140,6 @@ describe('config.validate()', () => { }); describe(`"style_src"`, () => { - it(`throws if 'rules' is also specified`, () => { - expect(() => - config.schema.validate({ - rules: [ - `script-src 'unsafe-eval' 'self'`, - `worker-src 'unsafe-eval' 'self'`, - `style-src 'unsafe-eval' 'self'`, - ], - style_src: [`'self'`], - }) - ).toThrowErrorMatchingInlineSnapshot( - `"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""` - ); - }); - it('throws if using an `nonce-*` value', () => { expect(() => config.schema.validate({ @@ -192,6 +149,7 @@ describe('config.validate()', () => { `"[style_src]: using \\"nonce-*\\" is considered insecure and is not allowed"` ); }); + it("throws if using `none` or `'none'`", () => { expect(() => config.schema.validate({ @@ -212,21 +170,6 @@ describe('config.validate()', () => { }); describe(`"connect_src"`, () => { - it(`throws if 'rules' is also specified`, () => { - expect(() => - config.schema.validate({ - rules: [ - `script-src 'unsafe-eval' 'self'`, - `worker-src 'unsafe-eval' 'self'`, - `style-src 'unsafe-eval' 'self'`, - ], - connect_src: [`'self'`], - }) - ).toThrowErrorMatchingInlineSnapshot( - `"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""` - ); - }); - it('throws if using an `nonce-*` value', () => { expect(() => config.schema.validate({ @@ -236,6 +179,7 @@ describe('config.validate()', () => { `"[connect_src]: using \\"nonce-*\\" is considered insecure and is not allowed"` ); }); + it("throws if using `none` or `'none'`", () => { expect(() => config.schema.validate({ @@ -256,21 +200,6 @@ describe('config.validate()', () => { }); describe(`"default_src"`, () => { - it(`throws if 'rules' is also specified`, () => { - expect(() => - config.schema.validate({ - rules: [ - `script-src 'unsafe-eval' 'self'`, - `worker-src 'unsafe-eval' 'self'`, - `style-src 'unsafe-eval' 'self'`, - ], - default_src: [`'self'`], - }) - ).toThrowErrorMatchingInlineSnapshot( - `"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""` - ); - }); - it('throws if using an `nonce-*` value', () => { expect(() => config.schema.validate({ @@ -280,6 +209,7 @@ describe('config.validate()', () => { `"[default_src]: using \\"nonce-*\\" is considered insecure and is not allowed"` ); }); + it("throws if using `none` or `'none'`", () => { expect(() => config.schema.validate({ @@ -300,21 +230,6 @@ describe('config.validate()', () => { }); describe(`"font_src"`, () => { - it(`throws if 'rules' is also specified`, () => { - expect(() => - config.schema.validate({ - rules: [ - `script-src 'unsafe-eval' 'self'`, - `worker-src 'unsafe-eval' 'self'`, - `style-src 'unsafe-eval' 'self'`, - ], - font_src: [`'self'`], - }) - ).toThrowErrorMatchingInlineSnapshot( - `"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""` - ); - }); - it('throws if using an `nonce-*` value', () => { expect(() => config.schema.validate({ @@ -324,6 +239,7 @@ describe('config.validate()', () => { `"[font_src]: using \\"nonce-*\\" is considered insecure and is not allowed"` ); }); + it("throws if using `none` or `'none'`", () => { expect(() => config.schema.validate({ @@ -344,21 +260,6 @@ describe('config.validate()', () => { }); describe(`"frame_src"`, () => { - it(`throws if 'rules' is also specified`, () => { - expect(() => - config.schema.validate({ - rules: [ - `script-src 'unsafe-eval' 'self'`, - `worker-src 'unsafe-eval' 'self'`, - `style-src 'unsafe-eval' 'self'`, - ], - frame_src: [`'self'`], - }) - ).toThrowErrorMatchingInlineSnapshot( - `"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""` - ); - }); - it('throws if using an `nonce-*` value', () => { expect(() => config.schema.validate({ @@ -368,6 +269,7 @@ describe('config.validate()', () => { `"[frame_src]: using \\"nonce-*\\" is considered insecure and is not allowed"` ); }); + it("throws if using `none` or `'none'`", () => { expect(() => config.schema.validate({ @@ -388,21 +290,6 @@ describe('config.validate()', () => { }); describe(`"img_src"`, () => { - it(`throws if 'rules' is also specified`, () => { - expect(() => - config.schema.validate({ - rules: [ - `script-src 'unsafe-eval' 'self'`, - `worker-src 'unsafe-eval' 'self'`, - `style-src 'unsafe-eval' 'self'`, - ], - img_src: [`'self'`], - }) - ).toThrowErrorMatchingInlineSnapshot( - `"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""` - ); - }); - it('throws if using an `nonce-*` value', () => { expect(() => config.schema.validate({ @@ -412,6 +299,7 @@ describe('config.validate()', () => { `"[img_src]: using \\"nonce-*\\" is considered insecure and is not allowed"` ); }); + it("throws if using `none` or `'none'`", () => { expect(() => config.schema.validate({ @@ -432,21 +320,6 @@ describe('config.validate()', () => { }); describe(`"frame_ancestors"`, () => { - it(`throws if 'rules' is also specified`, () => { - expect(() => - config.schema.validate({ - rules: [ - `script-src 'unsafe-eval' 'self'`, - `worker-src 'unsafe-eval' 'self'`, - `style-src 'unsafe-eval' 'self'`, - ], - frame_ancestors: [`'self'`], - }) - ).toThrowErrorMatchingInlineSnapshot( - `"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""` - ); - }); - it('throws if using an `nonce-*` value', () => { expect(() => config.schema.validate({ @@ -456,6 +329,7 @@ describe('config.validate()', () => { `"[frame_ancestors]: using \\"nonce-*\\" is considered insecure and is not allowed"` ); }); + it("throws if using `none` or `'none'`", () => { expect(() => config.schema.validate({ diff --git a/src/core/server/csp/config.ts b/src/core/server/csp/config.ts index 3a7cb20985ce..22aa11e4e0c3 100644 --- a/src/core/server/csp/config.ts +++ b/src/core/server/csp/config.ts @@ -39,7 +39,6 @@ const getDirectiveValueValidator = ({ allowNone, allowNonce }: DirectiveValidati const configSchema = schema.object( { - rules: schema.maybe(schema.arrayOf(schema.string())), script_src: schema.arrayOf(schema.string(), { defaultValue: [], validate: getDirectiveValidator({ allowNone: false, allowNonce: false }), @@ -89,9 +88,6 @@ const configSchema = schema.object( }, { validate: (cspConfig) => { - if (cspConfig.rules && hasDirectiveSpecified(cspConfig)) { - return `"csp.rules" cannot be used when specifying per-directive additions such as "script_src", "worker_src" or "style_src"`; - } const hasUnsafeInlineScriptSrc = cspConfig.script_src.includes(`unsafe-inline`) || cspConfig.script_src.includes(`'unsafe-inline'`); @@ -106,22 +102,6 @@ const configSchema = schema.object( } ); -const hasDirectiveSpecified = (rawConfig: CspConfigType): boolean => { - return Boolean( - rawConfig.script_src.length || - rawConfig.worker_src.length || - rawConfig.style_src.length || - rawConfig.connect_src.length || - rawConfig.default_src.length || - rawConfig.font_src.length || - rawConfig.frame_src.length || - rawConfig.img_src.length || - rawConfig.frame_ancestors.length || - rawConfig.report_uri.length || - rawConfig.report_to.length - ); -}; - /** * @internal */ @@ -132,25 +112,4 @@ export const config: ServiceConfigDescriptor = { // ? https://github.com/elastic/kibana/pull/52251 path: 'csp', schema: configSchema, - deprecations: () => [ - (rawConfig, fromPath, addDeprecation) => { - const cspConfig = rawConfig[fromPath]; - if (cspConfig?.rules) { - addDeprecation({ - message: - '`csp.rules` is deprecated in favor of directive specific configuration. Please use `csp.connect_src`, ' + - '`csp.default_src`, `csp.font_src`, `csp.frame_ancestors`, `csp.frame_src`, `csp.img_src`, ' + - '`csp.report_uri`, `csp.report_to`, `csp.script_src`, `csp.style_src`, and `csp.worker_src` instead.', - correctiveActions: { - manualSteps: [ - `Remove "csp.rules" from the Kibana config file."`, - `Add directive specific configurations to the config file using "csp.connect_src", "csp.default_src", "csp.font_src", ` + - `"csp.frame_ancestors", "csp.frame_src", "csp.img_src", "csp.report_uri", "csp.report_to", "csp.script_src", ` + - `"csp.style_src", and "csp.worker_src".`, - ], - }, - }); - } - }, - ], }; diff --git a/src/core/server/csp/csp_config.test.ts b/src/core/server/csp/csp_config.test.ts index a1bac7d4ae73..1ec78fae7532 100644 --- a/src/core/server/csp/csp_config.test.ts +++ b/src/core/server/csp/csp_config.test.ts @@ -34,11 +34,6 @@ describe('CspConfig', () => { CspConfig { "disableEmbedding": false, "header": "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'", - "rules": Array [ - "script-src 'unsafe-eval' 'self'", - "worker-src blob: 'self'", - "style-src 'unsafe-inline' 'self'", - ], "strict": true, "warnLegacyBrowsers": true, } @@ -50,13 +45,6 @@ describe('CspConfig', () => { }); describe('partial config', () => { - test('allows "rules" to be set and changes header', () => { - const rules = [`foo 'self'`, `bar 'self'`]; - const config = new CspConfig({ ...defaultConfig, rules }); - expect(config.rules).toEqual(rules); - expect(config.header).toMatchInlineSnapshot(`"foo 'self'; bar 'self'"`); - }); - test('allows "strict" to be set', () => { const config = new CspConfig({ ...defaultConfig, strict: false }); expect(config.strict).toEqual(false); @@ -70,67 +58,57 @@ describe('CspConfig', () => { expect(config.warnLegacyBrowsers).not.toEqual(CspConfig.DEFAULT.warnLegacyBrowsers); }); - test('allows "worker_src" to be set and changes header', () => { + test('allows "worker_src" to be set and changes header from defaults', () => { const config = new CspConfig({ ...defaultConfig, - rules: [], worker_src: ['foo', 'bar'], }); - expect(config.rules).toEqual([`worker-src foo bar`]); - expect(config.header).toEqual(`worker-src foo bar`); + expect(config.header).toEqual( + `script-src 'unsafe-eval' 'self'; worker-src blob: 'self' foo bar; style-src 'unsafe-inline' 'self'` + ); }); test('allows "style_src" to be set and changes header', () => { const config = new CspConfig({ ...defaultConfig, - rules: [], style_src: ['foo', 'bar'], }); - expect(config.rules).toEqual([`style-src foo bar`]); - expect(config.header).toEqual(`style-src foo bar`); + + expect(config.header).toEqual( + `script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self' foo bar` + ); }); test('allows "script_src" to be set and changes header', () => { const config = new CspConfig({ ...defaultConfig, - rules: [], script_src: ['foo', 'bar'], }); - expect(config.rules).toEqual([`script-src foo bar`]); - expect(config.header).toEqual(`script-src foo bar`); + + expect(config.header).toEqual( + `script-src 'unsafe-eval' 'self' foo bar; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'` + ); }); test('allows all directives to be set and changes header', () => { const config = new CspConfig({ ...defaultConfig, - rules: [], script_src: ['script', 'foo'], worker_src: ['worker', 'bar'], style_src: ['style', 'dolly'], }); - expect(config.rules).toEqual([ - `script-src script foo`, - `worker-src worker bar`, - `style-src style dolly`, - ]); expect(config.header).toEqual( - `script-src script foo; worker-src worker bar; style-src style dolly` + `script-src 'unsafe-eval' 'self' script foo; worker-src blob: 'self' worker bar; style-src 'unsafe-inline' 'self' style dolly` ); }); - test('applies defaults when `rules` is undefined', () => { + test('appends config directives to defaults', () => { const config = new CspConfig({ ...defaultConfig, - rules: undefined, script_src: ['script'], worker_src: ['worker'], style_src: ['style'], }); - expect(config.rules).toEqual([ - `script-src 'unsafe-eval' 'self' script`, - `worker-src blob: 'self' worker`, - `style-src 'unsafe-inline' 'self' style`, - ]); expect(config.header).toEqual( `script-src 'unsafe-eval' 'self' script; worker-src blob: 'self' worker; style-src 'unsafe-inline' 'self' style` ); @@ -139,25 +117,15 @@ describe('CspConfig', () => { describe('allows "disableEmbedding" to be set', () => { const disableEmbedding = true; - test('and changes rules/header if custom rules are not defined', () => { + test('and changes rules and header', () => { const config = new CspConfig({ ...defaultConfig, disableEmbedding }); expect(config.disableEmbedding).toEqual(disableEmbedding); expect(config.disableEmbedding).not.toEqual(CspConfig.DEFAULT.disableEmbedding); - expect(config.rules).toEqual(expect.arrayContaining([`frame-ancestors 'self'`])); expect(config.header).toMatchInlineSnapshot( `"script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'; frame-ancestors 'self'"` ); }); - test('and does not change rules/header if custom rules are defined', () => { - const rules = [`foo 'self'`, `bar 'self'`]; - const config = new CspConfig({ ...defaultConfig, disableEmbedding, rules }); - expect(config.disableEmbedding).toEqual(disableEmbedding); - expect(config.disableEmbedding).not.toEqual(CspConfig.DEFAULT.disableEmbedding); - expect(config.rules).toEqual(rules); - expect(config.header).toMatchInlineSnapshot(`"foo 'self'; bar 'self'"`); - }); - test('and overrides `frame-ancestors` if set', () => { const config = new CspConfig({ ...defaultConfig, diff --git a/src/core/server/csp/csp_config.ts b/src/core/server/csp/csp_config.ts index 13778088d9df..f0f968ecd0ce 100644 --- a/src/core/server/csp/csp_config.ts +++ b/src/core/server/csp/csp_config.ts @@ -16,11 +16,6 @@ const DEFAULT_CONFIG = Object.freeze(config.schema.validate({})); * @public */ export interface ICspConfig { - /** - * The CSP rules used for Kibana. - */ - readonly rules: string[]; - /** * Specify whether browsers that do not support CSP should be * able to use Kibana. Use `true` to block and `false` to allow. @@ -34,8 +29,7 @@ export interface ICspConfig { readonly warnLegacyBrowsers: boolean; /** - * Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled *and* no custom rules have been - * defined, a restrictive 'frame-ancestors' rule will be added to the default CSP rules. + * Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled, a restrictive 'frame-ancestors' rule will be added to the default CSP rules. */ readonly disableEmbedding: boolean; @@ -54,7 +48,6 @@ export class CspConfig implements ICspConfig { static readonly DEFAULT = new CspConfig(DEFAULT_CONFIG); readonly #directives: CspDirectives; - public readonly rules: string[]; public readonly strict: boolean; public readonly warnLegacyBrowsers: boolean; public readonly disableEmbedding: boolean; @@ -66,14 +59,11 @@ export class CspConfig implements ICspConfig { */ constructor(rawCspConfig: CspConfigType) { this.#directives = CspDirectives.fromConfig(rawCspConfig); - if (!rawCspConfig.rules?.length && rawCspConfig.disableEmbedding) { + if (rawCspConfig.disableEmbedding) { this.#directives.clearDirectiveValues('frame-ancestors'); this.#directives.addDirectiveValue('frame-ancestors', `'self'`); } - - this.rules = this.#directives.getRules(); this.header = this.#directives.getCspHeader(); - this.strict = rawCspConfig.strict; this.warnLegacyBrowsers = rawCspConfig.warnLegacyBrowsers; this.disableEmbedding = rawCspConfig.disableEmbedding; diff --git a/src/core/server/csp/csp_directives.test.ts b/src/core/server/csp/csp_directives.test.ts index 1077b6ea9f3c..f4a9e256e2f9 100644 --- a/src/core/server/csp/csp_directives.test.ts +++ b/src/core/server/csp/csp_directives.test.ts @@ -11,33 +11,12 @@ import { config as cspConfig } from './config'; describe('CspDirectives', () => { describe('#addDirectiveValue', () => { - it('properly updates the rules', () => { - const directives = new CspDirectives(); - directives.addDirectiveValue('style-src', 'foo'); - - expect(directives.getRules()).toMatchInlineSnapshot(` - Array [ - "style-src foo", - ] - `); - - directives.addDirectiveValue('style-src', 'bar'); - - expect(directives.getRules()).toMatchInlineSnapshot(` - Array [ - "style-src foo bar", - ] - `); - }); - it('properly updates the header', () => { const directives = new CspDirectives(); directives.addDirectiveValue('style-src', 'foo'); - expect(directives.getCspHeader()).toMatchInlineSnapshot(`"style-src foo"`); directives.addDirectiveValue('style-src', 'bar'); - expect(directives.getCspHeader()).toMatchInlineSnapshot(`"style-src foo bar"`); }); @@ -50,12 +29,6 @@ describe('CspDirectives', () => { expect(directives.getCspHeader()).toMatchInlineSnapshot( `"style-src foo bar; worker-src dolly"` ); - expect(directives.getRules()).toMatchInlineSnapshot(` - Array [ - "style-src foo bar", - "worker-src dolly", - ] - `); }); it('removes duplicates', () => { @@ -65,11 +38,6 @@ describe('CspDirectives', () => { directives.addDirectiveValue('style-src', 'bar'); expect(directives.getCspHeader()).toMatchInlineSnapshot(`"style-src foo bar"`); - expect(directives.getRules()).toMatchInlineSnapshot(` - Array [ - "style-src foo bar", - ] - `); }); it('automatically adds single quotes for keywords', () => { @@ -106,18 +74,6 @@ describe('CspDirectives', () => { }); describe('#fromConfig', () => { - it('returns the correct rules for the default config', () => { - const config = cspConfig.schema.validate({}); - const directives = CspDirectives.fromConfig(config); - expect(directives.getRules()).toMatchInlineSnapshot(` - Array [ - "script-src 'unsafe-eval' 'self'", - "worker-src blob: 'self'", - "style-src 'unsafe-inline' 'self'", - ] - `); - }); - it('returns the correct header for the default config', () => { const config = cspConfig.schema.validate({}); const directives = CspDirectives.fromConfig(config); @@ -126,75 +82,6 @@ describe('CspDirectives', () => { ); }); - it('handles config with rules', () => { - const config = cspConfig.schema.validate({ - rules: [`script-src 'self' http://foo.com`, `worker-src 'self'`], - }); - const directives = CspDirectives.fromConfig(config); - - expect(directives.getRules()).toMatchInlineSnapshot(` - Array [ - "script-src 'self' http://foo.com", - "worker-src 'self'", - ] - `); - expect(directives.getCspHeader()).toMatchInlineSnapshot( - `"script-src 'self' http://foo.com; worker-src 'self'"` - ); - }); - - it('adds single quotes for keyword for rules', () => { - const config = cspConfig.schema.validate({ - rules: [`script-src self http://foo.com`, `worker-src self`], - }); - const directives = CspDirectives.fromConfig(config); - - expect(directives.getRules()).toMatchInlineSnapshot(` - Array [ - "script-src 'self' http://foo.com", - "worker-src 'self'", - ] - `); - expect(directives.getCspHeader()).toMatchInlineSnapshot( - `"script-src 'self' http://foo.com; worker-src 'self'"` - ); - }); - - it('handles multiple whitespaces when parsing rules', () => { - const config = cspConfig.schema.validate({ - rules: [` script-src 'self' http://foo.com `, ` worker-src 'self' `], - }); - const directives = CspDirectives.fromConfig(config); - - expect(directives.getRules()).toMatchInlineSnapshot(` - Array [ - "script-src 'self' http://foo.com", - "worker-src 'self'", - ] - `); - expect(directives.getCspHeader()).toMatchInlineSnapshot( - `"script-src 'self' http://foo.com; worker-src 'self'"` - ); - }); - - it('supports unregistered directives', () => { - const config = cspConfig.schema.validate({ - rules: [`script-src 'self' http://foo.com`, `img-src 'self'`, 'foo bar'], - }); - const directives = CspDirectives.fromConfig(config); - - expect(directives.getRules()).toMatchInlineSnapshot(` - Array [ - "script-src 'self' http://foo.com", - "img-src 'self'", - "foo bar", - ] - `); - expect(directives.getCspHeader()).toMatchInlineSnapshot( - `"script-src 'self' http://foo.com; img-src 'self'; foo bar"` - ); - }); - it('adds default value for config with directives', () => { const config = cspConfig.schema.validate({ script_src: [`baz`], @@ -203,13 +90,6 @@ describe('CspDirectives', () => { }); const directives = CspDirectives.fromConfig(config); - expect(directives.getRules()).toMatchInlineSnapshot(` - Array [ - "script-src 'unsafe-eval' 'self' baz", - "worker-src blob: 'self' foo", - "style-src 'unsafe-inline' 'self' bar dolly", - ] - `); expect(directives.getCspHeader()).toMatchInlineSnapshot( `"script-src 'unsafe-eval' 'self' baz; worker-src blob: 'self' foo; style-src 'unsafe-inline' 'self' bar dolly"` ); @@ -227,22 +107,9 @@ describe('CspDirectives', () => { report_to: [`report-to`], }); const directives = CspDirectives.fromConfig(config); - - expect(directives.getRules()).toMatchInlineSnapshot(` - Array [ - "script-src 'unsafe-eval' 'self'", - "worker-src blob: 'self'", - "style-src 'unsafe-inline' 'self'", - "connect-src 'self' connect-src", - "default-src 'self' default-src", - "font-src 'self' font-src", - "frame-src 'self' frame-src", - "img-src 'self' img-src", - "frame-ancestors 'self' frame-ancestors", - "report-uri report-uri", - "report-to report-to", - ] - `); + expect(directives.getCspHeader()).toMatchInlineSnapshot( + `"script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'; connect-src 'self' connect-src; default-src 'self' default-src; font-src 'self' font-src; frame-src 'self' frame-src; img-src 'self' img-src; frame-ancestors 'self' frame-ancestors; report-uri report-uri; report-to report-to"` + ); }); it('adds single quotes for keywords in added directives', () => { @@ -250,14 +117,6 @@ describe('CspDirectives', () => { script_src: [`unsafe-hashes`], }); const directives = CspDirectives.fromConfig(config); - - expect(directives.getRules()).toMatchInlineSnapshot(` - Array [ - "script-src 'unsafe-eval' 'self' 'unsafe-hashes'", - "worker-src blob: 'self'", - "style-src 'unsafe-inline' 'self'", - ] - `); expect(directives.getCspHeader()).toMatchInlineSnapshot( `"script-src 'unsafe-eval' 'self' 'unsafe-hashes'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'"` ); diff --git a/src/core/server/csp/csp_directives.ts b/src/core/server/csp/csp_directives.ts index 9e3b60f7f1e4..d656210a054f 100644 --- a/src/core/server/csp/csp_directives.ts +++ b/src/core/server/csp/csp_directives.ts @@ -22,7 +22,7 @@ export type CspDirectiveName = | 'report-to'; /** - * The default rules that are always applied + * The default directives rules that are always applied */ export const defaultRules: Partial> = { 'script-src': [`'unsafe-eval'`, `'self'`], @@ -58,21 +58,18 @@ export class CspDirectives { } getCspHeader() { - return this.getRules().join('; '); - } - - getRules() { - return [...this.directives.entries()].map(([name, values]) => { - return [name, ...values].join(' '); - }); + return [...this.directives.entries()] + .map(([name, values]) => { + return [name, ...values].join(' '); + }) + .join('; '); } static fromConfig(config: CspConfigType): CspDirectives { const cspDirectives = new CspDirectives(); - // adding `csp.rules` or `default` rules - const initialRules = config.rules ? parseRules(config.rules) : { ...defaultRules }; - Object.entries(initialRules).forEach(([key, values]) => { + // combining `default` directive configurations + Object.entries(defaultRules).forEach(([key, values]) => { values?.forEach((value) => { cspDirectives.addDirectiveValue(key as CspDirectiveName, value); }); @@ -91,15 +88,6 @@ export class CspDirectives { } } -const parseRules = (rules: string[]): Partial> => { - const directives: Partial> = {}; - rules.forEach((rule) => { - const [name, ...values] = rule.replace(/\s+/g, ' ').trim().split(' '); - directives[name as CspDirectiveName] = values; - }); - return directives; -}; - const parseConfigDirectives = (cspConfig: CspConfigType): Map => { const map = new Map(); diff --git a/src/core/server/http_resources/integration_tests/http_resources_service.test.ts b/src/core/server/http_resources/integration_tests/http_resources_service.test.ts index 6f4f3c9c6e98..3b254df92903 100644 --- a/src/core/server/http_resources/integration_tests/http_resources_service.test.ts +++ b/src/core/server/http_resources/integration_tests/http_resources_service.test.ts @@ -12,12 +12,10 @@ import * as kbnTestServer from '../../../test_helpers/kbn_server'; describe('http resources service', () => { describe('register', () => { let root: ReturnType; - const defaultCspRules = "script-src 'self'"; + const defaultCspRules = + "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'"; beforeEach(async () => { root = kbnTestServer.createRoot({ - csp: { - rules: [defaultCspRules], - }, plugins: { initialize: false }, elasticsearch: { skipStartupConnectionCheck: true }, }); @@ -44,7 +42,7 @@ describe('http resources service', () => { expect(response.text.length).toBeGreaterThan(0); }); - it('attaches CSP header', async () => { + it('applies default CSP header', async () => { const { http, httpResources } = await root.setup(); const router = http.createRouter(''); diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index a1fd69f5e1c7..fb16e889a19c 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -754,8 +754,6 @@ export class CspConfig implements ICspConfig { // (undocumented) readonly header: string; // (undocumented) - readonly rules: string[]; - // (undocumented) readonly strict: boolean; // (undocumented) readonly warnLegacyBrowsers: boolean; @@ -1135,7 +1133,6 @@ export type IContextProvider { expect((await collector.fetch(mockedFetchContext)).warnLegacyBrowsers).toEqual(false); }); - test('fetches whether the csp rules have been changed or not', async () => { + test("fetches whether the csp directives's rules have been changed or not", async () => { const collector = new Collector(logger, createCspCollector(httpMock)); expect((await collector.fetch(mockedFetchContext)).rulesChangedFromDefault).toEqual(false); - updateCsp({ rules: ['not', 'default'] }); + updateCsp({ disableEmbedding: true }); expect((await collector.fetch(mockedFetchContext)).rulesChangedFromDefault).toEqual(true); }); test('does not include raw csp rules under any property names', async () => { const collector = new Collector(logger, createCspCollector(httpMock)); - // It's important that we do not send the value of csp.rules here as it + // It's important that we do not send the raw values of csp cirectives here as they // can be customized with values that can be identifiable to given // installs, such as URLs // - // We use a snapshot here to ensure csp.rules isn't finding its way into the + // We use a snapshot here to ensure raw values aren't finding their way into the // payload under some new and unexpected variable name (e.g. cspRules). expect(await collector.fetch(mockedFetchContext)).toMatchInlineSnapshot(` Object { From c926b14c32307a12b31938641594356655faf360 Mon Sep 17 00:00:00 2001 From: ymao1 Date: Mon, 11 Oct 2021 21:06:35 -0400 Subject: [PATCH 72/74] [Alerting] Showing last execution duration on Rule Management view (#113935) * Adding last duration to execution status and returning in alerting routes * Fixing types * Adding helper function to format duration * Returning rule timeout value in list rules API * Updating rules table to add duration column and tweaks to match mockup * Updating rules table to add duration column and tweaks to match mockup * i18n fix * Only showing duration warning if duration is long * Unit tests * i18n fix * Fixing functional test * Aligning warning icon * Reset last duration when rule is disabled then reenabled * Fixing functional test * Fixing functional test * Restoring muted badge. Fixing scss * Dont show muted badge if rule is disabled * Moving disabled icontip to right of rule name * Updating tooltips * Updating last run Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/alerting/common/alert.ts | 1 + x-pack/plugins/alerting/common/alert_type.ts | 1 + .../alerting/common/parse_duration.test.ts | 39 ++- .../plugins/alerting/common/parse_duration.ts | 16 + .../server/lib/alert_execution_status.test.ts | 49 +++ .../server/lib/alert_execution_status.ts | 20 +- .../alerting/server/routes/create_rule.ts | 3 +- .../alerting/server/routes/find_rules.ts | 3 +- .../alerting/server/routes/get_rule.ts | 3 +- .../alerting/server/routes/resolve_rule.ts | 3 +- .../alerting/server/routes/rule_types.test.ts | 3 + .../alerting/server/routes/rule_types.ts | 2 + .../alerting/server/routes/update_rule.ts | 1 + .../server/rule_type_registry.test.ts | 2 + .../alerting/server/rule_type_registry.ts | 3 + .../server/rules_client/rules_client.ts | 1 + .../server/rules_client/tests/enable.test.ts | 2 + .../server/saved_objects/mappings.json | 3 + .../server/task_runner/task_runner.test.ts | 2 + .../server/task_runner/task_runner.ts | 5 + x-pack/plugins/alerting/server/types.ts | 1 + .../translations/translations/ja-JP.json | 12 +- .../translations/translations/zh-CN.json | 14 +- .../lib/alert_api/common_transformations.ts | 2 + .../application/lib/alert_api/rule_types.ts | 2 + .../components/alert_status_filter.tsx | 4 +- .../alerts_list/components/alerts_list.scss | 11 + .../components/alerts_list.test.tsx | 57 +++- .../alerts_list/components/alerts_list.tsx | 311 ++++++++++++------ .../collapsed_item_actions.test.tsx | 2 +- .../components/rule_enabled_switch.test.tsx | 4 +- .../triggers_actions_ui/public/types.ts | 3 +- .../tests/alerting/rule_types.ts | 2 + .../spaces_only/tests/alerting/rule_types.ts | 2 + .../apps/triggers_actions_ui/alerts_list.ts | 130 +++----- .../page_objects/triggers_actions_ui_page.ts | 12 +- 36 files changed, 512 insertions(+), 219 deletions(-) diff --git a/x-pack/plugins/alerting/common/alert.ts b/x-pack/plugins/alerting/common/alert.ts index 1274e7b95b11..bf0c8e382c9d 100644 --- a/x-pack/plugins/alerting/common/alert.ts +++ b/x-pack/plugins/alerting/common/alert.ts @@ -35,6 +35,7 @@ export enum AlertExecutionStatusErrorReasons { export interface AlertExecutionStatus { status: AlertExecutionStatuses; lastExecutionDate: Date; + lastDuration?: number; error?: { reason: AlertExecutionStatusErrorReasons; message: string; diff --git a/x-pack/plugins/alerting/common/alert_type.ts b/x-pack/plugins/alerting/common/alert_type.ts index d71540b4418e..1b0ac28c9fa7 100644 --- a/x-pack/plugins/alerting/common/alert_type.ts +++ b/x-pack/plugins/alerting/common/alert_type.ts @@ -21,6 +21,7 @@ export interface AlertType< producer: string; minimumLicenseRequired: LicenseType; isExportable: boolean; + ruleTaskTimeout?: string; defaultScheduleInterval?: string; minimumScheduleInterval?: string; } diff --git a/x-pack/plugins/alerting/common/parse_duration.test.ts b/x-pack/plugins/alerting/common/parse_duration.test.ts index 9fbb662e2114..e68a3f479f22 100644 --- a/x-pack/plugins/alerting/common/parse_duration.test.ts +++ b/x-pack/plugins/alerting/common/parse_duration.test.ts @@ -5,7 +5,12 @@ * 2.0. */ -import { parseDuration, getDurationNumberInItsUnit, getDurationUnitValue } from './parse_duration'; +import { + parseDuration, + formatDuration, + getDurationNumberInItsUnit, + getDurationUnitValue, +} from './parse_duration'; test('parses seconds', () => { const result = parseDuration('10s'); @@ -39,6 +44,38 @@ test('throws error when suffix is missing', () => { ); }); +test('formats seconds', () => { + const result = formatDuration('10s'); + expect(result).toEqual('10 sec'); +}); + +test('formats minutes', () => { + const result = formatDuration('10m'); + expect(result).toEqual('10 min'); +}); + +test('formats hours', () => { + const result = formatDuration('10h'); + expect(result).toEqual('10 hr'); +}); + +test('formats days', () => { + const result = formatDuration('10d'); + expect(result).toEqual('10 day'); +}); + +test('format throws error when the format is invalid', () => { + expect(() => formatDuration('10x')).toThrowErrorMatchingInlineSnapshot( + `"Invalid duration \\"10x\\". Durations must be of the form {number}x. Example: 5s, 5m, 5h or 5d\\""` + ); +}); + +test('format throws error when suffix is missing', () => { + expect(() => formatDuration('1000')).toThrowErrorMatchingInlineSnapshot( + `"Invalid duration \\"1000\\". Durations must be of the form {number}x. Example: 5s, 5m, 5h or 5d\\""` + ); +}); + test('throws error when 0 based', () => { expect(() => parseDuration('0s')).toThrowErrorMatchingInlineSnapshot( `"Invalid duration \\"0s\\". Durations must be of the form {number}x. Example: 5s, 5m, 5h or 5d\\""` diff --git a/x-pack/plugins/alerting/common/parse_duration.ts b/x-pack/plugins/alerting/common/parse_duration.ts index 3494a48fc8ab..af4f1d2c1409 100644 --- a/x-pack/plugins/alerting/common/parse_duration.ts +++ b/x-pack/plugins/alerting/common/parse_duration.ts @@ -27,6 +27,22 @@ export function parseDuration(duration: string): number { ); } +export function formatDuration(duration: string): string { + const parsed = parseInt(duration, 10); + if (isSeconds(duration)) { + return `${parsed} sec`; + } else if (isMinutes(duration)) { + return `${parsed} min`; + } else if (isHours(duration)) { + return `${parsed} hr`; + } else if (isDays(duration)) { + return `${parsed} day`; + } + throw new Error( + `Invalid duration "${duration}". Durations must be of the form {number}x. Example: 5s, 5m, 5h or 5d"` + ); +} + export function getDurationNumberInItsUnit(duration: string): number { return parseInt(duration.replace(/[^0-9.]/g, ''), 10); } diff --git a/x-pack/plugins/alerting/server/lib/alert_execution_status.test.ts b/x-pack/plugins/alerting/server/lib/alert_execution_status.test.ts index 0a8d5632f169..93cf0c656c69 100644 --- a/x-pack/plugins/alerting/server/lib/alert_execution_status.test.ts +++ b/x-pack/plugins/alerting/server/lib/alert_execution_status.test.ts @@ -81,6 +81,7 @@ describe('AlertExecutionStatus', () => { expect(alertExecutionStatusToRaw({ lastExecutionDate: date, status })).toMatchInlineSnapshot(` Object { "error": null, + "lastDuration": 0, "lastExecutionDate": "2020-09-03T16:26:58.000Z", "status": "ok", } @@ -95,11 +96,24 @@ describe('AlertExecutionStatus', () => { "message": "wops", "reason": "decrypt", }, + "lastDuration": 0, "lastExecutionDate": "2020-09-03T16:26:58.000Z", "status": "ok", } `); }); + + test('status with a duration', () => { + expect(alertExecutionStatusToRaw({ lastExecutionDate: date, status, lastDuration: 1234 })) + .toMatchInlineSnapshot(` + Object { + "error": null, + "lastDuration": 1234, + "lastExecutionDate": "2020-09-03T16:26:58.000Z", + "status": "ok", + } + `); + }); }); describe('alertExecutionStatusFromRaw()', () => { @@ -177,6 +191,41 @@ describe('AlertExecutionStatus', () => { } `); }); + + test('valid status, date and duration', () => { + const result = alertExecutionStatusFromRaw(MockLogger, 'alert-id', { + status, + lastExecutionDate: date, + lastDuration: 1234, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "lastDuration": 1234, + "lastExecutionDate": 2020-09-03T16:26:58.000Z, + "status": "active", + } + `); + }); + + test('valid status, date, error and duration', () => { + const result = alertExecutionStatusFromRaw(MockLogger, 'alert-id', { + status, + lastExecutionDate: date, + error, + lastDuration: 1234, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "error": Object { + "message": "wops", + "reason": "execute", + }, + "lastDuration": 1234, + "lastExecutionDate": 2020-09-03T16:26:58.000Z, + "status": "active", + } + `); + }); }); }); diff --git a/x-pack/plugins/alerting/server/lib/alert_execution_status.ts b/x-pack/plugins/alerting/server/lib/alert_execution_status.ts index 47dfc659307a..82d851433170 100644 --- a/x-pack/plugins/alerting/server/lib/alert_execution_status.ts +++ b/x-pack/plugins/alerting/server/lib/alert_execution_status.ts @@ -32,11 +32,13 @@ export function executionStatusFromError(error: Error): AlertExecutionStatus { export function alertExecutionStatusToRaw({ lastExecutionDate, + lastDuration, status, error, }: AlertExecutionStatus): RawAlertExecutionStatus { return { lastExecutionDate: lastExecutionDate.toISOString(), + lastDuration: lastDuration ?? 0, status, // explicitly setting to null (in case undefined) due to partial update concerns error: error ?? null, @@ -50,7 +52,7 @@ export function alertExecutionStatusFromRaw( ): AlertExecutionStatus | undefined { if (!rawAlertExecutionStatus) return undefined; - const { lastExecutionDate, status = 'unknown', error } = rawAlertExecutionStatus; + const { lastExecutionDate, lastDuration, status = 'unknown', error } = rawAlertExecutionStatus; let parsedDateMillis = lastExecutionDate ? Date.parse(lastExecutionDate) : Date.now(); if (isNaN(parsedDateMillis)) { @@ -60,12 +62,20 @@ export function alertExecutionStatusFromRaw( parsedDateMillis = Date.now(); } - const parsedDate = new Date(parsedDateMillis); + const executionStatus: AlertExecutionStatus = { + status, + lastExecutionDate: new Date(parsedDateMillis), + }; + + if (null != lastDuration) { + executionStatus.lastDuration = lastDuration; + } + if (error) { - return { lastExecutionDate: parsedDate, status, error }; - } else { - return { lastExecutionDate: parsedDate, status }; + executionStatus.error = error; } + + return executionStatus; } export const getAlertExecutionStatusPending = (lastExecutionDate: string) => ({ diff --git a/x-pack/plugins/alerting/server/routes/create_rule.ts b/x-pack/plugins/alerting/server/routes/create_rule.ts index 55a98eb56bee..ed124bfbd3a2 100644 --- a/x-pack/plugins/alerting/server/routes/create_rule.ts +++ b/x-pack/plugins/alerting/server/routes/create_rule.ts @@ -67,7 +67,7 @@ const rewriteBodyRes: RewriteResponseCase> = ({ notifyWhen, muteAll, mutedInstanceIds, - executionStatus: { lastExecutionDate, ...executionStatus }, + executionStatus: { lastExecutionDate, lastDuration, ...executionStatus }, ...rest }) => ({ ...rest, @@ -84,6 +84,7 @@ const rewriteBodyRes: RewriteResponseCase> = ({ execution_status: { ...executionStatus, last_execution_date: lastExecutionDate, + last_duration: lastDuration, }, actions: actions.map(({ group, id, actionTypeId, params }) => ({ group, diff --git a/x-pack/plugins/alerting/server/routes/find_rules.ts b/x-pack/plugins/alerting/server/routes/find_rules.ts index a4a066728555..7826e924acdc 100644 --- a/x-pack/plugins/alerting/server/routes/find_rules.ts +++ b/x-pack/plugins/alerting/server/routes/find_rules.ts @@ -93,8 +93,9 @@ const rewriteBodyRes: RewriteResponseCase> = ({ muted_alert_ids: mutedInstanceIds, scheduled_task_id: scheduledTaskId, execution_status: executionStatus && { - ...omit(executionStatus, 'lastExecutionDate'), + ...omit(executionStatus, 'lastExecutionDate', 'lastDuration'), last_execution_date: executionStatus.lastExecutionDate, + last_duration: executionStatus.lastDuration, }, actions: actions.map(({ group, id, actionTypeId, params }) => ({ group, diff --git a/x-pack/plugins/alerting/server/routes/get_rule.ts b/x-pack/plugins/alerting/server/routes/get_rule.ts index c860ae725a25..4da9410517fe 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule.ts @@ -48,8 +48,9 @@ const rewriteBodyRes: RewriteResponseCase> = ({ muted_alert_ids: mutedInstanceIds, scheduled_task_id: scheduledTaskId, execution_status: executionStatus && { - ...omit(executionStatus, 'lastExecutionDate'), + ...omit(executionStatus, 'lastExecutionDate', 'lastDuration'), last_execution_date: executionStatus.lastExecutionDate, + last_duration: executionStatus.lastDuration, }, actions: actions.map(({ group, id, actionTypeId, params }) => ({ group, diff --git a/x-pack/plugins/alerting/server/routes/resolve_rule.ts b/x-pack/plugins/alerting/server/routes/resolve_rule.ts index 011d28780e71..a31cf5059b99 100644 --- a/x-pack/plugins/alerting/server/routes/resolve_rule.ts +++ b/x-pack/plugins/alerting/server/routes/resolve_rule.ts @@ -48,8 +48,9 @@ const rewriteBodyRes: RewriteResponseCase muted_alert_ids: mutedInstanceIds, scheduled_task_id: scheduledTaskId, execution_status: executionStatus && { - ...omit(executionStatus, 'lastExecutionDate'), + ...omit(executionStatus, 'lastExecutionDate', 'lastDuration'), last_execution_date: executionStatus.lastExecutionDate, + last_duration: executionStatus.lastDuration, }, actions: actions.map(({ group, id, actionTypeId, params }) => ({ group, diff --git a/x-pack/plugins/alerting/server/routes/rule_types.test.ts b/x-pack/plugins/alerting/server/routes/rule_types.test.ts index e4247c9de6ca..7deb2704fb7e 100644 --- a/x-pack/plugins/alerting/server/routes/rule_types.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule_types.test.ts @@ -49,6 +49,7 @@ describe('ruleTypesRoute', () => { defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', isExportable: true, + ruleTaskTimeout: '10m', recoveryActionGroup: RecoveredActionGroup, authorizedConsumers: {}, actionVariables: { @@ -76,6 +77,7 @@ describe('ruleTypesRoute', () => { minimum_license_required: 'basic', minimum_schedule_interval: '1m', is_exportable: true, + rule_task_timeout: '10m', recovery_action_group: RecoveredActionGroup, authorized_consumers: {}, action_variables: { @@ -118,6 +120,7 @@ describe('ruleTypesRoute', () => { "id": "recovered", "name": "Recovered", }, + "rule_task_timeout": "10m", }, ], } diff --git a/x-pack/plugins/alerting/server/routes/rule_types.ts b/x-pack/plugins/alerting/server/routes/rule_types.ts index 72502b25e9af..d1f24538d76d 100644 --- a/x-pack/plugins/alerting/server/routes/rule_types.ts +++ b/x-pack/plugins/alerting/server/routes/rule_types.ts @@ -20,6 +20,7 @@ const rewriteBodyRes: RewriteResponseCase = (result defaultActionGroupId, minimumLicenseRequired, isExportable, + ruleTaskTimeout, actionVariables, authorizedConsumers, minimumScheduleInterval, @@ -33,6 +34,7 @@ const rewriteBodyRes: RewriteResponseCase = (result default_action_group_id: defaultActionGroupId, minimum_license_required: minimumLicenseRequired, is_exportable: isExportable, + rule_task_timeout: ruleTaskTimeout, action_variables: actionVariables, authorized_consumers: authorizedConsumers, minimum_schedule_interval: minimumScheduleInterval, diff --git a/x-pack/plugins/alerting/server/routes/update_rule.ts b/x-pack/plugins/alerting/server/routes/update_rule.ts index 6e8024a0ddbf..007d24bb8251 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule.ts @@ -88,6 +88,7 @@ const rewriteBodyRes: RewriteResponseCase> = ({ execution_status: { status: executionStatus.status, last_execution_date: executionStatus.lastExecutionDate, + last_duration: executionStatus.lastDuration, }, } : {}), diff --git a/x-pack/plugins/alerting/server/rule_type_registry.test.ts b/x-pack/plugins/alerting/server/rule_type_registry.test.ts index beb5f264eb72..895a5047339e 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.test.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.test.ts @@ -494,6 +494,7 @@ describe('list()', () => { ], defaultActionGroupId: 'testActionGroup', isExportable: true, + ruleTaskTimeout: '20m', minimumLicenseRequired: 'basic', executor: jest.fn(), producer: 'alerts', @@ -530,6 +531,7 @@ describe('list()', () => { "id": "recovered", "name": "Recovered", }, + "ruleTaskTimeout": "20m", }, } `); diff --git a/x-pack/plugins/alerting/server/rule_type_registry.ts b/x-pack/plugins/alerting/server/rule_type_registry.ts index db02edf4d19d..452729a9a01e 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.ts @@ -48,6 +48,7 @@ export interface RegistryRuleType | 'producer' | 'minimumLicenseRequired' | 'isExportable' + | 'ruleTaskTimeout' | 'minimumScheduleInterval' | 'defaultScheduleInterval' > { @@ -327,6 +328,7 @@ export class RuleTypeRegistry { producer, minimumLicenseRequired, isExportable, + ruleTaskTimeout, minimumScheduleInterval, defaultScheduleInterval, }, @@ -340,6 +342,7 @@ export class RuleTypeRegistry { producer, minimumLicenseRequired, isExportable, + ruleTaskTimeout, minimumScheduleInterval, defaultScheduleInterval, enabledInLicense: !!this.licenseState.getLicenseCheckForAlertType( diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 2228b5d27910..2492517f4bdc 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -1133,6 +1133,7 @@ export class RulesClient { updatedAt: new Date().toISOString(), executionStatus: { status: 'pending', + lastDuration: 0, lastExecutionDate: new Date().toISOString(), error: null, }, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts index 7b8fbff4fca5..5e3f148c2fc1 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts @@ -260,6 +260,7 @@ describe('enable()', () => { ], executionStatus: { status: 'pending', + lastDuration: 0, lastExecutionDate: '2019-02-12T21:01:22.479Z', error: null, }, @@ -369,6 +370,7 @@ describe('enable()', () => { ], executionStatus: { status: 'pending', + lastDuration: 0, lastExecutionDate: '2019-02-12T21:01:22.479Z', error: null, }, diff --git a/x-pack/plugins/alerting/server/saved_objects/mappings.json b/x-pack/plugins/alerting/server/saved_objects/mappings.json index 21d7a05f2a76..05e221a47499 100644 --- a/x-pack/plugins/alerting/server/saved_objects/mappings.json +++ b/x-pack/plugins/alerting/server/saved_objects/mappings.json @@ -101,6 +101,9 @@ "lastExecutionDate": { "type": "date" }, + "lastDuration": { + "type": "long" + }, "error": { "properties": { "reason": { diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index bc477136ec11..c5ccc909eff4 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -338,6 +338,7 @@ describe('Task Runner', () => { { executionStatus: { error: null, + lastDuration: 0, lastExecutionDate: '1970-01-01T00:00:00.000Z', status: 'ok', }, @@ -4394,6 +4395,7 @@ describe('Task Runner', () => { { executionStatus: { error: null, + lastDuration: 0, lastExecutionDate: '1970-01-01T00:00:00.000Z', status: 'ok', }, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 28cc0f2dba4d..edf9bfe1b484 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -585,6 +585,11 @@ export class TaskRunner< event.kibana.alerting = event.kibana.alerting || {}; event.kibana.alerting.status = executionStatus.status; + // Copy duration into execution status if available + if (null != event.event?.duration) { + executionStatus.lastDuration = Math.round(event.event?.duration / Millis2Nanos); + } + // if executionStatus indicates an error, fill in fields in // event from it if (executionStatus.error) { diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 1dc8291d2875..82bb94b12184 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -185,6 +185,7 @@ export interface AlertMeta extends SavedObjectAttributes { export interface RawAlertExecutionStatus extends SavedObjectAttributes { status: AlertExecutionStatuses; lastExecutionDate: string; + lastDuration?: number; error: null | { reason: AlertExecutionStatusErrorReasons; message: string; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f36f3abe66a4..fbd9352fb427 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -25336,7 +25336,6 @@ "xpack.triggersActionsUI.sections.alertsList.alertErrorReasonRunning": "ルールの実行中にエラーが発生しました。", "xpack.triggersActionsUI.sections.alertsList.alertErrorReasonUnknown": "不明な理由でエラーが発生しました。", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTex": "アクション", - "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTitle": "アクション", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.alertTypeTitle": "型", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.deleteAriaLabel": "削除", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.deleteButtonTooltip": "削除", @@ -25347,7 +25346,6 @@ "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.nameTitle": "名前", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.scheduleTitle": "次の間隔で実行", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.statusTitle": "ステータス", - "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.tagsText": "タグ", "xpack.triggersActionsUI.sections.alertsList.alertStatusActive": "アクティブ", "xpack.triggersActionsUI.sections.alertsList.alertStatusError": "エラー", "xpack.triggersActionsUI.sections.alertsList.alertStatusFilterLabel": "ステータス", @@ -25383,11 +25381,11 @@ "xpack.triggersActionsUI.sections.alertsList.searchPlaceholderTitle": "検索", "xpack.triggersActionsUI.sections.alertsList.singleTitle": "ルール", "xpack.triggersActionsUI.sections.alertsList.totalItemsCountDescription": "{pageSize}/{totalItemCount}件のルールを表示しています。", - "xpack.triggersActionsUI.sections.alertsList.totalStausesActiveDescription": "有効:{totalStausesActive}", - "xpack.triggersActionsUI.sections.alertsList.totalStausesErrorDescription": "エラー:{totalStausesError}", - "xpack.triggersActionsUI.sections.alertsList.totalStausesOkDescription": "Ok:{totalStausesOk}", - "xpack.triggersActionsUI.sections.alertsList.totalStausesPendingDescription": "保留中:{totalStausesPending}", - "xpack.triggersActionsUI.sections.alertsList.totalStausesUnknownDescription": "不明:{totalStausesUnknown}", + "xpack.triggersActionsUI.sections.alertsList.totalStatusesActiveDescription": "有効:{totalStatusesActive}", + "xpack.triggersActionsUI.sections.alertsList.totalStatusesErrorDescription": "エラー:{totalStatusesError}", + "xpack.triggersActionsUI.sections.alertsList.totalStatusesOkDescription": "Ok:{totalStatusesOk}", + "xpack.triggersActionsUI.sections.alertsList.totalStatusesPendingDescription": "保留中:{totalStatusesPending}", + "xpack.triggersActionsUI.sections.alertsList.totalStatusesUnknownDescription": "不明:{totalStatusesUnknown}", "xpack.triggersActionsUI.sections.alertsList.typeFilterLabel": "型", "xpack.triggersActionsUI.sections.alertsList.unableToLoadConnectorTypesMessage": "コネクタータイプを読み込めません", "xpack.triggersActionsUI.sections.alertsList.unableToLoadRulesMessage": "ルールを読み込めません", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 11b951b97ae0..9408bb85db87 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -25763,7 +25763,6 @@ "xpack.triggersActionsUI.sections.alertsList.alertErrorReasonRunning": "运行规则时发生错误。", "xpack.triggersActionsUI.sections.alertsList.alertErrorReasonUnknown": "由于未知原因发生错误。", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTex": "操作", - "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTitle": "操作", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.alertTypeTitle": "类型", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.deleteAriaLabel": "删除", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.deleteButtonTooltip": "删除", @@ -25774,7 +25773,6 @@ "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.nameTitle": "名称", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.scheduleTitle": "运行间隔", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.statusTitle": "状态", - "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.tagsText": "标签", "xpack.triggersActionsUI.sections.alertsList.alertStatusActive": "活动", "xpack.triggersActionsUI.sections.alertsList.alertStatusError": "错误", "xpack.triggersActionsUI.sections.alertsList.alertStatusFilterLabel": "状态", @@ -25782,7 +25780,7 @@ "xpack.triggersActionsUI.sections.alertsList.alertStatusOk": "确定", "xpack.triggersActionsUI.sections.alertsList.alertStatusPending": "待处理", "xpack.triggersActionsUI.sections.alertsList.alertStatusUnknown": "未知", - "xpack.triggersActionsUI.sections.alertsList.attentionBannerTitle": "{totalStausesError, plural, other {# 个规则}}中有错误。", + "xpack.triggersActionsUI.sections.alertsList.attentionBannerTitle": "{totalStatusesError, plural, other {# 个规则}}中有错误。", "xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.buttonTitle": "管理规则", "xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.deleteAllTitle": "删除", "xpack.triggersActionsUI.sections.alertsList.bulkActionPopover.disableAllTitle": "禁用", @@ -25811,11 +25809,11 @@ "xpack.triggersActionsUI.sections.alertsList.searchPlaceholderTitle": "搜索", "xpack.triggersActionsUI.sections.alertsList.singleTitle": "规则", "xpack.triggersActionsUI.sections.alertsList.totalItemsCountDescription": "正在显示:{pageSize} 个规则(共 {totalItemCount} 个)。", - "xpack.triggersActionsUI.sections.alertsList.totalStausesActiveDescription": "活动:{totalStausesActive}", - "xpack.triggersActionsUI.sections.alertsList.totalStausesErrorDescription": "错误:{totalStausesError}", - "xpack.triggersActionsUI.sections.alertsList.totalStausesOkDescription": "确定:{totalStausesOk}", - "xpack.triggersActionsUI.sections.alertsList.totalStausesPendingDescription": "待处理:{totalStausesPending}", - "xpack.triggersActionsUI.sections.alertsList.totalStausesUnknownDescription": "未知:{totalStausesUnknown}", + "xpack.triggersActionsUI.sections.alertsList.totalStatusesActiveDescription": "活动:{totalStatusesActive}", + "xpack.triggersActionsUI.sections.alertsList.totalStatusesErrorDescription": "错误:{totalStatusesError}", + "xpack.triggersActionsUI.sections.alertsList.totalStatusesOkDescription": "确定:{totalStatusesOk}", + "xpack.triggersActionsUI.sections.alertsList.totalStatusesPendingDescription": "待处理:{totalStatusesPending}", + "xpack.triggersActionsUI.sections.alertsList.totalStatusesUnknownDescription": "未知:{totalStatusesUnknown}", "xpack.triggersActionsUI.sections.alertsList.typeFilterLabel": "类型", "xpack.triggersActionsUI.sections.alertsList.unableToLoadConnectorTypesMessage": "无法加载连接器类型", "xpack.triggersActionsUI.sections.alertsList.unableToLoadRulesMessage": "无法加载规则", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/common_transformations.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/common_transformations.ts index 5049a37c317d..6369937e5937 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/common_transformations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/common_transformations.ts @@ -22,9 +22,11 @@ const transformAction: RewriteRequestCase = ({ const transformExecutionStatus: RewriteRequestCase = ({ last_execution_date: lastExecutionDate, + last_duration: lastDuration, ...rest }) => ({ lastExecutionDate, + lastDuration, ...rest, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rule_types.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rule_types.ts index 54369d7959c9..67d317643ec0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rule_types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api/rule_types.ts @@ -21,6 +21,7 @@ const rewriteBodyReq: RewriteRequestCase = ({ minimum_license_required: minimumLicenseRequired, action_variables: actionVariables, authorized_consumers: authorizedConsumers, + rule_task_timeout: ruleTaskTimeout, ...rest }: AsApiContract) => ({ enabledInLicense, @@ -30,6 +31,7 @@ const rewriteBodyReq: RewriteRequestCase = ({ minimumLicenseRequired, actionVariables, authorizedConsumers, + ruleTaskTimeout, ...rest, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alert_status_filter.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alert_status_filter.tsx index aca111df97e3..50295548f9aa 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alert_status_filter.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alert_status_filter.tsx @@ -99,10 +99,10 @@ export function getHealthColor(status: AlertExecutionStatuses) { case 'error': return 'danger'; case 'ok': - return 'subdued'; + return 'primary'; case 'pending': return 'accent'; default: - return 'warning'; + return 'subdued'; } } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.scss b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.scss index c0e46b77b415..138605421f20 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.scss +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.scss @@ -31,3 +31,14 @@ opacity: 1; /* 2 */ } } + +.ruleDurationWarningIcon { + margin-bottom: $euiSizeXS; + margin-left: $euiSizeS; +} + +.ruleDisabledQuestionIcon { + bottom: $euiSizeXS; + margin-left: $euiSizeXS; + position: relative; +} \ No newline at end of file diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx index 958511128de0..53f5e25530e9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx @@ -17,6 +17,7 @@ import { AlertTypeModel, ValidationResult } from '../../../../types'; import { AlertExecutionStatusErrorReasons, ALERTS_FEATURE_ID, + parseDuration, } from '../../../../../../alerting/common'; import { useKibana } from '../../../../common/lib/kibana'; jest.mock('../../../../common/lib/kibana'); @@ -79,6 +80,7 @@ const alertTypeFromApi = { authorizedConsumers: { [ALERTS_FEATURE_ID]: { read: true, all: true }, }, + ruleTaskTimeout: '1m', }; ruleTypeRegistry.list.mockReturnValue([alertType]); actionTypeRegistry.list.mockReturnValue([]); @@ -170,6 +172,7 @@ describe('alerts_list component with items', () => { mutedInstanceIds: [], executionStatus: { status: 'active', + lastDuration: 500, lastExecutionDate: new Date('2020-08-20T19:23:38Z'), error: null, }, @@ -192,6 +195,7 @@ describe('alerts_list component with items', () => { mutedInstanceIds: [], executionStatus: { status: 'ok', + lastDuration: 61000, lastExecutionDate: new Date('2020-08-20T19:23:38Z'), error: null, }, @@ -214,6 +218,7 @@ describe('alerts_list component with items', () => { mutedInstanceIds: [], executionStatus: { status: 'pending', + lastDuration: 30234, lastExecutionDate: new Date('2020-08-20T19:23:38Z'), error: null, }, @@ -236,6 +241,7 @@ describe('alerts_list component with items', () => { mutedInstanceIds: [], executionStatus: { status: 'error', + lastDuration: 122000, lastExecutionDate: new Date('2020-08-20T19:23:38Z'), error: { reason: AlertExecutionStatusErrorReasons.Unknown, @@ -246,7 +252,7 @@ describe('alerts_list component with items', () => { { id: '5', name: 'test alert license error', - tags: ['tag1'], + tags: [], enabled: true, alertTypeId: 'test_alert_type', schedule: { interval: '5d' }, @@ -261,6 +267,7 @@ describe('alerts_list component with items', () => { mutedInstanceIds: [], executionStatus: { status: 'error', + lastDuration: 500, lastExecutionDate: new Date('2020-08-20T19:23:38Z'), error: { reason: AlertExecutionStatusErrorReasons.License, @@ -324,6 +331,53 @@ describe('alerts_list component with items', () => { await setup(); expect(wrapper.find('EuiBasicTable')).toHaveLength(1); expect(wrapper.find('EuiTableRow')).toHaveLength(mockedAlertsData.length); + + // Enabled switch column + expect( + wrapper.find('EuiTableRowCell[data-test-subj="alertsTableCell-enabled"]').length + ).toEqual(mockedAlertsData.length); + + // Name and rule type column + const ruleNameColumns = wrapper.find('EuiTableRowCell[data-test-subj="alertsTableCell-name"]'); + expect(ruleNameColumns.length).toEqual(mockedAlertsData.length); + mockedAlertsData.forEach((rule, index) => { + expect(ruleNameColumns.at(index).text()).toEqual(`Name${rule.name}${alertTypeFromApi.name}`); + }); + + // Tags column + expect( + wrapper.find('EuiTableRowCell[data-test-subj="alertsTableCell-tagsPopover"]').length + ).toEqual(mockedAlertsData.length); + // only show tags popover if tags exist on rule + const tagsBadges = wrapper.find('EuiBadge[data-test-subj="ruleTagsBadge"]'); + expect(tagsBadges.length).toEqual( + mockedAlertsData.filter((data) => data.tags.length > 0).length + ); + + // Last run column + expect( + wrapper.find('EuiTableRowCell[data-test-subj="alertsTableCell-lastExecutionDate"]').length + ).toEqual(mockedAlertsData.length); + + // Schedule interval column + expect( + wrapper.find('EuiTableRowCell[data-test-subj="alertsTableCell-interval"]').length + ).toEqual(mockedAlertsData.length); + + // Duration column + expect( + wrapper.find('EuiTableRowCell[data-test-subj="alertsTableCell-duration"]').length + ).toEqual(mockedAlertsData.length); + // show warning if duration is long + const durationWarningIcon = wrapper.find('EuiIconTip[data-test-subj="ruleDurationWarning"]'); + expect(durationWarningIcon.length).toEqual( + mockedAlertsData.filter( + (data) => + data.executionStatus.lastDuration > parseDuration(alertTypeFromApi.ruleTaskTimeout) + ).length + ); + + // Status column expect(wrapper.find('EuiTableRowCell[data-test-subj="alertsTableCell-status"]').length).toEqual( mockedAlertsData.length ); @@ -331,7 +385,6 @@ describe('alerts_list component with items', () => { expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-ok"]').length).toEqual(1); expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-pending"]').length).toEqual(1); expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-unknown"]').length).toEqual(0); - expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-error"]').length).toEqual(2); expect(wrapper.find('[data-test-subj="alertStatus-error-tooltip"]').length).toEqual(2); expect( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index 1daaf3b99612..91b1b14083ed 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -8,7 +8,8 @@ /* eslint-disable react-hooks/exhaustive-deps */ import { i18n } from '@kbn/i18n'; -import { capitalize, sortBy } from 'lodash'; +import { capitalize, padStart, sortBy } from 'lodash'; +import moment from 'moment'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useEffect, useState } from 'react'; import { @@ -29,6 +30,10 @@ import { EuiToolTip, EuiTableSortingType, EuiButtonIcon, + EuiHorizontalRule, + EuiPopover, + EuiPopoverTitle, + EuiIcon, } from '@elastic/eui'; import { useHistory } from 'react-router-dom'; @@ -66,6 +71,8 @@ import { AlertExecutionStatusValues, ALERTS_FEATURE_ID, AlertExecutionStatusErrorReasons, + formatDuration, + parseDuration, } from '../../../../../../alerting/common'; import { alertsStatusesTranslationsMapping, ALERT_STATUS_LICENSE_ERROR } from '../translations'; import { useKibana } from '../../../../common/lib/kibana'; @@ -114,6 +121,7 @@ export const AlertsList: React.FunctionComponent = () => { const [dismissAlertErrors, setDismissAlertErrors] = useState(false); const [editFlyoutVisible, setEditFlyoutVisibility] = useState(false); const [currentRuleToEdit, setCurrentRuleToEdit] = useState(null); + const [tagPopoverOpenIndex, setTagPopoverOpenIndex] = useState(-1); const [sort, setSort] = useState['sort']>({ field: 'name', @@ -334,7 +342,7 @@ export const AlertsList: React.FunctionComponent = () => { 'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.enabledTitle', { defaultMessage: 'Enabled' } ), - width: '90px', + width: '50px', render(_enabled: boolean | undefined, item: AlertTableItem) { return ( { const checkEnabledResult = checkAlertTypeEnabled(ruleType); const link = ( <> - { - history.push(routeToRuleDetails.replace(`:ruleId`, alert.id)); - }} - > - {name} - + + + + + { + history.push(routeToRuleDetails.replace(`:ruleId`, alert.id)); + }} + > + {name} + + + + {!checkEnabledResult.isEnabled && ( + + )} + + + + + + {alert.alertType} + + + ); - return checkEnabledResult.isEnabled ? ( - link - ) : ( + return ( <> {link} - + {alert.enabled && alert.muteAll && ( + + + + )} ); }, }, { - field: 'executionStatus.status', - name: i18n.translate( - 'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.statusTitle', - { defaultMessage: 'Status' } - ), - sortable: true, - truncateText: false, - width: '120px', - 'data-test-subj': 'alertsTableCell-status', - render: (_executionStatus: AlertExecutionStatus, item: AlertTableItem) => { - return renderAlertExecutionStatus(item.executionStatus, item); - }, - }, - { - field: 'alertType', - name: i18n.translate( - 'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.alertTypeTitle', - { defaultMessage: 'Type' } - ), + field: 'tags', + name: '', sortable: false, - truncateText: true, - render: (_count: number, item: AlertTableItem) => ( - {item.alertType} - ), - 'data-test-subj': 'alertsTableCell-alertType', + width: '50px', + 'data-test-subj': 'alertsTableCell-tagsPopover', + render: (tags: string[], item: AlertTableItem) => { + return tags.length > 0 ? ( + setTagPopoverOpenIndex(item.index)} + onClickAriaLabel="Tags" + iconOnClick={() => setTagPopoverOpenIndex(item.index)} + iconOnClickAriaLabel="Tags" + > + {tags.length} + + } + anchorPosition="upCenter" + isOpen={tagPopoverOpenIndex === item.index} + closePopover={() => setTagPopoverOpenIndex(-1)} + > + Tags +
+ {tags.map((tag: string, index: number) => ( + + {tag} + + ))} + + ) : null; + }, }, { - field: 'tagsText', - name: i18n.translate( - 'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.tagsText', - { defaultMessage: 'Tags' } - ), - sortable: false, - 'data-test-subj': 'alertsTableCell-tagsText', - render: (_count: number, item: AlertTableItem) => ( -
- {item.tagsText} -
- ), + field: 'executionStatus.lastExecutionDate', + name: 'Last run', + sortable: true, + width: '15%', + 'data-test-subj': 'alertsTableCell-lastExecutionDate', + render: (date: Date) => { + if (date) { + return ( + <> + + + {moment(date).format('MMM D, YYYY HH:mm:ssa')} + + + + {moment(date).fromNow()} + + + + + ); + } + }, }, { field: 'schedule.interval', width: '6%', name: i18n.translate( 'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.scheduleTitle', - { defaultMessage: 'Runs every' } + { defaultMessage: 'Interval' } ), sortable: false, truncateText: false, 'data-test-subj': 'alertsTableCell-interval', + render: (interval: string) => formatDuration(interval), }, { - width: '9%', - name: i18n.translate( - 'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.actionsTitle', - { defaultMessage: 'Actions' } + field: 'executionStatus.lastDuration', + width: '12%', + name: ( + + + Duration{' '} + + + ), - render: (item: AlertTableItem) => { + sortable: true, + truncateText: false, + 'data-test-subj': 'alertsTableCell-duration', + render: (value: number, item: AlertTableItem) => { + const ruleTypeTimeout: string | undefined = alertTypesState.data.get( + item.alertTypeId + )?.ruleTaskTimeout; + const ruleTypeTimeoutMillis: number | undefined = ruleTypeTimeout + ? parseDuration(ruleTypeTimeout) + : undefined; + const showDurationWarning: boolean = ruleTypeTimeoutMillis + ? value > ruleTypeTimeoutMillis + : false; + const duration = moment.duration(value); + const durationString = [duration.hours(), duration.minutes(), duration.seconds()] + .map((v: number) => padStart(`${v}`, 2, '0')) + .join(':'); + + // add millis + const millisString = padStart(`${duration.milliseconds()}`, 3, '0'); return ( - - {item.actionsCount} - -
- {item.muteAll ? ( - - - - ) : null} -
-
-
+ <> + {`${durationString}.${millisString}`} + {showDurationWarning && ( + + )} + ); }, - 'data-test-subj': 'alertsTableCell-actions', + }, + { + field: 'executionStatus.status', + name: i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.statusTitle', + { defaultMessage: 'Status' } + ), + sortable: true, + truncateText: false, + width: '120px', + 'data-test-subj': 'alertsTableCell-status', + render: (_executionStatus: AlertExecutionStatus, item: AlertTableItem) => { + return renderAlertExecutionStatus(item.executionStatus, item); + }, }, { name: '', @@ -669,7 +777,7 @@ export const AlertsList: React.FunctionComponent = () => { - + {!dismissAlertErrors && alertsStatusesTotal.error > 0 ? ( @@ -679,9 +787,9 @@ export const AlertsList: React.FunctionComponent = () => { title={ } @@ -726,10 +834,10 @@ export const AlertsList: React.FunctionComponent = () => { @@ -737,46 +845,45 @@ export const AlertsList: React.FunctionComponent = () => { - + - + - {/* Large to remain consistent with ActionsList table spacing */} - + { setAlertsState({ ...alertsState, isLoading }); }} /> - + {loadedItems.length || isFilterApplied ? ( table ) : alertTypesState.isLoading || alertsState.isLoading ? ( @@ -958,10 +1065,10 @@ function convertAlertsToTableItems( ruleTypeIndex: RuleTypeIndex, canExecuteActions: boolean ) { - return alerts.map((alert) => ({ + return alerts.map((alert, index: number) => ({ ...alert, + index, actionsCount: alert.actions.length, - tagsText: alert.tags.join(', '), alertType: ruleTypeIndex.get(alert.alertTypeId)?.name ?? alert.alertTypeId, isEditable: hasAllPrivilege(alert, ruleTypeIndex.get(alert.alertTypeId)) && diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/collapsed_item_actions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/collapsed_item_actions.test.tsx index 5a06b03311cb..807fe65a561f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/collapsed_item_actions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/collapsed_item_actions.test.tsx @@ -75,7 +75,7 @@ describe('CollapsedItemActions', () => { lastExecutionDate: new Date('2020-08-20T19:23:38Z'), }, actionsCount: 1, - tagsText: 'tag1', + index: 0, alertType: 'Test Alert Type', isEditable: true, enabledInLicense: true, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/rule_enabled_switch.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/rule_enabled_switch.test.tsx index bfa760b65ed4..da75faeda95e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/rule_enabled_switch.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/rule_enabled_switch.test.tsx @@ -40,7 +40,7 @@ describe('RuleEnabledSwitch', () => { enabledInLicense: true, isEditable: false, notifyWhen: null, - tagsText: 'test', + index: 0, updatedAt: new Date('2020-08-20T19:23:38Z'), }, onAlertChanged: jest.fn(), @@ -84,7 +84,7 @@ describe('RuleEnabledSwitch', () => { enabledInLicense: true, isEditable: true, notifyWhen: null, - tagsText: 'test', + index: 0, updatedAt: new Date('2020-08-20T19:23:38Z'), }, }} diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 2ef20f36b7ca..a78d1d52de0b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -197,6 +197,7 @@ export interface AlertType< | 'minimumLicenseRequired' | 'recoveryActionGroup' | 'defaultActionGroupId' + | 'ruleTaskTimeout' | 'defaultScheduleInterval' | 'minimumScheduleInterval' > { @@ -211,7 +212,7 @@ export type AlertUpdates = Omit; export interface AlertTableItem extends Alert { alertType: AlertType['name']; - tagsText: string; + index: number; actionsCount: number; isEditable: boolean; enabledInLicense: boolean; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rule_types.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rule_types.ts index f52f0977a630..b070219410fd 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rule_types.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rule_types.ts @@ -36,6 +36,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { name: 'Recovered', }, enabled_in_license: true, + rule_task_timeout: '5m', }; const expectedRestrictedNoOpType = { @@ -59,6 +60,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { minimum_license_required: 'basic', is_exportable: true, enabled_in_license: true, + rule_task_timeout: '5m', }; describe('rule_types', () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/rule_types.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/rule_types.ts index 86a0e269b26d..77638ed90fbe 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/rule_types.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/rule_types.ts @@ -44,6 +44,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { minimum_license_required: 'basic', is_exportable: true, enabled_in_license: true, + rule_task_timeout: '5m', }); expect(Object.keys(authorizedConsumers)).to.contain('alertsFixture'); }); @@ -129,6 +130,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { minimumLicenseRequired: 'basic', isExportable: true, enabledInLicense: true, + ruleTaskTimeout: '5m', }); expect(Object.keys(authorizedConsumers)).to.contain('alertsFixture'); }); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts index 3f36032523e8..dede48166966 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts @@ -88,9 +88,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const searchResults = await pageObjects.triggersActionsUI.getAlertsList(); expect(searchResults).to.have.length(3); - expect(searchResults[0].name).to.eql('a'); - expect(searchResults[1].name).to.eql('b'); - expect(searchResults[2].name).to.eql('c'); + // rule list shows name and rule type id + expect(searchResults[0].name).to.eql('aTest: Noop'); + expect(searchResults[1].name).to.eql('bTest: Noop'); + expect(searchResults[2].name).to.eql('cTest: Noop'); }); it('should search for alert', async () => { @@ -99,67 +100,54 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); const searchResults = await pageObjects.triggersActionsUI.getAlertsList(); - expect(searchResults).to.eql([ - { - name: createdAlert.name, - tagsText: 'foo, bar', - alertType: 'Test: Noop', - interval: '1m', - }, - ]); + expect(searchResults.length).to.equal(1); + expect(searchResults[0].name).to.equal(`${createdAlert.name}Test: Noop`); + expect(searchResults[0].interval).to.equal('1 min'); + expect(searchResults[0].tags).to.equal('2'); + expect(searchResults[0].duration).to.match(/\d{2}:\d{2}:\d{2}.\d{3}/); }); it('should update alert list on the search clear button click', async () => { await createAlert({ name: 'b' }); - await createAlert({ name: 'c' }); + await createAlert({ name: 'c', tags: [] }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts('b'); const searchResults = await pageObjects.triggersActionsUI.getAlertsList(); - expect(searchResults).to.eql([ - { - name: 'b', - tagsText: 'foo, bar', - alertType: 'Test: Noop', - interval: '1m', - }, - ]); + expect(searchResults.length).to.equal(1); + expect(searchResults[0].name).to.equal('bTest: Noop'); + expect(searchResults[0].interval).to.equal('1 min'); + expect(searchResults[0].tags).to.equal('2'); + expect(searchResults[0].duration).to.match(/\d{2}:\d{2}:\d{2}.\d{3}/); + const searchClearButton = await find.byCssSelector('.euiFormControlLayoutClearButton'); await searchClearButton.click(); await find.byCssSelector( '.euiBasicTable[data-test-subj="alertsList"]:not(.euiBasicTable-loading)' ); const searchResultsAfterClear = await pageObjects.triggersActionsUI.getAlertsList(); - expect(searchResultsAfterClear).to.eql([ - { - name: 'b', - tagsText: 'foo, bar', - alertType: 'Test: Noop', - interval: '1m', - }, - { - name: 'c', - tagsText: 'foo, bar', - alertType: 'Test: Noop', - interval: '1m', - }, - ]); + expect(searchResultsAfterClear.length).to.equal(2); + expect(searchResultsAfterClear[0].name).to.equal('bTest: Noop'); + expect(searchResultsAfterClear[0].interval).to.equal('1 min'); + expect(searchResultsAfterClear[0].tags).to.equal('2'); + expect(searchResultsAfterClear[0].duration).to.match(/\d{2}:\d{2}:\d{2}.\d{3}/); + expect(searchResultsAfterClear[1].name).to.equal('cTest: Noop'); + expect(searchResultsAfterClear[1].interval).to.equal('1 min'); + expect(searchResultsAfterClear[1].tags).to.equal(''); + expect(searchResultsAfterClear[1].duration).to.match(/\d{2}:\d{2}:\d{2}.\d{3}/); }); it('should search for tags', async () => { - const createdAlert = await createAlert(); + const createdAlert = await createAlert({ tags: ['tag', 'tagtag', 'taggity tag'] }); await refreshAlertsList(); - await pageObjects.triggersActionsUI.searchAlerts(`${createdAlert.name} foo`); + await pageObjects.triggersActionsUI.searchAlerts(`${createdAlert.name} tag`); const searchResults = await pageObjects.triggersActionsUI.getAlertsList(); - expect(searchResults).to.eql([ - { - name: createdAlert.name, - tagsText: 'foo, bar', - alertType: 'Test: Noop', - interval: '1m', - }, - ]); + expect(searchResults.length).to.equal(1); + expect(searchResults[0].name).to.equal(`${createdAlert.name}Test: Noop`); + expect(searchResults[0].interval).to.equal('1 min'); + expect(searchResults[0].tags).to.equal('3'); + expect(searchResults[0].duration).to.match(/\d{2}:\d{2}:\d{2}.\d{3}/); }); it('should display an empty list when search did not return any alerts', async () => { @@ -385,15 +373,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await retry.try(async () => { const filterErrorOnlyResults = await pageObjects.triggersActionsUI.getAlertsListWithStatus(); - expect(filterErrorOnlyResults).to.eql([ - { - name: failingAlert.name, - tagsText: 'foo, bar', - alertType: 'Test: Failing', - interval: '30s', - status: 'Error', - }, - ]); + expect(filterErrorOnlyResults.length).to.equal(1); + expect(filterErrorOnlyResults[0].name).to.equal(`${failingAlert.name}Test: Failing`); + expect(filterErrorOnlyResults[0].interval).to.equal('30 sec'); + expect(filterErrorOnlyResults[0].status).to.equal('Error'); + expect(filterErrorOnlyResults[0].duration).to.match(/\d{2}:\d{2}:\d{2}.\d{3}/); }); }); @@ -402,15 +386,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await retry.try(async () => { await refreshAlertsList(); const refreshResults = await pageObjects.triggersActionsUI.getAlertsListWithStatus(); - expect(refreshResults).to.eql([ - { - name: createdAlert.name, - tagsText: 'foo, bar', - alertType: 'Test: Noop', - interval: '1m', - status: 'Ok', - }, - ]); + expect(refreshResults.length).to.equal(1); + expect(refreshResults[0].name).to.equal(`${createdAlert.name}Test: Noop`); + expect(refreshResults[0].interval).to.equal('1 min'); + expect(refreshResults[0].status).to.equal('Ok'); + expect(refreshResults[0].duration).to.match(/\d{2}:\d{2}:\d{2}.\d{3}/); }); const alertsErrorBannerWhenNoErrors = await find.allByCssSelector( @@ -451,14 +431,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await retry.try(async () => { const filterFailingAlertOnlyResults = await pageObjects.triggersActionsUI.getAlertsList(); - expect(filterFailingAlertOnlyResults).to.eql([ - { - name: failingAlert.name, - tagsText: 'foo, bar', - alertType: 'Test: Failing', - interval: '30s', - }, - ]); + expect(filterFailingAlertOnlyResults.length).to.equal(1); + expect(filterFailingAlertOnlyResults[0].name).to.equal(`${failingAlert.name}Test: Failing`); + expect(filterFailingAlertOnlyResults[0].interval).to.equal('30 sec'); + expect(filterFailingAlertOnlyResults[0].duration).to.match(/\d{2}:\d{2}:\d{2}.\d{3}/); }); }); @@ -480,14 +456,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await retry.try(async () => { const filterWithSlackOnlyResults = await pageObjects.triggersActionsUI.getAlertsList(); - expect(filterWithSlackOnlyResults).to.eql([ - { - name: noopAlertWithAction.name, - tagsText: 'foo, bar', - alertType: 'Test: Noop', - interval: '1m', - }, - ]); + expect(filterWithSlackOnlyResults.length).to.equal(1); + expect(filterWithSlackOnlyResults[0].name).to.equal( + `${noopAlertWithAction.name}Test: Noop` + ); + expect(filterWithSlackOnlyResults[0].interval).to.equal('1 min'); + expect(filterWithSlackOnlyResults[0].duration).to.match(/\d{2}:\d{2}:\d{2}.\d{3}/); }); }); }); diff --git a/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts b/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts index 890315698f74..a49873c6d47b 100644 --- a/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts +++ b/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts @@ -22,18 +22,18 @@ export function TriggersActionsPageProvider({ getService }: FtrProviderContext) function getRowItemData(row: CustomCheerio, $: CustomCheerioStatic) { return { name: $(row).findTestSubject('alertsTableCell-name').find('.euiTableCellContent').text(), - tagsText: $(row) - .findTestSubject('alertsTableCell-tagsText') - .find('.euiTableCellContent') - .text(), - alertType: $(row) - .findTestSubject('alertsTableCell-alertType') + duration: $(row) + .findTestSubject('alertsTableCell-duration') .find('.euiTableCellContent') .text(), interval: $(row) .findTestSubject('alertsTableCell-interval') .find('.euiTableCellContent') .text(), + tags: $(row) + .findTestSubject('alertsTableCell-tagsPopover') + .find('.euiTableCellContent') + .text(), }; } From 243c2133aff98b21aef7e12ee65d50539dded4db Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Mon, 11 Oct 2021 21:35:00 -0500 Subject: [PATCH 73/74] [nit][pre-req] Strongly-type Shipper and Category (#114412) --- .../custom_integrations/common/index.ts | 61 ++++++++++++++++--- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/src/plugins/custom_integrations/common/index.ts b/src/plugins/custom_integrations/common/index.ts index e2408d312460..9af7c4ccd463 100755 --- a/src/plugins/custom_integrations/common/index.ts +++ b/src/plugins/custom_integrations/common/index.ts @@ -9,13 +9,11 @@ export const PLUGIN_ID = 'customIntegrations'; export const PLUGIN_NAME = 'customIntegrations'; -export interface IntegrationCategoryCount { - count: number; - id: IntegrationCategory; -} - +/** + * A map of category names and their corresponding titles. + */ +// TODO: consider i18n export const INTEGRATION_CATEGORY_DISPLAY = { - // Known EPR aws: 'AWS', azure: 'Azure', cloud: 'Cloud', @@ -49,13 +47,62 @@ export const INTEGRATION_CATEGORY_DISPLAY = { updates_available: 'Updates available', }; +/** + * A category applicable to an Integration. + */ export type IntegrationCategory = keyof typeof INTEGRATION_CATEGORY_DISPLAY; +/** + * The list of all available categories. + */ +// This `as` is necessary, as Object.keys cannot be strongly typed. +// see: https://github.com/Microsoft/TypeScript/issues/12870 +export const category = Object.keys(INTEGRATION_CATEGORY_DISPLAY) as IntegrationCategory[]; + +/** + * An object containing the id of an `IntegrationCategory` and the count of all Integrations in that category. + */ +export interface IntegrationCategoryCount { + count: number; + id: IntegrationCategory; +} + +/** + * A map of shipper names and their corresponding titles. + */ +// TODO: consider i18n +export const SHIPPER_DISPLAY = { + beats: 'Beats', + language_clients: 'Language clients', + other: 'Other', + sample_data: 'Sample data', + tests: 'Tests', + tutorial: 'Tutorials', +}; + +/** + * A shipper-- an internal or external system capable of storing data in ES/Kibana-- applicable to an Integration. + */ +export type Shipper = keyof typeof SHIPPER_DISPLAY; + +/** + * The list of all known shippers. + */ +// This `as` is necessary, as Object.keys cannot be strongly typed. +// see: https://github.com/Microsoft/TypeScript/issues/12870 +export const shipper = Object.keys(SHIPPER_DISPLAY) as Shipper[]; + +/** + * An icon representing an Integration. + */ export interface CustomIntegrationIcon { src: string; type: 'eui' | 'svg'; } +/** + * A definition of a dataintegration, which can be registered with Kibana. + */ export interface CustomIntegration { id: string; title: string; @@ -65,7 +112,7 @@ export interface CustomIntegration { isBeta: boolean; icons: CustomIntegrationIcon[]; categories: IntegrationCategory[]; - shipper: string; + shipper: Shipper; eprOverlap?: string; // name of the equivalent Elastic Agent integration in EPR. e.g. a beat module can correspond to an EPR-package, or an APM-tutorial. When completed, Integrations-UX can preferentially show the EPR-package, rather than the custom-integration } From 7c087fea8226ab8bee96cb44e1a5111701b4a904 Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 11 Oct 2021 23:01:02 -0500 Subject: [PATCH 74/74] [ci-stats] support sending meta with metrics (#114198) * [ci-stats] support sending meta with metrics * update kbn/pm dist * improve comments stat * update kbn/pm dist Co-authored-by: spalger Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../ci_stats_reporter/ci_stats_reporter.ts | 51 ++++++++++++++++--- packages/kbn-pm/dist/index.js | 25 +++++++-- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts index fe48ce99e685..05f54e8d38c8 100644 --- a/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts +++ b/packages/kbn-dev-utils/src/ci_stats_reporter/ci_stats_reporter.ts @@ -22,24 +22,43 @@ import { parseConfig, Config } from './ci_stats_config'; const BASE_URL = 'https://ci-stats.kibana.dev'; export interface CiStatsMetric { + /** Top-level categorization for the metric, e.g. "page load bundle size" */ group: string; + /** Specific sub-set of the "group", e.g. "dashboard" */ id: string; + /** integer value recorded as the value of this metric */ value: number; + /** optional limit which will generate an error on PRs when the metric exceeds the limit */ limit?: number; + /** + * path, relative to the repo, where the config file contianing limits + * is kept. Linked from PR comments instructing contributors how to fix + * their PRs. + */ limitConfigPath?: string; + /** Arbitrary key-value pairs which can be used for additional filtering/reporting */ + meta?: CiStatsMetadata; } -export interface CiStatsTimingMetadata { +export interface CiStatsMetadata { + /** + * Arbitrary key-value pairs which can be attached to CiStatsTiming and CiStatsMetric + * objects stored in the ci-stats service + */ [key: string]: string | string[] | number | boolean | undefined; } export interface CiStatsTiming { + /** Top-level categorization for the timing, e.g. "scripts/foo", process type, etc. */ group: string; + /** Specific timing (witin the "group" being tracked) e.g. "total" */ id: string; + /** time in milliseconds which should be recorded */ ms: number; - meta?: CiStatsTimingMetadata; + /** hash of key-value pairs which will be stored with the timing for additional filtering and reporting */ + meta?: CiStatsMetadata; } -export interface ReqOptions { +interface ReqOptions { auth: boolean; path: string; body: any; @@ -54,17 +73,34 @@ export interface TimingsOptions { /** value of data/uuid, automatically loaded if not specified */ kibanaUuid?: string | null; } + +export interface MetricsOptions { + /** Default metadata to add to each metric */ + defaultMeta?: CiStatsMetadata; +} export class CiStatsReporter { + /** + * Create a CiStatsReporter by inspecting the ENV for the necessary config + */ static fromEnv(log: ToolingLog) { return new CiStatsReporter(parseConfig(log), log); } constructor(private config: Config | undefined, private log: ToolingLog) {} + /** + * Determine if CI_STATS is explicitly disabled by the environment. To determine + * if the CiStatsReporter has enough information in the environment to send metrics + * for builds use #hasBuildConfig(). + */ isEnabled() { return process.env.CI_STATS_DISABLED !== 'true'; } + /** + * Determines if the CiStatsReporter is disabled by the environment, or properly + * configured and able to send stats + */ hasBuildConfig() { return this.isEnabled() && !!this.config?.apiToken && !!this.config?.buildId; } @@ -103,7 +139,7 @@ export class CiStatsReporter { const memUsage = process.memoryUsage(); const isElasticCommitter = email && email.endsWith('@elastic.co') ? true : false; - const defaultMetadata = { + const defaultMeta = { kibanaUuid, isElasticCommitter, committerHash: email @@ -127,7 +163,7 @@ export class CiStatsReporter { totalMem: Os.totalmem(), }; - this.log.debug('CIStatsReporter committerHash: %s', defaultMetadata.committerHash); + this.log.debug('CIStatsReporter committerHash: %s', defaultMeta.committerHash); return await this.req({ auth: !!buildId, @@ -135,8 +171,8 @@ export class CiStatsReporter { body: { buildId, upstreamBranch, + defaultMeta, timings, - defaultMetadata, }, bodyDesc: timings.length === 1 ? `${timings.length} timing` : `${timings.length} timings`, }); @@ -146,7 +182,7 @@ export class CiStatsReporter { * Report metrics data to the ci-stats service. If running outside of CI this method * does nothing as metrics can only be reported when associated with a specific CI build. */ - async metrics(metrics: CiStatsMetric[]) { + async metrics(metrics: CiStatsMetric[], options?: MetricsOptions) { if (!this.hasBuildConfig()) { return; } @@ -162,6 +198,7 @@ export class CiStatsReporter { path: '/v1/metrics', body: { buildId, + defaultMeta: options?.defaultMeta, metrics, }, bodyDesc: `metrics: ${metrics diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index a231fe21ea83..3c0e3fa94be0 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -9010,6 +9010,9 @@ var _ci_stats_config = __webpack_require__(218); const BASE_URL = 'https://ci-stats.kibana.dev'; class CiStatsReporter { + /** + * Create a CiStatsReporter by inspecting the ENV for the necessary config + */ static fromEnv(log) { return new CiStatsReporter((0, _ci_stats_config.parseConfig)(log), log); } @@ -9018,10 +9021,21 @@ class CiStatsReporter { this.config = config; this.log = log; } + /** + * Determine if CI_STATS is explicitly disabled by the environment. To determine + * if the CiStatsReporter has enough information in the environment to send metrics + * for builds use #hasBuildConfig(). + */ + isEnabled() { return process.env.CI_STATS_DISABLED !== 'true'; } + /** + * Determines if the CiStatsReporter is disabled by the environment, or properly + * configured and able to send stats + */ + hasBuildConfig() { var _this$config, _this$config2; @@ -9069,7 +9083,7 @@ class CiStatsReporter { const memUsage = process.memoryUsage(); const isElasticCommitter = email && email.endsWith('@elastic.co') ? true : false; - const defaultMetadata = { + const defaultMeta = { kibanaUuid, isElasticCommitter, committerHash: email ? _crypto.default.createHash('sha256').update(email).digest('hex').substring(0, 20) : undefined, @@ -9090,15 +9104,15 @@ class CiStatsReporter { osRelease: _os.default.release(), totalMem: _os.default.totalmem() }; - this.log.debug('CIStatsReporter committerHash: %s', defaultMetadata.committerHash); + this.log.debug('CIStatsReporter committerHash: %s', defaultMeta.committerHash); return await this.req({ auth: !!buildId, path: '/v1/timings', body: { buildId, upstreamBranch, - timings, - defaultMetadata + defaultMeta, + timings }, bodyDesc: timings.length === 1 ? `${timings.length} timing` : `${timings.length} timings` }); @@ -9109,7 +9123,7 @@ class CiStatsReporter { */ - async metrics(metrics) { + async metrics(metrics, options) { var _this$config4; if (!this.hasBuildConfig()) { @@ -9127,6 +9141,7 @@ class CiStatsReporter { path: '/v1/metrics', body: { buildId, + defaultMeta: options === null || options === void 0 ? void 0 : options.defaultMeta, metrics }, bodyDesc: `metrics: ${metrics.map(({

Gf@Nj*<=XI0eiIXyCH7m81Z`*>F)aO7nyuEDMC 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 { if (spacesApi && resolveResult.outcome === 'aliasMatch') { // We found this object by a legacy URL alias from its old ID; redirect the user to the page with its new ID, preserving any URL hash const newObjectId = resolveResult.alias_target_id!; // This is always defined if outcome === 'aliasMatch' - const newPath = http.basePath.prepend( - `path/to/this/page/${newObjectId}${window.location.hash}` - ); + const newPath = `/this/page/${newObjectId}${window.location.hash}`; // Use the *local* path within this app (do not include the "/app/appId" prefix) await spacesApi.ui.redirectLegacyUrl(newPath, OBJECT_NOUN); return; } @@ -255,9 +253,7 @@ const getLegacyUrlConflictCallout = () => { // callout with a warning for the user, and provide a way for them to navigate to the other object. const currentObjectId = savedObject.id; const otherObjectId = resolveResult.alias_target_id!; // This is always defined if outcome === 'conflict' - const otherObjectPath = http.basePath.prepend( - `path/to/this/page/${otherObjectId}${window.location.hash}` - ); + const otherObjectPath = `/this/page/${otherObjectId}${window.location.hash}`; // Use the *local* path within this app (do not include the "/app/appId" prefix) return ( <> {spacesApi.ui.components.getLegacyUrlConflict({ @@ -391,6 +387,13 @@ These should be handled on a case-by-case basis at the plugin owner's discretion * Any "secondary" objects on the page may handle the outcomes differently. If the secondary object ID is not important (for example, it just functions as a page anchor), it may make more sense to ignore the different outcomes. If the secondary object _is_ important but it is not directly represented in the UI, it may make more sense to throw a descriptive error when a `'conflict'` outcome is encountered. + - Embeddables should use `spacesApi.ui.components.getEmbeddableLegacyUrlConflict` to render conflict errors: ++ +image::images/sharing-saved-objects-faq-multiple-deep-link-objects-1.png["Sharing Saved Objects embeddable legacy URL conflict"] +Viewing details shows the user how to disable the alias and fix the problem using the +<>: ++ +image::images/sharing-saved-objects-faq-multiple-deep-link-objects-2.png["Sharing Saved Objects embeddable legacy URL conflict (showing details)"] - If the secondary object is resolved by an external service (such as the index pattern service), the service should simply make the full outcome available to consumers. From 30aeb8106c9c9861f1d845b032d90a7c108f3bc6 Mon Sep 17 00:00:00 2001 From: Khristinin Nikita Date: Sun, 10 Oct 2021 20:45:30 +0200 Subject: [PATCH 27/74] Make host card overview space aware (#113983) * Make host card overview space aware * Add cypress test * Move getHostRiskIndex to helpers * Fix cypress test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../security_solution/common/constants.ts | 2 +- .../overview/risky_hosts_panel.spec.ts | 14 +++++++++++- .../cypress/screens/kibana_navigation.ts | 4 ++++ .../cypress/tasks/api_calls/spaces.ts | 22 +++++++++++++++++++ .../cypress/tasks/kibana_navigation.ts | 12 +++++++++- .../security_solution/public/helpers.test.ts | 8 ++++++- .../security_solution/public/helpers.ts | 13 ++++++++++- .../use_hosts_risk_score.ts | 21 ++++++++++-------- .../plugins/security_solution/public/types.ts | 2 ++ .../es_archives/risky_hosts/data.json | 2 +- .../es_archives/risky_hosts/mappings.json | 6 ++--- 11 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 x-pack/plugins/security_solution/cypress/tasks/api_calls/spaces.ts diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 52c82f57d9ec..d2120faf09df 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -334,7 +334,7 @@ export const ELASTIC_NAME = 'estc'; export const METADATA_TRANSFORM_STATS_URL = `/api/transform/transforms/${METADATA_TRANSFORMS_PATTERN}/_stats`; -export const HOST_RISK_SCORES_INDEX = 'ml_host_risk_score_latest'; +export const RISKY_HOSTS_INDEX_PREFIX = 'ml_host_risk_score_latest_'; export const TRANSFORM_STATES = { ABORTING: 'aborting', diff --git a/x-pack/plugins/security_solution/cypress/integration/overview/risky_hosts_panel.spec.ts b/x-pack/plugins/security_solution/cypress/integration/overview/risky_hosts_panel.spec.ts index df57f7cc8d05..1c55a38b3249 100644 --- a/x-pack/plugins/security_solution/cypress/integration/overview/risky_hosts_panel.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/overview/risky_hosts_panel.spec.ts @@ -17,8 +17,12 @@ import { import { loginAndWaitForPage } from '../../tasks/login'; import { OVERVIEW_URL } from '../../urls/navigation'; import { cleanKibana } from '../../tasks/common'; +import { changeSpace } from '../../tasks/kibana_navigation'; +import { createSpace, removeSpace } from '../../tasks/api_calls/spaces'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; +const testSpaceName = 'test'; + describe('Risky Hosts Link Panel', () => { before(() => { cleanKibana(); @@ -40,10 +44,12 @@ describe('Risky Hosts Link Panel', () => { describe('enabled module', () => { before(() => { esArchiverLoad('risky_hosts'); + createSpace(testSpaceName); }); after(() => { esArchiverUnload('risky_hosts'); + removeSpace(testSpaceName); }); it('renders disabled dashboard module as expected when there are no hosts in the selected time period', () => { @@ -57,13 +63,19 @@ describe('Risky Hosts Link Panel', () => { cy.get(`${OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 hosts'); }); - it('renders dashboard module as expected when there are hosts in the selected time period', () => { + it('renders space aware dashboard module as expected when there are hosts in the selected time period', () => { loginAndWaitForPage(OVERVIEW_URL); cy.get( `${OVERVIEW_RISKY_HOSTS_LINKS} ${OVERVIEW_RISKY_HOSTS_LINKS_WARNING_INNER_PANEL}` ).should('not.exist'); cy.get(`${OVERVIEW_RISKY_HOSTS_VIEW_DASHBOARD_BUTTON}`).should('be.disabled'); cy.get(`${OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 1 host'); + + changeSpace(testSpaceName); + cy.visit(`/s/${testSpaceName}${OVERVIEW_URL}`); + cy.get(`${OVERVIEW_RISKY_HOSTS_VIEW_DASHBOARD_BUTTON}`).should('be.disabled'); + cy.get(`${OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT}`).should('have.text', 'Showing: 0 hosts'); + cy.get(`${OVERVIEW_RISKY_HOSTS_ENABLE_MODULE_BUTTON}`).should('exist'); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/kibana_navigation.ts b/x-pack/plugins/security_solution/cypress/screens/kibana_navigation.ts index 36b870598eff..c20f4bd054a7 100644 --- a/x-pack/plugins/security_solution/cypress/screens/kibana_navigation.ts +++ b/x-pack/plugins/security_solution/cypress/screens/kibana_navigation.ts @@ -25,3 +25,7 @@ export const OVERVIEW_PAGE = export const TIMELINES_PAGE = '[data-test-subj="collapsibleNavGroup-securitySolution"] [title="Timelines"]'; + +export const SPACES_BUTTON = '[data-test-subj="spacesNavSelector"]'; + +export const getGoToSpaceMenuItem = (space: string) => `[data-test-subj="${space}-gotoSpace"]`; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/spaces.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/spaces.ts new file mode 100644 index 000000000000..cd12fab70a89 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/spaces.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. + */ + +export const createSpace = (id: string) => { + cy.request({ + method: 'POST', + url: 'api/spaces/space', + body: { + id, + name: id, + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + }); +}; + +export const removeSpace = (id: string) => { + cy.request(`/api/spaces/space/${id}`); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/kibana_navigation.ts b/x-pack/plugins/security_solution/cypress/tasks/kibana_navigation.ts index 3b3fc0c6da4e..43630e63ebfe 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/kibana_navigation.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/kibana_navigation.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { KIBANA_NAVIGATION_TOGGLE } from '../screens/kibana_navigation'; +import { + KIBANA_NAVIGATION_TOGGLE, + SPACES_BUTTON, + getGoToSpaceMenuItem, +} from '../screens/kibana_navigation'; export const navigateFromKibanaCollapsibleTo = (page: string) => { cy.get(page).click(); @@ -14,3 +18,9 @@ export const navigateFromKibanaCollapsibleTo = (page: string) => { export const openKibanaNavigation = () => { cy.get(KIBANA_NAVIGATION_TOGGLE).click(); }; + +export const changeSpace = (space: string) => { + cy.get(`${SPACES_BUTTON}`).click(); + cy.get(getGoToSpaceMenuItem(space)).click(); + cy.get(`[data-test-subj="space-avatar-${space}"]`).should('exist'); +}; diff --git a/x-pack/plugins/security_solution/public/helpers.test.ts b/x-pack/plugins/security_solution/public/helpers.test.ts index eaaf518d486a..f2e37cd995a4 100644 --- a/x-pack/plugins/security_solution/public/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/helpers.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { parseRoute } from './helpers'; +import { parseRoute, getHostRiskIndex } from './helpers'; describe('public helpers parseRoute', () => { it('should properly parse hash route', () => { @@ -54,3 +54,9 @@ describe('public helpers parseRoute', () => { }); }); }); + +describe('public helpers export getHostRiskIndex', () => { + it('should properly return index if space is specified', () => { + expect(getHostRiskIndex('testName')).toEqual('ml_host_risk_score_latest_testName'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/helpers.ts b/x-pack/plugins/security_solution/public/helpers.ts index 9d842e0c0a12..aba46cffee19 100644 --- a/x-pack/plugins/security_solution/public/helpers.ts +++ b/x-pack/plugins/security_solution/public/helpers.ts @@ -9,7 +9,14 @@ import { isEmpty } from 'lodash/fp'; import { matchPath } from 'react-router-dom'; import { CoreStart } from '../../../../src/core/public'; -import { ALERTS_PATH, APP_ID, EXCEPTIONS_PATH, RULES_PATH, UEBA_PATH } from '../common/constants'; +import { + ALERTS_PATH, + APP_ID, + EXCEPTIONS_PATH, + RULES_PATH, + UEBA_PATH, + RISKY_HOSTS_INDEX_PREFIX, +} from '../common/constants'; import { FactoryQueryTypes, StrategyResponseType, @@ -147,3 +154,7 @@ export const isDetectionsPath = (pathname: string): boolean => { strict: false, }); }; + +export const getHostRiskIndex = (spaceId: string): string => { + return `${RISKY_HOSTS_INDEX_PREFIX}${spaceId}`; +}; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_hosts_risk_score.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_hosts_risk_score.ts index 75cf51194ab6..af663bb74f54 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_hosts_risk_score.ts +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_risky_host_links/use_hosts_risk_score.ts @@ -12,12 +12,11 @@ import { useDispatch } from 'react-redux'; import { useAppToasts } from '../../../common/hooks/use_app_toasts'; import { useKibana } from '../../../common/lib/kibana'; import { inputsActions } from '../../../common/store/actions'; - -import { HOST_RISK_SCORES_INDEX } from '../../../../common/constants'; import { isIndexNotFoundError } from '../../../common/utils/exceptions'; import { HostsRiskScore } from '../../../../common'; import { useHostsRiskScoreComplete } from './use_hosts_risk_score_complete'; import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; +import { getHostRiskIndex } from '../../../helpers'; export const QUERY_ID = 'host_risk_score'; const noop = () => {}; @@ -50,7 +49,7 @@ export const useHostsRiskScore = ({ const [loading, setLoading] = useState(riskyHostsFeatureEnabled); const { addError } = useAppToasts(); - const { data } = useKibana().services; + const { data, spaces } = useKibana().services; const dispatch = useDispatch(); @@ -99,14 +98,18 @@ export const useHostsRiskScore = ({ useEffect(() => { if (riskyHostsFeatureEnabled && (hostName || timerange)) { - start({ - data, - timerange: timerange ? { to: timerange.to, from: timerange.from, interval: '' } : undefined, - hostName, - defaultIndex: [HOST_RISK_SCORES_INDEX], + spaces.getActiveSpace().then((space) => { + start({ + data, + timerange: timerange + ? { to: timerange.to, from: timerange.from, interval: '' } + : undefined, + hostName, + defaultIndex: [getHostRiskIndex(space.id)], + }); }); } - }, [start, data, timerange, hostName, riskyHostsFeatureEnabled]); + }, [start, data, timerange, hostName, riskyHostsFeatureEnabled, spaces]); if ((!hostName && !timerange) || !riskyHostsFeatureEnabled) { return null; diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 3294d95bf909..1cec87fd35d1 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -9,6 +9,7 @@ import { CoreStart } from '../../../../src/core/public'; import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; +import { SpacesPluginStart } from '../../../plugins/spaces/public'; import { LensPublicStart } from '../../../plugins/lens/public'; import { NewsfeedPublicPluginStart } from '../../../../src/plugins/newsfeed/public'; import { Start as InspectorStart } from '../../../../src/plugins/inspector/public'; @@ -67,6 +68,7 @@ export interface StartPlugins { timelines: TimelinesUIStart; uiActions: UiActionsStart; ml?: MlPluginStart; + spaces: SpacesPluginStart; } export type StartServices = CoreStart & diff --git a/x-pack/test/security_solution_cypress/es_archives/risky_hosts/data.json b/x-pack/test/security_solution_cypress/es_archives/risky_hosts/data.json index 7327f0fc7689..e42a13ab8d8a 100644 --- a/x-pack/test/security_solution_cypress/es_archives/risky_hosts/data.json +++ b/x-pack/test/security_solution_cypress/es_archives/risky_hosts/data.json @@ -2,7 +2,7 @@ "type":"doc", "value":{ "id":"a4cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f", - "index":"ml_host_risk_score_latest", + "index":"ml_host_risk_score_latest_default", "source":{ "@timestamp":"2021-03-10T14:51:05.766Z", "risk_score":21, diff --git a/x-pack/test/security_solution_cypress/es_archives/risky_hosts/mappings.json b/x-pack/test/security_solution_cypress/es_archives/risky_hosts/mappings.json index 211c50f6baee..f71c9cf8ed4c 100644 --- a/x-pack/test/security_solution_cypress/es_archives/risky_hosts/mappings.json +++ b/x-pack/test/security_solution_cypress/es_archives/risky_hosts/mappings.json @@ -1,7 +1,7 @@ { "type": "index", "value": { - "index": "ml_host_risk_score_latest", + "index": "ml_host_risk_score_latest_default", "mappings": { "properties": { "@timestamp": { @@ -40,8 +40,8 @@ "settings": { "index": { "lifecycle": { - "name": "ml_host_risk_score_latest", - "rollover_alias": "ml_host_risk_score_latest" + "name": "ml_host_risk_score_latest_default", + "rollover_alias": "ml_host_risk_score_latest_default" }, "mapping": { "total_fields": { From c73dc234e39f707b386309e95ed742b7bf6559d4 Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Sun, 10 Oct 2021 23:47:55 -0400 Subject: [PATCH 28/74] [Fleet] Display upgrade integration button instead of save for upgrades (#114314) * Display upgrade integration button instead of save for upgrades * Skip endpoint tests * Revert "Skip endpoint tests" This reverts commit 3cfd1001716b27cbd7589f24f821e6dc0e86ad99. --- .../edit_package_policy_page/index.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index 35092cb67f7e..7a2f46247d14 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -584,10 +584,17 @@ export const EditPackagePolicyForm = memo<{ fill data-test-subj="saveIntegration" > - + {isUpgrade ? ( + + ) : ( + + )} From 62658899e98c691224ad65a407724beaf9ada9c3 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Mon, 11 Oct 2021 10:12:56 +0300 Subject: [PATCH 29/74] Bump hapi to a version with aborted request fix (#114414) * bump hapi to a version with aborted request fix * enable apm functional tests --- package.json | 4 ++-- vars/tasks.groovy | 15 +++++++-------- yarn.lock | 15 ++++++++++----- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 585d6489551b..705d902d6afe 100644 --- a/package.json +++ b/package.json @@ -117,8 +117,8 @@ "@hapi/boom": "^9.1.4", "@hapi/cookie": "^11.0.2", "@hapi/h2o2": "^9.1.0", - "@hapi/hapi": "^20.2.0", - "@hapi/hoek": "^9.2.0", + "@hapi/hapi": "^20.2.1", + "@hapi/hoek": "^9.2.1", "@hapi/inert": "^6.0.4", "@hapi/wreck": "^17.1.0", "@kbn/ace": "link:bazel-bin/packages/kbn-ace", diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 5c8f133331e5..1842e278282b 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -146,14 +146,13 @@ def functionalXpack(Map params = [:]) { } } - //temporarily disable apm e2e test since it's breaking due to a version upgrade. - // whenChanged([ - // 'x-pack/plugins/apm/', - // ]) { - // if (githubPr.isPr()) { - // task(kibanaPipeline.functionalTestProcess('xpack-APMCypress', './test/scripts/jenkins_apm_cypress.sh')) - // } - // } + whenChanged([ + 'x-pack/plugins/apm/', + ]) { + if (githubPr.isPr()) { + task(kibanaPipeline.functionalTestProcess('xpack-APMCypress', './test/scripts/jenkins_apm_cypress.sh')) + } + } whenChanged([ 'x-pack/plugins/uptime/', diff --git a/yarn.lock b/yarn.lock index 66aeb2808031..defff7a1df42 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2968,10 +2968,10 @@ "@hapi/validate" "1.x.x" "@hapi/wreck" "17.x.x" -"@hapi/hapi@^20.2.0": - version "20.2.0" - resolved "https://registry.yarnpkg.com/@hapi/hapi/-/hapi-20.2.0.tgz#bf0eca9cc591e83f3d72d06a998d31be35d044a1" - integrity sha512-yPH/z8KvlSLV8lI4EuId9z595fKKk5n6YA7H9UddWYWsBXMcnCyoFmHtYq0PCV4sNgKLD6QW9e27R9V9Z9aqqw== +"@hapi/hapi@^20.2.1": + version "20.2.1" + resolved "https://registry.yarnpkg.com/@hapi/hapi/-/hapi-20.2.1.tgz#7482bc28757cb4671623a61bdb5ce920bffc8a2f" + integrity sha512-OXAU+yWLwkMfPFic+KITo+XPp6Oxpgc9WUH+pxXWcTIuvWbgco5TC/jS8UDvz+NFF5IzRgF2CL6UV/KLdQYUSQ== dependencies: "@hapi/accept" "^5.0.1" "@hapi/ammo" "^5.0.1" @@ -3001,11 +3001,16 @@ "@hapi/hoek" "9.x.x" "@hapi/validate" "1.x.x" -"@hapi/hoek@9.x.x", "@hapi/hoek@^9.0.0", "@hapi/hoek@^9.0.4", "@hapi/hoek@^9.2.0": +"@hapi/hoek@9.x.x", "@hapi/hoek@^9.0.0", "@hapi/hoek@^9.0.4": version "9.2.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.0.tgz#f3933a44e365864f4dad5db94158106d511e8131" integrity sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug== +"@hapi/hoek@^9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" + integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw== + "@hapi/inert@^6.0.4": version "6.0.4" resolved "https://registry.yarnpkg.com/@hapi/inert/-/inert-6.0.4.tgz#0544221eabc457110a426818358d006e70ff1f41" From ab7e3e8d39b62709f09366f65dc7abf001cbe9f2 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 11 Oct 2021 01:36:24 -0700 Subject: [PATCH 30/74] [Fleet] De-dupe setting kibana.version constraint in get package request to EPR (#114376) --- .../plugins/fleet/server/services/epm/registry/index.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/fleet/server/services/epm/registry/index.ts b/x-pack/plugins/fleet/server/services/epm/registry/index.ts index 2dff3f43f3ec..aa2c3f1d4da3 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -96,16 +96,12 @@ export async function fetchList(params?: SearchParams): Promise { const registryUrl = getRegistryUrl(); - const kibanaVersion = appContextService.getKibanaVersion().split('-')[0]; // may be x.y.z-SNAPSHOT - const kibanaBranch = appContextService.getKibanaBranch(); const url = new URL( `${registryUrl}/search?package=${packageName}&internal=true&experimental=true` ); - // on master, request all packages regardless of version - if (kibanaVersion && kibanaBranch !== 'master') { - url.searchParams.set('kibana.version', kibanaVersion); - } + setKibanaVersion(url); + const res = await fetchUrl(url.toString()); const searchResults = JSON.parse(res); if (searchResults.length) { From 2dece3d446b9f7233ac13810bc8e2ccb392189f7 Mon Sep 17 00:00:00 2001 From: Giorgos Bamparopoulos Date: Mon, 11 Oct 2021 09:39:49 +0100 Subject: [PATCH 31/74] Update APM queries development doc (#114268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add links to field references and GET requests to the examples * Add troubleshooting info for failed requests * Add data model and running examples section * Add GET requests for query examples * Add `metricset` possible values Co-authored-by: Søren Louv-Jansen * Add transaction based and metric based queries Co-authored-by: Søren Louv-Jansen --- x-pack/plugins/apm/dev_docs/apm_queries.md | 157 +++++++++++++++++---- 1 file changed, 126 insertions(+), 31 deletions(-) diff --git a/x-pack/plugins/apm/dev_docs/apm_queries.md b/x-pack/plugins/apm/dev_docs/apm_queries.md index 7ca28f2e273c..8508e5a173c8 100644 --- a/x-pack/plugins/apm/dev_docs/apm_queries.md +++ b/x-pack/plugins/apm/dev_docs/apm_queries.md @@ -1,3 +1,9 @@ +# Data model +Elastic APM agents capture different types of information from within their instrumented applications. These are known as events, and can be spans, transactions, errors, or metrics. You can find more information [here](https://www.elastic.co/guide/en/apm/get-started/current/apm-data-model.html). + +# Running examples +You can run the example queries on the [edge cluster](https://edge-oblt.elastic.dev/) or any another cluster that contains APM data. + # Transactions Transactions are stored in two different formats: @@ -34,6 +40,8 @@ A pre-aggregated document where `_doc_count` is the number of transaction events } ``` +You can find all the APM transaction fields [here](https://www.elastic.co/guide/en/apm/server/current/exported-fields-apm-transaction.html). + The decision to use aggregated transactions or not is determined in [`getSearchAggregatedTransactions`](https://github.com/elastic/kibana/blob/a2ac439f56313b7a3fc4708f54a4deebf2615136/x-pack/plugins/apm/server/lib/helpers/aggregated_transactions/index.ts#L53-L79) and then used to specify [the transaction index](https://github.com/elastic/kibana/blob/a2ac439f56313b7a3fc4708f54a4deebf2615136/x-pack/plugins/apm/server/lib/suggestions/get_suggestions.ts#L30-L32) and [the latency field](https://github.com/elastic/kibana/blob/a2ac439f56313b7a3fc4708f54a4deebf2615136/x-pack/plugins/apm/server/lib/alerts/chart_preview/get_transaction_duration.ts#L62-L65) ### Latency @@ -45,6 +53,7 @@ Noteworthy fields: `transaction.duration.us`, `transaction.duration.histogram` #### Transaction-based latency ```json +GET apm-*-transaction-*,traces-apm*/_search?terminate_after=1000 { "size": 0, "query": { @@ -61,6 +70,7 @@ Noteworthy fields: `transaction.duration.us`, `transaction.duration.histogram` #### Metric-based latency ```json +GET apm-*-metric-*,metrics-apm*/_search?terminate_after=1000 { "size": 0, "query": { @@ -85,14 +95,64 @@ Throughput is the number of transactions per minute. This can be calculated usin Noteworthy fields: None (based on `doc_count`) -```js +#### Transaction-based throughput + +```json +GET apm-*-transaction-*,traces-apm*/_search?terminate_after=1000 +{ + "size": 0, + "query": { + "bool": { + "filter": [{ "terms": { "processor.event": ["transaction"] } }] + } + }, + "aggs": { + "timeseries": { + "date_histogram": { + "field": "@timestamp", + "fixed_interval": "60s" + }, + "aggs": { + "throughput": { + "rate": { + "unit": "minute" + } + } + } + } + } +} +``` + + +#### Metric-based throughput + +```json +GET apm-*-metric-*,metrics-apm*/_search?terminate_after=1000 { "size": 0, "query": { - // same filters as for latency + "bool": { + "filter": [ + { "terms": { "processor.event": ["metric"] } }, + { "term": { "metricset.name": "transaction" } } + ] + } }, "aggs": { - "throughput": { "rate": { "unit": "minute" } } + "timeseries": { + "date_histogram": { + "field": "@timestamp", + "fixed_interval": "60s" + }, + "aggs": { + "throughput": { + "rate": { + "unit": "minute" + } + } + } + } } } ``` @@ -102,11 +162,41 @@ Noteworthy fields: None (based on `doc_count`) Failed transaction rate is the number of transactions with `event.outcome=failure` per minute. Noteworthy fields: `event.outcome` -```js +#### Transaction-based failed transaction rate + + ```json +GET apm-*-transaction-*,traces-apm*/_search?terminate_after=1000 { "size": 0, "query": { - // same filters as for latency + "bool": { + "filter": [{ "terms": { "processor.event": ["transaction"] } }] + } + }, + "aggs": { + "outcomes": { + "terms": { + "field": "event.outcome", + "include": ["failure", "success"] + } + } + } +} +``` + +#### Metric-based failed transaction rate + +```json +GET apm-*-metric-*,metrics-apm*/_search?terminate_after=1000 +{ + "size": 0, + "query": { + "bool": { + "filter": [ + { "terms": { "processor.event": ["metric"] } }, + { "term": { "metricset.name": "transaction" } } + ] + } }, "aggs": { "outcomes": { @@ -121,7 +211,7 @@ Noteworthy fields: `event.outcome` # System metrics -System metrics are captured periodically (every 60 seconds by default). +System metrics are captured periodically (every 60 seconds by default). You can find all the System Metrics fields [here](https://www.elastic.co/guide/en/apm/server/current/exported-fields-system.html). ### CPU @@ -146,6 +236,7 @@ Noteworthy fields: `system.cpu.total.norm.pct`, `system.process.cpu.total.norm.p #### Query ```json +GET apm-*-metric-*,metrics-apm*/_search?terminate_after=1000 { "size": 0, "query": { @@ -185,18 +276,17 @@ Noteworthy fields: `system.memory.actual.free`, `system.memory.total`, #### Query -```js +```json +GET apm-*-metric-*,metrics-apm*/_search?terminate_after=1000 { "size": 0, "query": { "bool": { "filter": [ { "terms": { "processor.event": ["metric"] }}, - { "terms": { "metricset.name": ["app"] }} - - // ensure the memory fields exists + { "terms": { "metricset.name": ["app"] }}, { "exists": { "field": "system.memory.actual.free" }}, - { "exists": { "field": "system.memory.total" }}, + { "exists": { "field": "system.memory.total" }} ] } }, @@ -213,7 +303,7 @@ Noteworthy fields: `system.memory.actual.free`, `system.memory.total`, } ``` -Above example is overly simplified. In reality [we do a bit more](https://github.com/elastic/kibana/blob/fe9b5332e157fd456f81aecfd4ffa78d9e511a66/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts#L51-L71) to properly calculate memory usage inside containers +The above example is overly simplified. In reality [we do a bit more](https://github.com/elastic/kibana/blob/fe9b5332e157fd456f81aecfd4ffa78d9e511a66/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts#L51-L71) to properly calculate memory usage inside containers. Please note that an [Exists Query](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-query.html) is used in the filter context in the query to ensure that the memory fields exist. @@ -268,6 +358,7 @@ Noteworthy fields: `transaction.name`, `transaction.type`, `span.type`, `span.su #### Query ```json +GET apm-*-metric-*,metrics-apm*/_search?terminate_after=1000 { "size": 0, "query": { @@ -330,6 +421,7 @@ A pre-aggregated document with 73 span requests from opbeans-ruby to elasticsear The latency between a service and an (external) endpoint ```json +GET apm-*-metric-*,metrics-apm*/_search?terminate_after=1000 { "size": 0, "query": { @@ -360,6 +452,7 @@ Captures the number of requests made from a service to an (external) endpoint #### Query ```json +GET apm-*-metric-*,metrics-apm*/_search?terminate_after=1000 { "size": 0, "query": { @@ -372,10 +465,17 @@ Captures the number of requests made from a service to an (external) endpoint } }, "aggs": { - "throughput": { - "rate": { - "field": "span.destination.service.response_time.count", - "unit": "minute" + "timeseries": { + "date_histogram": { + "field": "@timestamp", + "fixed_interval": "60s" + }, + "aggs": { + "throughput": { + "rate": { + "unit": "minute" + } + } } } } @@ -390,27 +490,17 @@ Most Elasticsearch queries will need to have one or more filters. There are a co - stability: Running an aggregation on unrelated documents could cause the entire query to fail - performance: limiting the number of documents will make the query faster -```js +```json +GET apm-*-metric-*,metrics-apm*/_search?terminate_after=1000 { "query": { "bool": { "filter": [ - // service name { "term": { "service.name": "opbeans-go" }}, - - // service environment - { "term": { "service.environment": "testing" }} - - // transaction type - { "term": { "transaction.type": "request" }} - - // event type (possible values : transaction, span, metric, error) + { "term": { "service.environment": "testing" }}, + { "term": { "transaction.type": "request" }}, { "terms": { "processor.event": ["metric"] }}, - - // metric set is a subtype of `processor.event: metric` { "terms": { "metricset.name": ["transaction"] }}, - - // time range { "range": { "@timestamp": { @@ -422,5 +512,10 @@ Most Elasticsearch queries will need to have one or more filters. There are a co } ] } - }, + } +} ``` + +Possible values for `processor.event` are: `transaction`, `span`, `metric`, `error`. + +`metricset` is a subtype of `processor.event: metric`. Possible values are: `transaction`, `span_breakdown`, `transaction_breakdown`, `app`, `service_destination`, `agent_config` From 41c813bac438530a579b7896f6848dd56089145a Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 11 Oct 2021 11:56:36 +0300 Subject: [PATCH 32/74] [Visualization] Get rid of saved object loader and use savedObjectClient resolve (#113121) * First step: create saved_visualize_utils, starting use new get/save methods * Use new util methods in embeddable * move findListItem in utils * some clean up * clean up * Some fixes * Fix saved object tags * Some types fixes * Fix unit tests * Clean up code * Add unit tests for new utils * Fix lint * Fix tagging * Add unit tests * Some fixes * Clean up code * Fix lint * Fix types * put new methods in start contract * Fix imports * Fix lint * Fix comments * Fix lint * Fix CI * use local url instead of full path * Fix unit test * Some clean up * Fix nits * fix types Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/plugins/visualizations/kibana.json | 2 +- .../create_vis_embeddable_from_object.ts | 20 +- .../public/embeddable/visualize_embeddable.ts | 15 +- .../visualize_embeddable_factory.tsx | 63 ++- src/plugins/visualizations/public/index.ts | 2 + src/plugins/visualizations/public/mocks.ts | 7 + src/plugins/visualizations/public/plugin.ts | 51 +- .../public/saved_visualizations/_saved_vis.ts | 42 +- .../saved_visualizations.ts | 1 + src/plugins/visualizations/public/types.ts | 35 +- .../controls_references.ts | 0 .../saved_visualization_references/index.ts | 0 .../saved_visualization_references.test.ts | 0 .../saved_visualization_references.ts | 0 .../timeseries_references.ts | 0 .../utils/saved_visualize_utils.test.ts | 507 ++++++++++++++++++ .../public/utils/saved_visualize_utils.ts | 403 ++++++++++++++ src/plugins/visualizations/tsconfig.json | 1 + src/plugins/visualize/kibana.json | 3 +- .../visualize_editor_common.test.tsx | 112 ++++ .../components/visualize_editor_common.tsx | 56 +- .../components/visualize_listing.tsx | 9 +- .../visualize/public/application/types.ts | 3 +- .../application/utils/get_top_nav_config.tsx | 31 +- .../utils/get_visualization_instance.test.ts | 27 +- .../utils/get_visualization_instance.ts | 9 +- .../public/application/utils/mocks.ts | 1 - .../utils/use/use_saved_vis_instance.test.ts | 5 - .../utils/use/use_saved_vis_instance.ts | 3 - src/plugins/visualize/public/plugin.ts | 4 +- src/plugins/visualize/tsconfig.json | 3 +- 31 files changed, 1291 insertions(+), 124 deletions(-) rename src/plugins/visualizations/public/{saved_visualizations => utils}/saved_visualization_references/controls_references.ts (100%) rename src/plugins/visualizations/public/{saved_visualizations => utils}/saved_visualization_references/index.ts (100%) rename src/plugins/visualizations/public/{saved_visualizations => utils}/saved_visualization_references/saved_visualization_references.test.ts (100%) rename src/plugins/visualizations/public/{saved_visualizations => utils}/saved_visualization_references/saved_visualization_references.ts (100%) rename src/plugins/visualizations/public/{saved_visualizations => utils}/saved_visualization_references/timeseries_references.ts (100%) create mode 100644 src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts create mode 100644 src/plugins/visualizations/public/utils/saved_visualize_utils.ts create mode 100644 src/plugins/visualize/public/application/components/visualize_editor_common.test.tsx diff --git a/src/plugins/visualizations/kibana.json b/src/plugins/visualizations/kibana.json index 0afbec24c7c3..32430c9d4e4f 100644 --- a/src/plugins/visualizations/kibana.json +++ b/src/plugins/visualizations/kibana.json @@ -11,7 +11,7 @@ "inspector", "savedObjects" ], - "optionalPlugins": ["usageCollection"], + "optionalPlugins": ["usageCollection", "spaces", "savedObjectsTaggingOss"], "requiredBundles": ["kibanaUtils", "discover"], "extraPublicDirs": ["common/constants", "common/prepare_log_table", "common/expression_functions"], "owner": { diff --git a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts index cfa871f17b0e..c72b8618dc19 100644 --- a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts +++ b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts @@ -20,16 +20,10 @@ import { AttributeService, } from '../../../../plugins/embeddable/public'; import { DisabledLabEmbeddable } from './disabled_lab_embeddable'; -import { - getSavedVisualizationsLoader, - getUISettings, - getHttp, - getTimeFilter, - getCapabilities, -} from '../services'; +import { getUISettings, getHttp, getTimeFilter, getCapabilities } from '../services'; +import { urlFor } from '../utils/saved_visualize_utils'; import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory'; import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants'; -import { SavedVisualizationsLoader } from '../saved_visualizations'; import { IndexPattern } from '../../../data/public'; import { createVisualizeEmbeddableAsync } from './visualize_embeddable_async'; @@ -38,7 +32,6 @@ export const createVisEmbeddableFromObject = async ( vis: Vis, input: Partial & { id: string }, - savedVisualizationsLoader?: SavedVisualizationsLoader, attributeService?: AttributeService< VisualizeSavedObjectAttributes, VisualizeByValueInput, @@ -46,16 +39,12 @@ export const createVisEmbeddableFromObject = >, parent?: IContainer ): Promise => { - const savedVisualizations = getSavedVisualizationsLoader(); - try { const visId = vis.id as string; - const editPath = visId ? savedVisualizations.urlFor(visId) : '#/edit_by_value'; + const editPath = visId ? urlFor(visId) : '#/edit_by_value'; - const editUrl = visId - ? getHttp().basePath.prepend(`/app/visualize${savedVisualizations.urlFor(visId)}`) - : ''; + const editUrl = visId ? getHttp().basePath.prepend(`/app/visualize${urlFor(visId)}`) : ''; const isLabsEnabled = getUISettings().get(VISUALIZE_ENABLE_LABS_SETTING); if (!isLabsEnabled && vis.type.stage === 'experimental') { @@ -87,7 +76,6 @@ export const createVisEmbeddableFromObject = }, input, attributeService, - savedVisualizationsLoader, parent ); } catch (e) { diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index f5a7349b633e..0c7d58453db6 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -39,7 +39,7 @@ import { getExpressions, getUiActions } from '../services'; import { VIS_EVENT_TO_TRIGGER } from './events'; import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory'; import { SavedObjectAttributes } from '../../../../core/types'; -import { SavedVisualizationsLoader } from '../saved_visualizations'; +import { getSavedVisualization } from '../utils/saved_visualize_utils'; import { VisSavedObject } from '../types'; import { toExpressionAst } from './to_ast'; @@ -108,7 +108,6 @@ export class VisualizeEmbeddable VisualizeByValueInput, VisualizeByReferenceInput >; - private savedVisualizationsLoader?: SavedVisualizationsLoader; constructor( timefilter: TimefilterContract, @@ -119,7 +118,6 @@ export class VisualizeEmbeddable VisualizeByValueInput, VisualizeByReferenceInput >, - savedVisualizationsLoader?: SavedVisualizationsLoader, parent?: IContainer ) { super( @@ -144,7 +142,6 @@ export class VisualizeEmbeddable this.vis.uiState.on('change', this.uiStateChangeHandler); this.vis.uiState.on('reload', this.reload); this.attributeService = attributeService; - this.savedVisualizationsLoader = savedVisualizationsLoader; if (this.attributeService) { const isByValue = !this.inputIsRefType(initialInput); @@ -455,7 +452,15 @@ export class VisualizeEmbeddable }; getInputAsRefType = async (): Promise => { - const savedVis = await this.savedVisualizationsLoader?.get({}); + const { savedObjectsClient, data, spaces, savedObjectsTaggingOss } = await this.deps.start() + .plugins; + const savedVis = await getSavedVisualization({ + savedObjectsClient, + search: data.search, + dataViews: data.dataViews, + spaces, + savedObjectsTagging: savedObjectsTaggingOss?.getTaggingApi(), + }); if (!savedVis) { throw new Error('Error creating a saved vis object'); } diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index 9e22b33bdee9..9b1af5bea3fc 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -33,20 +33,20 @@ import type { import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; import type { SerializedVis, Vis } from '../vis'; import { createVisAsync } from '../vis_async'; -import { - getCapabilities, - getTypes, - getUISettings, - getSavedVisualizationsLoader, -} from '../services'; +import { getCapabilities, getTypes, getUISettings } from '../services'; import { showNewVisModal } from '../wizard'; -import { convertToSerializedVis } from '../saved_visualizations/_saved_vis'; +import { + convertToSerializedVis, + getSavedVisualization, + saveVisualization, + getFullPath, +} from '../utils/saved_visualize_utils'; import { extractControlsReferences, extractTimeSeriesReferences, injectTimeSeriesReferences, injectControlsReferences, -} from '../saved_visualizations/saved_visualization_references'; +} from '../utils/saved_visualization_references'; import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object'; import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants'; import { checkForDuplicateTitle } from '../../../saved_objects/public'; @@ -59,7 +59,15 @@ interface VisualizationAttributes extends SavedObjectAttributes { export interface VisualizeEmbeddableFactoryDeps { start: StartServicesGetter< - Pick + Pick< + VisualizationsStartDeps, + | 'inspector' + | 'embeddable' + | 'savedObjectsClient' + | 'data' + | 'savedObjectsTaggingOss' + | 'spaces' + > >; } @@ -147,17 +155,36 @@ export class VisualizeEmbeddableFactory input: Partial & { id: string }, parent?: IContainer ): Promise { - const savedVisualizations = getSavedVisualizationsLoader(); + const startDeps = await this.deps.start(); try { - const savedObject = await savedVisualizations.get(savedObjectId); + const savedObject = await getSavedVisualization( + { + savedObjectsClient: startDeps.core.savedObjects.client, + search: startDeps.plugins.data.search, + dataViews: startDeps.plugins.data.dataViews, + spaces: startDeps.plugins.spaces, + savedObjectsTagging: startDeps.plugins.savedObjectsTaggingOss?.getTaggingApi(), + }, + savedObjectId + ); + + if (savedObject.sharingSavedObjectProps?.outcome === 'conflict') { + return new ErrorEmbeddable( + i18n.translate('visualizations.embeddable.legacyURLConflict.errorMessage', { + defaultMessage: `This visualization has the same URL as a legacy alias. Disable the alias to resolve this error : {json}`, + values: { json: savedObject.sharingSavedObjectProps?.errorJSON }, + }), + input, + parent + ); + } const visState = convertToSerializedVis(savedObject); const vis = await createVisAsync(savedObject.visState.type, visState); return createVisEmbeddableFromObject(this.deps)( vis, input, - savedVisualizations, await this.getAttributeService(), parent ); @@ -173,11 +200,9 @@ export class VisualizeEmbeddableFactory if (input.savedVis) { const visState = input.savedVis; const vis = await createVisAsync(visState.type, visState); - const savedVisualizations = getSavedVisualizationsLoader(); return createVisEmbeddableFromObject(this.deps)( vis, input, - savedVisualizations, await this.getAttributeService(), parent ); @@ -201,9 +226,9 @@ export class VisualizeEmbeddableFactory confirmOverwrite: false, returnToOrigin: true, isTitleDuplicateConfirmed: true, + copyOnSave: false, }; savedVis.title = title; - savedVis.copyOnSave = false; savedVis.description = ''; savedVis.searchSourceFields = visObj?.data.searchSource?.getSerializedFields(); const serializedVis = (visObj as unknown as Vis).serialize(); @@ -217,7 +242,12 @@ export class VisualizeEmbeddableFactory if (visObj) { savedVis.uiStateJSON = visObj?.uiState.toString(); } - const id = await savedVis.save(saveOptions); + const { core, plugins } = await this.deps.start(); + const id = await saveVisualization(savedVis, saveOptions, { + savedObjectsClient: core.savedObjects.client, + overlays: core.overlays, + savedObjectsTagging: plugins.savedObjectsTaggingOss?.getTaggingApi(), + }); if (!id || id === '') { throw new Error( i18n.translate('visualizations.savingVisualizationFailed.errorMsg', { @@ -225,6 +255,7 @@ export class VisualizeEmbeddableFactory }) ); } + core.chrome.recentlyAccessed.add(getFullPath(id), savedVis.title, String(id)); return { id }; } catch (error) { throw error; diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts index 0886f230d101..e6ea3cd48955 100644 --- a/src/plugins/visualizations/public/index.ts +++ b/src/plugins/visualizations/public/index.ts @@ -38,6 +38,7 @@ export { VisToExpressionAst, VisToExpressionAstParams, VisEditorOptionsProps, + GetVisOptions, } from './types'; export { VisualizationListItem, VisualizationStage } from './vis_types/vis_type_alias_registry'; export { VISUALIZE_ENABLE_LABS_SETTING } from '../common/constants'; @@ -49,3 +50,4 @@ export { FakeParams, HistogramParams, } from '../common/expression_functions/xy_dimension'; +export { urlFor, getFullPath } from './utils/saved_visualize_utils'; diff --git a/src/plugins/visualizations/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts index 901593626a94..9b2d6bfe25b3 100644 --- a/src/plugins/visualizations/public/mocks.ts +++ b/src/plugins/visualizations/public/mocks.ts @@ -10,6 +10,7 @@ import { PluginInitializerContext } from '../../../core/public'; import { Schema, VisualizationsSetup, VisualizationsStart } from './'; import { Schemas } from './vis_types'; import { VisualizationsPlugin } from './plugin'; +import { spacesPluginMock } from '../../../../x-pack/plugins/spaces/public/mocks'; import { coreMock, applicationServiceMock } from '../../../core/public/mocks'; import { embeddablePluginMock } from '../../../plugins/embeddable/public/mocks'; import { expressionsPluginMock } from '../../../plugins/expressions/public/mocks'; @@ -18,6 +19,7 @@ import { usageCollectionPluginMock } from '../../../plugins/usage_collection/pub import { uiActionsPluginMock } from '../../../plugins/ui_actions/public/mocks'; import { inspectorPluginMock } from '../../../plugins/inspector/public/mocks'; import { savedObjectsPluginMock } from '../../../plugins/saved_objects/public/mocks'; +import { savedObjectTaggingOssPluginMock } from '../../saved_objects_tagging_oss/public/mocks'; const createSetupContract = (): VisualizationsSetup => ({ createBaseVisualization: jest.fn(), @@ -34,6 +36,9 @@ const createStartContract = (): VisualizationsStart => ({ savedVisualizationsLoader: { get: jest.fn(), } as any, + getSavedVisualization: jest.fn(), + saveVisualization: jest.fn(), + findListItems: jest.fn(), showNewVisModal: jest.fn(), createVis: jest.fn(), convertFromSerializedVis: jest.fn(), @@ -61,9 +66,11 @@ const createInstance = async () => { uiActions: uiActionsPluginMock.createStartContract(), application: applicationServiceMock.createStartContract(), embeddable: embeddablePluginMock.createStartContract(), + spaces: spacesPluginMock.createStartContract(), getAttributeService: jest.fn(), savedObjectsClient: coreMock.createStart().savedObjects.client, savedObjects: savedObjectsPluginMock.createStartContract(), + savedObjectsTaggingOss: savedObjectTaggingOssPluginMock.createStart(), }); return { diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index ee3e914aa4bc..47f544ce2f5d 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -5,6 +5,8 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + +import type { SavedObjectsFindOptionsReference } from 'kibana/public'; import { setUISettings, setTypes, @@ -30,6 +32,7 @@ import { VisualizeEmbeddableFactory, createVisEmbeddableFromObject, } from './embeddable'; +import type { SpacesPluginStart } from '../../../../x-pack/plugins/spaces/public'; import { TypesService } from './vis_types/types_service'; import { range as rangeExpressionFunction } from '../common/expression_functions/range'; import { visDimension as visDimensionExpressionFunction } from '../common/expression_functions/vis_dimension'; @@ -43,7 +46,10 @@ import { showNewVisModal } from './wizard'; import { convertFromSerializedVis, convertToSerializedVis, -} from './saved_visualizations/_saved_vis'; + getSavedVisualization, + saveVisualization, + findListItems, +} from './utils/saved_visualize_utils'; import { createSavedSearchesLoader } from '../../discover/public'; @@ -66,7 +72,9 @@ import type { import type { DataPublicPluginSetup, DataPublicPluginStart } from '../../../plugins/data/public'; import type { ExpressionsSetup, ExpressionsStart } from '../../expressions/public'; import type { EmbeddableSetup, EmbeddableStart } from '../../embeddable/public'; +import type { SavedObjectTaggingOssPluginStart } from '../../saved_objects_tagging_oss/public'; import { createVisAsync } from './vis_async'; +import type { VisSavedObject, SaveVisOptions, GetVisOptions } from './types'; /** * Interface for this plugin's returned setup/start contracts. @@ -82,6 +90,13 @@ export interface VisualizationsStart extends TypesStart { convertToSerializedVis: typeof convertToSerializedVis; convertFromSerializedVis: typeof convertFromSerializedVis; showNewVisModal: typeof showNewVisModal; + getSavedVisualization: (opts?: GetVisOptions | string) => Promise; + saveVisualization: (savedVis: VisSavedObject, saveOptions: SaveVisOptions) => Promise; + findListItems: ( + searchTerm: string, + listingLimit: number, + references?: SavedObjectsFindOptionsReference[] + ) => Promise<{ hits: Array>; total: number }>; __LEGACY: { createVisEmbeddableFromObject: ReturnType }; } @@ -103,6 +118,8 @@ export interface VisualizationsStartDeps { getAttributeService: EmbeddableStart['getAttributeService']; savedObjects: SavedObjectsStart; savedObjectsClient: SavedObjectsClientContract; + spaces?: SpacesPluginStart; + savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart; } /** @@ -149,7 +166,15 @@ export class VisualizationsPlugin public start( core: CoreStart, - { data, expressions, uiActions, embeddable, savedObjects }: VisualizationsStartDeps + { + data, + expressions, + uiActions, + embeddable, + savedObjects, + spaces, + savedObjectsTaggingOss, + }: VisualizationsStartDeps ): VisualizationsStart { const types = this.types.start(); setTypes(types); @@ -181,6 +206,28 @@ export class VisualizationsPlugin return { ...types, showNewVisModal, + getSavedVisualization: async (opts) => { + return getSavedVisualization( + { + search: data.search, + savedObjectsClient: core.savedObjects.client, + dataViews: data.dataViews, + spaces, + savedObjectsTagging: savedObjectsTaggingOss?.getTaggingApi(), + }, + opts + ); + }, + saveVisualization: async (savedVis, saveOptions) => { + return saveVisualization(savedVis, saveOptions, { + savedObjectsClient: core.savedObjects.client, + overlays: core.overlays, + savedObjectsTagging: savedObjectsTaggingOss?.getTaggingApi(), + }); + }, + findListItems: async (searchTerm, listingLimit, references) => { + return findListItems(core.savedObjects.client, types, searchTerm, listingLimit, references); + }, /** * creates new instance of Vis * @param {IndexPattern} indexPattern - index pattern to use diff --git a/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts b/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts index fb6c99ac8ef0..fbd8e414c273 100644 --- a/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts +++ b/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts @@ -16,11 +16,11 @@ import type { SavedObjectsStart, SavedObject } from '../../../../plugins/saved_objects/public'; // @ts-ignore import { updateOldState } from '../legacy/vis_update_state'; -import { extractReferences, injectReferences } from './saved_visualization_references'; +import { extractReferences, injectReferences } from '../utils/saved_visualization_references'; import { createSavedSearchesLoader } from '../../../discover/public'; import type { SavedObjectsClientContract } from '../../../../core/public'; import type { IndexPatternsContract } from '../../../../plugins/data/public'; -import type { ISavedVis, SerializedVis } from '../types'; +import type { ISavedVis } from '../types'; export interface SavedVisServices { savedObjectsClient: SavedObjectsClientContract; @@ -28,43 +28,7 @@ export interface SavedVisServices { indexPatterns: IndexPatternsContract; } -export const convertToSerializedVis = (savedVis: ISavedVis): SerializedVis => { - const { id, title, description, visState, uiStateJSON, searchSourceFields } = savedVis; - - const aggs = searchSourceFields && searchSourceFields.index ? visState.aggs || [] : visState.aggs; - - return { - id, - title, - type: visState.type, - description, - params: visState.params, - uiState: JSON.parse(uiStateJSON || '{}'), - data: { - aggs, - searchSource: searchSourceFields!, - savedSearchId: savedVis.savedSearchId, - }, - }; -}; - -export const convertFromSerializedVis = (vis: SerializedVis): ISavedVis => { - return { - id: vis.id, - title: vis.title, - description: vis.description, - visState: { - title: vis.title, - type: vis.type, - aggs: vis.data.aggs, - params: vis.params, - }, - uiStateJSON: JSON.stringify(vis.uiState), - searchSourceFields: vis.data.searchSource, - savedSearchId: vis.data.savedSearchId, - }; -}; - +/** @deprecated **/ export function createSavedVisClass(services: SavedVisServices) { const savedSearch = createSavedSearchesLoader(services); diff --git a/src/plugins/visualizations/public/saved_visualizations/saved_visualizations.ts b/src/plugins/visualizations/public/saved_visualizations/saved_visualizations.ts index d07d28b393dc..cec65b8f988b 100644 --- a/src/plugins/visualizations/public/saved_visualizations/saved_visualizations.ts +++ b/src/plugins/visualizations/public/saved_visualizations/saved_visualizations.ts @@ -22,6 +22,7 @@ export interface FindListItemsOptions { references?: SavedObjectsFindOptionsReference[]; } +/** @deprecated **/ export function createSavedVisLoader(services: SavedVisServicesWithVisualizations) { const { savedObjectsClient, visualizationTypes } = services; diff --git a/src/plugins/visualizations/public/types.ts b/src/plugins/visualizations/public/types.ts index d68599c0724f..5be8f49e9cdc 100644 --- a/src/plugins/visualizations/public/types.ts +++ b/src/plugins/visualizations/public/types.ts @@ -6,13 +6,14 @@ * Side Public License, v 1. */ -import { SavedObject } from '../../../plugins/saved_objects/public'; +import type { SavedObjectsMigrationVersion } from 'kibana/public'; import { IAggConfigs, SearchSourceFields, TimefilterContract, AggConfigSerialized, } from '../../../plugins/data/public'; +import type { ISearchSource } from '../../data/common'; import { ExpressionAstExpression } from '../../expressions/public'; import type { SerializedVis, Vis } from './vis'; @@ -36,9 +37,39 @@ export interface ISavedVis { uiStateJSON?: string; savedSearchRefName?: string; savedSearchId?: string; + sharingSavedObjectProps?: { + outcome?: 'aliasMatch' | 'exactMatch' | 'conflict'; + aliasTargetId?: string; + errorJSON?: string; + }; } -export interface VisSavedObject extends SavedObject, ISavedVis {} +export interface VisSavedObject extends ISavedVis { + lastSavedTitle: string; + getEsType: () => string; + getDisplayName?: () => string; + displayName: string; + migrationVersion?: SavedObjectsMigrationVersion; + searchSource?: ISearchSource; + version?: string; + tags?: string[]; +} + +export interface SaveVisOptions { + confirmOverwrite?: boolean; + isTitleDuplicateConfirmed?: boolean; + onTitleDuplicate?: () => void; + copyOnSave?: boolean; +} + +export interface GetVisOptions { + id?: string; + searchSource?: boolean; + migrationVersion?: SavedObjectsMigrationVersion; + savedSearchId?: string; + type?: string; + indexPattern?: string; +} export interface VisToExpressionAstParams { timefilter: TimefilterContract; diff --git a/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/controls_references.ts b/src/plugins/visualizations/public/utils/saved_visualization_references/controls_references.ts similarity index 100% rename from src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/controls_references.ts rename to src/plugins/visualizations/public/utils/saved_visualization_references/controls_references.ts diff --git a/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/index.ts b/src/plugins/visualizations/public/utils/saved_visualization_references/index.ts similarity index 100% rename from src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/index.ts rename to src/plugins/visualizations/public/utils/saved_visualization_references/index.ts diff --git a/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/saved_visualization_references.test.ts b/src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.test.ts similarity index 100% rename from src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/saved_visualization_references.test.ts rename to src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.test.ts diff --git a/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/saved_visualization_references.ts b/src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.ts similarity index 100% rename from src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/saved_visualization_references.ts rename to src/plugins/visualizations/public/utils/saved_visualization_references/saved_visualization_references.ts diff --git a/src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/timeseries_references.ts b/src/plugins/visualizations/public/utils/saved_visualization_references/timeseries_references.ts similarity index 100% rename from src/plugins/visualizations/public/saved_visualizations/saved_visualization_references/timeseries_references.ts rename to src/plugins/visualizations/public/utils/saved_visualization_references/timeseries_references.ts diff --git a/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts b/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts new file mode 100644 index 000000000000..83b16026de39 --- /dev/null +++ b/src/plugins/visualizations/public/utils/saved_visualize_utils.test.ts @@ -0,0 +1,507 @@ +/* + * 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 { ISearchSource } from '../../../data/common'; +import type { SpacesPluginStart } from '../../../../../x-pack/plugins/spaces/public'; +import type { SavedObjectsTaggingApi } from '../../../saved_objects_tagging_oss/public'; +import { coreMock } from '../../../../core/public/mocks'; +import { dataPluginMock } from '../../../data/public/mocks'; +import { SavedObjectsClientContract } from '../../../../core/public'; +import { + findListItems, + getSavedVisualization, + saveVisualization, + SAVED_VIS_TYPE, +} from './saved_visualize_utils'; +import { VisTypeAlias, TypesStart } from '../vis_types'; +import type { VisSavedObject } from '../types'; + +let visTypes = [] as VisTypeAlias[]; +const mockGetAliases = jest.fn(() => visTypes); +const mockGetTypes = jest.fn((type: string) => type) as unknown as TypesStart['get']; +jest.mock('../services', () => ({ + getSpaces: jest.fn(() => ({ + getActiveSpace: () => ({ + id: 'test', + }), + })), +})); + +const mockParseSearchSourceJSON = jest.fn(); +const mockInjectSearchSourceReferences = jest.fn(); +const mockExtractSearchSourceReferences = jest.fn((...args) => [{}, []]); + +jest.mock('../../../../plugins/data/public', () => ({ + extractSearchSourceReferences: jest.fn((...args) => mockExtractSearchSourceReferences(...args)), + injectSearchSourceReferences: jest.fn((...args) => mockInjectSearchSourceReferences(...args)), + parseSearchSourceJSON: jest.fn((...args) => mockParseSearchSourceJSON(...args)), +})); + +const mockInjectReferences = jest.fn(); +const mockExtractReferences = jest.fn(() => ({ references: [], attributes: {} })); +jest.mock('./saved_visualization_references', () => ({ + injectReferences: jest.fn((...args) => mockInjectReferences(...args)), + extractReferences: jest.fn(() => mockExtractReferences()), +})); + +let isTitleDuplicateConfirmed = true; +const mockCheckForDuplicateTitle = jest.fn(() => { + if (!isTitleDuplicateConfirmed) { + throw new Error(); + } +}); +const mockSaveWithConfirmation = jest.fn(() => ({ id: 'test-after-confirm' })); +jest.mock('../../../../plugins/saved_objects/public', () => ({ + checkForDuplicateTitle: jest.fn(() => mockCheckForDuplicateTitle()), + saveWithConfirmation: jest.fn(() => mockSaveWithConfirmation()), + isErrorNonFatal: jest.fn(() => true), +})); + +describe('saved_visualize_utils', () => { + const { overlays, savedObjects } = coreMock.createStart(); + const savedObjectsClient = savedObjects.client as jest.Mocked; + (savedObjectsClient.resolve as jest.Mock).mockImplementation(() => ({ + saved_object: { + references: [ + { + id: 'test', + type: 'index-pattern', + }, + ], + attributes: { + visState: JSON.stringify({ type: 'area' }), + kibanaSavedObjectMeta: { + searchSourceJSON: '{filter: []}', + }, + }, + _version: '1', + }, + outcome: 'exact', + alias_target_id: null, + })); + (savedObjectsClient.create as jest.Mock).mockImplementation(() => ({ id: 'test' })); + const { dataViews, search } = dataPluginMock.createStartContract(); + + describe('getSavedVisualization', () => { + beforeEach(() => { + mockParseSearchSourceJSON.mockClear(); + mockInjectSearchSourceReferences.mockClear(); + mockInjectReferences.mockClear(); + }); + it('should return object with defaults if was not provided id', async () => { + const savedVis = await getSavedVisualization({ + savedObjectsClient, + search, + dataViews, + spaces: Promise.resolve({ + getActiveSpace: () => ({ + id: 'test', + }), + }) as unknown as SpacesPluginStart, + }); + expect(savedVis).toBeDefined(); + expect(savedVis.title).toBe(''); + expect(savedVis.displayName).toBe(SAVED_VIS_TYPE); + }); + + it('should create search source if saved object has searchSourceJSON', async () => { + await getSavedVisualization( + { + savedObjectsClient, + search, + dataViews, + spaces: Promise.resolve({ + getActiveSpace: () => ({ + id: 'test', + }), + }) as unknown as SpacesPluginStart, + }, + { id: 'test', searchSource: true } + ); + expect(mockParseSearchSourceJSON).toHaveBeenCalledWith('{filter: []}'); + expect(mockInjectSearchSourceReferences).toHaveBeenCalled(); + expect(search.searchSource.create).toHaveBeenCalled(); + }); + + it('should inject references if saved object has references', async () => { + await getSavedVisualization( + { + savedObjectsClient, + search, + dataViews, + spaces: Promise.resolve({ + getActiveSpace: () => ({ + id: 'test', + }), + }) as unknown as SpacesPluginStart, + }, + { id: 'test', searchSource: true } + ); + expect(mockInjectReferences.mock.calls[0][1]).toEqual([ + { + id: 'test', + type: 'index-pattern', + }, + ]); + }); + + it('should call getTagIdsFromReferences if we provide savedObjectsTagging service', async () => { + const mockGetTagIdsFromReferences = jest.fn(() => ['test']); + await getSavedVisualization( + { + savedObjectsClient, + search, + dataViews, + spaces: Promise.resolve({ + getActiveSpace: () => ({ + id: 'test', + }), + }) as unknown as SpacesPluginStart, + savedObjectsTagging: { + ui: { + getTagIdsFromReferences: mockGetTagIdsFromReferences, + }, + } as unknown as SavedObjectsTaggingApi, + }, + { id: 'test', searchSource: true } + ); + expect(mockGetTagIdsFromReferences).toHaveBeenCalled(); + }); + }); + + describe('saveVisualization', () => { + let vis: VisSavedObject; + beforeEach(() => { + mockExtractSearchSourceReferences.mockClear(); + mockExtractReferences.mockClear(); + mockSaveWithConfirmation.mockClear(); + savedObjectsClient.create.mockClear(); + vis = { + visState: { + type: 'area', + }, + title: 'test', + uiStateJSON: '{}', + version: '1', + __tags: [], + lastSavedTitle: 'test', + displayName: 'test', + getEsType: () => 'vis', + } as unknown as VisSavedObject; + }); + + it('should return id after save', async () => { + const savedVisId = await saveVisualization(vis, {}, { savedObjectsClient, overlays }); + expect(savedObjectsClient.create).toHaveBeenCalled(); + expect(mockExtractReferences).toHaveBeenCalled(); + expect(savedVisId).toBe('test'); + }); + + it('should call extractSearchSourceReferences if we new vis has searchSourceFields', async () => { + vis.searchSourceFields = { fields: [] }; + await saveVisualization(vis, {}, { savedObjectsClient, overlays }); + expect(mockExtractSearchSourceReferences).toHaveBeenCalledWith(vis.searchSourceFields); + }); + + it('should serialize searchSource', async () => { + vis.searchSource = { + serialize: jest.fn(() => ({ searchSourceJSON: '{}', references: [] })), + } as unknown as ISearchSource; + await saveVisualization(vis, {}, { savedObjectsClient, overlays }); + expect(vis.searchSource?.serialize).toHaveBeenCalled(); + }); + + it('should call updateTagsReferences if we provide savedObjectsTagging service', async () => { + const mockUpdateTagsReferences = jest.fn(() => []); + await saveVisualization( + vis, + {}, + { + savedObjectsClient, + overlays, + savedObjectsTagging: { + ui: { + updateTagsReferences: mockUpdateTagsReferences, + }, + } as unknown as SavedObjectsTaggingApi, + } + ); + expect(mockUpdateTagsReferences).toHaveBeenCalled(); + }); + + describe('confirmOverwrite', () => { + it('as false we should not call saveWithConfirmation and just do create', async () => { + const savedVisId = await saveVisualization( + vis, + { confirmOverwrite: false }, + { savedObjectsClient, overlays } + ); + expect(savedObjectsClient.create).toHaveBeenCalled(); + expect(mockExtractReferences).toHaveBeenCalled(); + expect(mockSaveWithConfirmation).not.toHaveBeenCalled(); + expect(savedVisId).toBe('test'); + }); + + it('as true we should call saveWithConfirmation', async () => { + const savedVisId = await saveVisualization( + vis, + { confirmOverwrite: true }, + { savedObjectsClient, overlays } + ); + expect(savedObjectsClient.create).not.toHaveBeenCalled(); + expect(mockSaveWithConfirmation).toHaveBeenCalled(); + expect(savedVisId).toBe('test-after-confirm'); + }); + }); + + describe('isTitleDuplicateConfirmed', () => { + it('as false we should not save vis with duplicated title', async () => { + isTitleDuplicateConfirmed = false; + const savedVisId = await saveVisualization( + vis, + { isTitleDuplicateConfirmed }, + { savedObjectsClient, overlays } + ); + expect(savedObjectsClient.create).not.toHaveBeenCalled(); + expect(mockSaveWithConfirmation).not.toHaveBeenCalled(); + expect(mockCheckForDuplicateTitle).toHaveBeenCalled(); + expect(savedVisId).toBe(''); + expect(vis.id).toBeUndefined(); + }); + + it('as true we should save vis with duplicated title', async () => { + isTitleDuplicateConfirmed = true; + const savedVisId = await saveVisualization( + vis, + { isTitleDuplicateConfirmed }, + { savedObjectsClient, overlays } + ); + expect(mockCheckForDuplicateTitle).toHaveBeenCalled(); + expect(savedObjectsClient.create).toHaveBeenCalled(); + expect(savedVisId).toBe('test'); + expect(vis.id).toBe('test'); + }); + }); + }); + + describe('findListItems', () => { + function testProps() { + (savedObjectsClient.find as jest.Mock).mockImplementation(() => ({ + total: 0, + savedObjects: [], + })); + return { + savedObjectsClient, + search: '', + size: 10, + }; + } + + beforeEach(() => { + savedObjectsClient.find.mockClear(); + }); + + it('searches visualization title and description', async () => { + const props = testProps(); + const { find } = props.savedObjectsClient; + await findListItems( + props.savedObjectsClient, + { get: mockGetTypes, getAliases: mockGetAliases }, + props.search, + props.size + ); + expect(find.mock.calls).toMatchObject([ + [ + { + type: ['visualization'], + searchFields: ['title^3', 'description'], + }, + ], + ]); + }); + + it('searches searchFields and types specified by app extensions', async () => { + const props = testProps(); + visTypes = [ + { + appExtensions: { + visualizations: { + docTypes: ['bazdoc', 'etc'], + searchFields: ['baz', 'bing'], + }, + }, + } as VisTypeAlias, + ]; + const { find } = props.savedObjectsClient; + await findListItems( + props.savedObjectsClient, + { get: mockGetTypes, getAliases: mockGetAliases }, + props.search, + props.size + ); + expect(find.mock.calls).toMatchObject([ + [ + { + type: ['bazdoc', 'etc', 'visualization'], + searchFields: ['baz', 'bing', 'title^3', 'description'], + }, + ], + ]); + }); + + it('deduplicates types and search fields', async () => { + const props = testProps(); + visTypes = [ + { + appExtensions: { + visualizations: { + docTypes: ['bazdoc', 'bar'], + searchFields: ['baz', 'bing', 'barfield'], + }, + }, + } as VisTypeAlias, + { + appExtensions: { + visualizations: { + docTypes: ['visualization', 'foo', 'bazdoc'], + searchFields: ['baz', 'bing', 'foofield'], + }, + }, + } as VisTypeAlias, + ]; + const { find } = props.savedObjectsClient; + await findListItems( + props.savedObjectsClient, + { get: mockGetTypes, getAliases: mockGetAliases }, + props.search, + props.size + ); + expect(find.mock.calls).toMatchObject([ + [ + { + type: ['bazdoc', 'bar', 'visualization', 'foo'], + searchFields: ['baz', 'bing', 'barfield', 'foofield', 'title^3', 'description'], + }, + ], + ]); + }); + + it('searches the search term prefix', async () => { + const props = { + ...testProps(), + search: 'ahoythere', + }; + const { find } = props.savedObjectsClient; + await findListItems( + props.savedObjectsClient, + { get: mockGetTypes, getAliases: mockGetAliases }, + props.search, + props.size + ); + expect(find.mock.calls).toMatchObject([ + [ + { + search: 'ahoythere*', + }, + ], + ]); + }); + + it('searches with references', async () => { + const props = { + ...testProps(), + references: [ + { type: 'foo', id: 'hello' }, + { type: 'bar', id: 'dolly' }, + ], + }; + const { find } = props.savedObjectsClient; + await findListItems( + props.savedObjectsClient, + { get: mockGetTypes, getAliases: mockGetAliases }, + props.search, + props.size, + props.references + ); + expect(find.mock.calls).toMatchObject([ + [ + { + hasReference: [ + { type: 'foo', id: 'hello' }, + { type: 'bar', id: 'dolly' }, + ], + }, + ], + ]); + }); + + it('uses type-specific toListItem function, if available', async () => { + const props = testProps(); + + visTypes = [ + { + appExtensions: { + visualizations: { + docTypes: ['wizard'], + toListItem(savedObject) { + return { + id: savedObject.id, + title: `${(savedObject.attributes as { label: string }).label} THE GRAY`, + }; + }, + }, + }, + } as VisTypeAlias, + ]; + (props.savedObjectsClient.find as jest.Mock).mockImplementationOnce(async () => ({ + total: 2, + savedObjects: [ + { + id: 'lotr', + type: 'wizard', + attributes: { label: 'Gandalf' }, + }, + { + id: 'wat', + type: 'visualization', + attributes: { title: 'WATEVER', typeName: 'test' }, + }, + ], + })); + + const items = await findListItems( + props.savedObjectsClient, + { get: mockGetTypes, getAliases: mockGetAliases }, + props.search, + props.size + ); + expect(items).toEqual({ + total: 2, + hits: [ + { + id: 'lotr', + references: undefined, + title: 'Gandalf THE GRAY', + }, + { + id: 'wat', + references: undefined, + icon: undefined, + savedObjectType: 'visualization', + editUrl: '/edit/wat', + type: 'test', + typeName: 'test', + typeTitle: undefined, + title: 'WATEVER', + url: '#/edit/wat', + }, + ], + }); + }); + }); +}); diff --git a/src/plugins/visualizations/public/utils/saved_visualize_utils.ts b/src/plugins/visualizations/public/utils/saved_visualize_utils.ts new file mode 100644 index 000000000000..a28ee9486c4d --- /dev/null +++ b/src/plugins/visualizations/public/utils/saved_visualize_utils.ts @@ -0,0 +1,403 @@ +/* + * 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 _ from 'lodash'; +import type { + SavedObjectsFindOptionsReference, + SavedObjectsFindOptions, + SavedObjectsClientContract, + SavedObjectAttributes, + SavedObjectReference, +} from 'kibana/public'; +import type { OverlayStart } from '../../../../core/public'; +import { SavedObjectNotFound } from '../../../kibana_utils/public'; +import { + extractSearchSourceReferences, + injectSearchSourceReferences, + parseSearchSourceJSON, + DataPublicPluginStart, +} from '../../../../plugins/data/public'; +import { + checkForDuplicateTitle, + saveWithConfirmation, + isErrorNonFatal, +} from '../../../../plugins/saved_objects/public'; +import type { SavedObjectsTaggingApi } from '../../../saved_objects_tagging_oss/public'; +import type { SpacesPluginStart } from '../../../../../x-pack/plugins/spaces/public'; +import { VisualizationsAppExtension } from '../vis_types/vis_type_alias_registry'; +import type { + VisSavedObject, + SerializedVis, + ISavedVis, + SaveVisOptions, + GetVisOptions, +} from '../types'; +import type { TypesStart, BaseVisType } from '../vis_types'; +// @ts-ignore +import { updateOldState } from '../legacy/vis_update_state'; +import { injectReferences, extractReferences } from './saved_visualization_references'; + +export const SAVED_VIS_TYPE = 'visualization'; + +const getDefaults = (opts: GetVisOptions) => ({ + title: '', + visState: !opts.type ? null : { type: opts.type }, + uiStateJSON: '{}', + description: '', + savedSearchId: opts.savedSearchId, + version: 1, +}); + +export function getFullPath(id: string) { + return `/app/visualize#/edit/${id}`; +} + +export function urlFor(id: string) { + return `#/edit/${encodeURIComponent(id)}`; +} + +export function mapHitSource( + visTypes: Pick, + { + attributes, + id, + references, + }: { + attributes: SavedObjectAttributes; + id: string; + references: SavedObjectReference[]; + } +) { + const newAttributes: { + id: string; + references: SavedObjectReference[]; + url: string; + savedObjectType?: string; + editUrl?: string; + type?: BaseVisType; + icon?: BaseVisType['icon']; + image?: BaseVisType['image']; + typeTitle?: BaseVisType['title']; + error?: string; + } = { + id, + references, + url: urlFor(id), + ...attributes, + }; + + let typeName = attributes.typeName; + if (attributes.visState) { + try { + typeName = JSON.parse(String(attributes.visState)).type; + } catch (e) { + /* missing typename handled below */ + } + } + + if (!typeName || !visTypes.get(typeName as string)) { + newAttributes.error = 'Unknown visualization type'; + return newAttributes; + } + + newAttributes.type = visTypes.get(typeName as string); + newAttributes.savedObjectType = 'visualization'; + newAttributes.icon = newAttributes.type?.icon; + newAttributes.image = newAttributes.type?.image; + newAttributes.typeTitle = newAttributes.type?.title; + newAttributes.editUrl = `/edit/${id}`; + + return newAttributes; +} + +export const convertToSerializedVis = (savedVis: VisSavedObject): SerializedVis => { + const { id, title, description, visState, uiStateJSON, searchSourceFields } = savedVis; + + const aggs = searchSourceFields && searchSourceFields.index ? visState.aggs || [] : visState.aggs; + + return { + id, + title, + type: visState.type, + description, + params: visState.params, + uiState: JSON.parse(uiStateJSON || '{}'), + data: { + aggs, + searchSource: searchSourceFields!, + savedSearchId: savedVis.savedSearchId, + }, + }; +}; + +export const convertFromSerializedVis = (vis: SerializedVis): ISavedVis => { + return { + id: vis.id, + title: vis.title, + description: vis.description, + visState: { + title: vis.title, + type: vis.type, + aggs: vis.data.aggs, + params: vis.params, + }, + uiStateJSON: JSON.stringify(vis.uiState), + searchSourceFields: vis.data.searchSource, + savedSearchId: vis.data.savedSearchId, + }; +}; + +export async function findListItems( + savedObjectsClient: SavedObjectsClientContract, + visTypes: Pick, + search: string, + size: number, + references?: SavedObjectsFindOptionsReference[] +) { + const visAliases = visTypes.getAliases(); + const extensions = visAliases + .map((v) => v.appExtensions?.visualizations) + .filter(Boolean) as VisualizationsAppExtension[]; + const extensionByType = extensions.reduce((acc, m) => { + return m!.docTypes.reduce((_acc, type) => { + acc[type] = m; + return acc; + }, acc); + }, {} as { [visType: string]: VisualizationsAppExtension }); + const searchOption = (field: string, ...defaults: string[]) => + _(extensions).map(field).concat(defaults).compact().flatten().uniq().value() as string[]; + const searchOptions: SavedObjectsFindOptions = { + type: searchOption('docTypes', 'visualization'), + searchFields: searchOption('searchFields', 'title^3', 'description'), + search: search ? `${search}*` : undefined, + perPage: size, + page: 1, + defaultSearchOperator: 'AND' as 'AND', + hasReference: references, + }; + + const { total, savedObjects } = await savedObjectsClient.find( + searchOptions + ); + + return { + total, + hits: savedObjects.map((savedObject) => { + const config = extensionByType[savedObject.type]; + + if (config) { + return { + ...config.toListItem(savedObject), + references: savedObject.references, + }; + } else { + return mapHitSource(visTypes, savedObject); + } + }), + }; +} + +export async function getSavedVisualization( + services: { + savedObjectsClient: SavedObjectsClientContract; + search: DataPublicPluginStart['search']; + dataViews: DataPublicPluginStart['dataViews']; + spaces?: SpacesPluginStart; + savedObjectsTagging?: SavedObjectsTaggingApi; + }, + opts?: GetVisOptions | string +): Promise { + if (typeof opts !== 'object') { + opts = { id: opts } as GetVisOptions; + } + + const id = (opts.id as string) || ''; + const savedObject = { + id, + migrationVersion: opts.migrationVersion, + displayName: SAVED_VIS_TYPE, + getEsType: () => SAVED_VIS_TYPE, + getDisplayName: () => SAVED_VIS_TYPE, + searchSource: opts.searchSource ? services.search.searchSource.createEmpty() : undefined, + } as VisSavedObject; + const defaultsProps = getDefaults(opts); + + if (!id) { + Object.assign(savedObject, defaultsProps); + return savedObject; + } + + const { + saved_object: resp, + outcome, + alias_target_id: aliasTargetId, + } = await services.savedObjectsClient.resolve(SAVED_VIS_TYPE, id); + + if (!resp._version) { + throw new SavedObjectNotFound(SAVED_VIS_TYPE, id || ''); + } + + const attributes = _.cloneDeep(resp.attributes); + + if (attributes.visState && typeof attributes.visState === 'string') { + attributes.visState = JSON.parse(attributes.visState); + } + + // assign the defaults to the response + _.defaults(attributes, defaultsProps); + + Object.assign(savedObject, attributes); + savedObject.lastSavedTitle = savedObject.title; + + savedObject.sharingSavedObjectProps = { + aliasTargetId, + outcome, + errorJSON: + outcome === 'conflict' && services.spaces + ? JSON.stringify({ + targetType: SAVED_VIS_TYPE, + sourceId: id, + targetSpace: (await services.spaces.getActiveSpace()).id, + }) + : undefined, + }; + + const meta = (attributes.kibanaSavedObjectMeta || {}) as SavedObjectAttributes; + + if (meta.searchSourceJSON) { + try { + let searchSourceValues = parseSearchSourceJSON(meta.searchSourceJSON as string); + + if (opts.searchSource) { + searchSourceValues = injectSearchSourceReferences( + searchSourceValues as any, + resp.references + ); + savedObject.searchSource = await services.search.searchSource.create(searchSourceValues); + } else { + savedObject.searchSourceFields = searchSourceValues; + } + } catch (error: any) { + throw error; + } + } + + if (resp.references && resp.references.length > 0) { + injectReferences(savedObject, resp.references); + } + + if (services.savedObjectsTagging) { + savedObject.tags = services.savedObjectsTagging.ui.getTagIdsFromReferences(resp.references); + } + + savedObject.visState = await updateOldState(savedObject.visState); + if (savedObject.searchSourceFields?.index) { + await services.dataViews.get(savedObject.searchSourceFields.index as any); + } + + return savedObject; +} + +export async function saveVisualization( + savedObject: VisSavedObject, + { + confirmOverwrite = false, + isTitleDuplicateConfirmed = false, + onTitleDuplicate, + copyOnSave = false, + }: SaveVisOptions, + services: { + savedObjectsClient: SavedObjectsClientContract; + overlays: OverlayStart; + savedObjectsTagging?: SavedObjectsTaggingApi; + } +) { + // Save the original id in case the save fails. + const originalId = savedObject.id; + // Read https://github.com/elastic/kibana/issues/9056 and + // https://github.com/elastic/kibana/issues/9012 for some background into why this copyOnSave variable + // exists. + // The goal is to move towards a better rename flow, but since our users have been conditioned + // to expect a 'save as' flow during a rename, we are keeping the logic the same until a better + // UI/UX can be worked out. + if (copyOnSave) { + delete savedObject.id; + } + + const attributes: SavedObjectAttributes = { + visState: JSON.stringify(savedObject.visState), + title: savedObject.title, + uiStateJSON: savedObject.uiStateJSON, + description: savedObject.description, + savedSearchId: savedObject.savedSearchId, + version: savedObject.version, + }; + let references: SavedObjectReference[] = []; + + if (savedObject.searchSource) { + const { searchSourceJSON, references: searchSourceReferences } = + savedObject.searchSource.serialize(); + attributes.kibanaSavedObjectMeta = { searchSourceJSON }; + references.push(...searchSourceReferences); + } + + if (savedObject.searchSourceFields) { + const [searchSourceFields, searchSourceReferences] = extractSearchSourceReferences( + savedObject.searchSourceFields + ); + const searchSourceJSON = JSON.stringify(searchSourceFields); + attributes.kibanaSavedObjectMeta = { searchSourceJSON }; + references.push(...searchSourceReferences); + } + + if (services.savedObjectsTagging) { + references = services.savedObjectsTagging.ui.updateTagsReferences( + references, + savedObject.tags || [] + ); + } + + const extractedRefs = extractReferences({ attributes, references }); + + if (!extractedRefs.references) { + throw new Error('References not returned from extractReferences'); + } + + try { + await checkForDuplicateTitle( + { + ...savedObject, + copyOnSave, + } as any, + isTitleDuplicateConfirmed, + onTitleDuplicate, + services as any + ); + const createOpt = { + id: savedObject.id, + migrationVersion: savedObject.migrationVersion, + references: extractedRefs.references, + }; + const resp = confirmOverwrite + ? await saveWithConfirmation(attributes, savedObject, createOpt, services) + : await services.savedObjectsClient.create(SAVED_VIS_TYPE, extractedRefs.attributes, { + ...createOpt, + overwrite: true, + }); + + savedObject.id = resp.id; + savedObject.lastSavedTitle = savedObject.title; + return savedObject.id; + } catch (err: any) { + savedObject.id = originalId; + if (isErrorNonFatal(err)) { + return ''; + } + return Promise.reject(err); + } +} diff --git a/src/plugins/visualizations/tsconfig.json b/src/plugins/visualizations/tsconfig.json index 65ab83d5e0ba..eeaed655c3e7 100644 --- a/src/plugins/visualizations/tsconfig.json +++ b/src/plugins/visualizations/tsconfig.json @@ -23,5 +23,6 @@ { "path": "../usage_collection/tsconfig.json" }, { "path": "../kibana_utils/tsconfig.json" }, { "path": "../discover/tsconfig.json" }, + { "path": "../../../x-pack/plugins/spaces/tsconfig.json" }, ] } diff --git a/src/plugins/visualize/kibana.json b/src/plugins/visualize/kibana.json index afa9e3ce055b..bfb23bec2111 100644 --- a/src/plugins/visualize/kibana.json +++ b/src/plugins/visualize/kibana.json @@ -17,7 +17,8 @@ "home", "share", "savedObjectsTaggingOss", - "usageCollection" + "usageCollection", + "spaces" ], "requiredBundles": [ "kibanaUtils", diff --git a/src/plugins/visualize/public/application/components/visualize_editor_common.test.tsx b/src/plugins/visualize/public/application/components/visualize_editor_common.test.tsx new file mode 100644 index 000000000000..2c8478492005 --- /dev/null +++ b/src/plugins/visualize/public/application/components/visualize_editor_common.test.tsx @@ -0,0 +1,112 @@ +/* + * 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 { shallow, mount } from 'enzyme'; +import { VisualizeEditorCommon } from './visualize_editor_common'; +import { VisualizeEditorVisInstance } from '../types'; + +const mockGetLegacyUrlConflict = jest.fn(); +const mockRedirectLegacyUrl = jest.fn(() => Promise.resolve()); +jest.mock('../../../../kibana_react/public', () => ({ + useKibana: jest.fn(() => ({ + services: { + spaces: { + ui: { + redirectLegacyUrl: mockRedirectLegacyUrl, + components: { + getLegacyUrlConflict: mockGetLegacyUrlConflict, + }, + }, + }, + history: { + location: { + search: '?_g=test', + }, + }, + http: { + basePath: { + prepend: (url: string) => url, + }, + }, + }, + })), + withKibana: jest.fn((comp) => comp), +})); + +describe('VisualizeEditorCommon', () => { + it('should display a conflict callout if saved object conflicts', async () => { + shallow( + {}} + hasUnappliedChanges={false} + isEmbeddableRendered={false} + onAppLeave={() => {}} + visEditorRef={React.createRef()} + visInstance={ + { + savedVis: { + id: 'test', + sharingSavedObjectProps: { + outcome: 'conflict', + aliasTargetId: 'alias_id', + }, + }, + vis: { + type: { + title: 'TSVB', + }, + }, + } as VisualizeEditorVisInstance + } + /> + ); + expect(mockGetLegacyUrlConflict).toHaveBeenCalledWith({ + currentObjectId: 'test', + objectNoun: 'TSVB visualization', + otherObjectId: 'alias_id', + otherObjectPath: '#/edit/alias_id?_g=test', + }); + }); + + it('should redirect to new id if saved object aliasMatch', async () => { + mount( + {}} + hasUnappliedChanges={false} + isEmbeddableRendered={false} + onAppLeave={() => {}} + visEditorRef={React.createRef()} + visInstance={ + { + savedVis: { + id: 'test', + sharingSavedObjectProps: { + outcome: 'aliasMatch', + aliasTargetId: 'alias_id', + }, + }, + vis: { + type: { + title: 'TSVB', + }, + }, + } as VisualizeEditorVisInstance + } + /> + ); + expect(mockRedirectLegacyUrl).toHaveBeenCalledWith( + '#/edit/alias_id?_g=test', + 'TSVB visualization' + ); + }); +}); diff --git a/src/plugins/visualize/public/application/components/visualize_editor_common.tsx b/src/plugins/visualize/public/application/components/visualize_editor_common.tsx index a03073e61f59..6268ba5c936e 100644 --- a/src/plugins/visualize/public/application/components/visualize_editor_common.tsx +++ b/src/plugins/visualize/public/application/components/visualize_editor_common.tsx @@ -7,15 +7,19 @@ */ import './visualize_editor.scss'; -import React, { RefObject } from 'react'; +import React, { RefObject, useCallback, useEffect } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { EuiScreenReaderOnly } from '@elastic/eui'; import { AppMountParameters } from 'kibana/public'; import { VisualizeTopNav } from './visualize_top_nav'; import { ExperimentalVisInfo } from './experimental_vis_info'; +import { useKibana } from '../../../../kibana_react/public'; +import { urlFor } from '../../../../visualizations/public'; import { SavedVisInstance, VisualizeAppState, + VisualizeServices, VisualizeAppStateContainer, VisualizeEditorVisInstance, } from '../types'; @@ -53,6 +57,55 @@ export const VisualizeEditorCommon = ({ embeddableId, visEditorRef, }: VisualizeEditorCommonProps) => { + const { services } = useKibana(); + + useEffect(() => { + async function aliasMatchRedirect() { + const sharingSavedObjectProps = visInstance?.savedVis.sharingSavedObjectProps; + if (services.spaces && sharingSavedObjectProps?.outcome === 'aliasMatch') { + // We found this object by a legacy URL alias from its old ID; redirect the user to the page with its new ID, preserving any URL hash + const newObjectId = sharingSavedObjectProps?.aliasTargetId; // This is always defined if outcome === 'aliasMatch' + const newPath = `${urlFor(newObjectId!)}${services.history.location.search}`; + await services.spaces.ui.redirectLegacyUrl( + newPath, + i18n.translate('visualize.legacyUrlConflict.objectNoun', { + defaultMessage: '{visName} visualization', + values: { + visName: visInstance?.vis?.type.title, + }, + }) + ); + return; + } + } + + aliasMatchRedirect(); + }, [visInstance?.savedVis.sharingSavedObjectProps, visInstance?.vis?.type.title, services]); + + const getLegacyUrlConflictCallout = useCallback(() => { + // This function returns a callout component *if* we have encountered a "legacy URL conflict" scenario + const currentObjectId = visInstance?.savedVis.id; + const sharingSavedObjectProps = visInstance?.savedVis.sharingSavedObjectProps; + if (services.spaces && sharingSavedObjectProps?.outcome === 'conflict' && currentObjectId) { + // We have resolved to one object, but another object has a legacy URL alias associated with this ID/page. We should display a + // callout with a warning for the user, and provide a way for them to navigate to the other object. + const otherObjectId = sharingSavedObjectProps?.aliasTargetId!; // This is always defined if outcome === 'conflict' + const otherObjectPath = `${urlFor(otherObjectId)}${services.history.location.search}`; + return services.spaces.ui.components.getLegacyUrlConflict({ + objectNoun: i18n.translate('visualize.legacyUrlConflict.objectNoun', { + defaultMessage: '{visName} visualization', + values: { + visName: visInstance?.vis?.type.title, + }, + }), + currentObjectId, + otherObjectId, + otherObjectPath, + }); + } + return null; + }, [visInstance?.savedVis, services, visInstance?.vis?.type.title]); + return (