From c770e9d6cb5c363f6eb865eb44bace59368fcc10 Mon Sep 17 00:00:00 2001 From: Coen Warmer Date: Wed, 29 Mar 2023 12:05:35 +0200 Subject: [PATCH 01/10] Update links to Exploratory View in APM app to use new Exploratory View app (#153922) --- x-pack/plugins/apm/kibana.jsonc | 1 + .../analyze_data_button.test.tsx | 8 ++++---- .../apm_service_template/analyze_data_button.tsx | 4 ++-- x-pack/plugins/apm/public/plugin.ts | 14 ++++++++++++++ x-pack/plugins/apm/tsconfig.json | 1 + 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/apm/kibana.jsonc b/x-pack/plugins/apm/kibana.jsonc index 9d77d00b21bef..02ca85e30f0ad 100644 --- a/x-pack/plugins/apm/kibana.jsonc +++ b/x-pack/plugins/apm/kibana.jsonc @@ -19,6 +19,7 @@ "inspector", "licensing", "observability", + "exploratoryView", "ruleRegistry", "triggersActionsUi", "share", diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.test.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.test.tsx index f499cf88ecdb5..4f0a02e1705af 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.test.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.test.tsx @@ -38,7 +38,7 @@ describe('AnalyzeDataButton', () => { render(); expect((screen.getByRole('link') as HTMLAnchorElement).href).toEqual( - 'http://localhost/app/observability/exploratory-view/#?reportType=kpi-over-time&sr=!((dt:ux,mt:transaction.duration.us,n:testServiceName-response-latency,op:average,rdf:(service.environment:!(testEnvironment),service.name:!(testServiceName)),time:(from:now-15m,to:now)))' + 'http://localhost/app/exploratory-view/#?reportType=kpi-over-time&sr=!((dt:ux,mt:transaction.duration.us,n:testServiceName-response-latency,op:average,rdf:(service.environment:!(testEnvironment),service.name:!(testServiceName)),time:(from:now-15m,to:now)))' ); }); }); @@ -48,7 +48,7 @@ describe('AnalyzeDataButton', () => { render(); expect((screen.getByRole('link') as HTMLAnchorElement).href).toEqual( - 'http://localhost/app/observability/exploratory-view/#?reportType=kpi-over-time&sr=!((dt:mobile,mt:transaction.duration.us,n:testServiceName-response-latency,op:average,rdf:(service.environment:!(testEnvironment),service.name:!(testServiceName)),time:(from:now-15m,to:now)))' + 'http://localhost/app/exploratory-view/#?reportType=kpi-over-time&sr=!((dt:mobile,mt:transaction.duration.us,n:testServiceName-response-latency,op:average,rdf:(service.environment:!(testEnvironment),service.name:!(testServiceName)),time:(from:now-15m,to:now)))' ); }); }); @@ -58,7 +58,7 @@ describe('AnalyzeDataButton', () => { render(); expect((screen.getByRole('link') as HTMLAnchorElement).href).toEqual( - 'http://localhost/app/observability/exploratory-view/#?reportType=kpi-over-time&sr=!((dt:mobile,mt:transaction.duration.us,n:testServiceName-response-latency,op:average,rdf:(service.environment:!(ENVIRONMENT_NOT_DEFINED),service.name:!(testServiceName)),time:(from:now-15m,to:now)))' + 'http://localhost/app/exploratory-view/#?reportType=kpi-over-time&sr=!((dt:mobile,mt:transaction.duration.us,n:testServiceName-response-latency,op:average,rdf:(service.environment:!(ENVIRONMENT_NOT_DEFINED),service.name:!(testServiceName)),time:(from:now-15m,to:now)))' ); }); }); @@ -68,7 +68,7 @@ describe('AnalyzeDataButton', () => { render(); expect((screen.getByRole('link') as HTMLAnchorElement).href).toEqual( - 'http://localhost/app/observability/exploratory-view/#?reportType=kpi-over-time&sr=!((dt:mobile,mt:transaction.duration.us,n:testServiceName-response-latency,op:average,rdf:(service.environment:!(ALL_VALUES),service.name:!(testServiceName)),time:(from:now-15m,to:now)))' + 'http://localhost/app/exploratory-view/#?reportType=kpi-over-time&sr=!((dt:mobile,mt:transaction.duration.us,n:testServiceName-response-latency,op:average,rdf:(service.environment:!(ALL_VALUES),service.name:!(testServiceName)),time:(from:now-15m,to:now)))' ); }); }); diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.tsx index 0c1e86aa5fa24..abeb6cb8fc961 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.tsx @@ -9,8 +9,8 @@ import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { createExploratoryViewUrl } from '@kbn/observability-plugin/public'; -import { ALL_VALUES_SELECTED } from '@kbn/observability-plugin/public'; +import { createExploratoryViewUrl } from '@kbn/exploratory-view-plugin/public'; +import { ALL_VALUES_SELECTED } from '@kbn/exploratory-view-plugin/public'; import { isMobileAgentName, isRumAgentName, diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index 89f17be8d471b..ba7b455c05bb6 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -23,6 +23,7 @@ import type { } from '@kbn/data-plugin/public'; import { LensPublicStart } from '@kbn/lens-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import type { ExploratoryViewPublicSetup } from '@kbn/exploratory-view-plugin/public'; import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import { Start as InspectorPluginStart } from '@kbn/inspector-plugin/public'; @@ -72,6 +73,7 @@ export type ApmPluginStart = void; export interface ApmPluginSetupDeps { alerting?: AlertingPluginPublicSetup; data: DataPublicPluginSetup; + exploratoryView: ExploratoryViewPublicSetup; unifiedSearch: UnifiedSearchPublicPluginStart; features: FeaturesPluginSetup; home?: HomePublicPluginSetup; @@ -271,6 +273,18 @@ export class ApmPlugin implements Plugin { }, }); + plugins.exploratoryView.register({ + appName: 'apm', + hasData: async () => { + const dataHelper = await getApmDataHelper(); + return await dataHelper.getHasData(); + }, + fetchData: async (params: FetchDataParams) => { + const dataHelper = await getApmDataHelper(); + return await dataHelper.fetchObservabilityOverviewPageData(params); + }, + }); + const { observabilityRuleTypeRegistry } = plugins.observability; core.application.register({ diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index 149b43ebc6483..d4d263af763a9 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -81,6 +81,7 @@ "@kbn/safer-lodash-set", "@kbn/shared-ux-router", "@kbn/alerts-as-data-utils", + "@kbn/exploratory-view-plugin", ], "exclude": [ "target/**/*", From ec58ba62df737f050f169ff4b2d69dded6c95cdf Mon Sep 17 00:00:00 2001 From: Sloane Perrault Date: Wed, 29 Mar 2023 07:46:19 -0400 Subject: [PATCH 02/10] [Enterprise Search] Search Applications - Icons for Document Flyout (#153899) ## Summary - Updates document flyout to include icon for the field types image ### Checklist Delete any items that are not applicable to this PR. - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --- .../components/engine/engine_schema.tsx | 3 +- .../engine_search_preview/convert_results.ts | 14 +++++++ .../engine_search_preview/document_flyout.tsx | 37 ++++++++++++------- .../engine_search_preview_logic.ts | 22 +++++++++++ .../components/engine/field_icon.tsx | 31 ++++++++++++++++ 5 files changed, 92 insertions(+), 15 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/field_icon.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_schema.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_schema.tsx index a5ec5aa7a0557..8a5bae680e34e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_schema.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_schema.tsx @@ -24,8 +24,6 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { FieldIcon } from '@kbn/react-field'; - import { SchemaField } from '../../../../../common/types/engines'; import { docLinks } from '../../../shared/doc_links'; import { generateEncodedPath } from '../../../shared/encode_path_params'; @@ -37,6 +35,7 @@ import { EnterpriseSearchEnginesPageTemplate } from '../layout/engines_page_temp import { EngineIndicesLogic } from './engine_indices_logic'; import { EngineViewLogic } from './engine_view_logic'; +import { FieldIcon } from './field_icon'; const SchemaFieldDetails: React.FC<{ schemaField: SchemaField }> = ({ schemaField }) => { const { navigateToUrl } = useValues(KibanaLogic); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/convert_results.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/convert_results.ts index 8d00549c6554c..c55200e7917ce 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/convert_results.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/convert_results.ts @@ -28,6 +28,10 @@ export interface ConvertedResult { value: string; } +export interface ConvertedResultWithType extends ConvertedResult { + type: string; +} + export const convertResults = (result: Record): ConvertedResult[] => { const flattenedResult = flattenObject(result); const unsortedFields = Object.entries(flattenedResult).map( @@ -39,3 +43,13 @@ export const convertResults = (result: Record): ConvertedResult const sortedFields = unsortedFields.sort((a, b) => a.field.localeCompare(b.field)); return sortedFields; }; + +export const addTypeToResults = ( + results: ConvertedResult[], + fieldTypes: Record +): ConvertedResultWithType[] => { + return results.map((result) => { + const type = fieldTypes[result.field]; + return { ...result, type }; + }); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/document_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/document_flyout.tsx index ee7c7f5524419..0d1151cc8db9d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/document_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/document_flyout.tsx @@ -7,6 +7,8 @@ import React, { useMemo } from 'react'; +import { useValues } from 'kea'; + import { EuiBasicTableColumn, EuiFlexGroup, @@ -21,14 +23,21 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ConvertedResult, convertResults } from './convert_results'; +import { FieldIcon } from '../field_icon'; + +import { addTypeToResults, ConvertedResultWithType, convertResults } from './convert_results'; import { useSelectedDocument } from './document_context'; +import { EngineSearchPreviewLogic } from './engine_search_preview_logic'; export const DocumentFlyout: React.FC = () => { + const { fieldTypesByIndex } = useValues(EngineSearchPreviewLogic); const { selectedDocument, setSelectedDocument } = useSelectedDocument(); - const [id, items] = useMemo((): [string | null, ConvertedResult[]] => { - if (!selectedDocument) return [null, []]; + const index = selectedDocument?._meta.rawHit._index; + + const [id, items] = useMemo((): [string | null, ConvertedResultWithType[]] => { + const fieldTypes = fieldTypesByIndex[index]; + if (!selectedDocument || !fieldTypes) return [null, []]; const { _meta: { id: encodedId }, id: _id, @@ -41,24 +50,26 @@ export const DocumentFlyout: React.FC = () => { ), id: parsedId, }; - return [parsedId, convertResults(fields)]; - }, [selectedDocument]); + return [parsedId, addTypeToResults(convertResults(fields), fieldTypes)]; + }, [fieldTypesByIndex, index, selectedDocument]); if (selectedDocument === null) return null; - const columns: Array> = [ + const columns: Array> = [ { - field: 'field', name: i18n.translate( 'xpack.enterpriseSearch.content.engine.searchPreview.documentFlyout.fieldLabel', { defaultMessage: 'Field' } ), - render: (key: string) => ( - - - {key} - - + render: ({ field: key, type }: ConvertedResultWithType) => ( + + + + + {key} + + + ), truncateText: false, }, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/engine_search_preview_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/engine_search_preview_logic.ts index be453e046225a..e7ac955827d47 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/engine_search_preview_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/engine_search_preview/engine_search_preview_logic.ts @@ -19,6 +19,7 @@ interface EngineSearchPreviewActions { export interface EngineSearchPreviewValues { engineFieldCapabilitiesData: typeof FetchEngineFieldCapabilitiesApiLogic.values.data; engineName: typeof EngineNameLogic.values.engineName; + fieldTypesByIndex: Record>; resultFields: Record; searchableFields: Record; sortableFields: string[]; @@ -50,6 +51,27 @@ export const EngineSearchPreviewLogic = kea< }), path: ['enterprise_search', 'content', 'engine_search_preview_logic'], selectors: ({ selectors }) => ({ + fieldTypesByIndex: [ + () => [selectors.engineFieldCapabilitiesData], + (data: EngineSearchPreviewValues['engineFieldCapabilitiesData']) => { + if (!data) return {}; + + return data.fields.reduce( + (out: Record>, field) => + field.indices.reduce( + (acc: Record>, index) => ({ + ...acc, + [index.name]: { + ...(acc[index.name] || {}), + [field.name]: index.type, + }, + }), + out + ), + {} + ); + }, + ], resultFields: [ () => [selectors.engineFieldCapabilitiesData], (data: EngineSearchPreviewValues['engineFieldCapabilitiesData']) => { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/field_icon.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/field_icon.tsx new file mode 100644 index 0000000000000..614b0582ba07b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/engine/field_icon.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { EuiTokenProps } from '@elastic/eui'; +import { FieldIcon as KbnFieldIcon } from '@kbn/react-field'; + +// Remappings from type to a supported `FieldIcon` type +const typeToFieldIconType: Partial> = { + integer: 'number', +}; + +// Mappings for types missing from `FieldIcon` +const typeToEuiIconMap: Partial> = { + object: { color: 'euiColorVis3', iconType: 'tokenObject' }, +}; + +export interface FieldIconProps { + type: string; +} + +export const FieldIcon: React.FC = (props) => { + const type = typeToFieldIconType[props.type] || props.type; + const overrides = typeToEuiIconMap[type] || {}; + return ; +}; From e13f1c5fb20f0526d5f247f1e6fa250083097c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Wed, 29 Mar 2023 09:02:38 -0400 Subject: [PATCH 03/10] Fix flaky RBAC Legacy test due to archive containing tasks and rules together (#153803) Resolves https://github.com/elastic/kibana/issues/153800 Resolves https://github.com/elastic/kibana/issues/142704 Resolves https://github.com/elastic/kibana/issues/153801 Resolves https://github.com/elastic/kibana/issues/142947 Resolves https://github.com/elastic/kibana/issues/140867 Similar to https://github.com/elastic/kibana/pull/152841#discussion_r1147716618, the rule and tasks archives don't seem to play nicely when combined. The flakiness goes away when loading the rules then the tasks in sequence. Otherwise, the tasks sometimes run before it can find the rule, causing the task to delete itself. I took a look at why the task would run an not be able to find the rule. My best guess after looking at a failing flaky test is that the task manager migration completes before the .kibana. And while .kibana migrates, the task runs and fails to load the task because the .kibana index is in an interim state. Flaky test runner: https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2045 --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../group2/tests/alerting/rbac_legacy.ts | 15 +- .../alerts_legacy/{ => rules}/data.json | 159 ------------------ .../alerts_legacy/{ => rules}/mappings.json | 121 ------------- .../es_archives/alerts_legacy/tasks/data.json | 158 +++++++++++++++++ .../alerts_legacy/tasks/mappings.json | 120 +++++++++++++ 5 files changed, 286 insertions(+), 287 deletions(-) rename x-pack/test/functional/es_archives/alerts_legacy/{ => rules}/data.json (70%) rename x-pack/test/functional/es_archives/alerts_legacy/{ => rules}/mappings.json (95%) create mode 100644 x-pack/test/functional/es_archives/alerts_legacy/tasks/data.json create mode 100644 x-pack/test/functional/es_archives/alerts_legacy/tasks/mappings.json diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/rbac_legacy.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/rbac_legacy.ts index 36bc3637d5185..65b0389392104 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/rbac_legacy.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/rbac_legacy.ts @@ -56,17 +56,17 @@ export default function alertTests({ getService }: FtrProviderContext) { ), }; - // Failing: See https://github.com/elastic/kibana/issues/140867 - // Failing: See https://github.com/elastic/kibana/issues/142704 - // Failing: See https://github.com/elastic/kibana/issues/153801 - // Failing: See https://github.com/elastic/kibana/issues/153800 - describe.skip('alerts', () => { + describe('alerts', () => { const authorizationIndex = '.kibana-test-authorization'; const objectRemover = new ObjectRemover(supertest); before(async () => { await esTestIndexTool.destroy(); - await esArchiver.load('x-pack/test/functional/es_archives/alerts_legacy'); + // Not 100% sure why, seems the rules need to be loaded separately to avoid the task + // failing to load the rule during execution and deleting itself. Otherwise + // we have flakiness + await esArchiver.load('x-pack/test/functional/es_archives/alerts_legacy/rules'); + await esArchiver.load('x-pack/test/functional/es_archives/alerts_legacy/tasks'); await esTestIndexTool.setup(); await es.indices.create({ index: authorizationIndex }); await setupSpacesAndUsers(getService); @@ -75,7 +75,8 @@ export default function alertTests({ getService }: FtrProviderContext) { after(async () => { await esTestIndexTool.destroy(); await es.indices.delete({ index: authorizationIndex }); - await esArchiver.unload('x-pack/test/functional/es_archives/alerts_legacy'); + await esArchiver.unload('x-pack/test/functional/es_archives/alerts_legacy/tasks'); + await esArchiver.unload('x-pack/test/functional/es_archives/alerts_legacy/rules'); }); for (const scenario of UserAtSpaceScenarios) { diff --git a/x-pack/test/functional/es_archives/alerts_legacy/data.json b/x-pack/test/functional/es_archives/alerts_legacy/rules/data.json similarity index 70% rename from x-pack/test/functional/es_archives/alerts_legacy/data.json rename to x-pack/test/functional/es_archives/alerts_legacy/rules/data.json index 770e8e7c15617..2c0552acb6054 100644 --- a/x-pack/test/functional/es_archives/alerts_legacy/data.json +++ b/x-pack/test/functional/es_archives/alerts_legacy/rules/data.json @@ -83,37 +83,6 @@ } } -{ - "type": "doc", - "value": { - "id": "task:e9c069d0-eea4-11ea-a285-352ee3aecffa", - "index": ".kibana_task_manager_1", - "source": { - "migrationVersion": { - "task": "7.6.0" - }, - "references": [ - ], - "task": { - "attempts": 0, - "params": "{\"alertId\":\"6ee9630a-a20e-44af-9465-217a3717d2ab\",\"spaceId\":\"space1\"}", - "retryAt": null, - "runAt": "2020-09-04T11:51:05.197Z", - "scheduledAt": "2020-09-04T11:51:05.197Z", - "scope": [ - "alerting" - ], - "startedAt": null, - "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", - "status": "idle", - "taskType": "alerting:test.always-firing" - }, - "type": "task", - "updated_at": "2020-09-04T11:51:05.197Z" - } - } -} - { "type": "doc", "value": { @@ -176,38 +145,6 @@ } } -{ - "type": "doc", - "value": { - "id": "task:e39a02f0-eea4-11ea-a285-352ee3aecffa", - "index": ".kibana_task_manager_1", - "source": { - "migrationVersion": { - "task": "7.6.0" - }, - "references": [ - ], - "task": { - "attempts": 0, - "ownerId": null, - "params": "{\"alertId\":\"b384be60-ec53-4b26-857e-0253ee55b277\",\"spaceId\":\"space1\"}", - "retryAt": null, - "runAt": "2020-09-04T11:51:04.804Z", - "scheduledAt": "2020-09-04T11:50:54.879Z", - "scope": [ - "alerting" - ], - "startedAt": null, - "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", - "status": "idle", - "taskType": "alerting:test.always-firing" - }, - "type": "task", - "updated_at": "2020-09-04T11:51:04.273Z" - } - } -} - { "type": "doc", "value": { @@ -270,38 +207,6 @@ } } -{ - "type": "doc", - "value": { - "id": "task:e8885f00-eea4-11ea-a285-352ee3aecffa", - "index": ".kibana_task_manager_1", - "source": { - "migrationVersion": { - "task": "7.6.0" - }, - "references": [ - ], - "task": { - "attempts": 1, - "ownerId": "kibana:5b2de169-2785-441b-ae8c-186a1936b17d", - "params": "{\"alertId\":\"5cc59319-74ee-4edc-8646-a79ea91067cd\",\"spaceId\":\"space1\"}", - "retryAt": "2020-09-04T12:01:05.793Z", - "runAt": "2020-09-04T11:51:03.152Z", - "scheduledAt": "2020-09-04T11:51:03.152Z", - "scope": [ - "alerting" - ], - "startedAt": "2020-09-04T11:51:05.793Z", - "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", - "status": "running", - "taskType": "alerting:test.always-firing" - }, - "type": "task", - "updated_at": "2020-09-04T11:51:05.794Z" - } - } -} - { "type": "doc", "value": { @@ -364,38 +269,6 @@ } } -{ - "type": "doc", - "value": { - "id": "task:e616c2c0-eea4-11ea-a285-352ee3aecffa", - "index": ".kibana_task_manager_1", - "source": { - "migrationVersion": { - "task": "7.6.0" - }, - "references": [ - ], - "task": { - "attempts": 1, - "ownerId": "kibana:5b2de169-2785-441b-ae8c-186a1936b17d", - "params": "{\"alertId\":\"d41a6abb-b93b-46df-a80a-926221ea847c\",\"spaceId\":\"space1\"}", - "retryAt": "2020-09-04T12:01:05.793Z", - "runAt": "2020-09-04T11:51:04.804Z", - "scheduledAt": "2020-09-04T11:50:59.052Z", - "scope": [ - "alerting" - ], - "startedAt": "2020-09-04T11:51:05.793Z", - "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", - "status": "idle", - "taskType": "alerting:test.always-firing" - }, - "type": "task", - "updated_at": "2020-09-04T11:51:05.794Z" - } - } -} - { "type": "doc", "value": { @@ -457,35 +330,3 @@ } } } - -{ - "type": "doc", - "value": { - "id": "task:e4df5430-eea4-11ea-a285-352ee3aecffa", - "index": ".kibana_task_manager_1", - "source": { - "migrationVersion": { - "task": "7.6.0" - }, - "references": [ - ], - "task": { - "attempts": 1, - "ownerId": "kibana:5b2de169-2785-441b-ae8c-186a1936b17d", - "params": "{\"alertId\":\"362e362b-a137-4aa2-9434-43e3d0d84a34\",\"spaceId\":\"space1\"}", - "retryAt": "2020-09-04T12:01:05.793Z", - "runAt": "2020-09-04T11:51:04.804Z", - "scheduledAt": "2020-09-04T11:50:57.011Z", - "scope": [ - "alerting" - ], - "startedAt": "2020-09-04T11:51:05.793Z", - "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", - "status": "running", - "taskType": "alerting:test.always-firing" - }, - "type": "task", - "updated_at": "2020-09-04T11:51:05.794Z" - } - } -} \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/alerts_legacy/mappings.json b/x-pack/test/functional/es_archives/alerts_legacy/rules/mappings.json similarity index 95% rename from x-pack/test/functional/es_archives/alerts_legacy/mappings.json rename to x-pack/test/functional/es_archives/alerts_legacy/rules/mappings.json index 6e40f811e1af4..23f170737f498 100644 --- a/x-pack/test/functional/es_archives/alerts_legacy/mappings.json +++ b/x-pack/test/functional/es_archives/alerts_legacy/rules/mappings.json @@ -2520,124 +2520,3 @@ } } } - -{ - "type": "index", - "value": { - "aliases": { - ".kibana_task_manager": { - } - }, - "index": ".kibana_task_manager_1", - "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "namespace": "2f4316de49999235636386fe51dc06c1", - "namespaces": "2f4316de49999235636386fe51dc06c1", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "task": "235412e52d09e7165fac8a67a43ad6b4", - "type": "2f4316de49999235636386fe51dc06c1", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0" - } - }, - "dynamic": "strict", - "properties": { - "migrationVersion": { - "dynamic": "true", - "properties": { - "task": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "namespace": { - "type": "keyword" - }, - "namespaces": { - "type": "keyword" - }, - "references": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "task": { - "properties": { - "attempts": { - "type": "integer" - }, - "ownerId": { - "type": "keyword" - }, - "params": { - "type": "text" - }, - "retryAt": { - "type": "date" - }, - "runAt": { - "type": "date" - }, - "schedule": { - "properties": { - "interval": { - "type": "keyword" - } - } - }, - "scheduledAt": { - "type": "date" - }, - "scope": { - "type": "keyword" - }, - "startedAt": { - "type": "date" - }, - "state": { - "type": "text" - }, - "status": { - "type": "keyword" - }, - "taskType": { - "type": "keyword" - }, - "user": { - "type": "keyword" - } - } - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "number_of_replicas": "0", - "number_of_shards": "1" - } - } - } -} diff --git a/x-pack/test/functional/es_archives/alerts_legacy/tasks/data.json b/x-pack/test/functional/es_archives/alerts_legacy/tasks/data.json new file mode 100644 index 0000000000000..e7c99521ff360 --- /dev/null +++ b/x-pack/test/functional/es_archives/alerts_legacy/tasks/data.json @@ -0,0 +1,158 @@ +{ + "type": "doc", + "value": { + "id": "task:e9c069d0-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 0, + "params": "{\"alertId\":\"6ee9630a-a20e-44af-9465-217a3717d2ab\",\"spaceId\":\"space1\"}", + "retryAt": null, + "runAt": "2020-09-04T11:51:05.197Z", + "scheduledAt": "2020-09-04T11:51:05.197Z", + "scope": [ + "alerting" + ], + "startedAt": null, + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "idle", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:05.197Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e39a02f0-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 0, + "ownerId": null, + "params": "{\"alertId\":\"b384be60-ec53-4b26-857e-0253ee55b277\",\"spaceId\":\"space1\"}", + "retryAt": null, + "runAt": "2020-09-04T11:51:04.804Z", + "scheduledAt": "2020-09-04T11:50:54.879Z", + "scope": [ + "alerting" + ], + "startedAt": null, + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "idle", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:04.273Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e8885f00-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 1, + "ownerId": "kibana:5b2de169-2785-441b-ae8c-186a1936b17d", + "params": "{\"alertId\":\"5cc59319-74ee-4edc-8646-a79ea91067cd\",\"spaceId\":\"space1\"}", + "retryAt": "2020-09-04T12:01:05.793Z", + "runAt": "2020-09-04T11:51:03.152Z", + "scheduledAt": "2020-09-04T11:51:03.152Z", + "scope": [ + "alerting" + ], + "startedAt": "2020-09-04T11:51:05.793Z", + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "running", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:05.794Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e616c2c0-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 1, + "ownerId": "kibana:5b2de169-2785-441b-ae8c-186a1936b17d", + "params": "{\"alertId\":\"d41a6abb-b93b-46df-a80a-926221ea847c\",\"spaceId\":\"space1\"}", + "retryAt": "2020-09-04T12:01:05.793Z", + "runAt": "2020-09-04T11:51:04.804Z", + "scheduledAt": "2020-09-04T11:50:59.052Z", + "scope": [ + "alerting" + ], + "startedAt": "2020-09-04T11:51:05.793Z", + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "idle", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:05.794Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "task:e4df5430-eea4-11ea-a285-352ee3aecffa", + "index": ".kibana_task_manager_1", + "source": { + "migrationVersion": { + "task": "7.6.0" + }, + "references": [ + ], + "task": { + "attempts": 1, + "ownerId": "kibana:5b2de169-2785-441b-ae8c-186a1936b17d", + "params": "{\"alertId\":\"362e362b-a137-4aa2-9434-43e3d0d84a34\",\"spaceId\":\"space1\"}", + "retryAt": "2020-09-04T12:01:05.793Z", + "runAt": "2020-09-04T11:51:04.804Z", + "scheduledAt": "2020-09-04T11:50:57.011Z", + "scope": [ + "alerting" + ], + "startedAt": "2020-09-04T11:51:05.793Z", + "state": "{\"previousStartedAt\":null,\"alertTypeState\":{},\"alertInstances\":{}}", + "status": "running", + "taskType": "alerting:test.always-firing" + }, + "type": "task", + "updated_at": "2020-09-04T11:51:05.794Z" + } + } +} diff --git a/x-pack/test/functional/es_archives/alerts_legacy/tasks/mappings.json b/x-pack/test/functional/es_archives/alerts_legacy/tasks/mappings.json new file mode 100644 index 0000000000000..d139264b8de93 --- /dev/null +++ b/x-pack/test/functional/es_archives/alerts_legacy/tasks/mappings.json @@ -0,0 +1,120 @@ +{ + "type": "index", + "value": { + "aliases": { + ".kibana_task_manager": { + } + }, + "index": ".kibana_task_manager_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "task": "235412e52d09e7165fac8a67a43ad6b4", + "type": "2f4316de49999235636386fe51dc06c1", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0" + } + }, + "dynamic": "strict", + "properties": { + "migrationVersion": { + "dynamic": "true", + "properties": { + "task": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "task": { + "properties": { + "attempts": { + "type": "integer" + }, + "ownerId": { + "type": "keyword" + }, + "params": { + "type": "text" + }, + "retryAt": { + "type": "date" + }, + "runAt": { + "type": "date" + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledAt": { + "type": "date" + }, + "scope": { + "type": "keyword" + }, + "startedAt": { + "type": "date" + }, + "state": { + "type": "text" + }, + "status": { + "type": "keyword" + }, + "taskType": { + "type": "keyword" + }, + "user": { + "type": "keyword" + } + } + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} From 4c586a71d4e05025e42cb88b275d950d6504ea5c Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Wed, 29 Mar 2023 09:15:39 -0400 Subject: [PATCH 04/10] [Logs UI] Support inline Log Views in the UI (#152933) ## Summary This closes #142840. It is the UI portion of support for inline Log Views. ## Visible changes to the UI ### ML warning ![Screenshot 2023-03-10 at 14 55 34](https://user-images.githubusercontent.com/471693/224348959-8db70d91-dc8b-4f4e-926b-ec05e7481b78.png) ### Alert dropdown warning ![Screenshot 2023-03-10 at 14 55 43](https://user-images.githubusercontent.com/471693/224349026-cdf17767-225a-4ecd-af8a-b90e2a21816f.png) ### Settings page warning ![Screenshot 2023-03-10 at 14 56 02](https://user-images.githubusercontent.com/471693/224349066-bcb63ba8-fee8-4b7a-b41b-7d89e09f002a.png) ## Reviewer hints / notes - The ACs on the issue are quite extensive and should provide a good number of things to test. - Make use of the "playground" page (see below) to make this easier - The `AlertDropdown` has been made lazy as the page load bundle increased by 100kb otherwise. - Our `link-to` functionality is scoped to persisted Log Views only at the moment as historically they've only accepted a path segment, not full query parameters. We can look to extend this in the future once we have concrete linking needs. ## Questions - I have allowed the Log View client to handle both the inline and persisted Log Views. I wonder if the function names become confusing? (e.g. are the RESTful prefixes misleading now?). - The ML warning splash page links to settings to revert to a persisted Log View. It could also be done in place on the page. I went back and forth over whether we should keep the reverting in one place? ## Testing There is now a "state machine playground" available at the following URL: `/app/logs/state-machine-playground`, it is enabled in developer mode only. It's not fancy or pretty it just serves to make testing things easier. There are various buttons, e.g. `Switch to inline Log View`, to facilitate easier testing whilst a Log View switcher is not in the UI itself. You can utilise these buttons, and then head to other pages to ensure things are working correctly, e.g. warning callouts and disabled buttons etc. If you'd like to play with the options used, e.g. for `update`, amend the code within `state_machine_playground.tsx`. It's also useful just to sit on this page, try various things, and verify what happens in the developer tools (does the context update correctly etc). ## Known issues - When saving on the settings page we actually revert to a "Loading data sources" state. This is also the case on `main`. The reason for this is the check within settings looks like so: ```ts if ((isLoading || isUninitialized) && !resolvedLogView) { return ; } ``` but the `resolvedLogView` state matching looks like so: ```ts const resolvedLogView = useSelector(logViewStateService, (state) => state.matches('checkingStatus') || state.matches('resolvedPersistedLogView') || state.matches('resolvedInlineLogView') ? state.context.resolvedLogView : undefined ); ``` so even though we have resolved a Log View previously the state matching overrides this. I'd prefer to follow this up in a separate issue as I'd like to think through the ramifications a bit more. It's not a bug, but it is jarring UX. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/infra/common/constants.ts | 1 - .../common/log_analysis/job_parameters.ts | 8 +- .../plugins/infra/common/log_views/types.ts | 1 + .../alert_details_app_section/index.tsx | 5 +- .../components/alert_dropdown.tsx | 41 +++- .../components/expression_editor/criteria.tsx | 13 +- .../criterion_preview_chart.tsx | 13 +- .../components/expression_editor/editor.tsx | 19 +- .../hooks/use_chart_preview_data.tsx | 13 +- .../components/lazy_alert_dropdown.tsx | 16 ++ .../public/alerting/log_threshold/index.ts | 2 +- .../components/log_stream/log_stream.tsx | 16 +- .../logging/inline_log_view_splash_page.tsx | 72 +++++++ .../log_entry_flyout/log_entry_flyout.tsx | 17 +- .../logs/log_analysis/api/ml_cleanup.ts | 12 +- .../api/ml_get_jobs_summary_api.ts | 6 +- .../log_analysis/log_analysis_cleanup.tsx | 16 +- .../logs/log_analysis/log_analysis_module.tsx | 24 +-- .../log_analysis_module_status.tsx | 14 +- .../log_analysis/log_analysis_module_types.ts | 6 +- .../log_entry_categories/module_descriptor.ts | 14 +- .../use_log_entry_categories_module.tsx | 8 +- .../log_entry_rate/module_descriptor.ts | 14 +- .../use_log_entry_rate_module.tsx | 8 +- .../infra/public/containers/logs/log_entry.ts | 11 +- .../log_highlights/log_entry_highlights.tsx | 7 +- .../logs/log_highlights/log_highlights.tsx | 9 +- .../log_highlights/log_summary_highlights.ts | 7 +- .../containers/logs/log_stream/index.ts | 10 +- .../log_stream/use_fetch_log_entries_after.ts | 18 +- .../use_fetch_log_entries_around.ts | 10 +- .../use_fetch_log_entries_before.ts | 18 +- .../logs/log_summary/log_summary.test.tsx | 25 ++- .../logs/log_summary/log_summary.tsx | 7 +- .../logs/log_summary/with_summary.ts | 4 +- .../view_log_in_context.ts | 7 +- .../public/containers/source_id/source_id.ts | 32 ---- .../infra/public/hooks/use_log_view.mock.ts | 10 +- .../infra/public/hooks/use_log_view.ts | 84 ++++++-- .../log_view_state/src/defaults.ts} | 6 +- .../log_view_state/src/index.ts | 2 + .../log_view_state/src/notifications.ts | 8 +- .../log_view_state/src/state_machine.ts | 181 +++++++++++++++--- .../log_view_state/src/types.ts | 56 ++++-- .../src/url_state_storage_service.ts | 135 +++++++++++++ .../xstate_helpers/src/index.ts | 1 + .../src/state_machine_playground.tsx | 81 ++++++++ .../pages/link_to/link_to_logs.test.tsx | 24 +-- .../public/pages/link_to/link_to_logs.tsx | 6 +- .../pages/link_to/redirect_to_logs.test.tsx | 6 +- .../public/pages/link_to/redirect_to_logs.tsx | 8 +- .../pages/link_to/redirect_to_node_logs.tsx | 10 +- .../log_entry_categories/page_providers.tsx | 19 +- .../page_results_content.tsx | 8 +- .../top_categories/category_details_row.tsx | 7 +- .../top_categories/top_categories_section.tsx | 7 +- .../top_categories/top_categories_table.tsx | 9 +- .../get_log_entry_category_datasets.ts | 7 +- .../get_log_entry_category_examples.ts | 7 +- .../get_top_log_entry_categories.ts | 7 +- .../use_log_entry_categories_results.ts | 13 +- .../use_log_entry_category_examples.tsx | 9 +- .../logs/log_entry_rate/page_providers.tsx | 22 ++- .../log_entry_rate/page_results_content.tsx | 10 +- .../sections/anomalies/expanded_row.tsx | 8 +- .../service_calls/get_log_entry_anomalies.ts | 7 +- .../get_log_entry_anomalies_datasets.ts | 7 +- .../service_calls/get_log_entry_examples.ts | 7 +- .../use_log_entry_anomalies_results.ts | 13 +- .../log_entry_rate/use_log_entry_examples.ts | 9 +- .../infra/public/pages/logs/page_content.tsx | 10 +- .../public/pages/logs/page_providers.tsx | 4 +- .../logs/settings/inline_log_view_callout.tsx | 47 +++++ .../source_configuration_settings.tsx | 21 +- .../pages/logs/stream/page_logs_content.tsx | 12 +- .../pages/logs/stream/page_providers.tsx | 12 +- .../logs/stream/page_view_log_in_context.tsx | 4 +- .../services/log_views/log_views_client.ts | 64 +++++-- .../infra/public/services/log_views/types.ts | 10 +- .../public/utils/logs_overview_fetchers.ts | 7 +- x-pack/test/functional/apps/infra/link_to.ts | 3 +- 81 files changed, 1097 insertions(+), 415 deletions(-) create mode 100644 x-pack/plugins/infra/public/alerting/log_threshold/components/lazy_alert_dropdown.tsx create mode 100644 x-pack/plugins/infra/public/components/logging/inline_log_view_splash_page.tsx delete mode 100644 x-pack/plugins/infra/public/containers/source_id/source_id.ts rename x-pack/plugins/infra/public/{containers/source_id/index.ts => observability_logs/log_view_state/src/defaults.ts} (63%) create mode 100644 x-pack/plugins/infra/public/observability_logs/log_view_state/src/url_state_storage_service.ts create mode 100644 x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/state_machine_playground.tsx create mode 100644 x-pack/plugins/infra/public/pages/logs/settings/inline_log_view_callout.tsx diff --git a/x-pack/plugins/infra/common/constants.ts b/x-pack/plugins/infra/common/constants.ts index 4c70e34c9899f..9fcb488e02fda 100644 --- a/x-pack/plugins/infra/common/constants.ts +++ b/x-pack/plugins/infra/common/constants.ts @@ -5,7 +5,6 @@ * 2.0. */ -export const DEFAULT_SOURCE_ID = 'default'; export const METRICS_INDEX_PATTERN = 'metrics-*,metricbeat-*'; export const LOGS_INDEX_PATTERN = 'logs-*,filebeat-*,kibana_sample_data_logs*'; export const METRICS_APP = 'metrics'; diff --git a/x-pack/plugins/infra/common/log_analysis/job_parameters.ts b/x-pack/plugins/infra/common/log_analysis/job_parameters.ts index b8fa429195ff3..1a695af80a4d5 100644 --- a/x-pack/plugins/infra/common/log_analysis/job_parameters.ts +++ b/x-pack/plugins/infra/common/log_analysis/job_parameters.ts @@ -16,11 +16,11 @@ export const partitionField = 'event.dataset'; export const getJobIdPrefix = (spaceId: string, sourceId: string) => `kibana-logs-ui-${spaceId}-${sourceId}-`; -export const getJobId = (spaceId: string, sourceId: string, jobType: string) => - `${getJobIdPrefix(spaceId, sourceId)}${jobType}`; +export const getJobId = (spaceId: string, logViewId: string, jobType: string) => + `${getJobIdPrefix(spaceId, logViewId)}${jobType}`; -export const getDatafeedId = (spaceId: string, sourceId: string, jobType: string) => - `datafeed-${getJobId(spaceId, sourceId, jobType)}`; +export const getDatafeedId = (spaceId: string, logViewId: string, jobType: string) => + `datafeed-${getJobId(spaceId, logViewId, jobType)}`; export const datasetFilterRT = rt.union([ rt.strict({ diff --git a/x-pack/plugins/infra/common/log_views/types.ts b/x-pack/plugins/infra/common/log_views/types.ts index 5bf9585bb1c2f..ca6dd95330f8c 100644 --- a/x-pack/plugins/infra/common/log_views/types.ts +++ b/x-pack/plugins/infra/common/log_views/types.ts @@ -14,6 +14,7 @@ export interface LogViewsStaticConfig { export const logViewOriginRT = rt.keyof({ stored: null, internal: null, + inline: null, 'infra-source-stored': null, 'infra-source-internal': null, 'infra-source-fallback': null, diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/index.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/index.tsx index ad5ef8a99f23b..bedd7a127394a 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/index.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/index.tsx @@ -42,7 +42,10 @@ const AlertDetailsAppSection = ({ rule, alert }: AlertDetailsAppSectionProps) => { const { services: { @@ -33,7 +48,9 @@ export const AlertDropdown = () => { observability, }, } = useKibanaContextForPlugin(); - const canCreateAlerts = capabilities?.logs?.save ?? false; + const { isPersistedLogView } = useLogViewContext(); + const readOnly = !capabilities?.logs?.save; + const canCreateAlerts = (!readOnly && isPersistedLogView) ?? false; const [popoverOpen, setPopoverOpen] = useState(false); const [flyoutVisible, setFlyoutVisible] = useState(false); @@ -61,8 +78,20 @@ export const AlertDropdown = () => { icon="bell" key="createLink" onClick={openFlyout} - toolTipContent={!canCreateAlerts ? readOnlyUserTooltipContent : undefined} - toolTipTitle={!canCreateAlerts ? readOnlyUserTooltipTitle : undefined} + toolTipContent={ + !canCreateAlerts + ? readOnly + ? readOnlyUserTooltipContent + : inlineLogViewTooltipContent + : undefined + } + toolTipTitle={ + !canCreateAlerts + ? readOnly + ? readOnlyUserTooltipTitle + : inlineLogViewTooltipTitle + : undefined + } > { /> , ]; - }, [manageRulesLinkProps, canCreateAlerts, openFlyout]); + }, [canCreateAlerts, openFlyout, readOnly, manageRulesLinkProps]); return ( <> @@ -104,3 +133,7 @@ export const AlertDropdown = () => { ); }; + +// Allow for lazy loading +// eslint-disable-next-line import/no-default-export +export default AlertDropdown; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx index f26bab6b65f56..c5af2938f5456 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx @@ -9,7 +9,10 @@ import React, { useCallback } from 'react'; import { EuiFlexItem, EuiFlexGroup, EuiButtonEmpty, EuiAccordion, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import type { ResolvedLogViewField } from '../../../../../common/log_views'; +import type { + PersistedLogViewReference, + ResolvedLogViewField, +} from '../../../../../common/log_views'; import { Criterion } from './criterion'; import { PartialRuleParams, @@ -39,7 +42,7 @@ interface SharedProps { defaultCriterion: PartialCriterionType; errors: Errors['criteria']; ruleParams: PartialRuleParams; - sourceId: string; + logViewReference: PersistedLogViewReference; updateCriteria: (criteria: PartialCriteriaType) => void; } @@ -64,7 +67,7 @@ interface CriteriaWrapperProps { addCriterion: () => void; criteria: PartialCountCriteriaType; errors: CriterionErrors; - sourceId: SharedProps['sourceId']; + logViewReference: SharedProps['logViewReference']; isRatio?: boolean; } @@ -77,7 +80,7 @@ const CriteriaWrapper: React.FC = (props) => { fields, errors, ruleParams, - sourceId, + logViewReference, isRatio = false, } = props; @@ -105,7 +108,7 @@ const CriteriaWrapper: React.FC = (props) => { diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx index e5ea692ce7792..01d96cebdad9b 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx @@ -21,6 +21,7 @@ import { import { EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { ExecutionTimeRange } from '../../../../types'; import { ChartContainer, @@ -55,7 +56,7 @@ const GROUP_LIMIT = 5; interface Props { ruleParams: PartialRuleParams; chartCriterion: Partial; - sourceId: string; + logViewReference: PersistedLogViewReference; showThreshold: boolean; executionTimeRange?: ExecutionTimeRange; } @@ -63,7 +64,7 @@ interface Props { export const CriterionPreview: React.FC = ({ ruleParams, chartCriterion, - sourceId, + logViewReference, showThreshold, executionTimeRange, }) => { @@ -105,7 +106,7 @@ export const CriterionPreview: React.FC = ({ ? NUM_BUCKETS : NUM_BUCKETS / 4 } // Display less data for groups due to space limitations - sourceId={sourceId} + logViewReference={logViewReference} threshold={ruleParams.count} chartAlertParams={chartAlertParams} showThreshold={showThreshold} @@ -116,7 +117,7 @@ export const CriterionPreview: React.FC = ({ interface ChartProps { buckets: number; - sourceId: string; + logViewReference: PersistedLogViewReference; threshold?: Threshold; chartAlertParams: GetLogAlertsChartPreviewDataAlertParamsSubset; showThreshold: boolean; @@ -125,7 +126,7 @@ interface ChartProps { const CriterionPreviewChart: React.FC = ({ buckets, - sourceId, + logViewReference, threshold, chartAlertParams, showThreshold, @@ -141,7 +142,7 @@ const CriterionPreviewChart: React.FC = ({ hasError, chartPreviewData: series, } = useChartPreviewData({ - sourceId, + logViewReference, ruleParams: chartAlertParams, buckets, executionTimeRange, diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx index 2f6f7a8583ae3..d9275a1ff0178 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx @@ -27,7 +27,6 @@ import { } from '../../../../../common/alerting/logs/log_threshold/types'; import { decodeOrThrow } from '../../../../../common/runtime_types'; import { ObjectEntries } from '../../../../../common/utility_types'; -import { useSourceId } from '../../../../containers/source_id'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { LogViewProvider, useLogViewContext } from '../../../../hooks/use_log_view'; import { GroupByExpression } from '../../../common/group_by_expression/group_by_expression'; @@ -54,11 +53,6 @@ const DEFAULT_BASE_EXPRESSION = { const DEFAULT_FIELD = 'log.level'; -const createLogViewReference = (logViewId: string): PersistedLogViewReference => ({ - logViewId, - type: 'log-view-reference', -}); - const createDefaultCriterion = ( availableFields: ResolvedLogViewField[], value: ExpressionCriteria['value'] @@ -100,7 +94,6 @@ export const ExpressionEditor: React.FC< RuleTypeParamsExpressionProps > = (props) => { const isInternal = props.metadata?.isInternal ?? false; - const [logViewId] = useSourceId(); const { services: { logViews }, } = useKibanaContextForPlugin(); // injected during alert registration @@ -112,7 +105,7 @@ export const ExpressionEditor: React.FC< ) : ( - + @@ -163,7 +156,11 @@ export const Editor: React.FC { const { setRuleParams, ruleParams, errors } = props; const [hasSetDefaults, setHasSetDefaults] = useState(false); - const { logViewId, resolvedLogView } = useLogViewContext(); + const { logViewReference, resolvedLogView } = useLogViewContext(); + + if (logViewReference.type !== 'log-view-reference') { + throw new Error('The Log Threshold rule type only supports persisted Log Views'); + } const { criteria: criteriaErrors, @@ -230,8 +227,6 @@ export const Editor: React.FC createLogViewReference(logViewId), [logViewId]); - const defaultCountAlertParams = useMemo( () => createDefaultCountRuleParams(supportedFields, logViewReference), [supportedFields, logViewReference] @@ -279,7 +274,7 @@ export const Editor: React.FC ) : null; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx index 913962f8703d1..82a1916734192 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx @@ -8,6 +8,7 @@ import { useState, useMemo } from 'react'; import { HttpHandler } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { PersistedLogViewReference } from '../../../../../../common/log_views'; import { ExecutionTimeRange } from '../../../../../types'; import { useTrackedPromise } from '../../../../../utils/use_tracked_promise'; import { @@ -20,14 +21,14 @@ import { decodeOrThrow } from '../../../../../../common/runtime_types'; import { GetLogAlertsChartPreviewDataAlertParamsSubset } from '../../../../../../common/http_api/log_alerts'; interface Options { - sourceId: string; + logViewReference: PersistedLogViewReference; ruleParams: GetLogAlertsChartPreviewDataAlertParamsSubset; buckets: number; executionTimeRange?: ExecutionTimeRange; } export const useChartPreviewData = ({ - sourceId, + logViewReference, ruleParams, buckets, executionTimeRange, @@ -43,7 +44,7 @@ export const useChartPreviewData = ({ createPromise: async () => { setHasError(false); return await callGetChartPreviewDataAPI( - sourceId, + logViewReference, http!.fetch, ruleParams, buckets, @@ -58,7 +59,7 @@ export const useChartPreviewData = ({ setHasError(true); }, }, - [sourceId, http, ruleParams, buckets] + [logViewReference, http, ruleParams, buckets] ); const isLoading = useMemo( @@ -75,7 +76,7 @@ export const useChartPreviewData = ({ }; export const callGetChartPreviewDataAPI = async ( - sourceId: string, + logViewReference: PersistedLogViewReference, fetch: HttpHandler, alertParams: GetLogAlertsChartPreviewDataAlertParamsSubset, buckets: number, @@ -86,7 +87,7 @@ export const callGetChartPreviewDataAPI = async ( body: JSON.stringify( getLogAlertsChartPreviewDataRequestPayloadRT.encode({ data: { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, alertParams, buckets, executionTimeRange, diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/lazy_alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/lazy_alert_dropdown.tsx new file mode 100644 index 0000000000000..902a3af8839e8 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/lazy_alert_dropdown.tsx @@ -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 React from 'react'; + +const LazyAlertDropdown = React.lazy(() => import('./alert_dropdown')); + +export const LazyAlertDropdownWrapper = () => ( + }> + + +); diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/index.ts b/x-pack/plugins/infra/public/alerting/log_threshold/index.ts index b6eff8ef3826e..588d6008ab639 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/index.ts +++ b/x-pack/plugins/infra/public/alerting/log_threshold/index.ts @@ -6,4 +6,4 @@ */ export * from './log_threshold_rule_type'; -export { AlertDropdown } from './components/alert_dropdown'; +export { LazyAlertDropdownWrapper } from './components/lazy_alert_dropdown'; diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx b/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx index 6a3e8bfb6352b..5ae7daf0731b7 100644 --- a/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx +++ b/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx @@ -13,7 +13,7 @@ import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { noop } from 'lodash'; import { LogEntryCursor } from '../../../common/log_entry'; -import { defaultLogViewsStaticConfig } from '../../../common/log_views'; +import { defaultLogViewsStaticConfig, LogViewReference } from '../../../common/log_views'; import { BuiltEsQuery, useLogStream } from '../../containers/logs/log_stream'; import { useLogView } from '../../hooks/use_log_view'; import { LogViewsClient } from '../../services/log_views'; @@ -63,14 +63,8 @@ type LogColumnDefinition = export interface LogStreamProps extends LogStreamContentProps { height?: string | number; } - -interface LogView { - type: 'log-view-reference'; - logViewId: string; -} - interface LogStreamContentProps { - logView: LogView; + logView: LogViewReference; startTimestamp: number; endTimestamp: number; query?: string | Query | BuiltEsQuery; @@ -120,7 +114,7 @@ Read more at https://github.com/elastic/kibana/blob/main/src/plugins/kibana_reac ); } - const { openLogEntryFlyout } = useLogEntryFlyout(logView.logViewId); + const { openLogEntryFlyout } = useLogEntryFlyout(logView); const kibanaQuerySettings = useKibanaQuerySettings(); @@ -135,7 +129,7 @@ Read more at https://github.com/elastic/kibana/blob/main/src/plugins/kibana_reac load: loadLogView, resolvedLogView, } = useLogView({ - logViewId: logView.logViewId, + initialLogViewReference: logView, logViews, }); @@ -166,7 +160,7 @@ Read more at https://github.com/elastic/kibana/blob/main/src/plugins/kibana_reac isLoadingMore, isReloading: isLoadingEntries, } = useLogStream({ - sourceId: logView.logViewId, + logViewReference: logView, startTimestamp, endTimestamp, query: parsedQuery, diff --git a/x-pack/plugins/infra/public/components/logging/inline_log_view_splash_page.tsx b/x-pack/plugins/infra/public/components/logging/inline_log_view_splash_page.tsx new file mode 100644 index 0000000000000..cebe7ea21d9c4 --- /dev/null +++ b/x-pack/plugins/infra/public/components/logging/inline_log_view_splash_page.tsx @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiButton } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { LazyObservabilityPageTemplateProps } from '@kbn/observability-plugin/public'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { EuiText } from '@elastic/eui'; +import { PageTemplate } from '../page_template'; + +type InlineLogViewSplashPageProps = { + revertToDefaultLogView: () => void; +} & LazyObservabilityPageTemplateProps; + +export const InlineLogViewSplashPage: React.FC = (props) => { + const { revertToDefaultLogView, ...templateProps } = props; + return ( + + + + ); +}; + +export const InlineLogViewSplashPrompt: React.FC<{ + revertToDefaultLogView: InlineLogViewSplashPageProps['revertToDefaultLogView']; +}> = ({ revertToDefaultLogView }) => { + const title = ( + + ); + + const ctaButton = ( + + + + ); + + const description = ( + + ); + + return ( + {title}} + body={ + +

{description}

+
+ } + actions={ctaButton} + /> + ); +}; diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx index d7b1b45e02425..619d83cf270c1 100644 --- a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_flyout.tsx @@ -23,6 +23,7 @@ import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public'; import { OverlayRef } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { LogViewReference } from '../../../../common/log_views'; import { TimeKey } from '../../../../common/time'; import { useLogEntry } from '../../../containers/logs/log_entry'; import { CenteredEuiFlyoutBody } from '../../centered_flyout_body'; @@ -35,10 +36,10 @@ export interface LogEntryFlyoutProps { logEntryId: string | null | undefined; onCloseFlyout: () => void; onSetFieldFilter?: (filter: Query, logEntryId: string, timeKey?: TimeKey) => void; - sourceId: string | null | undefined; + logViewReference: LogViewReference | null | undefined; } -export const useLogEntryFlyout = (sourceId: string) => { +export const useLogEntryFlyout = (logViewReference: LogViewReference) => { const flyoutRef = useRef(); const { services: { http, data, uiSettings, application }, @@ -63,12 +64,12 @@ export const useLogEntryFlyout = (sourceId: string) => { ); }, - [http, data, uiSettings, application, openFlyout, sourceId, closeLogEntryFlyout] + [http, data, uiSettings, application, openFlyout, logViewReference, closeLogEntryFlyout] ); useEffect(() => { @@ -87,7 +88,7 @@ export const LogEntryFlyout = ({ logEntryId, onCloseFlyout, onSetFieldFilter, - sourceId, + logViewReference, }: LogEntryFlyoutProps) => { const { cancelRequest: cancelLogEntryRequest, @@ -98,15 +99,15 @@ export const LogEntryFlyout = ({ logEntry, total: logEntryRequestTotal, } = useLogEntry({ - sourceId, + logViewReference, logEntryId, }); useEffect(() => { - if (sourceId && logEntryId) { + if (logViewReference && logEntryId) { fetchLogEntry(); } - }, [fetchLogEntry, sourceId, logEntryId]); + }, [fetchLogEntry, logViewReference, logEntryId]); return ( diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts index 17377e1484e26..2912cdf74a36e 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts @@ -13,7 +13,7 @@ import { decodeOrThrow } from '../../../../../common/runtime_types'; interface DeleteJobsRequestArgs { spaceId: string; - sourceId: string; + logViewId: string; jobTypes: JobType[]; } @@ -21,14 +21,14 @@ export const callDeleteJobs = async ( requestArgs: DeleteJobsRequestArgs, fetch: HttpHandler ) => { - const { spaceId, sourceId, jobTypes } = requestArgs; + const { spaceId, logViewId, jobTypes } = requestArgs; // NOTE: Deleting the jobs via this API will delete the datafeeds at the same time const deleteJobsResponse = await fetch('/api/ml/jobs/delete_jobs', { method: 'POST', body: JSON.stringify( deleteJobsRequestPayloadRT.encode({ - jobIds: jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)), + jobIds: jobTypes.map((jobType) => getJobId(spaceId, logViewId, jobType)), }) ), }); @@ -44,7 +44,7 @@ export const callGetJobDeletionTasks = async (fetch: HttpHandler) => { interface StopDatafeedsRequestArgs { spaceId: string; - sourceId: string; + logViewId: string; jobTypes: JobType[]; } @@ -52,14 +52,14 @@ export const callStopDatafeeds = async ( requestArgs: StopDatafeedsRequestArgs, fetch: HttpHandler ) => { - const { spaceId, sourceId, jobTypes } = requestArgs; + const { spaceId, logViewId, jobTypes } = requestArgs; // Stop datafeed due to https://github.com/elastic/kibana/issues/44652 const stopDatafeedResponse = await fetch('/api/ml/jobs/stop_datafeeds', { method: 'POST', body: JSON.stringify( stopDatafeedsRequestPayloadRT.encode({ - datafeedIds: jobTypes.map((jobType) => getDatafeedId(spaceId, sourceId, jobType)), + datafeedIds: jobTypes.map((jobType) => getDatafeedId(spaceId, logViewId, jobType)), }) ), }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts index 142304269687e..b737427b23912 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts @@ -13,7 +13,7 @@ import { decodeOrThrow } from '../../../../../common/runtime_types'; interface RequestArgs { spaceId: string; - sourceId: string; + logViewId: string; jobTypes: JobType[]; } @@ -21,12 +21,12 @@ export const callJobsSummaryAPI = async ( requestArgs: RequestArgs, fetch: HttpHandler ) => { - const { spaceId, sourceId, jobTypes } = requestArgs; + const { spaceId, logViewId, jobTypes } = requestArgs; const response = await fetch('/api/ml/jobs/jobs_summary', { method: 'POST', body: JSON.stringify( fetchJobStatusRequestPayloadRT.encode({ - jobIds: jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)), + jobIds: jobTypes.map((jobType) => getJobId(spaceId, logViewId, jobType)), }) ), }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx index 7269a530409f6..2a5493f153ff3 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx @@ -11,12 +11,12 @@ import { callDeleteJobs, callGetJobDeletionTasks, callStopDatafeeds } from './ap export const cleanUpJobsAndDatafeeds = async ( spaceId: string, - sourceId: string, + logViewId: string, jobTypes: JobType[], fetch: HttpHandler ) => { try { - await callStopDatafeeds({ spaceId, sourceId, jobTypes }, fetch); + await callStopDatafeeds({ spaceId, logViewId, jobTypes }, fetch); } catch (err) { // Proceed only if datafeed has been deleted or didn't exist in the first place if (err?.response?.status !== 404) { @@ -24,27 +24,27 @@ export const cleanUpJobsAndDatafeeds = async ( } } - return await deleteJobs(spaceId, sourceId, jobTypes, fetch); + return await deleteJobs(spaceId, logViewId, jobTypes, fetch); }; const deleteJobs = async ( spaceId: string, - sourceId: string, + logViewId: string, jobTypes: JobType[], fetch: HttpHandler ) => { - const deleteJobsResponse = await callDeleteJobs({ spaceId, sourceId, jobTypes }, fetch); - await waitUntilJobsAreDeleted(spaceId, sourceId, jobTypes, fetch); + const deleteJobsResponse = await callDeleteJobs({ spaceId, logViewId, jobTypes }, fetch); + await waitUntilJobsAreDeleted(spaceId, logViewId, jobTypes, fetch); return deleteJobsResponse; }; const waitUntilJobsAreDeleted = async ( spaceId: string, - sourceId: string, + logViewId: string, jobTypes: JobType[], fetch: HttpHandler ) => { - const moduleJobIds = jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)); + const moduleJobIds = jobTypes.map((jobType) => getJobId(spaceId, logViewId, jobType)); while (true) { const { jobIds: jobIdsBeingDeleted } = await callGetJobDeletionTasks(fetch); const needToWait = jobIdsBeingDeleted.some((jobId) => moduleJobIds.includes(jobId)); diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx index 29e5508b24d38..3695c6952cd16 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx @@ -21,7 +21,7 @@ export const useLogAnalysisModule = ({ moduleDescriptor: ModuleDescriptor; }) => { const { services } = useKibanaContextForPlugin(); - const { spaceId, sourceId, timestampField, runtimeMappings } = sourceConfiguration; + const { spaceId, sourceId: logViewId, timestampField, runtimeMappings } = sourceConfiguration; const [moduleStatus, dispatchModuleStatus] = useModuleStatus(moduleDescriptor.jobTypes); const trackMetric = useUiTracker({ app: 'infra_logs' }); @@ -31,21 +31,21 @@ export const useLogAnalysisModule = ({ cancelPreviousOn: 'resolution', createPromise: async () => { dispatchModuleStatus({ type: 'fetchingJobStatuses' }); - return await moduleDescriptor.getJobSummary(spaceId, sourceId, services.http.fetch); + return await moduleDescriptor.getJobSummary(spaceId, logViewId, services.http.fetch); }, onResolve: (jobResponse) => { dispatchModuleStatus({ type: 'fetchedJobStatuses', payload: jobResponse, spaceId, - sourceId, + logViewId, }); }, onReject: () => { dispatchModuleStatus({ type: 'failedFetchingJobStatuses' }); }, }, - [spaceId, sourceId] + [spaceId, logViewId] ); const [, setUpModule] = useTrackedPromise( @@ -64,7 +64,7 @@ export const useLogAnalysisModule = ({ datasetFilter, { indices: selectedIndices, - sourceId, + sourceId: logViewId, spaceId, timestampField, runtimeMappings, @@ -73,7 +73,7 @@ export const useLogAnalysisModule = ({ ); const jobSummaries = await moduleDescriptor.getJobSummary( spaceId, - sourceId, + logViewId, services.http.fetch ); return { setupResult, jobSummaries }; @@ -104,7 +104,7 @@ export const useLogAnalysisModule = ({ jobSetupResults: jobs, jobSummaries, spaceId, - sourceId, + logViewId, }); }, onReject: (e: any) => { @@ -114,17 +114,17 @@ export const useLogAnalysisModule = ({ } }, }, - [moduleDescriptor.setUpModule, spaceId, sourceId, timestampField] + [moduleDescriptor.setUpModule, spaceId, logViewId, timestampField] ); const [cleanUpModuleRequest, cleanUpModule] = useTrackedPromise( { cancelPreviousOn: 'resolution', createPromise: async () => { - return await moduleDescriptor.cleanUpModule(spaceId, sourceId, services.http.fetch); + return await moduleDescriptor.cleanUpModule(spaceId, logViewId, services.http.fetch); }, }, - [spaceId, sourceId] + [spaceId, logViewId] ); const isCleaningUp = useMemo( @@ -156,8 +156,8 @@ export const useLogAnalysisModule = ({ }, [dispatchModuleStatus]); const jobIds = useMemo( - () => moduleDescriptor.getJobIds(spaceId, sourceId), - [moduleDescriptor, spaceId, sourceId] + () => moduleDescriptor.getJobIds(spaceId, logViewId), + [moduleDescriptor, spaceId, logViewId] ); return { diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx index 5c4623075dec2..90f36b40d14d7 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_status.tsx @@ -29,7 +29,7 @@ type StatusReducerAction = | { type: 'startedSetup' } | { type: 'finishedSetup'; - sourceId: string; + logViewId: string; spaceId: string; jobSetupResults: SetupMlModuleResponsePayload['jobs']; jobSummaries: FetchJobStatusResponsePayload; @@ -40,7 +40,7 @@ type StatusReducerAction = | { type: 'fetchedJobStatuses'; spaceId: string; - sourceId: string; + logViewId: string; payload: FetchJobStatusResponsePayload; } | { type: 'failedFetchingJobStatuses' } @@ -84,13 +84,13 @@ const createStatusReducer = }; } case 'finishedSetup': { - const { datafeedSetupResults, jobSetupResults, jobSummaries, spaceId, sourceId } = action; + const { datafeedSetupResults, jobSetupResults, jobSummaries, spaceId, logViewId } = action; const nextJobStatus = jobTypes.reduce( (accumulatedJobStatus, jobType) => ({ ...accumulatedJobStatus, [jobType]: - hasSuccessfullyCreatedJob(getJobId(spaceId, sourceId, jobType))(jobSetupResults) && - hasSuccessfullyStartedDatafeed(getDatafeedId(spaceId, sourceId, jobType))( + hasSuccessfullyCreatedJob(getJobId(spaceId, logViewId, jobType))(jobSetupResults) && + hasSuccessfullyStartedDatafeed(getDatafeedId(spaceId, logViewId, jobType))( datafeedSetupResults ) ? 'started' @@ -142,13 +142,13 @@ const createStatusReducer = }; } case 'fetchedJobStatuses': { - const { payload: jobSummaries, spaceId, sourceId } = action; + const { payload: jobSummaries, spaceId, logViewId } = action; const { setupStatus } = state; const nextJobStatus = jobTypes.reduce( (accumulatedJobStatus, jobType) => ({ ...accumulatedJobStatus, - [jobType]: getJobStatus(getJobId(spaceId, sourceId, jobType))(jobSummaries), + [jobType]: getJobStatus(getJobId(spaceId, logViewId, jobType))(jobSummaries), }), {} as Record ); diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts index b26916a22f7b0..d82b8f66c8b77 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts @@ -25,10 +25,10 @@ export interface ModuleDescriptor { moduleDescription: string; jobTypes: JobType[]; bucketSpan: number; - getJobIds: (spaceId: string, sourceId: string) => Record; + getJobIds: (spaceId: string, logViewId: string) => Record; getJobSummary: ( spaceId: string, - sourceId: string, + logViewId: string, fetch: HttpHandler ) => Promise; getModuleDefinition: (fetch: HttpHandler) => Promise; @@ -41,7 +41,7 @@ export interface ModuleDescriptor { ) => Promise; cleanUpModule: ( spaceId: string, - sourceId: string, + logViewId: string, fetch: HttpHandler ) => Promise; validateSetupIndices: ( diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts index 02e977dfadd32..38a1c5c907528 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts @@ -36,21 +36,21 @@ const moduleDescription = i18n.translate( } ); -const getJobIds = (spaceId: string, sourceId: string) => +const getJobIds = (spaceId: string, logViewId: string) => logEntryCategoriesJobTypes.reduce( (accumulatedJobIds, jobType) => ({ ...accumulatedJobIds, - [jobType]: getJobId(spaceId, sourceId, jobType), + [jobType]: getJobId(spaceId, logViewId, jobType), }), {} as Record ); -const getJobSummary = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { +const getJobSummary = async (spaceId: string, logViewId: string, fetch: HttpHandler) => { const response = await callJobsSummaryAPI( - { spaceId, sourceId, jobTypes: logEntryCategoriesJobTypes }, + { spaceId, logViewId, jobTypes: logEntryCategoriesJobTypes }, fetch ); - const jobIds = Object.values(getJobIds(spaceId, sourceId)); + const jobIds = Object.values(getJobIds(spaceId, logViewId)); return response.filter((jobSummary) => jobIds.includes(jobSummary.id)); }; @@ -130,8 +130,8 @@ const setUpModule = async ( ); }; -const cleanUpModule = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { - return await cleanUpJobsAndDatafeeds(spaceId, sourceId, logEntryCategoriesJobTypes, fetch); +const cleanUpModule = async (spaceId: string, logViewId: string, fetch: HttpHandler) => { + return await cleanUpJobsAndDatafeeds(spaceId, logViewId, logEntryCategoriesJobTypes, fetch); }; const validateSetupIndices = async ( diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_module.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_module.tsx index 433a24cffd937..3f4d802940974 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_module.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/use_log_entry_categories_module.tsx @@ -17,13 +17,13 @@ import { useLogEntryCategoriesQuality } from './use_log_entry_categories_quality export const useLogEntryCategoriesModule = ({ indexPattern, - sourceId, + logViewId, spaceId, timestampField, runtimeMappings, }: { indexPattern: string; - sourceId: string; + logViewId: string; spaceId: string; timestampField: string; runtimeMappings: estypes.MappingRuntimeFields; @@ -31,12 +31,12 @@ export const useLogEntryCategoriesModule = ({ const sourceConfiguration: ModuleSourceConfiguration = useMemo( () => ({ indices: indexPattern.split(','), - sourceId, + sourceId: logViewId, spaceId, timestampField, runtimeMappings, }), - [indexPattern, sourceId, spaceId, timestampField, runtimeMappings] + [indexPattern, logViewId, spaceId, timestampField, runtimeMappings] ); const logAnalysisModule = useLogAnalysisModule({ diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts index 8acf7ce9d9a5b..8cb0bb8a21799 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts @@ -35,21 +35,21 @@ const moduleDescription = i18n.translate( } ); -const getJobIds = (spaceId: string, sourceId: string) => +const getJobIds = (spaceId: string, logViewId: string) => logEntryRateJobTypes.reduce( (accumulatedJobIds, jobType) => ({ ...accumulatedJobIds, - [jobType]: getJobId(spaceId, sourceId, jobType), + [jobType]: getJobId(spaceId, logViewId, jobType), }), {} as Record ); -const getJobSummary = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { +const getJobSummary = async (spaceId: string, logViewId: string, fetch: HttpHandler) => { const response = await callJobsSummaryAPI( - { spaceId, sourceId, jobTypes: logEntryRateJobTypes }, + { spaceId, logViewId, jobTypes: logEntryRateJobTypes }, fetch ); - const jobIds = Object.values(getJobIds(spaceId, sourceId)); + const jobIds = Object.values(getJobIds(spaceId, logViewId)); return response.filter((jobSummary) => jobIds.includes(jobSummary.id)); }; @@ -122,8 +122,8 @@ const setUpModule = async ( ); }; -const cleanUpModule = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { - return await cleanUpJobsAndDatafeeds(spaceId, sourceId, logEntryRateJobTypes, fetch); +const cleanUpModule = async (spaceId: string, logViewId: string, fetch: HttpHandler) => { + return await cleanUpJobsAndDatafeeds(spaceId, logViewId, logEntryRateJobTypes, fetch); }; const validateSetupIndices = async ( diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/use_log_entry_rate_module.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/use_log_entry_rate_module.tsx index 46587969ca2ab..65bddee00ce30 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/use_log_entry_rate_module.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/use_log_entry_rate_module.tsx @@ -16,13 +16,13 @@ import { logEntryRateModule } from './module_descriptor'; export const useLogEntryRateModule = ({ indexPattern, - sourceId, + logViewId, spaceId, timestampField, runtimeMappings, }: { indexPattern: string; - sourceId: string; + logViewId: string; spaceId: string; timestampField: string; runtimeMappings: estypes.MappingRuntimeFields; @@ -30,12 +30,12 @@ export const useLogEntryRateModule = ({ const sourceConfiguration: ModuleSourceConfiguration = useMemo( () => ({ indices: indexPattern.split(','), - sourceId, + sourceId: logViewId, spaceId, timestampField, runtimeMappings, }), - [indexPattern, sourceId, spaceId, timestampField, runtimeMappings] + [indexPattern, logViewId, spaceId, timestampField, runtimeMappings] ); const logAnalysisModule = useLogAnalysisModule({ diff --git a/x-pack/plugins/infra/public/containers/logs/log_entry.ts b/x-pack/plugins/infra/public/containers/logs/log_entry.ts index 958097fc3baa5..51b98329ddd5e 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entry.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entry.ts @@ -6,6 +6,7 @@ */ import { useCallback } from 'react'; +import { LogViewReference } from '../../../common/log_views'; import { decodeOrThrow } from '../../../common/runtime_types'; import { logEntrySearchRequestParamsRT, @@ -19,26 +20,26 @@ import { } from '../../utils/data_search'; export const useLogEntry = ({ - sourceId, + logViewReference, logEntryId, }: { - sourceId: string | null | undefined; + logViewReference: LogViewReference | null | undefined; logEntryId: string | null | undefined; }) => { const { search: fetchLogEntry, requests$: logEntrySearchRequests$ } = useDataSearch({ getRequest: useCallback(() => { - return !!logEntryId && !!sourceId + return !!logEntryId && !!logViewReference ? { request: { params: logEntrySearchRequestParamsRT.encode({ - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, logEntryId, }), }, options: { strategy: LOG_ENTRY_SEARCH_STRATEGY }, } : null; - }, [sourceId, logEntryId]), + }, [logViewReference, logEntryId]), parseResponses: parseLogEntrySearchResponses, }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx index d9f7ab078ae0b..ea0e1fa326c78 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx @@ -6,6 +6,7 @@ */ import { useEffect, useMemo, useState } from 'react'; +import { LogViewReference } from '../../../../common/log_views'; import { LogEntriesHighlightsResponse } from '../../../../common/http_api'; import { LogEntry } from '../../../../common/log_entry'; import { TimeKey } from '../../../../common/time'; @@ -14,7 +15,7 @@ import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { fetchLogEntriesHighlights } from './api/fetch_log_entries_highlights'; export const useLogEntryHighlights = ( - sourceId: string, + logViewReference: LogViewReference, sourceVersion: string | undefined, startTimestamp: number | null, endTimestamp: number | null, @@ -37,7 +38,7 @@ export const useLogEntryHighlights = ( return await fetchLogEntriesHighlights( { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, startTimestamp, endTimestamp, center: centerPoint, @@ -52,7 +53,7 @@ export const useLogEntryHighlights = ( setLogEntryHighlights(response.data); }, }, - [sourceId, startTimestamp, endTimestamp, centerPoint, size, filterQuery, highlightTerms] + [logViewReference, startTimestamp, endTimestamp, centerPoint, size, filterQuery, highlightTerms] ); useEffect(() => { diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx index 9ca6858c3450e..0a6710731bcb1 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_highlights.tsx @@ -8,6 +8,7 @@ import createContainer from 'constate'; import { useState } from 'react'; import useThrottle from 'react-use/lib/useThrottle'; +import { LogViewReference } from '../../../../common/log_views'; import { useLogEntryHighlights } from './log_entry_highlights'; import { useLogSummaryHighlights } from './log_summary_highlights'; import { useNextAndPrevious } from './next_and_previous'; @@ -17,7 +18,7 @@ import { TimeKey } from '../../../../common/time'; const FETCH_THROTTLE_INTERVAL = 3000; interface UseLogHighlightsStateProps { - sourceId: string; + logViewReference: LogViewReference; sourceVersion: string | undefined; centerCursor: TimeKey | null; size: number; @@ -25,7 +26,7 @@ interface UseLogHighlightsStateProps { } export const useLogHighlightsState = ({ - sourceId, + logViewReference, sourceVersion, centerCursor, size, @@ -40,7 +41,7 @@ export const useLogHighlightsState = ({ const { logEntryHighlights, logEntryHighlightsById, loadLogEntryHighlightsRequest } = useLogEntryHighlights( - sourceId, + logViewReference, sourceVersion, throttledStartTimestamp, throttledEndTimestamp, @@ -51,7 +52,7 @@ export const useLogHighlightsState = ({ ); const { logSummaryHighlights, loadLogSummaryHighlightsRequest } = useLogSummaryHighlights( - sourceId, + logViewReference, sourceVersion, throttledStartTimestamp, throttledEndTimestamp, diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts index e994e2a013bb7..8c5f7fb7ae778 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts @@ -8,6 +8,7 @@ import { useEffect, useMemo, useState } from 'react'; import { debounce } from 'lodash'; +import { LogViewReference } from '../../../../common/log_views'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { fetchLogSummaryHighlights } from './api/fetch_log_summary_highlights'; import { LogEntriesSummaryHighlightsResponse } from '../../../../common/http_api'; @@ -15,7 +16,7 @@ import { useBucketSize } from '../log_summary/bucket_size'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; export const useLogSummaryHighlights = ( - sourceId: string, + logViewReference: LogViewReference, sourceVersion: string | undefined, startTimestamp: number | null, endTimestamp: number | null, @@ -39,7 +40,7 @@ export const useLogSummaryHighlights = ( return await fetchLogSummaryHighlights( { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, startTimestamp, endTimestamp, bucketSize, @@ -53,7 +54,7 @@ export const useLogSummaryHighlights = ( setLogSummaryHighlights(response.data); }, }, - [sourceId, startTimestamp, endTimestamp, bucketSize, filterQuery, highlightTerms] + [logViewReference, startTimestamp, endTimestamp, bucketSize, filterQuery, highlightTerms] ); const debouncedLoadSummaryHighlights = useMemo( diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts b/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts index 2a81afa234729..e1b425b112f8c 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts @@ -12,7 +12,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; import useSetState from 'react-use/lib/useSetState'; import { LogEntry, LogEntryCursor } from '../../../../common/log_entry'; -import { LogViewColumnConfiguration } from '../../../../common/log_views'; +import { LogViewColumnConfiguration, LogViewReference } from '../../../../common/log_views'; import { useSubscription } from '../../../utils/use_observable'; import { useFetchLogEntriesAfter } from './use_fetch_log_entries_after'; import { useFetchLogEntriesAround } from './use_fetch_log_entries_around'; @@ -21,7 +21,7 @@ import { useFetchLogEntriesBefore } from './use_fetch_log_entries_before'; export type BuiltEsQuery = ReturnType; interface LogStreamProps { - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; endTimestamp: number; query?: BuiltEsQuery; @@ -52,7 +52,7 @@ const INITIAL_STATE: LogStreamState = { const LOG_ENTRIES_CHUNK_SIZE = 200; export function useLogStream({ - sourceId, + logViewReference, startTimestamp, endTimestamp, query, @@ -85,13 +85,13 @@ export function useLogStream({ const commonFetchArguments = useMemo( () => ({ - sourceId, + logViewReference, startTimestamp, endTimestamp, query: cachedQuery, columnOverrides: columns, }), - [columns, endTimestamp, cachedQuery, sourceId, startTimestamp] + [columns, endTimestamp, cachedQuery, logViewReference, startTimestamp] ); const { diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_after.ts b/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_after.ts index 3ee39fbda3d49..168d2e8577b85 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_after.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_after.ts @@ -11,7 +11,7 @@ import { Observable } from 'rxjs'; import { exhaustMap } from 'rxjs/operators'; import { IKibanaSearchRequest } from '@kbn/data-plugin/public'; import { LogEntryAfterCursor } from '../../../../common/log_entry'; -import { LogViewColumnConfiguration } from '../../../../common/log_views'; +import { LogViewColumnConfiguration, LogViewReference } from '../../../../common/log_views'; import { decodeOrThrow } from '../../../../common/runtime_types'; import { logEntriesSearchRequestParamsRT, @@ -34,21 +34,21 @@ export const useLogEntriesAfterRequest = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }: { columnOverrides?: LogViewColumnConfiguration[]; endTimestamp: number; highlightPhrase?: string; query?: LogEntriesSearchRequestQuery; - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; }) => { const { search: fetchLogEntriesAfter, requests$: logEntriesAfterSearchRequests$ } = useDataSearch( { getRequest: useCallback( (cursor: LogEntryAfterCursor['after'], params: { size: number; extendTo?: number }) => { - return !!sourceId + return !!logViewReference ? { request: { params: logEntriesSearchRequestParamsRT.encode({ @@ -58,7 +58,7 @@ export const useLogEntriesAfterRequest = ({ highlightPhrase, query: query as JsonObject, size: params.size, - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, startTimestamp, }), }, @@ -66,7 +66,7 @@ export const useLogEntriesAfterRequest = ({ } : null; }, - [columnOverrides, endTimestamp, highlightPhrase, query, sourceId, startTimestamp] + [columnOverrides, endTimestamp, highlightPhrase, query, logViewReference, startTimestamp] ), parseResponses: parseLogEntriesAfterSearchResponses, } @@ -107,14 +107,14 @@ export const useFetchLogEntriesAfter = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }: { columnOverrides?: LogViewColumnConfiguration[]; endTimestamp: number; highlightPhrase?: string; query?: LogEntriesSearchRequestQuery; - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; }) => { const { fetchLogEntriesAfter, logEntriesAfterSearchRequests$ } = useLogEntriesAfterRequest({ @@ -122,7 +122,7 @@ export const useFetchLogEntriesAfter = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_around.ts b/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_around.ts index 748281d5517a7..a6bd8ba794288 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_around.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_around.ts @@ -9,7 +9,7 @@ import { useCallback } from 'react'; import { combineLatest, Observable, ReplaySubject } from 'rxjs'; import { last, map, startWith, switchMap } from 'rxjs/operators'; import { LogEntryCursor } from '../../../../common/log_entry'; -import { LogViewColumnConfiguration } from '../../../../common/log_views'; +import { LogViewColumnConfiguration, LogViewReference } from '../../../../common/log_views'; import { LogEntriesSearchRequestQuery } from '../../../../common/search_strategies/log_entries/log_entries'; import { flattenDataSearchResponseDescriptor } from '../../../utils/data_search'; import { useObservable, useObservableState } from '../../../utils/use_observable'; @@ -21,14 +21,14 @@ export const useFetchLogEntriesAround = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }: { columnOverrides?: LogViewColumnConfiguration[]; endTimestamp: number; highlightPhrase?: string; query?: LogEntriesSearchRequestQuery; - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; }) => { const { fetchLogEntriesBefore } = useLogEntriesBeforeRequest({ @@ -36,7 +36,7 @@ export const useFetchLogEntriesAround = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }); @@ -45,7 +45,7 @@ export const useFetchLogEntriesAround = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_before.ts b/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_before.ts index 581d31a28a8c5..a745b5f13a19f 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_before.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_stream/use_fetch_log_entries_before.ts @@ -11,7 +11,7 @@ import { Observable } from 'rxjs'; import { exhaustMap } from 'rxjs/operators'; import { IKibanaSearchRequest } from '@kbn/data-plugin/public'; import { LogEntryBeforeCursor } from '../../../../common/log_entry'; -import { LogViewColumnConfiguration } from '../../../../common/log_views'; +import { LogViewColumnConfiguration, LogViewReference } from '../../../../common/log_views'; import { decodeOrThrow } from '../../../../common/runtime_types'; import { logEntriesSearchRequestParamsRT, @@ -34,21 +34,21 @@ export const useLogEntriesBeforeRequest = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }: { columnOverrides?: LogViewColumnConfiguration[]; endTimestamp: number; highlightPhrase?: string; query?: LogEntriesSearchRequestQuery; - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; }) => { const { search: fetchLogEntriesBefore, requests$: logEntriesBeforeSearchRequests$ } = useDataSearch({ getRequest: useCallback( (cursor: LogEntryBeforeCursor['before'], params: { size: number; extendTo?: number }) => { - return !!sourceId + return !!logViewReference ? { request: { params: logEntriesSearchRequestParamsRT.encode({ @@ -58,7 +58,7 @@ export const useLogEntriesBeforeRequest = ({ highlightPhrase, query: query as JsonObject, size: params.size, - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, startTimestamp: params.extendTo ?? startTimestamp, }), }, @@ -66,7 +66,7 @@ export const useLogEntriesBeforeRequest = ({ } : null; }, - [columnOverrides, endTimestamp, highlightPhrase, query, sourceId, startTimestamp] + [columnOverrides, endTimestamp, highlightPhrase, query, logViewReference, startTimestamp] ), parseResponses: parseLogEntriesBeforeSearchResponses, }); @@ -106,14 +106,14 @@ export const useFetchLogEntriesBefore = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }: { columnOverrides?: LogViewColumnConfiguration[]; endTimestamp: number; highlightPhrase?: string; query?: LogEntriesSearchRequestQuery; - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; }) => { const { fetchLogEntriesBefore, logEntriesBeforeSearchRequests$ } = useLogEntriesBeforeRequest({ @@ -121,7 +121,7 @@ export const useFetchLogEntriesBefore = ({ endTimestamp, highlightPhrase, query, - sourceId, + logViewReference, startTimestamp, }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx index fe02367e4b1ff..183950edf9777 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx @@ -14,6 +14,12 @@ import { useLogSummary } from './log_summary'; import { fetchLogSummary } from './api/fetch_log_summary'; import { datemathToEpochMillis } from '../../../utils/datemath'; +const LOG_VIEW_REFERENCE = { type: 'log-view-reference' as const, logViewId: 'LOG_VIEW_ID' }; +const CHANGED_LOG_VIEW_REFERENCE = { + type: 'log-view-reference' as const, + logViewId: 'CHANGED_LOG_VIEW_ID', +}; + // Typescript doesn't know that `fetchLogSummary` is a jest mock. // We use a second variable with a type cast to help the compiler further down the line. jest.mock('./api/fetch_log_summary', () => ({ fetchLogSummary: jest.fn() })); @@ -32,7 +38,7 @@ describe('useLogSummary hook', () => { }); it('provides an empty list of buckets by default', () => { - const { result } = renderHook(() => useLogSummary('SOURCE_ID', null, null, null)); + const { result } = renderHook(() => useLogSummary(LOG_VIEW_REFERENCE, null, null, null)); expect(result.current.buckets).toEqual([]); }); @@ -51,9 +57,9 @@ describe('useLogSummary hook', () => { .mockResolvedValueOnce(secondMockResponse); const { result, waitForNextUpdate, rerender } = renderHook( - ({ sourceId }) => useLogSummary(sourceId, startTimestamp, endTimestamp, null), + ({ logViewReference }) => useLogSummary(logViewReference, startTimestamp, endTimestamp, null), { - initialProps: { sourceId: 'INITIAL_SOURCE_ID' }, + initialProps: { logViewReference: LOG_VIEW_REFERENCE }, } ); @@ -62,19 +68,19 @@ describe('useLogSummary hook', () => { expect(fetchLogSummaryMock).toHaveBeenCalledTimes(1); expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - logView: { logViewId: 'INITIAL_SOURCE_ID', type: 'log-view-reference' }, + logView: LOG_VIEW_REFERENCE, }), expect.anything() ); expect(result.current.buckets).toEqual(firstMockResponse.data.buckets); - rerender({ sourceId: 'CHANGED_SOURCE_ID' }); + rerender({ logViewReference: CHANGED_LOG_VIEW_REFERENCE }); await waitForNextUpdate(); expect(fetchLogSummaryMock).toHaveBeenCalledTimes(2); expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ - logView: { logViewId: 'CHANGED_SOURCE_ID', type: 'log-view-reference' }, + logView: CHANGED_LOG_VIEW_REFERENCE, }), expect.anything() ); @@ -96,7 +102,8 @@ describe('useLogSummary hook', () => { .mockResolvedValueOnce(secondMockResponse); const { result, waitForNextUpdate, rerender } = renderHook( - ({ filterQuery }) => useLogSummary('SOURCE_ID', startTimestamp, endTimestamp, filterQuery), + ({ filterQuery }) => + useLogSummary(LOG_VIEW_REFERENCE, startTimestamp, endTimestamp, filterQuery), { initialProps: { filterQuery: 'INITIAL_FILTER_QUERY' }, } @@ -134,7 +141,7 @@ describe('useLogSummary hook', () => { const firstRange = createMockDateRange(); const { waitForNextUpdate, rerender } = renderHook( ({ startTimestamp, endTimestamp }) => - useLogSummary('SOURCE_ID', startTimestamp, endTimestamp, null), + useLogSummary(LOG_VIEW_REFERENCE, startTimestamp, endTimestamp, null), { initialProps: firstRange, } @@ -171,7 +178,7 @@ describe('useLogSummary hook', () => { const firstRange = createMockDateRange(); const { waitForNextUpdate, rerender } = renderHook( ({ startTimestamp, endTimestamp }) => - useLogSummary('SOURCE_ID', startTimestamp, endTimestamp, null), + useLogSummary(LOG_VIEW_REFERENCE, startTimestamp, endTimestamp, null), { initialProps: firstRange, } diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx index c4b933ab04cd0..c2792300c5f34 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx @@ -8,6 +8,7 @@ import { useEffect } from 'react'; import { exhaustMap, map, Observable } from 'rxjs'; import { HttpHandler } from '@kbn/core-http-browser'; +import { LogViewReference } from '../../../../common/log_views'; import { useObservableState, useReplaySubject } from '../../../utils/use_observable'; import { fetchLogSummary } from './api/fetch_log_summary'; import { LogEntriesSummaryRequest, LogEntriesSummaryResponse } from '../../../../common/http_api'; @@ -17,7 +18,7 @@ import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; export type LogSummaryBuckets = LogEntriesSummaryResponse['data']['buckets']; export const useLogSummary = ( - sourceId: string, + logViewReference: LogViewReference, startTimestamp: number | null, endTimestamp: number | null, filterQuery: string | null @@ -35,7 +36,7 @@ export const useLogSummary = ( pushLogSummaryBucketsArgs([ { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, startTimestamp, endTimestamp, bucketSize, @@ -49,7 +50,7 @@ export const useLogSummary = ( filterQuery, pushLogSummaryBucketsArgs, services.http.fetch, - sourceId, + logViewReference, startTimestamp, ]); diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts index 102999a199530..1dc5c7021d253 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts @@ -25,7 +25,7 @@ export const WithSummary = ({ end: number | null; }>; }) => { - const { logViewId } = useLogViewContext(); + const { logViewReference } = useLogViewContext(); const serializedParsedQuery = useSelector(useLogStreamPageStateContext(), (logStreamPageState) => logStreamPageState.matches({ hasLogViewIndices: 'initialized' }) ? stringify(logStreamPageState.context.parsedQuery) @@ -38,7 +38,7 @@ export const WithSummary = ({ const throttledEndTimestamp = useThrottle(endTimestamp, FETCH_THROTTLE_INTERVAL); const { buckets, start, end } = useLogSummary( - logViewId, + logViewReference, throttledStartTimestamp, throttledEndTimestamp, serializedParsedQuery diff --git a/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts b/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts index b22a73d77cc54..1ea8f71da129e 100644 --- a/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts +++ b/x-pack/plugins/infra/public/containers/logs/view_log_in_context/view_log_in_context.ts @@ -7,10 +7,11 @@ import { useState } from 'react'; import createContainer from 'constate'; +import { LogViewReference } from '../../../../common/log_views'; import { LogEntry } from '../../../../common/log_entry'; interface ViewLogInContextProps { - sourceId: string; + logViewReference: LogViewReference; startTimestamp: number; endTimestamp: number; } @@ -27,13 +28,13 @@ export const useViewLogInContext = ( props: ViewLogInContextProps ): [ViewLogInContextState, ViewLogInContextCallbacks] => { const [contextEntry, setContextEntry] = useState(); - const { startTimestamp, endTimestamp, sourceId } = props; + const { startTimestamp, endTimestamp, logViewReference } = props; return [ { startTimestamp, endTimestamp, - sourceId, + logViewReference, contextEntry, }, { diff --git a/x-pack/plugins/infra/public/containers/source_id/source_id.ts b/x-pack/plugins/infra/public/containers/source_id/source_id.ts deleted file mode 100644 index f7c7c11b87fe2..0000000000000 --- a/x-pack/plugins/infra/public/containers/source_id/source_id.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 * as runtimeTypes from 'io-ts'; - -import { pipe } from 'fp-ts/lib/pipeable'; -import { fold } from 'fp-ts/lib/Either'; -import { constant, identity } from 'fp-ts/lib/function'; -import { useUrlState, replaceStateKeyInQueryString } from '../../utils/use_url_state'; - -const SOURCE_ID_URL_STATE_KEY = 'sourceId'; - -export const useSourceId = () => { - return useUrlState({ - defaultState: 'default', - decodeUrlState: decodeSourceIdUrlState, - encodeUrlState: encodeSourceIdUrlState, - urlStateKey: SOURCE_ID_URL_STATE_KEY, - }); -}; - -export const replaceSourceIdInQueryString = (sourceId: string) => - replaceStateKeyInQueryString(SOURCE_ID_URL_STATE_KEY, sourceId); - -const sourceIdRuntimeType = runtimeTypes.union([runtimeTypes.string, runtimeTypes.undefined]); -const encodeSourceIdUrlState = sourceIdRuntimeType.encode; -const decodeSourceIdUrlState = (value: unknown) => - pipe(sourceIdRuntimeType.decode(value), fold(constant(undefined), identity)); diff --git a/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts b/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts index cdf702f741c40..3d95dfb72abb0 100644 --- a/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts +++ b/x-pack/plugins/infra/public/hooks/use_log_view.mock.ts @@ -36,12 +36,18 @@ export const createUninitializedUseLogViewMock = load: jest.fn(), retry: jest.fn(), logView: undefined, - logViewId, + logViewReference: { type: 'log-view-reference', logViewId }, logViewStatus: undefined, resolvedLogView: undefined, update: jest.fn(), - logViewStateService: interpret(createPureLogViewStateMachine({ logViewId })), + changeLogViewReference: jest.fn(), + revertToDefaultLogView: jest.fn(), + logViewStateService: interpret( + createPureLogViewStateMachine({ logViewReference: { type: 'log-view-reference', logViewId } }) + ), logViewStateNotifications: createLogViewNotificationChannel(), + isPersistedLogView: false, + isInlineLogView: false, }); export const createLoadingUseLogViewMock = diff --git a/x-pack/plugins/infra/public/hooks/use_log_view.ts b/x-pack/plugins/infra/public/hooks/use_log_view.ts index 8afc517dbfcac..258fbd76850fc 100644 --- a/x-pack/plugins/infra/public/hooks/use_log_view.ts +++ b/x-pack/plugins/infra/public/hooks/use_log_view.ts @@ -7,62 +7,90 @@ import { useInterpret, useSelector } from '@xstate/react'; import createContainer from 'constate'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useState } from 'react'; import { waitFor } from 'xstate/lib/waitFor'; -import { LogViewAttributes } from '../../common/log_views'; +import { LogViewAttributes, LogViewReference } from '../../common/log_views'; import { createLogViewNotificationChannel, createLogViewStateMachine, + DEFAULT_LOG_VIEW, } from '../observability_logs/log_view_state'; import type { ILogViewsClient } from '../services/log_views'; import { isDevMode } from '../utils/dev_mode'; +import { useKbnUrlStateStorageFromRouterContext } from '../utils/kbn_url_state_context'; +import { useKibanaContextForPlugin } from './use_kibana'; export const useLogView = ({ - logViewId, + initialLogViewReference, logViews, useDevTools = isDevMode(), }: { - logViewId: string; + initialLogViewReference?: LogViewReference; logViews: ILogViewsClient; useDevTools?: boolean; }) => { + const { + services: { + notifications: { toasts: toastsService }, + }, + } = useKibanaContextForPlugin(); + + const urlStateStorage = useKbnUrlStateStorageFromRouterContext(); + const [logViewStateNotifications] = useState(() => createLogViewNotificationChannel()); const logViewStateService = useInterpret( () => createLogViewStateMachine({ initialContext: { - logViewId, + logViewReference: initialLogViewReference ?? DEFAULT_LOG_VIEW, }, logViews, notificationChannel: logViewStateNotifications, + toastsService, + urlStateStorage, }), { devTools: useDevTools, } ); - useEffect(() => { - logViewStateService.send({ - type: 'LOG_VIEW_ID_CHANGED', - logViewId, - }); - }, [logViewId, logViewStateService]); + const changeLogViewReference = useCallback( + (logViewReference: LogViewReference) => { + logViewStateService.send({ + type: 'LOG_VIEW_REFERENCE_CHANGED', + logViewReference, + }); + }, + [logViewStateService] + ); + + const logViewReference = useSelector( + logViewStateService, + (state) => state.context.logViewReference + ); const logView = useSelector(logViewStateService, (state) => - state.matches('resolving') || state.matches('checkingStatus') || state.matches('resolved') + state.matches('resolving') || + state.matches('checkingStatus') || + state.matches('resolvedPersistedLogView') || + state.matches('resolvedInlineLogView') ? state.context.logView : undefined ); const resolvedLogView = useSelector(logViewStateService, (state) => - state.matches('checkingStatus') || state.matches('resolved') + state.matches('checkingStatus') || + state.matches('resolvedPersistedLogView') || + state.matches('resolvedInlineLogView') ? state.context.resolvedLogView : undefined ); const logViewStatus = useSelector(logViewStateService, (state) => - state.matches('resolved') ? state.context.status : undefined + state.matches('resolvedPersistedLogView') || state.matches('resolvedInlineLogView') + ? state.context.status + : undefined ); const isLoadingLogView = useSelector(logViewStateService, (state) => state.matches('loading')); @@ -91,6 +119,13 @@ export const useLogView = ({ state.matches('checkingStatusFailed') ); + const isPersistedLogView = useSelector( + logViewStateService, + (state) => state.context.logViewReference.type === 'log-view-reference' + ); + + const isInlineLogView = !isPersistedLogView; + const latestLoadLogViewFailures = useSelector(logViewStateService, (state) => state.matches('loadingFailed') || state.matches('resolutionFailed') || @@ -118,7 +153,10 @@ export const useLogView = ({ const doneState = await waitFor( logViewStateService, - (state) => state.matches('updatingFailed') || state.matches('resolved') + (state) => + state.matches('updatingFailed') || + state.matches('resolvedPersistedLogView') || + state.matches('resolvedInlineLogView') ); if (doneState.matches('updatingFailed')) { @@ -128,8 +166,12 @@ export const useLogView = ({ [logViewStateService] ); + const revertToDefaultLogView = useCallback(() => { + changeLogViewReference(DEFAULT_LOG_VIEW); + }, [changeLogViewReference]); + return { - // underlying state machine + // Underlying state machine logViewStateService, logViewStateNotifications, @@ -147,17 +189,21 @@ export const useLogView = ({ isLoadingLogViewStatus, isResolvingLogView, - // data - logViewId, + // Data + logViewReference, logView, resolvedLogView, logViewStatus, derivedDataView: resolvedLogView?.dataViewReference, + isInlineLogView, + isPersistedLogView, - // actions + // Actions load: retry, retry, update, + changeLogViewReference, + revertToDefaultLogView, }; }; diff --git a/x-pack/plugins/infra/public/containers/source_id/index.ts b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/defaults.ts similarity index 63% rename from x-pack/plugins/infra/public/containers/source_id/index.ts rename to x-pack/plugins/infra/public/observability_logs/log_view_state/src/defaults.ts index 158169c82f897..eb31f91ed12fb 100644 --- a/x-pack/plugins/infra/public/containers/source_id/index.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/defaults.ts @@ -5,4 +5,8 @@ * 2.0. */ -export * from './source_id'; +const DEFAULT_LOG_VIEW_ID = 'default'; +export const DEFAULT_LOG_VIEW = { + type: 'log-view-reference' as const, + logViewId: DEFAULT_LOG_VIEW_ID, +}; diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/index.ts b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/index.ts index 82e7b9fbd2796..5dcfedc6faa49 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/index.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/index.ts @@ -8,3 +8,5 @@ export { createLogViewNotificationChannel, type LogViewNotificationEvent } from './notifications'; export * from './state_machine'; export * from './types'; +export * from './defaults'; +export * from './url_state_storage_service'; diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/notifications.ts b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/notifications.ts index 4b5733452c037..105f911f6c789 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/notifications.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/notifications.ts @@ -5,14 +5,14 @@ * 2.0. */ -import { LogViewStatus, ResolvedLogView } from '../../../../common/log_views'; +import { LogViewReference, LogViewStatus, ResolvedLogView } from '../../../../common/log_views'; import { createNotificationChannel } from '../../xstate_helpers'; import { LogViewContext, LogViewEvent } from './types'; export type LogViewNotificationEvent = | { type: 'LOADING_LOG_VIEW_STARTED'; - logViewId: string; + logViewReference: LogViewReference; } | { type: 'LOADING_LOG_VIEW_SUCCEEDED'; @@ -29,10 +29,10 @@ export const createLogViewNotificationChannel = () => export const logViewNotificationEventSelectors = { loadingLogViewStarted: (context: LogViewContext) => - 'logViewId' in context + 'logViewReference' in context ? ({ type: 'LOADING_LOG_VIEW_STARTED', - logViewId: context.logViewId, + logViewReference: context.logViewReference, } as LogViewNotificationEvent) : undefined, loadingLogViewSucceeded: (context: LogViewContext) => diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/state_machine.ts b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/state_machine.ts index aa94ebcce6a01..2074119bb329c 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/state_machine.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/state_machine.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { IToasts } from '@kbn/core/public'; +import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import { catchError, from, map, of, throwError } from 'rxjs'; import { createMachine, actions, assign } from 'xstate'; import { ILogViewsClient } from '../../../services/log_views'; @@ -13,15 +15,20 @@ import { LogViewNotificationEvent, logViewNotificationEventSelectors } from './n import { LogViewContext, LogViewContextWithError, - LogViewContextWithId, LogViewContextWithLogView, + LogViewContextWithReference, LogViewContextWithResolvedLogView, LogViewContextWithStatus, LogViewEvent, LogViewTypestate, } from './types'; +import { + initializeFromUrl, + updateContextInUrl, + listenForUrlChanges, +} from './url_state_storage_service'; -export const createPureLogViewStateMachine = (initialContext: LogViewContextWithId) => +export const createPureLogViewStateMachine = (initialContext: LogViewContextWithReference) => /** @xstate-layout N4IgpgJg5mDOIC5QBkD2UBqBLMB3AxMgPIDiA+hgJICiA6mZQCJkDCAEgIIByJ1jA2gAYAuolAAHVLCwAXLKgB2YkAA9EAFkEBmAHQAmAGwB2dQYAcAVkGmAjGYMAaEAE9EAWi3qjO9WcGCLIzMbLWMLAE4AX0inNEwcAgBVAAVGDgAVaiFRJBBJaTlFZTUEG20DfUEDLUtwg3CbdXUtJ1cEDz11HSMLTzMagwtjAOjY9Gw8HQBXBSxZuQBDABssAC9IfGzlfNl5JVySz3CdGwMwrRsbCK0a1sQyiqNDCMEjcK09QT8LUZA4idwOiWqAWEDmUEIRA4jEoPDIAGVEiwWNQ+HwtrkdoV9qASkZPDoLnpwmZniTgmY7ghSTorP5fFo6lorHobL9-gkgSCwQoIcRobDyAAxDiUZDokTbKS7IoHRBWMzdT4vCxDPQ3PRUvTqnThcL+OoWVmdU7qdnjTkAJzgqCWADdwfgAErUeFEZCJdKUIhcMgisUSnISaXY4qIcm0y6XJ5mdR6Iw2IxanV6g2DY3qRPm+KTa2wW0O3nO13uz3e32I5GoxiBqUFPZh0ra7z9S6CBo9G4tFyIGl06z9JlWOzZgE6ADGAAswOOANbg+EyBYyKawfDsagsADSgoR6QyiXhftF4oEksxIYbctKFhCOksxjsF3CQSTPYQ2t0qfb6ZsJqMo6clOM7zryi7Lqu65sJuO5wvC+7pIeCJIiiaJnkGeSXrKuL3K+3RnJ8eqGPGgRUm8PjEvq5jBKE6oATEfwWrmNr2hsLr8swxDkFQdAYsG9bYao9x6BYXSkjcBrqp8RiOO+Hg6NUAzhEM6gkvUNRmgxHKTMCoLgkKCxYEsbHUOkToAJp8ZhAk4kJCCMl0MlPKqoS+O2b5tJ0jynGc+KCHowR1HogHMfmSxTNiBlGSZZmWee-EyrZJT6jYCkfARVxaDJsZaqY3Q+cYWj+YFBghYCwFzguS4rrAUXGRAxaxVZWJXjhpSZt4ng2O84Qie2njdp5eUJmchXFd1BjBVpTGAlM4gQMujopGkXpwv6p7NVhSXCV43SqfU2qqYYWVUm42rHAOcb1L12gsmV0zzYtRbLRku6VqhNboXWiWNi+3j6spfSDB84TqKdZjHAY-kRE05hfMSbLTTms2PXIvJ1SZHFkFxFA0LQm02Y2xiKsyJJNPqNxQ5Scl-hYOgBEVt6fsYqn0QxCioBAcDKNpuDfaG14hIq9T2FcjSWEEgync0XQkv4k1ZQYjQRD8SNjjMcy7MsayQPzrV2XYFQi0rt6+IE9gWFSZR09qZzNN1fRDFEaucrpPJQHrgklF4ej0yJInKRDpIeYg5hKoMZhvGUbxFfRYzIzoeYFuCnvbQgcs+Gb+oib4x1UsE4e9PYZzWLe90VaBUDgTVqeNmLF1GlU9S+FDRpkcLKkhO8Vwkqr8djknrEQLX16spcCl0poRoGE0NztxP1QvmLARPM7-eu9y+mGfVI9tV4RuXOqgSJsSIlUrRJyr8fdT+FUfeMQng8RXsGPDxehPXkvlTVAmQy9Le59JqX2JPiF8gxMyR3LtOSqYFqqrlfrvA2jcfAWH6IYGePtwjnwiCcYqHxbCqk0Jpdekw5oLTRh7d+P1P5nAUp8Dq6hRJeFVKdTovtSR2CaD+TMTQzD3TIU9KACCqECzauLOmYtiZZRqAOVhxJ7ysljCEGSTQ4xs0iEAA */ createMachine( { @@ -33,11 +40,22 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith states: { uninitialized: { always: { - target: 'loading', + target: 'initializingFromUrl', + }, + }, + initializingFromUrl: { + on: { + INITIALIZED_FROM_URL: { + target: 'loading', + actions: ['storeLogViewReference'], + }, + }, + invoke: { + src: 'initializeFromUrl', }, }, loading: { - entry: 'notifyLoadingStarted', + entry: ['notifyLoadingStarted', 'updateContextInUrl'], invoke: { src: 'loadLogView', }, @@ -76,18 +94,69 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith target: 'checkingStatusFailed', actions: 'storeError', }, - CHECKING_STATUS_SUCCEEDED: { - target: 'resolved', - actions: 'storeStatus', - }, + CHECKING_STATUS_SUCCEEDED: [ + { + target: 'resolvedPersistedLogView', + actions: 'storeStatus', + cond: 'isPersistedLogView', + }, + { + target: 'resolvedInlineLogView', + actions: 'storeStatus', + }, + ], }, }, - resolved: { - entry: 'notifyLoadingSucceeded', + resolvedPersistedLogView: { + invoke: { + src: 'listenForUrlChanges', + }, + entry: ['notifyLoadingSucceeded', 'updateContextInUrl'], on: { + PERSIST_INLINE_LOG_VIEW: undefined, RELOAD_LOG_VIEW: { target: 'loading', }, + LOG_VIEW_URL_KEY_REMOVED: { + actions: 'updateContextInUrl', + }, + }, + }, + resolvedInlineLogView: { + invoke: { + src: 'listenForUrlChanges', + }, + entry: ['notifyLoadingSucceeded', 'updateContextInUrl'], + on: { + PERSIST_INLINE_LOG_VIEW: { + target: 'persistingInlineLogView', + }, + LOG_VIEW_URL_KEY_REMOVED: { + actions: 'updateContextInUrl', + }, + }, + }, + persistingInlineLogView: { + invoke: { + src: 'persistInlineLogView', + }, + on: { + PERSISTING_INLINE_LOG_VIEW_FAILED: { + target: 'persistingInlineLogViewFailed', + actions: 'storeError', + }, + PERSISTING_INLINE_LOG_VIEW_SUCCEEDED: { + target: 'resolving', + actions: ['convertInlineLogViewReferenceToPersistedLogViewReference', 'storeLogView'], + }, + }, + }, + persistingInlineLogViewFailed: { + entry: 'notifyPersistingInlineLogViewFailed', + on: { + RETRY_PERSISTING_INLINE_LOG_VIEW: { + target: 'persistingInlineLogView', + }, }, }, loadingFailed: { @@ -126,7 +195,7 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith }, UPDATING_SUCCEEDED: { target: 'resolving', - actions: 'storeLogView', + actions: ['updateLogViewReference', 'storeLogView'], }, }, }, @@ -140,10 +209,9 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith }, }, on: { - LOG_VIEW_ID_CHANGED: { + LOG_VIEW_REFERENCE_CHANGED: { target: '.loading', - actions: 'storeLogViewId', - cond: 'isLogViewIdDifferent', + actions: 'storeLogViewReference', }, UPDATE: { target: '.updating', @@ -155,11 +223,11 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith notifyLoadingStarted: actions.pure(() => undefined), notifyLoadingSucceeded: actions.pure(() => undefined), notifyLoadingFailed: actions.pure(() => undefined), - storeLogViewId: assign((context, event) => - 'logViewId' in event + storeLogViewReference: assign((context, event) => + 'logViewReference' in event && event.logViewReference !== null ? ({ - logViewId: event.logViewId, - } as LogViewContextWithId) + logViewReference: event.logViewReference, + } as LogViewContextWithReference) : {} ), storeLogView: assign((context, event) => @@ -190,28 +258,55 @@ export const createPureLogViewStateMachine = (initialContext: LogViewContextWith } as LogViewContextWithError) : {} ), + convertInlineLogViewReferenceToPersistedLogViewReference: assign((context, event) => + 'logView' in event && context.logViewReference.type === 'log-view-inline' + ? ({ + logViewReference: { + type: 'log-view-reference', + logViewId: context.logViewReference.id, + }, + } as LogViewContextWithReference) + : {} + ), + updateLogViewReference: assign((context, event) => + 'attributes' in event && context.logViewReference.type === 'log-view-inline' + ? ({ + logViewReference: { + ...context.logViewReference, + attributes: { + ...context.logViewReference.attributes, + ...event.attributes, + }, + }, + } as LogViewContextWithReference) + : {} + ), }, guards: { - isLogViewIdDifferent: (context, event) => - 'logViewId' in event ? event.logViewId !== context.logViewId : false, + isPersistedLogView: (context, event) => + context.logViewReference.type === 'log-view-reference', }, } ); export interface LogViewStateMachineDependencies { - initialContext: LogViewContextWithId; + initialContext: LogViewContextWithReference; logViews: ILogViewsClient; notificationChannel?: NotificationChannel; + toastsService: IToasts; + urlStateStorage: IKbnUrlStateStorage; } export const createLogViewStateMachine = ({ initialContext, logViews, notificationChannel, + toastsService, + urlStateStorage, }: LogViewStateMachineDependencies) => createPureLogViewStateMachine(initialContext).withConfig({ - actions: - notificationChannel != null + actions: { + ...(notificationChannel != null ? { notifyLoadingStarted: notificationChannel.notify( logViewNotificationEventSelectors.loadingLogViewStarted @@ -223,13 +318,17 @@ export const createLogViewStateMachine = ({ logViewNotificationEventSelectors.loadingLogViewFailed ), } - : {}, + : {}), + updateContextInUrl: updateContextInUrl({ toastsService, urlStateStorage }), + }, services: { + initializeFromUrl: initializeFromUrl({ toastsService, urlStateStorage }), + listenForUrlChanges: listenForUrlChanges({ urlStateStorage }), loadLogView: (context) => from( - 'logViewId' in context - ? logViews.getLogView(context.logViewId) - : throwError(() => new Error('Failed to load log view: No id found in context.')) + 'logViewReference' in context + ? logViews.getLogView(context.logViewReference) + : throwError(() => new Error('Failed to load log view')) ).pipe( map( (logView): LogViewEvent => ({ @@ -246,8 +345,8 @@ export const createLogViewStateMachine = ({ ), updateLogView: (context, event) => from( - 'logViewId' in context && event.type === 'UPDATE' - ? logViews.putLogView(context.logViewId, event.attributes) + 'logViewReference' in context && event.type === 'UPDATE' + ? logViews.putLogView(context.logViewReference, event.attributes) : throwError( () => new Error( @@ -268,6 +367,30 @@ export const createLogViewStateMachine = ({ }) ) ), + persistInlineLogView: (context, event) => + from( + 'logViewReference' in context && + event.type === 'PERSIST_INLINE_LOG_VIEW' && + context.logViewReference.type === 'log-view-inline' + ? logViews.putLogView( + { type: 'log-view-reference', logViewId: context.logViewReference.id }, + context.logViewReference.attributes + ) + : throwError(() => new Error('Failed to persist inline Log View.')) + ).pipe( + map( + (logView): LogViewEvent => ({ + type: 'PERSISTING_INLINE_LOG_VIEW_SUCCEEDED', + logView, + }) + ), + catchError((error) => + of({ + type: 'PERSISTING_INLINE_LOG_VIEW_FAILED', + error, + }) + ) + ), resolveLogView: (context) => from( 'logView' in context diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/types.ts b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/types.ts index 3a997748c5c38..3a7914e8ff6dc 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/types.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/types.ts @@ -9,14 +9,15 @@ import type { ActorRef } from 'xstate'; import type { LogView, LogViewAttributes, + LogViewReference, LogViewStatus, ResolvedLogView, } from '../../../../common/log_views'; import { type NotificationChannel } from '../../xstate_helpers'; import { type LogViewNotificationEvent } from './notifications'; -export interface LogViewContextWithId { - logViewId: string; +export interface LogViewContextWithReference { + logViewReference: LogViewReference; } export interface LogViewContextWithLogView { @@ -38,46 +39,55 @@ export interface LogViewContextWithError { export type LogViewTypestate = | { value: 'uninitialized'; - context: LogViewContextWithId; + context: LogViewContextWithReference; } | { value: 'loading'; - context: LogViewContextWithId; + context: LogViewContextWithReference; } | { value: 'resolving'; - context: LogViewContextWithId & LogViewContextWithLogView; + context: LogViewContextWithReference & LogViewContextWithLogView; } | { value: 'checkingStatus'; - context: LogViewContextWithId & LogViewContextWithLogView & LogViewContextWithResolvedLogView; + context: LogViewContextWithReference & + LogViewContextWithLogView & + LogViewContextWithResolvedLogView; } | { - value: 'resolved'; - context: LogViewContextWithId & + value: 'resolvedPersistedLogView'; + context: LogViewContextWithReference & + LogViewContextWithLogView & + LogViewContextWithResolvedLogView & + LogViewContextWithStatus; + } + | { + value: 'resolvedInlineLogView'; + context: LogViewContextWithReference & LogViewContextWithLogView & LogViewContextWithResolvedLogView & LogViewContextWithStatus; } | { value: 'updating'; - context: LogViewContextWithId; + context: LogViewContextWithReference; } | { value: 'loadingFailed'; - context: LogViewContextWithId & LogViewContextWithError; + context: LogViewContextWithReference & LogViewContextWithError; } | { value: 'updatingFailed'; - context: LogViewContextWithId & LogViewContextWithError; + context: LogViewContextWithReference & LogViewContextWithError; } | { value: 'resolutionFailed'; - context: LogViewContextWithId & LogViewContextWithLogView & LogViewContextWithError; + context: LogViewContextWithReference & LogViewContextWithLogView & LogViewContextWithError; } | { value: 'checkingStatusFailed'; - context: LogViewContextWithId & LogViewContextWithLogView & LogViewContextWithError; + context: LogViewContextWithReference & LogViewContextWithLogView & LogViewContextWithError; }; export type LogViewContext = LogViewTypestate['context']; @@ -86,8 +96,12 @@ export type LogViewStateValue = LogViewTypestate['value']; export type LogViewEvent = | { - type: 'LOG_VIEW_ID_CHANGED'; - logViewId: string; + type: 'LOG_VIEW_REFERENCE_CHANGED'; + logViewReference: LogViewReference; + } + | { + type: 'INITIALIZED_FROM_URL'; + logViewReference: LogViewReference | null; } | { type: 'LOADING_SUCCEEDED'; @@ -130,7 +144,17 @@ export type LogViewEvent = } | { type: 'RELOAD_LOG_VIEW'; - }; + } + | { + type: 'PERSIST_INLINE_LOG_VIEW'; + } + | { type: 'PERSISTING_INLINE_LOG_VIEW_FAILED'; error: Error } + | { type: 'PERSISTING_INLINE_LOG_VIEW_SUCCEEDED'; logView: LogView } + | { + type: 'RETRY_PERSISTING_INLINE_LOG_VIEW'; + } + | { type: 'LOG_VIEW_URL_KEY_REMOVED' } + | { type: 'LOG_VIEW_URL_KEY_CHANGED' }; export type LogViewActorRef = ActorRef; export type LogViewNotificationChannel = NotificationChannel< diff --git a/x-pack/plugins/infra/public/observability_logs/log_view_state/src/url_state_storage_service.ts b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/url_state_storage_service.ts new file mode 100644 index 0000000000000..dd9098b09052e --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/log_view_state/src/url_state_storage_service.ts @@ -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 * as rt from 'io-ts'; +import { IToasts } from '@kbn/core/public'; +import { IKbnUrlStateStorage, withNotifyOnErrors } from '@kbn/kibana-utils-plugin/public'; +import { InvokeCreator } from 'xstate'; +import * as Either from 'fp-ts/lib/Either'; +import { identity, pipe } from 'fp-ts/lib/function'; +import { map } from 'rxjs'; +import { createPlainError, formatErrors } from '../../../../common/runtime_types'; +import { + LogViewReference, + logViewReferenceRT, + PersistedLogViewReference, +} from '../../../../common/log_views'; +import { LogViewContext, LogViewEvent } from './types'; +import { replaceStateKeyInQueryString } from '../../../utils/url_state'; + +export const defaultLogViewKey = 'logView'; +const defaultLegacySourceIdKey = 'sourceId'; + +interface LogViewUrlStateDependencies { + logViewKey?: string; + sourceIdKey?: string; + toastsService: IToasts; + urlStateStorage: IKbnUrlStateStorage; +} + +export const updateContextInUrl = + ({ urlStateStorage, logViewKey = defaultLogViewKey }: LogViewUrlStateDependencies) => + (context: LogViewContext, _event: LogViewEvent) => { + if (!('logViewReference' in context)) { + throw new Error('Missing keys from context needed to sync to the URL'); + } + + urlStateStorage.set(logViewKey, logViewStateInUrlRT.encode(context.logViewReference), { + replace: true, + }); + }; + +export const initializeFromUrl = + ({ + logViewKey = defaultLogViewKey, + sourceIdKey = defaultLegacySourceIdKey, + toastsService, + urlStateStorage, + }: LogViewUrlStateDependencies): InvokeCreator => + (_context, _event) => + (send) => { + const logViewQueryValueFromUrl = urlStateStorage.get(logViewKey); + const logViewQueryE = decodeLogViewQueryValueFromUrl(logViewQueryValueFromUrl); + + const legacySourceIdQueryValueFromUrl = urlStateStorage.get(sourceIdKey); + const sourceIdQueryE = decodeSourceIdQueryValueFromUrl(legacySourceIdQueryValueFromUrl); + + if (Either.isLeft(logViewQueryE) || Either.isLeft(sourceIdQueryE)) { + withNotifyOnErrors(toastsService).onGetError( + createPlainError( + formatErrors([ + ...(Either.isLeft(logViewQueryE) ? logViewQueryE.left : []), + ...(Either.isLeft(sourceIdQueryE) ? sourceIdQueryE.left : []), + ]) + ) + ); + + send({ + type: 'INITIALIZED_FROM_URL', + logViewReference: null, + }); + } else { + send({ + type: 'INITIALIZED_FROM_URL', + logViewReference: pipe( + // Via the legacy sourceId key + pipe( + sourceIdQueryE.right, + Either.fromNullable(null), + Either.map(convertSourceIdToReference) + ), + // Via the logView key + Either.alt(() => pipe(logViewQueryE.right, Either.fromNullable(null))), + Either.fold(identity, identity) + ), + }); + } + }; + +// NOTE: Certain navigations within the Logs solution will remove the logView URL key, +// we want to ensure the logView key is present in the URL at all times by monitoring for it's removal. +export const listenForUrlChanges = + ({ + urlStateStorage, + logViewKey = defaultLogViewKey, + }: { + urlStateStorage: LogViewUrlStateDependencies['urlStateStorage']; + logViewKey?: LogViewUrlStateDependencies['logViewKey']; + }): InvokeCreator => + (context, event) => { + return urlStateStorage + .change$(logViewKey) + .pipe( + map((value) => + value === undefined || value === null + ? { type: 'LOG_VIEW_URL_KEY_REMOVED' } + : { type: 'LOG_VIEW_URL_KEY_CHANGED' } + ) + ); + }; + +const logViewStateInUrlRT = rt.union([logViewReferenceRT, rt.null]); +const sourceIdStateInUrl = rt.union([rt.string, rt.null]); + +const decodeLogViewQueryValueFromUrl = (queryValueFromUrl: unknown) => { + return logViewStateInUrlRT.decode(queryValueFromUrl); +}; + +const decodeSourceIdQueryValueFromUrl = (queryValueFromUrl: unknown) => { + return sourceIdStateInUrl.decode(queryValueFromUrl); +}; + +const convertSourceIdToReference = (sourceId: string): PersistedLogViewReference => { + return { + type: 'log-view-reference' as const, + logViewId: sourceId, + }; +}; + +// NOTE: Used by link-to components +export const replaceLogViewInQueryString = (logViewReference: LogViewReference) => + replaceStateKeyInQueryString(defaultLogViewKey, logViewReference); diff --git a/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/index.ts b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/index.ts index fa0e5da4cb4c1..8e6f993a9755e 100644 --- a/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/index.ts +++ b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/index.ts @@ -9,3 +9,4 @@ export * from './invalid_state_callout'; export * from './notification_channel'; export * from './send_actions'; export * from './types'; +export * from './state_machine_playground'; diff --git a/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/state_machine_playground.tsx b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/state_machine_playground.tsx new file mode 100644 index 0000000000000..78f1c5f15dee9 --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/state_machine_playground.tsx @@ -0,0 +1,81 @@ +/* + * 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 } from '@elastic/eui'; +import React, { useCallback } from 'react'; +import { useLogViewContext } from '../../../hooks/use_log_view'; + +export const StateMachinePlayground = () => { + const { changeLogViewReference, revertToDefaultLogView, update, isLoading, logViewStateService } = + useLogViewContext(); + + const switchToInlineLogView = useCallback(() => { + changeLogViewReference({ + type: 'log-view-inline', + id: 'playground-log-view', + attributes: { + name: 'playground-log-view-name', + description: 'from the state machine playground', + logIndices: { type: 'index_name', indexName: 'logs-*' }, + logColumns: [ + { + fieldColumn: { + id: 'playground-field-column', + field: 'event.dataset', + }, + }, + ], + }, + }); + }, [changeLogViewReference]); + + const updateLogView = useCallback(() => { + update({ + name: 'Updated playground name', + }); + }, [update]); + + const persistInlineLogView = useCallback(() => { + logViewStateService.send({ + type: 'PERSIST_INLINE_LOG_VIEW', + }); + }, [logViewStateService]); + + return ( + <> + {isLoading && 'Is loading'} + switchToInlineLogView()} + > + {'Switch to inline Log View'} + + persistInlineLogView()} + > + {'Persist inline Log View'} + + revertToDefaultLogView()} + > + {'Revert to default (persisted) Log View'} + + updateLogView()} + > + {'Update log view'} + + + ); +}; diff --git a/x-pack/plugins/infra/public/pages/link_to/link_to_logs.test.tsx b/x-pack/plugins/infra/public/pages/link_to/link_to_logs.test.tsx index c6af835dad803..d3dd705a59811 100644 --- a/x-pack/plugins/infra/public/pages/link_to/link_to_logs.test.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/link_to_logs.test.tsx @@ -22,6 +22,8 @@ import { LinkToLogsPage } from './link_to_logs'; jest.mock('../../hooks/use_log_view'); const useLogViewMock = useLogView as jest.MockedFunction; +const LOG_VIEW_REFERENCE = '(logViewId:default,type:log-view-reference)'; +const OTHER_LOG_VIEW_REFERENCE = '(logViewId:OTHER_SOURCE,type:log-view-reference)'; const renderRoutes = (routes: React.ReactElement) => { const history = createMemoryHistory(); @@ -71,7 +73,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'FILTER_FIELD:FILTER_VALUE'),refreshInterval:(pause:!t,value:5000),timeRange:(from:'2019-02-20T12:58:09.404Z',to:'2019-02-20T14:58:09.404Z'))"` ); @@ -92,7 +94,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('OTHER_SOURCE'); + expect(searchParams.get('logView')).toEqual(OTHER_LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:''),refreshInterval:(pause:!t,value:5000))"` ); @@ -113,7 +115,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'FILTER_FIELD:FILTER_VALUE'),refreshInterval:(pause:!t,value:5000),timeRange:(from:'2019-02-20T12:58:09.404Z',to:'2019-02-20T14:58:09.404Z'))"` ); @@ -134,7 +136,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('OTHER_SOURCE'); + expect(searchParams.get('logView')).toEqual(OTHER_LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:''),refreshInterval:(pause:!t,value:5000))"` ); @@ -155,7 +157,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'host.name: HOST_NAME'),refreshInterval:(pause:!t,value:5000))"` ); @@ -176,7 +178,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'(host.name: HOST_NAME) and (FILTER_FIELD:FILTER_VALUE)'),refreshInterval:(pause:!t,value:5000),timeRange:(from:'2019-02-20T12:58:09.404Z',to:'2019-02-20T14:58:09.404Z'))"` ); @@ -197,7 +199,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('OTHER_SOURCE'); + expect(searchParams.get('logView')).toEqual(OTHER_LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'host.name: HOST_NAME'),refreshInterval:(pause:!t,value:5000))"` ); @@ -233,7 +235,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'container.id: CONTAINER_ID'),refreshInterval:(pause:!t,value:5000))"` ); @@ -254,7 +256,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'(container.id: CONTAINER_ID) and (FILTER_FIELD:FILTER_VALUE)'),refreshInterval:(pause:!t,value:5000),timeRange:(from:'2019-02-20T12:58:09.404Z',to:'2019-02-20T14:58:09.404Z'))"` ); @@ -291,7 +293,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'kubernetes.pod.uid: POD_UID'),refreshInterval:(pause:!t,value:5000))"` ); @@ -310,7 +312,7 @@ describe('LinkToLogsPage component', () => { expect(history.location.pathname).toEqual('/stream'); const searchParams = new URLSearchParams(history.location.search); - expect(searchParams.get('sourceId')).toEqual('default'); + expect(searchParams.get('logView')).toEqual(LOG_VIEW_REFERENCE); expect(searchParams.get('logFilter')).toMatchInlineSnapshot( `"(query:(language:kuery,query:'(kubernetes.pod.uid: POD_UID) and (FILTER_FIELD:FILTER_VALUE)'),refreshInterval:(pause:!t,value:5000),timeRange:(from:'2019-02-20T12:58:09.404Z',to:'2019-02-20T14:58:09.404Z'))"` ); diff --git a/x-pack/plugins/infra/public/pages/link_to/link_to_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/link_to_logs.tsx index 85d38e7f476d8..60afe9c98bd54 100644 --- a/x-pack/plugins/infra/public/pages/link_to/link_to_logs.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/link_to_logs.tsx @@ -26,11 +26,11 @@ export const LinkToLogsPage: React.FC = (props) => { return ( - - + + ); diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx index 58ba0df004f26..c5c3e4d507582 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.test.tsx @@ -20,7 +20,7 @@ describe('RedirectToLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); @@ -34,7 +34,7 @@ describe('RedirectToLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); @@ -46,7 +46,7 @@ describe('RedirectToLogs component', () => { expect(component).toMatchInlineSnapshot(` `); }); diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx index 32bc4c909284d..4d4533596fd57 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx @@ -8,27 +8,27 @@ import React from 'react'; import { match as RouteMatch, Redirect, RouteComponentProps } from 'react-router-dom'; import { flowRight } from 'lodash'; -import { replaceSourceIdInQueryString } from '../../containers/source_id'; import { replaceLogPositionInQueryString } from '../../observability_logs/log_stream_position_state/src/url_state_storage_service'; import { replaceLogFilterInQueryString } from '../../observability_logs/log_stream_query_state'; import { getFilterFromLocation, getTimeFromLocation } from './query_params'; +import { replaceLogViewInQueryString } from '../../observability_logs/log_view_state'; type RedirectToLogsType = RouteComponentProps<{}>; interface RedirectToLogsProps extends RedirectToLogsType { match: RouteMatch<{ - sourceId?: string; + logViewId?: string; }>; } export const RedirectToLogs = ({ location, match }: RedirectToLogsProps) => { - const sourceId = match.params.sourceId || 'default'; + const logViewId = match.params.logViewId || 'default'; const filter = getFilterFromLocation(location); const time = getTimeFromLocation(location); const searchString = flowRight( replaceLogFilterInQueryString({ language: 'kuery', query: filter }, time), replaceLogPositionInQueryString(time), - replaceSourceIdInQueryString(sourceId) + replaceLogViewInQueryString({ type: 'log-view-reference', logViewId }) )(''); return ; diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx index 4f5fb79acc76f..7be728273e5b6 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx @@ -14,28 +14,28 @@ import { flowRight } from 'lodash'; import { findInventoryFields } from '../../../common/inventory_models'; import { InventoryItemType } from '../../../common/inventory_models/types'; import { LoadingPage } from '../../components/loading_page'; -import { replaceSourceIdInQueryString } from '../../containers/source_id'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { useLogView } from '../../hooks/use_log_view'; import { replaceLogFilterInQueryString } from '../../observability_logs/log_stream_query_state'; import { getFilterFromLocation, getTimeFromLocation } from './query_params'; import { replaceLogPositionInQueryString } from '../../observability_logs/log_stream_position_state/src/url_state_storage_service'; +import { replaceLogViewInQueryString } from '../../observability_logs/log_view_state'; type RedirectToNodeLogsType = RouteComponentProps<{ nodeId: string; nodeType: InventoryItemType; - sourceId?: string; + logViewId?: string; }>; export const RedirectToNodeLogs = ({ match: { - params: { nodeId, nodeType, sourceId = 'default' }, + params: { nodeId, nodeType, logViewId = 'default' }, }, location, }: RedirectToNodeLogsType) => { const { services } = useKibanaContextForPlugin(); const { isLoading, load } = useLogView({ - logViewId: sourceId, + initialLogViewReference: { type: 'log-view-reference', logViewId }, logViews: services.logViews.client, }); @@ -65,7 +65,7 @@ export const RedirectToNodeLogs = ({ const searchString = flowRight( replaceLogFilterInQueryString({ language: 'kuery', query: filter }, time), replaceLogPositionInQueryString(time), - replaceSourceIdInQueryString(sourceId) + replaceLogViewInQueryString({ type: 'log-view-reference', logViewId }) )(''); return ; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx index 35150368b586c..6e1d3f4a4f0bd 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_providers.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { InlineLogViewSplashPage } from '../../../components/logging/inline_log_view_splash_page'; import { LogAnalysisSetupFlyoutStateProvider } from '../../../components/logging/log_analysis_setup/setup_flyout'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { LogEntryCategoriesModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; @@ -14,8 +15,15 @@ import { useLogViewContext } from '../../../hooks/use_log_view'; import { ConnectedLogViewErrorPage } from '../shared/page_log_view_error'; export const LogEntryCategoriesPageProviders: React.FunctionComponent = ({ children }) => { - const { hasFailedLoading, isLoading, isUninitialized, resolvedLogView, logViewId } = - useLogViewContext(); + const { + hasFailedLoading, + isLoading, + isUninitialized, + resolvedLogView, + logViewReference, + isPersistedLogView, + revertToDefaultLogView, + } = useLogViewContext(); const { space } = useActiveKibanaSpace(); // This is a rather crude way of guarding the dependent providers against @@ -23,15 +31,20 @@ export const LogEntryCategoriesPageProviders: React.FunctionComponent = ({ child // React concurrent mode and Suspense in order to handle that more gracefully. if (space == null) { return null; + } else if (!isPersistedLogView) { + return ; } else if (hasFailedLoading) { return ; } else if (isLoading || isUninitialized) { return ; } else if (resolvedLogView != null) { + if (logViewReference.type === 'log-view-inline') { + throw new Error('Logs ML features only support persisted Log View references'); + } return ( @@ -265,7 +265,7 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent< = ({ categoryId, timeRange, sourceId }) => { + logViewReference: PersistedLogViewReference; +}> = ({ categoryId, timeRange, logViewReference }) => { const { getLogEntryCategoryExamples, hasFailedLoadingLogEntryCategoryExamples, @@ -27,7 +28,7 @@ export const CategoryDetailsRow: React.FunctionComponent<{ categoryId, endTime: timeRange.endTime, exampleCount, - sourceId, + logViewReference, startTime: timeRange.startTime, }); diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx index 0aabc570a89e8..c9f93ee618ddb 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/top_categories_section.tsx @@ -9,6 +9,7 @@ import { EuiLoadingSpinner } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { PersistedLogViewReference } from '../../../../../../common/log_views'; import { LogEntryCategory } from '../../../../../../common/log_analysis'; import { TimeRange } from '../../../../../../common/time'; import { LoadingOverlayWrapper } from '../../../../../components/loading_overlay_wrapper'; @@ -18,7 +19,7 @@ import { SortOptions, ChangeSortOptions } from '../../use_log_entry_categories_r export const TopCategoriesSection: React.FunctionComponent<{ isLoadingTopCategories?: boolean; jobId: string; - sourceId: string; + logViewReference: PersistedLogViewReference; timeRange: TimeRange; topCategories: LogEntryCategory[]; sortOptions: SortOptions; @@ -26,7 +27,7 @@ export const TopCategoriesSection: React.FunctionComponent<{ }> = ({ isLoadingTopCategories = false, jobId, - sourceId, + logViewReference, timeRange, topCategories, sortOptions, @@ -40,7 +41,7 @@ export const TopCategoriesSection: React.FunctionComponent<{ > ), }), {} ), - [expandedCategories, sourceId, timeRange] + [expandedCategories, logViewReference, timeRange] ); return ( diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts index 34358b983e12b..e2f34f644ecaa 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts @@ -6,6 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { getLogEntryCategoryDatasetsRequestPayloadRT, @@ -15,7 +16,7 @@ import { import { decodeOrThrow } from '../../../../../common/runtime_types'; interface RequestArgs { - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; endTime: number; } @@ -24,14 +25,14 @@ export const callGetLogEntryCategoryDatasetsAPI = async ( requestArgs: RequestArgs, fetch: HttpHandler ) => { - const { sourceId, startTime, endTime } = requestArgs; + const { logViewReference, startTime, endTime } = requestArgs; const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_DATASETS_PATH, { method: 'POST', body: JSON.stringify( getLogEntryCategoryDatasetsRequestPayloadRT.encode({ data: { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, timeRange: { startTime, endTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts index e3b99750af715..52f9f17747b5a 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts @@ -6,6 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { getLogEntryCategoryExamplesRequestPayloadRT, @@ -15,7 +16,7 @@ import { import { decodeOrThrow } from '../../../../../common/runtime_types'; interface RequestArgs { - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; endTime: number; categoryId: number; @@ -26,7 +27,7 @@ export const callGetLogEntryCategoryExamplesAPI = async ( requestArgs: RequestArgs, fetch: HttpHandler ) => { - const { sourceId, startTime, endTime, categoryId, exampleCount } = requestArgs; + const { logViewReference, startTime, endTime, categoryId, exampleCount } = requestArgs; const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_EXAMPLES_PATH, { method: 'POST', @@ -35,7 +36,7 @@ export const callGetLogEntryCategoryExamplesAPI = async ( data: { categoryId, exampleCount, - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, timeRange: { startTime, endTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts index 93e9daf0b9cb6..0cd04d999183f 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts @@ -6,6 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { getLogEntryCategoriesRequestPayloadRT, @@ -16,7 +17,7 @@ import { CategoriesSort } from '../../../../../common/log_analysis'; import { decodeOrThrow } from '../../../../../common/runtime_types'; interface RequestArgs { - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; endTime: number; categoryCount: number; @@ -28,7 +29,7 @@ export const callGetTopLogEntryCategoriesAPI = async ( requestArgs: RequestArgs, fetch: HttpHandler ) => { - const { sourceId, startTime, endTime, categoryCount, datasets, sort } = requestArgs; + const { logViewReference, startTime, endTime, categoryCount, datasets, sort } = requestArgs; const intervalDuration = endTime - startTime; const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORIES_PATH, { @@ -36,7 +37,7 @@ export const callGetTopLogEntryCategoriesAPI = async ( body: JSON.stringify( getLogEntryCategoriesRequestPayloadRT.encode({ data: { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, timeRange: { startTime, endTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts index b4cfb301e0402..f7e05c885f026 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts @@ -7,6 +7,7 @@ import { useMemo, useState } from 'react'; +import { PersistedLogViewReference } from '../../../../common/log_views'; import { GetLogEntryCategoriesSuccessResponsePayload, GetLogEntryCategoryDatasetsSuccessResponsePayload, @@ -30,7 +31,7 @@ export const useLogEntryCategoriesResults = ({ endTime, onGetLogEntryCategoryDatasetsError, onGetTopLogEntryCategoriesError, - sourceId, + logViewReference, startTime, }: { categoriesCount: number; @@ -38,7 +39,7 @@ export const useLogEntryCategoriesResults = ({ endTime: number; onGetLogEntryCategoryDatasetsError?: (error: Error) => void; onGetTopLogEntryCategoriesError?: (error: Error) => void; - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; }) => { const [sortOptions, setSortOptions] = useState({ @@ -56,7 +57,7 @@ export const useLogEntryCategoriesResults = ({ createPromise: async () => { return await callGetTopLogEntryCategoriesAPI( { - sourceId, + logViewReference, startTime, endTime, categoryCount: categoriesCount, @@ -79,7 +80,7 @@ export const useLogEntryCategoriesResults = ({ } }, }, - [categoriesCount, endTime, filteredDatasets, sourceId, startTime, sortOptions] + [categoriesCount, endTime, filteredDatasets, logViewReference, startTime, sortOptions] ); const [getLogEntryCategoryDatasetsRequest, getLogEntryCategoryDatasets] = useTrackedPromise( @@ -87,7 +88,7 @@ export const useLogEntryCategoriesResults = ({ cancelPreviousOn: 'creation', createPromise: async () => { return await callGetLogEntryCategoryDatasetsAPI( - { sourceId, startTime, endTime }, + { logViewReference, startTime, endTime }, services.http.fetch ); }, @@ -104,7 +105,7 @@ export const useLogEntryCategoriesResults = ({ } }, }, - [categoriesCount, endTime, sourceId, startTime] + [categoriesCount, endTime, logViewReference, startTime] ); const isLoadingTopLogEntryCategories = useMemo( diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx index 010bcaa47942e..8152cae426448 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx @@ -6,6 +6,7 @@ */ import { useMemo, useState } from 'react'; +import { PersistedLogViewReference } from '../../../../common/log_views'; import { LogEntryCategoryExample } from '../../../../common/http_api'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; @@ -16,13 +17,13 @@ export const useLogEntryCategoryExamples = ({ categoryId, endTime, exampleCount, - sourceId, + logViewReference, startTime, }: { categoryId: number; endTime: number; exampleCount: number; - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; }) => { const { services } = useKibanaContextForPlugin(); @@ -37,7 +38,7 @@ export const useLogEntryCategoryExamples = ({ createPromise: async () => { return await callGetLogEntryCategoryExamplesAPI( { - sourceId, + logViewReference, startTime, endTime, categoryId, @@ -50,7 +51,7 @@ export const useLogEntryCategoryExamples = ({ setLogEntryCategoryExamples(examples); }, }, - [categoryId, endTime, exampleCount, sourceId, startTime] + [categoryId, endTime, exampleCount, logViewReference, startTime] ); const isLoadingLogEntryCategoryExamples = useMemo( diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx index f3ddc6c7cc57d..7e1381cbd4f21 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/page_providers.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { InlineLogViewSplashPage } from '../../../components/logging/inline_log_view_splash_page'; import { LogAnalysisSetupFlyoutStateProvider } from '../../../components/logging/log_analysis_setup/setup_flyout'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { LogEntryCategoriesModuleProvider } from '../../../containers/logs/log_analysis/modules/log_entry_categories'; @@ -16,8 +17,16 @@ import { useLogViewContext } from '../../../hooks/use_log_view'; import { ConnectedLogViewErrorPage } from '../shared/page_log_view_error'; export const LogEntryRatePageProviders: React.FunctionComponent = ({ children }) => { - const { hasFailedLoading, isLoading, isUninitialized, logViewId, resolvedLogView } = - useLogViewContext(); + const { + hasFailedLoading, + isLoading, + isUninitialized, + logViewReference, + resolvedLogView, + isPersistedLogView, + revertToDefaultLogView, + } = useLogViewContext(); + const { space } = useActiveKibanaSpace(); // This is a rather crude way of guarding the dependent providers against @@ -25,23 +34,28 @@ export const LogEntryRatePageProviders: React.FunctionComponent = ({ children }) // React concurrent mode and Suspense in order to handle that more gracefully. if (space == null) { return null; + } else if (!isPersistedLogView) { + return ; } else if (isLoading || isUninitialized) { return ; } else if (hasFailedLoading) { return ; } else if (resolvedLogView != null) { + if (logViewReference.type === 'log-view-inline') { + throw new Error('Logs ML features only support persisted Log Views'); + } return ( ) : null} diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx index ef5db53ebdc31..493b5c4077c69 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/sections/anomalies/expanded_row.tsx @@ -28,7 +28,11 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ anomaly: LogEntryAnomaly; timeRange: TimeRange; }> = ({ anomaly, timeRange }) => { - const { logViewId } = useLogViewContext(); + const { logViewReference } = useLogViewContext(); + + if (logViewReference.type === 'log-view-inline') { + throw new Error('Logs ML features only support persisted Log Views'); + } const { getLogEntryExamples, @@ -39,7 +43,7 @@ export const AnomaliesTableExpandedRow: React.FunctionComponent<{ dataset: anomaly.dataset, endTime: anomaly.startTime + anomaly.duration, exampleCount: EXAMPLE_COUNT, - sourceId: logViewId, + logViewReference, startTime: anomaly.startTime, categoryId: isCategoryAnomaly(anomaly) ? anomaly.categoryId : undefined, }); diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts index 7916cad0f1e07..ef39f6ac5f012 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts @@ -6,6 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { getLogEntryAnomaliesRequestPayloadRT, getLogEntryAnomaliesSuccessReponsePayloadRT, @@ -15,7 +16,7 @@ import { decodeOrThrow } from '../../../../../common/runtime_types'; import { AnomaliesSort, Pagination } from '../../../../../common/log_analysis'; interface RequestArgs { - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; endTime: number; sort: AnomaliesSort; @@ -24,13 +25,13 @@ interface RequestArgs { } export const callGetLogEntryAnomaliesAPI = async (requestArgs: RequestArgs, fetch: HttpHandler) => { - const { sourceId, startTime, endTime, sort, pagination, datasets } = requestArgs; + const { logViewReference, startTime, endTime, sort, pagination, datasets } = requestArgs; const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_ANOMALIES_PATH, { method: 'POST', body: JSON.stringify( getLogEntryAnomaliesRequestPayloadRT.encode({ data: { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, timeRange: { startTime, endTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts index 16a8092f290f8..8915f514d1bfb 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts @@ -6,6 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { decodeOrThrow } from '../../../../../common/runtime_types'; import { getLogEntryAnomaliesDatasetsRequestPayloadRT, @@ -14,7 +15,7 @@ import { } from '../../../../../common/http_api/log_analysis'; interface RequestArgs { - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; endTime: number; } @@ -23,13 +24,13 @@ export const callGetLogEntryAnomaliesDatasetsAPI = async ( requestArgs: RequestArgs, fetch: HttpHandler ) => { - const { sourceId, startTime, endTime } = requestArgs; + const { logViewReference, startTime, endTime } = requestArgs; const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_ANOMALIES_DATASETS_PATH, { method: 'POST', body: JSON.stringify( getLogEntryAnomaliesDatasetsRequestPayloadRT.encode({ data: { - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, timeRange: { startTime, endTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts index 0e44e5b02feb7..75580caa6db8a 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts @@ -6,6 +6,7 @@ */ import type { HttpHandler } from '@kbn/core/public'; +import { PersistedLogViewReference } from '../../../../../common/log_views'; import { getLogEntryExamplesRequestPayloadRT, @@ -15,7 +16,7 @@ import { import { decodeOrThrow } from '../../../../../common/runtime_types'; interface RequestArgs { - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; endTime: number; dataset: string; @@ -24,7 +25,7 @@ interface RequestArgs { } export const callGetLogEntryExamplesAPI = async (requestArgs: RequestArgs, fetch: HttpHandler) => { - const { sourceId, startTime, endTime, dataset, exampleCount, categoryId } = requestArgs; + const { logViewReference, startTime, endTime, dataset, exampleCount, categoryId } = requestArgs; const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_RATE_EXAMPLES_PATH, { method: 'POST', body: JSON.stringify( @@ -32,7 +33,7 @@ export const callGetLogEntryExamplesAPI = async (requestArgs: RequestArgs, fetch data: { dataset, exampleCount, - logView: { type: 'log-view-reference', logViewId: sourceId }, + logView: logViewReference, timeRange: { startTime, endTime, diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts index 80123bb10e550..f68aa20d41f0e 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts @@ -7,6 +7,7 @@ import { useMemo, useState, useCallback, useEffect, useReducer } from 'react'; import useMount from 'react-use/lib/useMount'; +import { PersistedLogViewReference } from '../../../../common/log_views'; import { useTrackedPromise, CanceledPromiseError } from '../../../utils/use_tracked_promise'; import { callGetLogEntryAnomaliesAPI } from './service_calls/get_log_entry_anomalies'; import { callGetLogEntryAnomaliesDatasetsAPI } from './service_calls/get_log_entry_anomalies_datasets'; @@ -137,7 +138,7 @@ const STATE_DEFAULTS: ReducerStateDefaults = { export const useLogEntryAnomaliesResults = ({ endTime, startTime, - sourceId, + logViewReference, defaultSortOptions, defaultPaginationOptions, onGetLogEntryAnomaliesDatasetsError, @@ -145,7 +146,7 @@ export const useLogEntryAnomaliesResults = ({ }: { endTime: number; startTime: number; - sourceId: string; + logViewReference: PersistedLogViewReference; defaultSortOptions: AnomaliesSort; defaultPaginationOptions: Pick; onGetLogEntryAnomaliesDatasetsError?: (error: Error) => void; @@ -183,7 +184,7 @@ export const useLogEntryAnomaliesResults = ({ } = reducerState; return await callGetLogEntryAnomaliesAPI( { - sourceId, + logViewReference, startTime: queryStartTime, endTime: queryEndTime, sort: sortOptions, @@ -216,7 +217,7 @@ export const useLogEntryAnomaliesResults = ({ }, }, [ - sourceId, + logViewReference, dispatch, reducerState.timeRange, reducerState.sortOptions, @@ -294,7 +295,7 @@ export const useLogEntryAnomaliesResults = ({ cancelPreviousOn: 'creation', createPromise: async () => { return await callGetLogEntryAnomaliesDatasetsAPI( - { sourceId, startTime, endTime }, + { logViewReference, startTime, endTime }, services.http.fetch ); }, @@ -311,7 +312,7 @@ export const useLogEntryAnomaliesResults = ({ } }, }, - [endTime, sourceId, startTime] + [endTime, logViewReference, startTime] ); const isLoadingDatasets = useMemo( diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts index 8f6269c46c3df..a678f5deaf07a 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts @@ -6,6 +6,7 @@ */ import { useMemo, useState } from 'react'; +import { PersistedLogViewReference } from '../../../../common/log_views'; import { LogEntryExample } from '../../../../common/log_analysis'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; @@ -16,14 +17,14 @@ export const useLogEntryExamples = ({ dataset, endTime, exampleCount, - sourceId, + logViewReference, startTime, categoryId, }: { dataset: string; endTime: number; exampleCount: number; - sourceId: string; + logViewReference: PersistedLogViewReference; startTime: number; categoryId?: string; }) => { @@ -36,7 +37,7 @@ export const useLogEntryExamples = ({ createPromise: async () => { return await callGetLogEntryExamplesAPI( { - sourceId, + logViewReference, startTime, endTime, dataset, @@ -50,7 +51,7 @@ export const useLogEntryExamples = ({ setLogEntryExamples(examples); }, }, - [dataset, endTime, exampleCount, sourceId, startTime] + [dataset, endTime, exampleCount, logViewReference, startTime] ); const isLoadingLogEntryExamples = useMemo( diff --git a/x-pack/plugins/infra/public/pages/logs/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/page_content.tsx index 0be59c785919f..17def99bc1612 100644 --- a/x-pack/plugins/infra/public/pages/logs/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/page_content.tsx @@ -12,7 +12,7 @@ import { Switch } from 'react-router-dom'; import { Route } from '@kbn/shared-ux-router'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { HeaderMenuPortal, useLinkProps } from '@kbn/observability-plugin/public'; -import { AlertDropdown } from '../../alerting/log_threshold'; +import { LazyAlertDropdownWrapper } from '../../alerting/log_threshold'; import { HelpCenterContent } from '../../components/help_center_content'; import { useReadOnlyBadge } from '../../hooks/use_readonly_badge'; import { HeaderActionMenuContext } from '../../utils/header_action_menu_provider'; @@ -21,9 +21,12 @@ import { LogEntryCategoriesPage } from './log_entry_categories'; import { LogEntryRatePage } from './log_entry_rate'; import { LogsSettingsPage } from './settings'; import { StreamPage } from './stream'; +import { isDevMode } from '../../utils/dev_mode'; +import { StateMachinePlayground } from '../../observability_logs/xstate_helpers'; import { NotFoundPage } from '../404'; export const LogsPageContent: React.FunctionComponent = () => { + const enableDeveloperRoutes = isDevMode(); const uiCapabilities = useKibana().services.application?.capabilities; const { setHeaderActionMenu, theme$ } = useContext(HeaderActionMenuContext); @@ -71,7 +74,7 @@ export const LogsPageContent: React.FunctionComponent = () => { {settingsTabTitle} - + { + {enableDeveloperRoutes && ( + + )} diff --git a/x-pack/plugins/infra/public/pages/logs/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/page_providers.tsx index 92db5f003932c..c6548cfbd40aa 100644 --- a/x-pack/plugins/infra/public/pages/logs/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/page_providers.tsx @@ -7,16 +7,14 @@ import React from 'react'; import { LogAnalysisCapabilitiesProvider } from '../../containers/logs/log_analysis'; -import { useSourceId } from '../../containers/source_id'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { LogViewProvider } from '../../hooks/use_log_view'; export const LogsPageProviders: React.FunctionComponent = ({ children }) => { - const [sourceId] = useSourceId(); const { services } = useKibanaContextForPlugin(); return ( - + {children} ); diff --git a/x-pack/plugins/infra/public/pages/logs/settings/inline_log_view_callout.tsx b/x-pack/plugins/infra/public/pages/logs/settings/inline_log_view_callout.tsx new file mode 100644 index 0000000000000..ed332f97ea471 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/logs/settings/inline_log_view_callout.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 { EuiButton } from '@elastic/eui'; +import { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; + +export const InlineLogViewCallout = ({ + revertToDefaultLogView, +}: { + revertToDefaultLogView: () => void; +}) => { + return ( + + <> +

+ {i18n.translate('xpack.infra.logs.settings.inlineLogViewCalloutDescription', { + defaultMessage: + 'An inline Log View is currently being used, changes will be synchronized to the URL, but they will not be persisted.', + })} +

+ + + + +
+ ); +}; diff --git a/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx b/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx index f826be82ac674..6ee20c00b9e64 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx @@ -28,6 +28,7 @@ import { LogColumnsConfigurationPanel } from './log_columns_configuration_panel' import { NameConfigurationPanel } from './name_configuration_panel'; import { LogSourceConfigurationFormErrors } from './source_configuration_form_errors'; import { useLogSourceConfigurationFormState } from './source_configuration_form_state'; +import { InlineLogViewCallout } from './inline_log_view_callout'; export const LogsSettingsPage = () => { const uiCapabilities = useKibana().services.application?.capabilities; @@ -46,8 +47,16 @@ export const LogsSettingsPage = () => { }, ]); - const { logView, hasFailedLoadingLogView, isLoading, isUninitialized, update, resolvedLogView } = - useLogViewContext(); + const { + logView, + hasFailedLoadingLogView, + isLoading, + isUninitialized, + update, + resolvedLogView, + isInlineLogView, + revertToDefaultLogView, + } = useLogViewContext(); const availableFields = useMemo( () => resolvedLogView?.fields.map((field) => field.name) ?? [], @@ -141,6 +150,14 @@ export const LogsSettingsPage = () => { ) : ( <> + {isInlineLogView && ( + + + + + + + )} ) : null} - + { const { startTimestamp, endTimestamp } = useLogPositionStateContext(); - const { logViewId } = useLogViewContext(); + const { logViewReference } = useLogViewContext(); if (!startTimestamp || !endTimestamp) { return null; @@ -35,7 +35,7 @@ const ViewLogInContext: React.FC = ({ children }) => { {children} @@ -45,7 +45,7 @@ const ViewLogInContext: React.FC = ({ children }) => { const LogEntriesStateProvider: React.FC<{ logStreamPageState: InitializedLogStreamPageState; }> = ({ children, logStreamPageState }) => { - const { logViewId } = useLogViewContext(); + const { logViewReference } = useLogViewContext(); const { startTimestamp, endTimestamp, targetPosition } = useLogPositionStateContext(); const { context: { parsedQuery }, @@ -58,7 +58,7 @@ const LogEntriesStateProvider: React.FC<{ return ( = ({ children, logStreamPageState }) => { - const { logViewId, logView } = useLogViewContext(); + const { logViewReference, logView } = useLogViewContext(); const { topCursor, bottomCursor, entries } = useLogStreamContext(); const serializedParsedQuery = useMemo( () => stringify(logStreamPageState.context.parsedQuery), @@ -80,7 +80,7 @@ const LogHighlightsState: React.FC<{ ); const highlightsProps = { - sourceId: logViewId, + logViewReference, sourceVersion: logView?.version, entriesStart: topCursor, entriesEnd: bottomCursor, diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx index 9b4a2ffb4185b..15dbbcca7ce9f 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_view_log_in_context.tsx @@ -25,7 +25,7 @@ import { LogStream } from '../../../components/log_stream'; const MODAL_MARGIN = 25; export const PageViewLogInContext: React.FC = () => { - const [{ contextEntry, startTimestamp, endTimestamp, sourceId }, { setContextEntry }] = + const [{ contextEntry, startTimestamp, endTimestamp, logViewReference }, { setContextEntry }] = useViewLogInProviderContext(); const closeModal = useCallback(() => setContextEntry(undefined), [setContextEntry]); const { width: vw, height: vh } = useViewportDimensions(); @@ -56,7 +56,7 @@ export const PageViewLogInContext: React.FC = () => { { + public async getLogView(logViewReference: LogViewReference): Promise { + if (logViewReference.type === 'log-view-inline') { + return { + ...logViewReference, + origin: 'inline', + }; + } + + const { logViewId } = logViewReference; const response = await this.http.get(getLogViewUrl(logViewId)).catch((error) => { throw new FetchLogViewError(`Failed to fetch log view "${logViewId}": ${error}`); }); @@ -51,8 +61,8 @@ export class LogViewsClient implements ILogViewsClient { return data; } - public async getResolvedLogView(logViewId: string): Promise { - const logView = await this.getLogView(logViewId); + public async getResolvedLogView(logViewReference: LogViewReference): Promise { + const logView = await this.getLogView(logViewReference); const resolvedLogView = await this.resolveLogView(logView.id, logView.attributes); return resolvedLogView; } @@ -98,24 +108,44 @@ export class LogViewsClient implements ILogViewsClient { } public async putLogView( - logViewId: string, + logViewReference: LogViewReference, logViewAttributes: Partial ): Promise { - const response = await this.http - .put(getLogViewUrl(logViewId), { - body: JSON.stringify(putLogViewRequestPayloadRT.encode({ attributes: logViewAttributes })), - }) - .catch((error) => { - throw new PutLogViewError(`Failed to write log view "${logViewId}": ${error}`); - }); + if (logViewReference.type === 'log-view-inline') { + const { id } = logViewReference; + const attributes = decodeOrThrow( + rt.partial(logViewAttributesRT.type.props), + (message: string) => + new PutLogViewError(`Failed to decode inline log view "${id}": ${message}"`) + )(logViewAttributes); + return { + id, + attributes: { + ...logViewReference.attributes, + ...attributes, + }, + origin: 'inline', + }; + } else { + const { logViewId } = logViewReference; + const response = await this.http + .put(getLogViewUrl(logViewId), { + body: JSON.stringify( + putLogViewRequestPayloadRT.encode({ attributes: logViewAttributes }) + ), + }) + .catch((error) => { + throw new PutLogViewError(`Failed to write log view "${logViewId}": ${error}`); + }); - const { data } = decodeOrThrow( - getLogViewResponsePayloadRT, - (message: string) => - new PutLogViewError(`Failed to decode written log view "${logViewId}": ${message}"`) - )(response); + const { data } = decodeOrThrow( + getLogViewResponsePayloadRT, + (message: string) => + new PutLogViewError(`Failed to decode written log view "${logViewId}": ${message}"`) + )(response); - return data; + return data; + } } public async resolveLogView( diff --git a/x-pack/plugins/infra/public/services/log_views/types.ts b/x-pack/plugins/infra/public/services/log_views/types.ts index 9054ef79b4a4c..6fd7a4d208b3c 100644 --- a/x-pack/plugins/infra/public/services/log_views/types.ts +++ b/x-pack/plugins/infra/public/services/log_views/types.ts @@ -11,6 +11,7 @@ import { DataViewsContract } from '@kbn/data-views-plugin/public'; import { LogView, LogViewAttributes, + LogViewReference, LogViewStatus, ResolvedLogView, } from '../../../common/log_views'; @@ -28,9 +29,12 @@ export interface LogViewsServiceStartDeps { } export interface ILogViewsClient { - getLogView(logViewId: string): Promise; + getLogView(logViewReference: LogViewReference): Promise; getResolvedLogViewStatus(resolvedLogView: ResolvedLogView): Promise; - getResolvedLogView(logViewId: string): Promise; - putLogView(logViewId: string, logViewAttributes: Partial): Promise; + getResolvedLogView(logViewReference: LogViewReference): Promise; + putLogView( + logViewReference: LogViewReference, + logViewAttributes: Partial + ): Promise; resolveLogView(logViewId: string, logViewAttributes: LogViewAttributes): Promise; } diff --git a/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts b/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts index 825a070ed15dc..511ef829553b3 100644 --- a/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts +++ b/x-pack/plugins/infra/public/utils/logs_overview_fetchers.ts @@ -12,8 +12,9 @@ import { FetchDataParams, LogsFetchDataResponse, } from '@kbn/observability-plugin/public'; -import { DEFAULT_SOURCE_ID, TIMESTAMP_FIELD } from '../../common/constants'; +import { TIMESTAMP_FIELD } from '../../common/constants'; import { InfraClientStartDeps, InfraClientStartServicesAccessor } from '../types'; +import { DEFAULT_LOG_VIEW } from '../observability_logs/log_view_state/src/defaults'; interface StatsAggregation { buckets: Array<{ @@ -38,7 +39,7 @@ type StatsAndSeries = Pick; export function getLogsHasDataFetcher(getStartServices: InfraClientStartServicesAccessor) { return async () => { const [, , { logViews }] = await getStartServices(); - const resolvedLogView = await logViews.client.getResolvedLogView(DEFAULT_SOURCE_ID); + const resolvedLogView = await logViews.client.getResolvedLogView(DEFAULT_LOG_VIEW); const logViewStatus = await logViews.client.getResolvedLogViewStatus(resolvedLogView); const hasData = logViewStatus.index === 'available'; @@ -56,7 +57,7 @@ export function getLogsOverviewDataFetcher( ): FetchData { return async (params) => { const [, { data }, { logViews }] = await getStartServices(); - const resolvedLogView = await logViews.client.getResolvedLogView(DEFAULT_SOURCE_ID); + const resolvedLogView = await logViews.client.getResolvedLogView(DEFAULT_LOG_VIEW); const { stats, series } = await fetchLogsOverview( { diff --git a/x-pack/test/functional/apps/infra/link_to.ts b/x-pack/test/functional/apps/infra/link_to.ts index 890f85369877c..6598e0e03d6bc 100644 --- a/x-pack/test/functional/apps/infra/link_to.ts +++ b/x-pack/test/functional/apps/infra/link_to.ts @@ -10,6 +10,7 @@ import { URL } from 'url'; import { FtrProviderContext } from '../../ftr_provider_context'; const ONE_HOUR = 60 * 60 * 1000; +const LOG_VIEW_REFERENCE = '(logViewId:default,type:log-view-reference)'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const pageObjects = getPageObjects(['common']); @@ -51,7 +52,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(parsedUrl.searchParams.get('logPosition')).to.be( `(position:(tiebreaker:0,time:${timestamp}))` ); - expect(parsedUrl.searchParams.get('sourceId')).to.be('default'); + expect(parsedUrl.searchParams.get('logView')).to.be(LOG_VIEW_REFERENCE); expect(documentTitle).to.contain('Stream - Logs - Observability - Elastic'); }); }); From c4ad98b685413573fd06e400bf99ed307c75a0cb Mon Sep 17 00:00:00 2001 From: Ashokaditya <1849116+ashokaditya@users.noreply.github.com> Date: Wed, 29 Mar 2023 16:03:44 +0200 Subject: [PATCH 05/10] [Security Solution][Endpoint][Response Actions] Show an OS/platform icon on Responder for the running agent (#152821) ## Summary Adds a platform icon to the responder console header. In addition, it shows the applied policy name and agent platform in the `status` action result. Screenshot 2023-03-07 at 11 59 52 Screenshot 2023-03-07 at 11 59 25 Screenshot 2023-03-07 at 11 58 09 Screenshot 2023-03-09 at 15 27 32 **Last seen date Fix** Here's a clip with and without the additional style where you can see that there's additional spacing on the date item. This is an existing issue and has not been introduced with the new changes in the PR. ![last-seen-spacing](https://user-images.githubusercontent.com/1849116/223472588-918f53d2-98d3-43da-a737-6319bab402f6.gif) ### Checklist - [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/main/packages/kbn-i18n/README.md) - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --- .../status_action.tsx | 42 +++++++++- .../components/header_endpoint_info.test.tsx | 4 + .../components/header_endpoint_info.tsx | 84 ++++++++++++------- .../components/platforms/index.tsx | 8 ++ .../components/platforms/logos/linux.svg | 1 + .../components/platforms/logos/macos.svg | 1 + .../components/platforms/logos/windows.svg | 1 + .../platforms/platform_icon.test.tsx | 38 +++++++++ .../components/platforms/platform_icon.tsx | 49 +++++++++++ 9 files changed, 196 insertions(+), 32 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/index.tsx create mode 100644 x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/logos/linux.svg create mode 100644 x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/logos/macos.svg create mode 100644 x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/logos/windows.svg create mode 100644 x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/platform_icon.test.tsx create mode 100644 x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/platform_icon.tsx diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/status_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/status_action.tsx index 342e7654ec390..94b7aa9dd5160 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/status_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/status_action.tsx @@ -165,6 +165,16 @@ export const EndpointStatusActionResult = memo< ), description: {agentStatus()}, }, + { + title: ( + + {i18n.translate('xpack.securitySolution.endpointResponseActions.status.platform', { + defaultMessage: 'Platform', + })} + + ), + description: {endpointDetails.metadata.host.os.full}, + }, { title: ( @@ -189,6 +199,37 @@ export const EndpointStatusActionResult = memo< ), }, + { + title: ( + + {i18n.translate( + 'xpack.securitySolution.endpointResponseActions.status.appliedPolicyVersion', + { + defaultMessage: 'Policy version', + } + )} + + ), + description: ( + + {`v${endpointDetails.metadata.Endpoint.policy.applied.endpoint_policy_version}`} + + ), + }, + { + title: ( + + {i18n.translate('xpack.securitySolution.endpointResponseActions.status.policyName', { + defaultMessage: 'Policy name', + })} + + ), + description: ( + + {endpointDetails.metadata.Endpoint.policy.applied.name} + + ), + }, { title: ( @@ -205,7 +246,6 @@ export const EndpointStatusActionResult = memo< { defaultMessage: 'Last active' } )} value={endpointDetails.metadata['@timestamp']} - className="eui-textTruncate" /> ), diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.test.tsx index 78667399f04d9..4941fd59686cc 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.test.tsx @@ -62,4 +62,8 @@ describe('Responder header endpoint info', () => { const lastUpdated = await renderResult.findByTestId('responderHeaderLastSeen'); expect(lastUpdated).toBeTruthy(); }); + it('should show platform icon', async () => { + const platformIcon = await renderResult.findByTestId('responderHeaderEndpointPlatformIcon'); + expect(platformIcon).toBeTruthy(); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.tsx index 37bfb77ce8a96..2ffcf76b8ba01 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/header_endpoint_info.tsx @@ -10,15 +10,25 @@ import { EuiFlexGroup, EuiFlexItem, EuiText, - EuiLoadingContent, + EuiSkeletonText, EuiToolTip, EuiSpacer, } from '@elastic/eui'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { FormattedMessage, FormattedRelative } from '@kbn/i18n-react'; import { useGetEndpointDetails } from '../../../hooks/endpoint/use_get_endpoint_details'; import type { EndpointHostIsolationStatusProps } from '../../../../common/components/endpoint/host_isolation'; import { EndpointAgentAndIsolationStatus } from '../../endpoint_agent_and_isolation_status'; import { useGetEndpointPendingActionsSummary } from '../../../hooks/response_actions/use_get_endpoint_pending_actions_summary'; +import type { Platform } from './platforms'; +import { PlatformIcon } from './platforms'; + +const IconContainer = euiStyled.div` + height: 100%; + display: flex; + align-items: center; + justify-content: center; +`; interface HeaderEndpointInfoProps { endpointId: string; @@ -48,7 +58,7 @@ export const HeaderEndpointInfo = memo(({ endpointId }) }, [endpointPendingActions?.data]); if (isFetching && endpointPendingActions === undefined) { - return ; + return ; } if (!endpointDetails) { @@ -56,41 +66,53 @@ export const HeaderEndpointInfo = memo(({ endpointId }) } return ( - + + + + + + - - - - -
{endpointDetails.metadata.host.name}
-
-
+ + + + + + +
{endpointDetails.metadata.host.name}
+
+
+
+ + + +
- + + + , + }} + /> +
- - - - , - }} - /> - -
); }); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/index.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/index.tsx new file mode 100644 index 0000000000000..7c473ddf63bdb --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/index.tsx @@ -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 { PlatformIcon, type Platform } from './platform_icon'; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/logos/linux.svg b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/logos/linux.svg new file mode 100644 index 0000000000000..5325e16edfa5f --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/logos/linux.svg @@ -0,0 +1 @@ + diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/logos/macos.svg b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/logos/macos.svg new file mode 100644 index 0000000000000..07c683d2d2274 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/logos/macos.svg @@ -0,0 +1 @@ + diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/logos/windows.svg b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/logos/windows.svg new file mode 100644 index 0000000000000..5f34665ffe409 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/logos/windows.svg @@ -0,0 +1 @@ + diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/platform_icon.test.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/platform_icon.test.tsx new file mode 100644 index 0000000000000..c528134f2f106 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/platform_icon.test.tsx @@ -0,0 +1,38 @@ +/* + * 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 { AppContextTestRender } from '../../../../../common/mock/endpoint'; +import { createAppRootMockRenderer } from '../../../../../common/mock/endpoint'; +import { PlatformIcon, type PlatformIconProps } from './platform_icon'; + +describe('PlatformIcon', () => { + let render: (props: PlatformIconProps) => ReturnType; + let renderResult: ReturnType; + let mockedContext: AppContextTestRender; + + beforeEach(() => { + mockedContext = createAppRootMockRenderer(); + render = (props) => + (renderResult = mockedContext.render( + + )); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it.each(['linux', 'macos', 'windows', 'unknown'] as Array)( + 'should render icon for %s', + (platform) => { + render({ platform }); + const icon = renderResult.getByTestId('test'); + expect(icon).toBeTruthy(); + } + ); +}); diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/platform_icon.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/platform_icon.tsx new file mode 100644 index 0000000000000..6aa9c0853f206 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/components/platforms/platform_icon.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiIcon, type EuiIconProps } from '@elastic/eui'; +import React, { memo, useMemo } from 'react'; +import linuxSvg from './logos/linux.svg'; +import windowsSvg from './logos/windows.svg'; +import macosSvg from './logos/macos.svg'; + +export type Platform = 'macos' | 'linux' | 'windows'; +const getPlatformIcon = (platform: Platform): string | null => { + switch (platform) { + case 'macos': + return macosSvg; + case 'linux': + return linuxSvg; + case 'windows': + return windowsSvg; + default: + return null; + } +}; + +export interface PlatformIconProps { + platform: Platform; + size?: EuiIconProps['size']; + 'data-test-subj'?: string; +} + +export const PlatformIcon = memo( + ({ platform, size = 'xl', 'data-test-subj': dataTestSubj }) => { + const platformIcon = useMemo(() => getPlatformIcon(platform), [platform]); + + return ( + + ); + } +); + +PlatformIcon.displayName = 'PlatformIcon'; From 383454e30fa224e73fc01571b877962d8c94ca9e Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 29 Mar 2023 08:22:31 -0600 Subject: [PATCH 06/10] [maps] pass request meta to ITMSSource.getUrlTemplate (#153871) Fixes https://github.com/elastic/kibana/issues/153804 PR resolves issue by passing `SourceRequestMeta` to `ITMSSource.getUrlTemplate`, giving `getUrlTemplate` the state needed to update raster URL when required. PR also resolves the following tech debt: * rename `searchFilters` -> `requestMeta` to better reflect contents of parameter. * Map API's leak lots of implementation details. This PR took the time to clean up one instance by moving `isGeoGridPrecisionAware` and `getGeoGridPrecision` from `ISource` to `IESAggSource` * moved `geogridPrecision` from `VectorSourceRequestMeta` into `sourceMeta`. * Removed `VectorJoinSourceRequestMeta` since its same as `VectorSourceRequestMeta` now that `VectorSourceRequestMeta` does not contain `geogridPrecision` --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../public/classes/custom_raster_source.tsx | 14 +--- .../data_request_descriptor_types.ts | 16 ++--- .../layers/build_vector_request_meta.ts | 3 +- .../raster_tile_layer/raster_tile_layer.ts | 14 +++- .../layers/vector_layer/bounds_data.ts | 2 +- .../layers/vector_layer/vector_layer.tsx | 5 +- .../sources/es_agg_source/es_agg_source.ts | 18 +++--- .../classes/sources/es_agg_source/index.ts | 3 +- .../classes/sources/es_agg_source/types.ts | 21 ++++++ .../es_geo_grid_source.test.ts | 5 +- .../es_geo_grid_source/es_geo_grid_source.tsx | 43 +++++++------ .../es_geo_line_source/es_geo_line_source.tsx | 14 ++-- .../es_pew_pew_source/es_pew_pew_source.tsx | 17 +++-- .../es_search_source/es_search_source.test.ts | 6 +- .../es_search_source/es_search_source.tsx | 64 +++++++++---------- .../classes/sources/es_source/es_source.ts | 57 ++++++++--------- .../sources/es_term_source/es_term_source.ts | 10 +-- .../maps/public/classes/sources/source.ts | 10 --- .../sources/table_source/table_source.test.ts | 7 +- .../sources/table_source/table_source.ts | 5 +- .../term_join_source/term_join_source.ts | 4 +- .../classes/sources/tms_source/index.ts | 4 +- .../vector_source/mvt_vector_source.ts | 2 +- .../sources/vector_source/vector_source.tsx | 9 +-- .../public/classes/util/can_skip_fetch.ts | 13 +--- 25 files changed, 188 insertions(+), 178 deletions(-) create mode 100644 x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts diff --git a/x-pack/examples/third_party_maps_source_example/public/classes/custom_raster_source.tsx b/x-pack/examples/third_party_maps_source_example/public/classes/custom_raster_source.tsx index 3720235173751..7da9dd12b4265 100644 --- a/x-pack/examples/third_party_maps_source_example/public/classes/custom_raster_source.tsx +++ b/x-pack/examples/third_party_maps_source_example/public/classes/custom_raster_source.tsx @@ -12,8 +12,8 @@ import { FieldFormatter, MIN_ZOOM, MAX_ZOOM } from '@kbn/maps-plugin/common'; import type { AbstractSourceDescriptor, Attribution, - DataFilters, DataRequestMeta, + SourceRequestMeta, Timeslice, } from '@kbn/maps-plugin/common/descriptor_types'; import type { @@ -102,10 +102,6 @@ export class CustomRasterSource implements IRasterSource { return false; } - isGeoGridPrecisionAware(): boolean { - return false; - } - isQueryAware(): boolean { return false; } @@ -138,10 +134,6 @@ export class CustomRasterSource implements IRasterSource { return []; } - getGeoGridPrecision(zoom: number): number { - return 0; - } - isESSource(): boolean { return false; } @@ -179,11 +171,11 @@ export class CustomRasterSource implements IRasterSource { return true; } - async getUrlTemplate(dataFilters: DataFilters): Promise { + async getUrlTemplate(requestMeta: SourceRequestMeta): Promise { const defaultUrl = 'https://new.nowcoast.noaa.gov/arcgis/rest/services/nowcoast/radar_meteo_imagery_nexrad_time/MapServer/export?dpi=96&transparent=true&format=png32&time={time}&layers=show%3A3&bbox=-{bbox-epsg-3857}&bboxSR=3857&imageSR=3857&size=256%2C256&f=image'; - const { timeslice, timeFilters } = dataFilters; + const { timeslice, timeFilters } = requestMeta; let timestamp; if (timeslice) { diff --git a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts index dd809258ef0c0..9bdbfdb9b3b2e 100644 --- a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts @@ -37,21 +37,21 @@ export type DataFilters = { executionContext: KibanaExecutionContext; }; -export type VectorSourceRequestMeta = DataFilters & { +export type SourceRequestMeta = DataFilters & { applyGlobalQuery: boolean; applyGlobalTime: boolean; applyForceRefresh: boolean; + sourceQuery?: Query; + isForceRefresh: boolean; +}; + +export type VectorSourceRequestMeta = SourceRequestMeta & { fieldNames: string[]; - geogridPrecision?: number; timesliceMaskField?: string; - sourceQuery?: Query; sourceMeta: object | null; - isForceRefresh: boolean; isFeatureEditorOpenForLayer: boolean; }; -export type VectorJoinSourceRequestMeta = Omit; - export type VectorStyleRequestMeta = DataFilters & { dynamicStyleFields: string[]; isTimeAware: boolean; @@ -89,8 +89,8 @@ export type DataRequestMeta = { // request stop time in milliseconds since epoch requestStopTime?: number; } & Partial< - VectorSourceRequestMeta & - VectorJoinSourceRequestMeta & + SourceRequestMeta & + VectorSourceRequestMeta & VectorStyleRequestMeta & ESSearchSourceResponseMeta & ESGeoLineSourceResponseMeta & diff --git a/x-pack/plugins/maps/public/classes/layers/build_vector_request_meta.ts b/x-pack/plugins/maps/public/classes/layers/build_vector_request_meta.ts index e8d74003c6790..e0df7ab8d6dd5 100644 --- a/x-pack/plugins/maps/public/classes/layers/build_vector_request_meta.ts +++ b/x-pack/plugins/maps/public/classes/layers/build_vector_request_meta.ts @@ -22,11 +22,10 @@ export function buildVectorRequestMeta( return { ...dataFilters, fieldNames: _.uniq(fieldNames).sort(), - geogridPrecision: source.getGeoGridPrecision(dataFilters.zoom), sourceQuery: sourceQuery ? sourceQuery : undefined, applyGlobalQuery: source.getApplyGlobalQuery(), applyGlobalTime: source.getApplyGlobalTime(), - sourceMeta: source.getSyncMeta(), + sourceMeta: source.getSyncMeta(dataFilters), applyForceRefresh: source.isESSource() ? source.getApplyForceRefresh() : false, isForceRefresh, isFeatureEditorOpenForLayer, diff --git a/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.ts b/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.ts index bd1b266d34881..cb12d047cbccb 100644 --- a/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/raster_tile_layer/raster_tile_layer.ts @@ -65,11 +65,21 @@ export class RasterTileLayer extends AbstractLayer { return this._style; } - async syncData({ startLoading, stopLoading, onLoadError, dataFilters }: DataRequestContext) { + async syncData({ + startLoading, + stopLoading, + onLoadError, + dataFilters, + isForceRefresh, + }: DataRequestContext) { const source = this.getSource(); const nextMeta = { ...dataFilters, + applyGlobalQuery: source.getApplyGlobalQuery(), applyGlobalTime: source.getApplyGlobalTime(), + applyForceRefresh: source.isESSource() ? source.getApplyForceRefresh() : false, + sourceQuery: this.getQuery() || undefined, + isForceRefresh, }; const prevDataRequest = this.getSourceDataRequest(); if (prevDataRequest) { @@ -80,7 +90,7 @@ export class RasterTileLayer extends AbstractLayer { try { startLoading(SOURCE_DATA_REQUEST_ID, requestToken, nextMeta); const data: RasterTileSourceData = { - url: await source.getUrlTemplate(dataFilters), + url: await source.getUrlTemplate(nextMeta), }; stopLoading(SOURCE_DATA_REQUEST_ID, requestToken, data, {}); } catch (error) { diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/bounds_data.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/bounds_data.ts index b46353fcef933..de8d20669c8be 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/bounds_data.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/bounds_data.ts @@ -32,7 +32,7 @@ export async function syncBoundsData({ const requestToken = Symbol(`${SOURCE_BOUNDS_DATA_REQUEST_ID}-${layerId}`); - // Do not pass all searchFilters to source.getBoundsForFilters(). + // Do not pass all requestMeta to source.getBoundsForFilters(). // For example, do not want to filter bounds request by extent and buffer. const boundsFilters = { sourceQuery: sourceQuery ? sourceQuery : undefined, diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index bff6a297fcd74..f8931b4a5755d 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -45,7 +45,6 @@ import { ESTermSourceDescriptor, JoinDescriptor, StyleMetaDescriptor, - VectorJoinSourceRequestMeta, VectorLayerDescriptor, VectorSourceRequestMeta, VectorStyleRequestMeta, @@ -566,14 +565,14 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { const sourceDataId = join.getSourceDataRequestId(); const requestToken = Symbol(`layer-join-refresh:${this.getId()} - ${sourceDataId}`); - const joinRequestMeta: VectorJoinSourceRequestMeta = buildVectorRequestMeta( + const joinRequestMeta = buildVectorRequestMeta( joinSource, joinSource.getFieldNames(), dataFilters, joinSource.getWhereQuery(), isForceRefresh, isFeatureEditorOpenForLayer - ) as VectorJoinSourceRequestMeta; + ); const prevDataRequest = this.getDataRequest(sourceDataId); const canSkipFetch = await canSkipSourceUpdate({ diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts index 42b9c0ed73c77..fa7f329beb97a 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { GeoJsonProperties } from 'geojson'; import { DataView } from '@kbn/data-plugin/common'; -import { IESSource } from '../es_source'; +import type { IESAggSource } from './types'; import { AbstractESSource } from '../es_source'; import { esAggFieldsFactory, IESAggField } from '../../fields/agg'; import { AGG_TYPE, COUNT_PROP_LABEL, FIELD_ORIGIN } from '../../../../common/constants'; @@ -19,14 +19,6 @@ import { ITooltipProperty } from '../../tooltips/tooltip_property'; export const DEFAULT_METRIC = { type: AGG_TYPE.COUNT }; -export interface IESAggSource extends IESSource { - getAggKey(aggType: AGG_TYPE, fieldName: string): string; - getAggLabel(aggType: AGG_TYPE, fieldLabel: string): Promise; - getMetricFields(): IESAggField[]; - getMetricFieldForName(fieldName: string): IESAggField | null; - getValueAggsDsl(indexPattern: DataView): { [key: string]: unknown }; -} - export abstract class AbstractESAggSource extends AbstractESSource implements IESAggSource { private readonly _metricFields: IESAggField[]; @@ -139,4 +131,12 @@ export abstract class AbstractESAggSource extends AbstractESSource implements IE return await Promise.all(promises); } + + isGeoGridPrecisionAware(): boolean { + return false; + } + + getGeoGridPrecision(zoom: number): number { + return 0; + } } diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts index 52ae53597acb1..033d937aa9391 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts @@ -5,4 +5,5 @@ * 2.0. */ -export * from './es_agg_source'; +export type { IESAggSource } from './types'; +export { AbstractESAggSource, DEFAULT_METRIC } from './es_agg_source'; diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts new file mode 100644 index 0000000000000..d9cb6fcd95a10 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DataView } from '@kbn/data-plugin/common'; +import { AGG_TYPE } from '../../../../common/constants'; +import type { IESSource } from '../es_source'; +import { IESAggField } from '../../fields/agg'; + +export interface IESAggSource extends IESSource { + getAggKey(aggType: AGG_TYPE, fieldName: string): string; + getAggLabel(aggType: AGG_TYPE, fieldLabel: string): Promise; + getMetricFields(): IESAggField[]; + getMetricFieldForName(fieldName: string): IESAggField | null; + getValueAggsDsl(indexPattern: DataView): { [key: string]: unknown }; + isGeoGridPrecisionAware(): boolean; + getGeoGridPrecision(zoom: number): number; +} diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts index 65325fed5d957..6709ef3ab5144 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts @@ -157,7 +157,6 @@ describe('ESGeoGridSource', () => { const vectorSourceRequestMeta: VectorSourceRequestMeta = { isReadOnly: false, - geogridPrecision: 4, filters: [], timeFilters: { from: 'now', @@ -175,7 +174,7 @@ describe('ESGeoGridSource', () => { language: 'KQL', }, sourceMeta: null, - zoom: 0, + zoom: 2, // returns 4 precision isForceRefresh: false, isFeatureEditorOpenForLayer: false, executionContext: { name: APP_ID }, @@ -290,7 +289,7 @@ describe('ESGeoGridSource', () => { type: SOURCE_TYPES.ES_GEO_GRID, requestType: RENDER_AS.HEATMAP, }); - expect(superFineSource.getGeoGridPrecision(10)).toBe(NaN); + expect(superFineSource.getGeoGridPrecision(10)).toBe(0); }); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx index c19e715326bc3..c1ad49619aa70 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.tsx @@ -43,6 +43,7 @@ import { LICENSED_FEATURES } from '../../../licensed_features'; import { getHttp } from '../../../kibana_services'; import { GetFeatureActionsArgs, GeoJsonWithMeta, IMvtVectorSource } from '../vector_source'; import { + DataFilters, ESGeoGridSourceDescriptor, MapExtent, SizeDynamicOptions, @@ -56,7 +57,11 @@ import { isMvt } from './is_mvt'; import { VectorStyle } from '../../styles/vector/vector_style'; import { getIconSize } from './get_icon_size'; -type ESGeoGridSourceSyncMeta = Pick; +interface ESGeoGridSourceSyncMeta { + geogridPrecision: number; + requestType: RENDER_AS; + resolution: GRID_RESOLUTION; +} const MAX_GEOTILE_LEVEL = 29; @@ -135,8 +140,9 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo ); } - getSyncMeta(): ESGeoGridSourceSyncMeta { + getSyncMeta(dataFilters: DataFilters): ESGeoGridSourceSyncMeta { return { + geogridPrecision: this.getGeoGridPrecision(dataFilters.zoom), requestType: this._descriptor.requestType, resolution: this._descriptor.resolution, }; @@ -189,9 +195,9 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo } getGeoGridPrecision(zoom: number): number { - if (this._descriptor.resolution === GRID_RESOLUTION.SUPER_FINE) { + if (this.isMvt()) { // The target-precision needs to be determined server side. - return NaN; + return 0; } const targetGeotileLevel = Math.ceil(zoom) + this._getGeoGridPrecisionResolutionDelta(); @@ -441,17 +447,17 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo async getGeoJsonWithMeta( layerName: string, - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, registerCancelCallback: (callback: () => void) => void, isRequestStillActive: () => boolean, inspectorAdapters: Adapters ): Promise { - if (!searchFilters.buffer) { + if (!requestMeta.buffer) { throw new Error('Cannot get GeoJson without searchFilter.buffer'); } const indexPattern: DataView = await this.getIndexPattern(); - const searchSource: ISearchSource = await this.makeSearchSource(searchFilters, 0); + const searchSource: ISearchSource = await this.makeSearchSource(requestMeta, 0); searchSource.setField('trackTotalHits', false); let bucketsPerGrid = 1; @@ -469,32 +475,33 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo // https://github.com/elastic/elasticsearch/issues/60626 const supportsCompositeAgg = !(await this._isGeoShape()); + const precision = this.getGeoGridPrecision(requestMeta.zoom); const features: Feature[] = supportsCompositeAgg && tooManyBuckets ? await this._compositeAggRequest({ searchSource, - searchSessionId: searchFilters.searchSessionId, + searchSessionId: requestMeta.searchSessionId, indexPattern, - precision: searchFilters.geogridPrecision || 0, + precision, layerName, registerCancelCallback, bucketsPerGrid, isRequestStillActive, - bufferedExtent: searchFilters.buffer, + bufferedExtent: requestMeta.buffer, inspectorAdapters, - executionContext: searchFilters.executionContext, + executionContext: requestMeta.executionContext, }) : await this._nonCompositeAggRequest({ searchSource, - searchSessionId: searchFilters.searchSessionId, + searchSessionId: requestMeta.searchSessionId, indexPattern, - precision: searchFilters.geogridPrecision || 0, + precision, layerName, registerCancelCallback, - bufferedExtent: searchFilters.buffer, + bufferedExtent: requestMeta.buffer, tooManyBuckets, inspectorAdapters, - executionContext: searchFilters.executionContext, + executionContext: requestMeta.executionContext, }); return { @@ -513,13 +520,13 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo } async getTileUrl( - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, refreshToken: string, hasLabels: boolean, buffer: number ): Promise { const dataView = await this.getIndexPattern(); - const searchSource = await this.makeSearchSource(searchFilters, 0); + const searchSource = await this.makeSearchSource(requestMeta, 0); searchSource.setField('aggs', this.getValueAggsDsl(dataView)); const mvtUrlServicePath = getHttp().basePath.prepend( @@ -535,7 +542,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo params.set('requestBody', encodeMvtResponseBody(searchSource.getSearchRequestBody())); params.set('renderAs', this._descriptor.requestType); params.set('token', refreshToken); - const executionContextId = getExecutionContextId(searchFilters.executionContext); + const executionContextId = getExecutionContextId(requestMeta.executionContext); if (executionContextId) { params.set('executionContextId', executionContextId); } diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx index 33b2b2e38317b..3711252094160 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx @@ -163,7 +163,7 @@ export class ESGeoLineSource extends AbstractESAggSource { async getGeoJsonWithMeta( layerName: string, - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, registerCancelCallback: (callback: () => void) => void, isRequestStillActive: () => boolean, inspectorAdapters: Adapters @@ -182,7 +182,7 @@ export class ESGeoLineSource extends AbstractESAggSource { // // Fetch entities // - const entitySearchSource = await this.makeSearchSource(searchFilters, 0); + const entitySearchSource = await this.makeSearchSource(requestMeta, 0); entitySearchSource.setField('trackTotalHits', false); const splitField = getField(indexPattern, this._descriptor.splitField); const cardinalityAgg = { precision_threshold: 1 }; @@ -217,10 +217,10 @@ export class ESGeoLineSource extends AbstractESAggSource { requestDescription: i18n.translate('xpack.maps.source.esGeoLine.entityRequestDescription', { defaultMessage: 'Elasticsearch terms request to fetch entities within map buffer.', }), - searchSessionId: searchFilters.searchSessionId, + searchSessionId: requestMeta.searchSessionId, executionContext: mergeExecutionContext( { description: 'es_geo_line:entities' }, - searchFilters.executionContext + requestMeta.executionContext ), requestsAdapter: inspectorAdapters.requests, }); @@ -255,7 +255,7 @@ export class ESGeoLineSource extends AbstractESAggSource { indexPattern ).query; } - const tracksSearchFilters = { ...searchFilters }; + const tracksSearchFilters = { ...requestMeta }; delete tracksSearchFilters.buffer; const tracksSearchSource = await this.makeSearchSource(tracksSearchFilters, 0); tracksSearchSource.setField('trackTotalHits', false); @@ -293,10 +293,10 @@ export class ESGeoLineSource extends AbstractESAggSource { defaultMessage: 'Elasticsearch geo_line request to fetch tracks for entities. Tracks are not filtered by map buffer.', }), - searchSessionId: searchFilters.searchSessionId, + searchSessionId: requestMeta.searchSessionId, executionContext: mergeExecutionContext( { description: 'es_geo_line:tracks' }, - searchFilters.executionContext + requestMeta.executionContext ), requestsAdapter: inspectorAdapters.requests, }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.tsx index ae00e00e58ae6..0b3bd3f77625f 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/es_pew_pew_source.tsx @@ -29,6 +29,7 @@ import { DataRequestAbortError } from '../../util/data_request'; import { mergeExecutionContext } from '../execution_context_utils'; import { SourceEditorArgs } from '../source'; import { + DataFilters, ESPewPewSourceDescriptor, MapExtent, VectorSourceRequestMeta, @@ -85,6 +86,12 @@ export class ESPewPewSource extends AbstractESAggSource { return false; } + getSyncMeta(dataFilters: DataFilters) { + return { + geogridPrecision: this.getGeoGridPrecision(dataFilters.zoom), + }; + } + isGeoGridPrecisionAware() { return true; } @@ -125,13 +132,13 @@ export class ESPewPewSource extends AbstractESAggSource { async getGeoJsonWithMeta( layerName: string, - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, registerCancelCallback: (callback: () => void) => void, isRequestStillActive: () => boolean, inspectorAdapters: Adapters ): Promise { const indexPattern = await this.getIndexPattern(); - const searchSource = await this.makeSearchSource(searchFilters, 0); + const searchSource = await this.makeSearchSource(requestMeta, 0); searchSource.setField('trackTotalHits', false); searchSource.setField('aggs', { destSplit: { @@ -149,7 +156,7 @@ export class ESPewPewSource extends AbstractESAggSource { sourceGrid: { geotile_grid: { field: this._descriptor.sourceGeoField, - precision: searchFilters.geogridPrecision, + precision: this.getGeoGridPrecision(requestMeta.zoom), size: 500, }, aggs: { @@ -183,10 +190,10 @@ export class ESPewPewSource extends AbstractESAggSource { requestDescription: i18n.translate('xpack.maps.source.pewPew.inspectorDescription', { defaultMessage: 'Source-destination connections request', }), - searchSessionId: searchFilters.searchSessionId, + searchSessionId: requestMeta.searchSessionId, executionContext: mergeExecutionContext( { description: 'es_pew_pew_source:connections' }, - searchFilters.executionContext + requestMeta.executionContext ), requestsAdapter: inspectorAdapters.requests, }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts index 5157848cc5765..15bcee33e0463 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.test.ts @@ -88,7 +88,7 @@ describe('ESSearchSource', () => { }); }); - const searchFilters: VectorSourceRequestMeta = { + const requestMeta: VectorSourceRequestMeta = { isReadOnly: false, filters: [], zoom: 0, @@ -116,7 +116,7 @@ describe('ESSearchSource', () => { geoField: geoFieldName, indexPatternId: 'ipId', }); - const tileUrl = await esSearchSource.getTileUrl(searchFilters, '1234', false, 5); + const tileUrl = await esSearchSource.getTileUrl(requestMeta, '1234', false, 5); const urlParts = tileUrl.split('?'); expect(urlParts[0]).toEqual('rootdir/api/maps/mvt/getTile/{z}/{x}/{y}.pbf'); @@ -140,7 +140,7 @@ describe('ESSearchSource', () => { }); const tileUrl = await esSearchSource.getTileUrl( { - ...searchFilters, + ...requestMeta, executionContext: { name: APP_ID, id: 'map1234' }, }, '1234', diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx index 3b8ab273fbbc6..6a422df54782f 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx @@ -271,7 +271,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource async _getTopHits( layerName: string, - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, registerCancelCallback: (callback: () => void) => void, inspectorAdapters: Adapters ) { @@ -283,7 +283,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource const indexPattern: DataView = await this.getIndexPattern(); - const fieldNames = searchFilters.fieldNames.filter( + const fieldNames = requestMeta.fieldNames.filter( (fieldName) => fieldName !== this._descriptor.geoField ); const { docValueFields, sourceOnlyFields, scriptFields } = getDocValueAndSourceFields( @@ -324,7 +324,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource shard_size: DEFAULT_MAX_BUCKETS_LIMIT, }; - const searchSource = await this.makeSearchSource(searchFilters, 0); + const searchSource = await this.makeSearchSource(requestMeta, 0); searchSource.setField('trackTotalHits', false); searchSource.setField('aggs', { totalEntities: { @@ -354,10 +354,10 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource searchSource, registerCancelCallback, requestDescription: 'Elasticsearch document top hits request', - searchSessionId: searchFilters.searchSessionId, + searchSessionId: requestMeta.searchSessionId, executionContext: mergeExecutionContext( { description: 'es_search_source:top_hits' }, - searchFilters.executionContext + requestMeta.executionContext ), requestsAdapter: inspectorAdapters.requests, }); @@ -388,17 +388,17 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource }; } - // searchFilters.fieldNames contains geo field and any fields needed for styling features + // requestMeta.fieldNames contains geo field and any fields needed for styling features // Performs Elasticsearch search request being careful to pull back only required fields to minimize response size async _getSearchHits( layerName: string, - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, registerCancelCallback: (callback: () => void) => void, inspectorAdapters: Adapters ) { const indexPattern = await this.getIndexPattern(); - const fieldNames = searchFilters.fieldNames.filter( + const fieldNames = requestMeta.fieldNames.filter( (fieldName) => fieldName !== this._descriptor.geoField ); const { docValueFields, sourceOnlyFields } = getDocValueAndSourceFields( @@ -411,20 +411,20 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource // Use Kibana global time extent instead of timeslice extent when all documents for global time extent can be loaded // to allow for client-side masking of timeslice - const searchFiltersWithoutTimeslice = { ...searchFilters }; - delete searchFiltersWithoutTimeslice.timeslice; - const useSearchFiltersWithoutTimeslice = - searchFilters.timeslice !== undefined && - (await this.canLoadAllDocuments(searchFiltersWithoutTimeslice, registerCancelCallback)); + const requestMetaWithoutTimeslice = { ...requestMeta }; + delete requestMetaWithoutTimeslice.timeslice; + const useRequestMetaWithoutTimeslice = + requestMeta.timeslice !== undefined && + (await this.canLoadAllDocuments(requestMetaWithoutTimeslice, registerCancelCallback)); const maxResultWindow = await this.getMaxResultWindow(); const searchSource = await this.makeSearchSource( - useSearchFiltersWithoutTimeslice ? searchFiltersWithoutTimeslice : searchFilters, + useRequestMetaWithoutTimeslice ? requestMetaWithoutTimeslice : requestMeta, maxResultWindow, initialSearchContext ); searchSource.setField('trackTotalHits', maxResultWindow + 1); - searchSource.setField('fieldsFromSource', searchFilters.fieldNames); // Setting "fields" filters out unused scripted fields + searchSource.setField('fieldsFromSource', requestMeta.fieldNames); // Setting "fields" filters out unused scripted fields if (sourceOnlyFields.length === 0) { searchSource.setField('source', false); // do not need anything from _source } else { @@ -441,24 +441,24 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource searchSource, registerCancelCallback, requestDescription: 'Elasticsearch document request', - searchSessionId: searchFilters.searchSessionId, + searchSessionId: requestMeta.searchSessionId, executionContext: mergeExecutionContext( { description: 'es_search_source:doc_search' }, - searchFilters.executionContext + requestMeta.executionContext ), requestsAdapter: inspectorAdapters.requests, }); const isTimeExtentForTimeslice = - searchFilters.timeslice !== undefined && !useSearchFiltersWithoutTimeslice; + requestMeta.timeslice !== undefined && !useRequestMetaWithoutTimeslice; return { hits: resp.hits.hits.reverse(), // Reverse hits so top documents by sort are drawn on top meta: { resultsCount: resp.hits.hits.length, areResultsTrimmed: isTotalHitsGreaterThan(resp.hits.total, resp.hits.hits.length), timeExtent: isTimeExtentForTimeslice - ? searchFilters.timeslice - : timerangeToTimeextent(searchFilters.timeFilters), + ? requestMeta.timeslice + : timerangeToTimeextent(requestMeta.timeFilters), isTimeExtentForTimeslice, }, }; @@ -519,7 +519,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource async getGeoJsonWithMeta( layerName: string, - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, registerCancelCallback: (callback: () => void) => void, isRequestStillActive: () => boolean, inspectorAdapters: Adapters @@ -527,10 +527,10 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource const indexPattern = await this.getIndexPattern(); const { hits, meta } = this._isTopHits() - ? await this._getTopHits(layerName, searchFilters, registerCancelCallback, inspectorAdapters) + ? await this._getTopHits(layerName, requestMeta, registerCancelCallback, inspectorAdapters) : await this._getSearchHits( layerName, - searchFilters, + requestMeta, registerCancelCallback, inspectorAdapters ); @@ -546,7 +546,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource }); return properties; }; - const epochMillisFields = searchFilters.fieldNames.filter((fieldName) => { + const epochMillisFields = requestMeta.fieldNames.filter((fieldName) => { const field = getField(indexPattern, fieldName); return field.readFromDocValues && field.type === 'date'; }); @@ -841,7 +841,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource } async getTileUrl( - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, refreshToken: string, hasLabels: boolean, buffer: number @@ -849,7 +849,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource const dataView = await this.getIndexPattern(); const indexSettings = await loadIndexSettings(dataView.getIndexPattern()); - const searchSource = await this.makeSearchSource(searchFilters, indexSettings.maxResultWindow); + const searchSource = await this.makeSearchSource(requestMeta, indexSettings.maxResultWindow); // searchSource calls dataView.getComputedFields to seed docvalueFields // dataView.getComputedFields adds each date field in the dataView to docvalueFields to ensure standardized date format across kibana // we don't need these as they request unneeded fields and bloat responses @@ -864,7 +864,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource // use fields API searchSource.setField( 'fields', - searchFilters.fieldNames + requestMeta.fieldNames .filter((fieldName) => { return fieldName !== this._descriptor.geoField; }) @@ -895,7 +895,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource params.set('buffer', buffer.toString()); params.set('requestBody', encodeMvtResponseBody(requestBody)); params.set('token', refreshToken); - const executionContextId = getExecutionContextId(searchFilters.executionContext); + const executionContextId = getExecutionContextId(requestMeta.executionContext); if (executionContextId) { params.set('executionContextId', executionContextId); } @@ -947,22 +947,22 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource } async canLoadAllDocuments( - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, registerCancelCallback: (callback: () => void) => void ) { const abortController = new AbortController(); registerCancelCallback(() => abortController.abort()); const maxResultWindow = await this.getMaxResultWindow(); - const searchSource = await this.makeSearchSource(searchFilters, 0); + const searchSource = await this.makeSearchSource(requestMeta, 0); searchSource.setField('trackTotalHits', maxResultWindow + 1); const { rawResponse: resp } = await lastValueFrom( searchSource.fetch$({ abortSignal: abortController.signal, - sessionId: searchFilters.searchSessionId, + sessionId: requestMeta.searchSessionId, legacyHitsTotal: false, executionContext: mergeExecutionContext( { description: 'es_search_source:all_doc_counts' }, - searchFilters.executionContext + requestMeta.executionContext ), }) ); diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts index f941c610962d2..d37f02a079a2d 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts @@ -15,6 +15,7 @@ import type { KibanaExecutionContext } from '@kbn/core/public'; import { RequestAdapter } from '@kbn/inspector-plugin/common/adapters/request'; import { lastValueFrom } from 'rxjs'; import type { TimeRange } from '@kbn/es-query'; +import type { IESAggSource } from '../es_agg_source'; import { AbstractVectorSource, BoundsRequestMeta } from '../vector_source'; import { getAutocompleteService, @@ -33,7 +34,6 @@ import { AbstractSourceDescriptor, DynamicStylePropertyOptions, MapExtent, - VectorJoinSourceRequestMeta, VectorSourceRequestMeta, } from '../../../../common/descriptor_types'; import { IVectorStyle } from '../../styles/vector/vector_style'; @@ -207,47 +207,46 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource } async makeSearchSource( - searchFilters: VectorSourceRequestMeta | VectorJoinSourceRequestMeta | BoundsRequestMeta, + requestMeta: VectorSourceRequestMeta | BoundsRequestMeta, limit: number, initialSearchContext?: object ): Promise { const indexPattern = await this.getIndexPattern(); - const globalFilters: Filter[] = searchFilters.applyGlobalQuery ? searchFilters.filters : []; + const globalFilters: Filter[] = requestMeta.applyGlobalQuery ? requestMeta.filters : []; const allFilters: Filter[] = [...globalFilters]; - if (searchFilters.joinKeyFilter) { - allFilters.push(searchFilters.joinKeyFilter); + if (requestMeta.joinKeyFilter) { + allFilters.push(requestMeta.joinKeyFilter); } - if (this.isFilterByMapBounds() && 'buffer' in searchFilters && searchFilters.buffer) { + if (this.isFilterByMapBounds() && 'buffer' in requestMeta && requestMeta.buffer) { // buffer can be empty const geoField = await this._getGeoField(); const buffer: MapExtent = - this.isGeoGridPrecisionAware() && - 'geogridPrecision' in searchFilters && - typeof searchFilters.geogridPrecision === 'number' - ? expandToTileBoundaries(searchFilters.buffer, searchFilters.geogridPrecision) - : searchFilters.buffer; + 'isGeoGridPrecisionAware' in this && + 'getGeoGridPrecision' in this && + (this as IESAggSource).isGeoGridPrecisionAware() + ? expandToTileBoundaries( + requestMeta.buffer, + (this as IESAggSource).getGeoGridPrecision(requestMeta.zoom) + ) + : requestMeta.buffer; const extentFilter = createExtentFilter(buffer, [geoField.name]); allFilters.push(extentFilter); } let isFeatureEditorOpenForLayer = false; - if ('isFeatureEditorOpenForLayer' in searchFilters) { - isFeatureEditorOpenForLayer = searchFilters.isFeatureEditorOpenForLayer; + if ('isFeatureEditorOpenForLayer' in requestMeta) { + isFeatureEditorOpenForLayer = requestMeta.isFeatureEditorOpenForLayer; } - if ( - searchFilters.applyGlobalTime && - (await this.isTimeAware()) && - !isFeatureEditorOpenForLayer - ) { - const timeRange = searchFilters.timeslice + if (requestMeta.applyGlobalTime && (await this.isTimeAware()) && !isFeatureEditorOpenForLayer) { + const timeRange = requestMeta.timeslice ? { - from: new Date(searchFilters.timeslice.from).toISOString(), - to: new Date(searchFilters.timeslice.to).toISOString(), + from: new Date(requestMeta.timeslice.from).toISOString(), + to: new Date(requestMeta.timeslice.to).toISOString(), mode: 'absolute' as 'absolute', } - : searchFilters.timeFilters; + : requestMeta.timeFilters; const filter = getTimeFilter().createFilter(indexPattern, timeRange); if (filter) { allFilters.push(filter); @@ -260,23 +259,23 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource searchSource.setField('index', indexPattern); searchSource.setField('size', limit); searchSource.setField('filter', allFilters); - if (searchFilters.applyGlobalQuery && !isFeatureEditorOpenForLayer) { - searchSource.setField('query', searchFilters.query); + if (requestMeta.applyGlobalQuery && !isFeatureEditorOpenForLayer) { + searchSource.setField('query', requestMeta.query); } const parents = []; - if (searchFilters.sourceQuery && !isFeatureEditorOpenForLayer) { + if (requestMeta.sourceQuery && !isFeatureEditorOpenForLayer) { const layerSearchSource = searchService.searchSource.createEmpty(); layerSearchSource.setField('index', indexPattern); - layerSearchSource.setField('query', searchFilters.sourceQuery); + layerSearchSource.setField('query', requestMeta.sourceQuery); parents.push(layerSearchSource); } - if (searchFilters.embeddableSearchContext && !isFeatureEditorOpenForLayer) { + if (requestMeta.embeddableSearchContext && !isFeatureEditorOpenForLayer) { const embeddableSearchSource = searchService.searchSource.createEmpty(); embeddableSearchSource.setField('index', indexPattern); - embeddableSearchSource.setField('query', searchFilters.embeddableSearchContext.query); - embeddableSearchSource.setField('filter', searchFilters.embeddableSearchContext.filters); + embeddableSearchSource.setField('query', requestMeta.embeddableSearchContext.query); + embeddableSearchSource.setField('filter', requestMeta.embeddableSearchContext.filters); parents.push(embeddableSearchSource); } diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts index ce4f70c635612..5540454702114 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts @@ -26,7 +26,7 @@ import { } from '../../../../common/elasticsearch_util'; import { ESTermSourceDescriptor, - VectorJoinSourceRequestMeta, + VectorSourceRequestMeta, } from '../../../../common/descriptor_types'; import { PropertiesMap } from '../../../../common/elasticsearch_util'; import { isValidStringConfig } from '../../util/valid_string_config'; @@ -122,7 +122,7 @@ export class ESTermSource extends AbstractESAggSource implements ITermJoinSource } async getPropertiesMap( - searchFilters: VectorJoinSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, leftSourceName: string, leftFieldName: string, registerCancelCallback: (callback: () => void) => void, @@ -133,7 +133,7 @@ export class ESTermSource extends AbstractESAggSource implements ITermJoinSource } const indexPattern = await this.getIndexPattern(); - const searchSource: ISearchSource = await this.makeSearchSource(searchFilters, 0); + const searchSource: ISearchSource = await this.makeSearchSource(requestMeta, 0); searchSource.setField('trackTotalHits', false); const termsField = getField(indexPattern, this._termField.getName()); const termsAgg = { @@ -158,10 +158,10 @@ export class ESTermSource extends AbstractESAggSource implements ITermJoinSource rightSource: `${indexPattern.getName()}:${this._termField.getName()}`, }, }), - searchSessionId: searchFilters.searchSessionId, + searchSessionId: requestMeta.searchSessionId, executionContext: mergeExecutionContext( { description: 'es_term_source:terms' }, - searchFilters.executionContext + requestMeta.executionContext ), requestsAdapter: inspectorAdapters.requests, }); diff --git a/x-pack/plugins/maps/public/classes/sources/source.ts b/x-pack/plugins/maps/public/classes/sources/source.ts index 030cbd0c49393..7ccf15bfed2f3 100644 --- a/x-pack/plugins/maps/public/classes/sources/source.ts +++ b/x-pack/plugins/maps/public/classes/sources/source.ts @@ -46,7 +46,6 @@ export interface ISource { getType(): string; isFieldAware(): boolean; isFilterByMapBounds(): boolean; - isGeoGridPrecisionAware(): boolean; isQueryAware(): boolean; isTimeAware(): Promise; getImmutableProperties(): Promise; @@ -61,7 +60,6 @@ export interface ISource { getApplyForceRefresh(): boolean; getIndexPatternIds(): string[]; getQueryableIndexPatternIds(): string[]; - getGeoGridPrecision(zoom: number): number; createFieldFormatter(field: IField): Promise; getValueSuggestions(field: IField, query: string): Promise; getMinZoom(): number; @@ -109,10 +107,6 @@ export class AbstractSource implements ISource { return false; } - isGeoGridPrecisionAware(): boolean { - return false; - } - isQueryAware(): boolean { return false; } @@ -145,10 +139,6 @@ export class AbstractSource implements ISource { return []; } - getGeoGridPrecision(zoom: number): number { - return 0; - } - isESSource(): boolean { return false; } diff --git a/x-pack/plugins/maps/public/classes/sources/table_source/table_source.test.ts b/x-pack/plugins/maps/public/classes/sources/table_source/table_source.test.ts index 30aa2e3ed258c..e43d3de268d31 100644 --- a/x-pack/plugins/maps/public/classes/sources/table_source/table_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/table_source/table_source.test.ts @@ -7,10 +7,7 @@ import { TableSource } from './table_source'; import { FIELD_ORIGIN } from '../../../../common/constants'; -import { - VectorJoinSourceRequestMeta, - VectorSourceRequestMeta, -} from '../../../../common/descriptor_types'; +import { VectorSourceRequestMeta } from '../../../../common/descriptor_types'; describe('TableSource', () => { describe('getName', () => { @@ -57,7 +54,7 @@ describe('TableSource', () => { }); const propertiesMap = await tableSource.getPropertiesMap( - {} as unknown as VectorJoinSourceRequestMeta, + {} as unknown as VectorSourceRequestMeta, 'a', 'b', () => {} diff --git a/x-pack/plugins/maps/public/classes/sources/table_source/table_source.ts b/x-pack/plugins/maps/public/classes/sources/table_source/table_source.ts index bfb143ef8b831..0bfdbc90be847 100644 --- a/x-pack/plugins/maps/public/classes/sources/table_source/table_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/table_source/table_source.ts @@ -12,7 +12,6 @@ import { FIELD_ORIGIN, SOURCE_TYPES, VECTOR_SHAPE_TYPE } from '../../../../commo import { MapExtent, TableSourceDescriptor, - VectorJoinSourceRequestMeta, VectorSourceRequestMeta, } from '../../../../common/descriptor_types'; import { ITermJoinSource } from '../term_join_source'; @@ -60,7 +59,7 @@ export class TableSource extends AbstractVectorSource implements ITermJoinSource } async getPropertiesMap( - searchFilters: VectorJoinSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, leftSourceName: string, leftFieldName: string, registerCancelCallback: (callback: () => void) => void @@ -189,7 +188,7 @@ export class TableSource extends AbstractVectorSource implements ITermJoinSource // Could be useful to implement, e.g. to preview raw csv data async getGeoJsonWithMeta( layerName: string, - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, registerCancelCallback: (callback: () => void) => void, isRequestStillActive: () => boolean ): Promise { diff --git a/x-pack/plugins/maps/public/classes/sources/term_join_source/term_join_source.ts b/x-pack/plugins/maps/public/classes/sources/term_join_source/term_join_source.ts index 6e3b12be53aaf..30e834fdf11f5 100644 --- a/x-pack/plugins/maps/public/classes/sources/term_join_source/term_join_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/term_join_source/term_join_source.ts @@ -10,7 +10,7 @@ import type { KibanaExecutionContext } from '@kbn/core/public'; import { Query } from '@kbn/data-plugin/common/query'; import { Adapters } from '@kbn/inspector-plugin/common/adapters'; import { IField } from '../../fields/field'; -import { VectorJoinSourceRequestMeta } from '../../../../common/descriptor_types'; +import { VectorSourceRequestMeta } from '../../../../common/descriptor_types'; import { PropertiesMap } from '../../../../common/elasticsearch_util'; import { ITooltipProperty } from '../../tooltips/tooltip_property'; import { ISource } from '../source'; @@ -20,7 +20,7 @@ export interface ITermJoinSource extends ISource { getTermField(): IField; getWhereQuery(): Query | undefined; getPropertiesMap( - searchFilters: VectorJoinSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, leftSourceName: string, leftFieldName: string, registerCancelCallback: (callback: () => void) => void, diff --git a/x-pack/plugins/maps/public/classes/sources/tms_source/index.ts b/x-pack/plugins/maps/public/classes/sources/tms_source/index.ts index d18a00df34f07..4c8ca304f83b0 100644 --- a/x-pack/plugins/maps/public/classes/sources/tms_source/index.ts +++ b/x-pack/plugins/maps/public/classes/sources/tms_source/index.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { DataFilters } from '../../../../common/descriptor_types'; +import { SourceRequestMeta } from '../../../../common/descriptor_types'; import { ISource } from '../source'; export interface ITMSSource extends ISource { - getUrlTemplate(dataFilters: DataFilters): Promise; + getUrlTemplate(requestMeta: SourceRequestMeta): Promise; } diff --git a/x-pack/plugins/maps/public/classes/sources/vector_source/mvt_vector_source.ts b/x-pack/plugins/maps/public/classes/sources/vector_source/mvt_vector_source.ts index 8842d96fdeb51..829afe5917cd5 100644 --- a/x-pack/plugins/maps/public/classes/sources/vector_source/mvt_vector_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/vector_source/mvt_vector_source.ts @@ -14,7 +14,7 @@ export interface IMvtVectorSource extends IVectorSource { * Append refreshToken as a URL parameter to force tile re-fetch on refresh (not required) */ getTileUrl( - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, refreshToken: string, hasLabels: boolean, buffer: number diff --git a/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.tsx b/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.tsx index 020b99fc429e2..f56785b0f6f53 100644 --- a/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.tsx @@ -28,6 +28,7 @@ import { ITooltipProperty, TooltipProperty } from '../../tooltips/tooltip_proper import { AbstractSource, ISource } from '../source'; import { IField } from '../../fields/field'; import { + DataFilters, ESSearchSourceResponseMeta, MapExtent, Timeslice, @@ -96,7 +97,7 @@ export interface IVectorSource extends ISource { ): Promise; getGeoJsonWithMeta( layerName: string, - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, registerCancelCallback: (callback: () => void) => void, isRequestStillActive: () => boolean, inspectorAdapters: Adapters @@ -112,7 +113,7 @@ export interface IVectorSource extends ISource { * Vector layer avoids unnecessarily re-fetching source data. * Use getSyncMeta to expose fields that require source data re-fetch when changed. */ - getSyncMeta(): object | null; + getSyncMeta(dataFilters: DataFilters): object | null; getFieldNames(): string[]; createField({ fieldName }: { fieldName: string }): IField; @@ -194,7 +195,7 @@ export class AbstractVectorSource extends AbstractSource implements IVectorSourc async getGeoJsonWithMeta( layerName: string, - searchFilters: VectorSourceRequestMeta, + requestMeta: VectorSourceRequestMeta, registerCancelCallback: (callback: () => void) => void, isRequestStillActive: () => boolean, inspectorAdapters: Adapters @@ -238,7 +239,7 @@ export class AbstractVectorSource extends AbstractSource implements IVectorSourc return { tooltipContent: null, areResultsTrimmed: false }; } - getSyncMeta(): object | null { + getSyncMeta(dataFilters: DataFilters): object | null { return null; } diff --git a/x-pack/plugins/maps/public/classes/util/can_skip_fetch.ts b/x-pack/plugins/maps/public/classes/util/can_skip_fetch.ts index 71d4730880b96..963a0cf4d08ad 100644 --- a/x-pack/plugins/maps/public/classes/util/can_skip_fetch.ts +++ b/x-pack/plugins/maps/public/classes/util/can_skip_fetch.ts @@ -72,9 +72,8 @@ export async function canSkipSourceUpdate({ const timeAware = await source.isTimeAware(); const isFieldAware = source.isFieldAware(); const isQueryAware = source.isQueryAware(); - const isGeoGridPrecisionAware = source.isGeoGridPrecisionAware(); - if (!timeAware && !extentAware && !isFieldAware && !isQueryAware && !isGeoGridPrecisionAware) { + if (!timeAware && !extentAware && !isFieldAware && !isQueryAware) { return !!prevDataRequest && prevDataRequest.hasDataOrRequestInProgress(); } @@ -128,16 +127,7 @@ export async function canSkipSourceUpdate({ updateDueToSearchSessionId = prevMeta.searchSessionId !== nextRequestMeta.searchSessionId; } - let updateDueToPrecisionChange = false; let updateDueToExtentChange = false; - - if (isGeoGridPrecisionAware) { - updateDueToPrecisionChange = !_.isEqual( - prevMeta.geogridPrecision, - nextRequestMeta.geogridPrecision - ); - } - if (extentAware) { updateDueToExtentChange = updateDueToExtent(prevMeta, nextRequestMeta); } @@ -154,7 +144,6 @@ export async function canSkipSourceUpdate({ !updateDueToFilters && !updateDueToSourceQuery && !updateDueToApplyGlobalQuery && - !updateDueToPrecisionChange && !updateDueToSourceMetaChange && !updateDueToSearchSessionId ); From 686ccad2183ec2fc36a70d2d9f20d28f270db247 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Wed, 29 Mar 2023 16:25:43 +0200 Subject: [PATCH 07/10] [Synthetics] Add private location info in location labels (#153856) --- .../components/location_status_badges.tsx | 2 +- .../monitor_add_edit/form/field_config.tsx | 22 ++++++++++++++++++- .../components/monitor_add_edit/types.ts | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/location_status_badges.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/location_status_badges.tsx index f0957fb5cdfa6..3115597135a96 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/location_status_badges.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/location_status_badges.tsx @@ -109,7 +109,7 @@ const MonitorDetailLinkForLocation = ({ return ( } + iconType={() => } color="hollow" iconOnClickAriaLabel={CLICK_LOCATION_LABEL} iconOnClick={() => { diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx index 43905cd5899a4..110ac698e22f7 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/form/field_config.tsx @@ -28,6 +28,8 @@ import { EuiTextAreaProps, EuiButtonGroupProps, EuiSuperSelectProps, + EuiHighlight, + EuiBadge, } from '@elastic/eui'; import { FieldText, @@ -406,7 +408,11 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ isServiceManaged: location.isServiceManaged || false, })), selectedOptions: Object.values(field?.value || {}).map((location) => ({ - color: locations.some((s) => s.id === location.id) ? 'default' : 'danger', + color: locations.some((s) => s.id === location.id) + ? location.isServiceManaged + ? 'default' + : 'primary' + : 'danger', label: locations?.find((loc) => location.id === loc.id)?.label ?? location.id, id: location.id || '', isServiceManaged: location.isServiceManaged || false, @@ -418,6 +424,20 @@ export const FIELD = (readOnly?: boolean): FieldMap => ({ }); }, isDisabled: readOnly, + renderOption: (option: FormLocation, searchValue: string) => { + return ( + + + {option.label} + + {!option.isServiceManaged && ( + + Private + + )} + + ); + }, }; }, }, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/types.ts b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/types.ts index 264d710d081a1..b3b86eef542fe 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/types.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_add_edit/types.ts @@ -44,7 +44,7 @@ export type FormConfig = MonitorFields & { [AlertConfigKey.STATUS_ENABLED]: boolean; [ConfigKey.LOCATIONS]: FormLocation[]; - /* Dot notiation keys must have a type configuration both for their flattened and nested + /* Dot notation keys must have a type configuration both for their flattened and nested * variation in order for types to register for react hook form. For example, `AlertConfigKey.STATUS_ENABLED` * must be defined both as `alert.config.enabled: boolean` and `alert: { config: { enabled: boolean } }` */ alert: { From 5b179310b80f91ab856de857e3a129aa33451b22 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 29 Mar 2023 17:58:24 +0300 Subject: [PATCH 08/10] [TSVB2Lens] Enable the metric bar when max is defined (#153924) ## Summary Part of https://github.com/elastic/kibana/issues/151289 This PR addresses the first bullet. If a max is set, it navigates to the new metric with the bar on. ![lens](https://user-images.githubusercontent.com/17003240/228484956-6c5c8d8d-f3da-42f1-8f6d-83884aaab979.gif) Note: I am not addressing the second one because it needs discussion with the team ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../convert_to_lens/lib/configurations/metric/index.test.ts | 2 ++ .../convert_to_lens/lib/configurations/metric/index.ts | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.test.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.test.ts index 0fabef6eebdbe..9185f77cb73b2 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.test.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.test.ts @@ -296,6 +296,7 @@ describe('getConfigurationForGauge', () => { palette: undefined, maxAccessor: maxColumnId, color: '#FFFFFF', + showBar: true, }); expect(mockGetPalette).toBeCalledTimes(1); }); @@ -338,6 +339,7 @@ describe('getConfigurationForGauge', () => { metricAccessor: columnId1, palette, maxAccessor: maxColumnId, + showBar: true, }); expect(mockGetPalette).toBeCalledTimes(1); }); diff --git a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts index e6814b0797a1a..1892330bda9a9 100644 --- a/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts +++ b/src/plugins/vis_types/timeseries/public/convert_to_lens/lib/configurations/metric/index.ts @@ -52,7 +52,7 @@ export const getConfigurationForGauge = ( model: Panel, layer: Layer, bucket: Column | undefined, - gaugeMaxColumn: Column + gaugeMaxColumn?: Column ): MetricVisConfiguration | null => { const primarySeries = model.series[0]; const primaryMetricWithCollapseFn = getMetricWithCollapseFn(primarySeries); @@ -73,7 +73,8 @@ export const getConfigurationForGauge = ( layerType: 'data', metricAccessor: primaryColumn?.columnId, breakdownByAccessor: bucket?.columnId, - maxAccessor: gaugeMaxColumn.columnId, + maxAccessor: gaugeMaxColumn?.columnId, + showBar: Boolean(gaugeMaxColumn), palette: gaugePalette, collapseFn: primaryMetricWithCollapseFn.collapseFn, ...(gaugePalette ? {} : { color: primaryColor }), From 0ee23684e34b5ebb39cd2a9fb0134db77be1628b Mon Sep 17 00:00:00 2001 From: Coen Warmer Date: Wed, 29 Mar 2023 17:01:35 +0200 Subject: [PATCH 09/10] Remove Playwright e2e test from Observability (#153934) --- .buildkite/ftr_configs.yml | 1 - .../pull_request/observability_plugin.yml | 15 -- .../pipelines/pull_request/pipeline.ts | 13 +- packages/kbn-ts-projects/ts_projects.ts | 1 - x-pack/plugins/observability/e2e/README.md | 15 -- .../e2e/journeys/exploratory_view.ts | 108 -------- .../observability/e2e/journeys/index.ts | 10 - .../e2e/journeys/single_metric.journey.ts | 65 ----- .../e2e/journeys/step_duration.journey.ts | 96 ------- .../observability/e2e/synthetics_run.ts | 45 ---- .../plugins/observability/e2e/tsconfig.json | 9 - x-pack/plugins/synthetics/e2e/config.ts | 2 +- .../e2e/helpers}/parse_args_params.ts | 0 .../e2e/helpers}/record_video.ts | 0 .../e2e/helpers}/synthetics_runner.ts | 2 +- .../e2e/helpers}/test_reporter.ts | 0 .../e2e => synthetics/e2e/helpers}/utils.ts | 0 .../synthetics/add_monitor.journey.ts | 2 +- .../default_status_alert.journey.ts | 2 +- .../synthetics/alerting_default.journey.ts | 4 +- .../synthetics/data_retention.journey.ts | 4 +- .../e2e/journeys/synthetics/detail_flyout.ts | 2 +- .../synthetics/getting_started.journey.ts | 2 +- .../synthetics/global_parameters.journey.ts | 4 +- .../synthetics/management_list.journey.ts | 4 +- .../monitor_summary.journey.ts | 2 +- .../synthetics/monitor_selector.journey.ts | 4 +- .../synthetics/overview_scrolling.journey.ts | 2 +- .../synthetics/overview_search.journey.ts | 2 +- .../synthetics/overview_sorting.journey.ts | 2 +- .../synthetics/private_locations.journey.ts | 4 +- .../synthetics/project_api_keys.journey.ts | 2 +- .../project_monitor_read_only.journey.ts | 2 +- .../synthetics/step_details.journey.ts | 2 +- .../synthetics/test_now_mode.journey.ts | 4 +- .../synthetics/test_run_details.journey.ts | 4 +- .../uptime/alerts/default_email_settings.ts | 7 +- .../status_alert_flyouts_in_alerting_app.ts | 4 +- .../tls_alert_flyouts_in_alerting_app.ts | 4 +- .../journeys/uptime/data_view_permissions.ts | 8 +- .../journeys/uptime/locations/locations.ts | 2 +- .../uptime/monitor_details.journey.ts | 2 +- .../monitor_details/monitor_alerts.journey.ts | 4 +- .../monitor_details.journey.ts | 4 +- .../monitor_details/ping_redirects.journey.ts | 4 +- .../uptime/monitor_management.journey.ts | 4 +- .../monitor_management_enablement.journey.ts | 2 +- .../journeys/uptime/monitor_name.journey.ts | 4 +- .../add_monitor_private_location.ts | 4 +- .../private_locations/manage_locations.ts | 4 +- .../read_only_user/monitor_management.ts | 4 +- .../journeys/uptime/step_duration.journey.ts | 2 +- .../e2e/journeys/uptime/uptime.journey.ts | 4 +- .../page_objects/uptime/monitor_details.tsx | 2 +- .../uptime/monitor_management.tsx | 2 +- .../e2e/page_objects/uptime/settings.tsx | 2 +- .../plugins/synthetics/e2e/synthetics_run.ts | 4 +- .../e2e/tasks/es_archiver.ts | 0 .../ux/e2e/helpers/parse_args_params.ts | 33 +++ x-pack/plugins/ux/e2e/helpers/record_video.ts | 36 +++ .../ux/e2e/helpers/synthetics_runner.ts | 164 ++++++++++++ .../plugins/ux/e2e/helpers/test_reporter.ts | 246 ++++++++++++++++++ .../ux/e2e/journeys/core_web_vitals.ts | 2 +- x-pack/plugins/ux/e2e/journeys/page_views.ts | 2 +- .../ux/e2e/journeys/url_ux_query.journey.ts | 2 +- .../e2e/journeys/ux_client_metrics.journey.ts | 2 +- .../ux/e2e/journeys/ux_js_errors.journey.ts | 2 +- .../journeys/ux_long_task_metric_journey.ts | 2 +- .../journeys/ux_visitor_breakdown.journey.ts | 2 +- x-pack/plugins/ux/e2e/synthetics_run.ts | 4 +- x-pack/plugins/ux/e2e/tasks/es_archiver.ts | 37 +++ 71 files changed, 592 insertions(+), 455 deletions(-) delete mode 100644 .buildkite/pipelines/pull_request/observability_plugin.yml delete mode 100644 x-pack/plugins/observability/e2e/README.md delete mode 100644 x-pack/plugins/observability/e2e/journeys/exploratory_view.ts delete mode 100644 x-pack/plugins/observability/e2e/journeys/index.ts delete mode 100644 x-pack/plugins/observability/e2e/journeys/single_metric.journey.ts delete mode 100644 x-pack/plugins/observability/e2e/journeys/step_duration.journey.ts delete mode 100644 x-pack/plugins/observability/e2e/synthetics_run.ts delete mode 100644 x-pack/plugins/observability/e2e/tsconfig.json rename x-pack/plugins/{observability/e2e => synthetics/e2e/helpers}/parse_args_params.ts (100%) rename x-pack/plugins/{observability/e2e => synthetics/e2e/helpers}/record_video.ts (100%) rename x-pack/plugins/{observability/e2e => synthetics/e2e/helpers}/synthetics_runner.ts (98%) rename x-pack/plugins/{observability/e2e => synthetics/e2e/helpers}/test_reporter.ts (100%) rename x-pack/plugins/{observability/e2e => synthetics/e2e/helpers}/utils.ts (100%) rename x-pack/plugins/{observability => synthetics}/e2e/tasks/es_archiver.ts (100%) create mode 100644 x-pack/plugins/ux/e2e/helpers/parse_args_params.ts create mode 100644 x-pack/plugins/ux/e2e/helpers/record_video.ts create mode 100644 x-pack/plugins/ux/e2e/helpers/synthetics_runner.ts create mode 100644 x-pack/plugins/ux/e2e/helpers/test_reporter.ts create mode 100644 x-pack/plugins/ux/e2e/tasks/es_archiver.ts diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index f6ea46272316a..baee9753ce56c 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -55,7 +55,6 @@ disabled: - x-pack/plugins/synthetics/e2e/config.ts - x-pack/plugins/synthetics/e2e/synthetics_run.ts - x-pack/plugins/ux/e2e/synthetics_run.ts - - x-pack/plugins/observability/e2e/synthetics_run.ts - x-pack/plugins/exploratory_view/e2e/synthetics_run.ts # Configs that exist but weren't running in CI when this file was introduced diff --git a/.buildkite/pipelines/pull_request/observability_plugin.yml b/.buildkite/pipelines/pull_request/observability_plugin.yml deleted file mode 100644 index 55ffaf9f5f5f8..0000000000000 --- a/.buildkite/pipelines/pull_request/observability_plugin.yml +++ /dev/null @@ -1,15 +0,0 @@ -steps: - - command: .buildkite/scripts/steps/functional/observability_plugin.sh - label: 'Observability @elastic/synthetics Tests' - agents: - queue: n2-4-spot - depends_on: build - timeout_in_minutes: 120 - artifact_paths: - - 'x-pack/plugins/observability/e2e/.journeys/**/*' - retry: - automatic: - - exit_status: '-1' - limit: 3 - - exit_status: '*' - limit: 1 diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.ts b/.buildkite/scripts/pipelines/pull_request/pipeline.ts index fdaca3654b15c..4fc571ba51d8a 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.ts +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.ts @@ -137,10 +137,6 @@ const uploadPipeline = (pipelineContent: string | object) => { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/osquery_cypress.yml')); } - if (await doAnyChangesMatch([/^x-pack\/plugins\/observability/])) { - pipeline.push(getPipeline('.buildkite/pipelines/pull_request/observability_plugin.yml')); - } - if (await doAnyChangesMatch([/^x-pack\/plugins\/exploratory_view/])) { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/exploratory_view_plugin.yml')); } @@ -148,20 +144,13 @@ const uploadPipeline = (pipelineContent: string | object) => { if ( await doAnyChangesMatch([ /^x-pack\/plugins\/synthetics/, - /^x-pack\/plugins\/observability\/public\/components\/shared\/exploratory_view/, /^x-pack\/plugins\/exploratory_view/, ]) ) { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/synthetics_plugin.yml')); } - if ( - await doAnyChangesMatch([ - /^x-pack\/plugins\/ux/, - /^x-pack\/plugins\/observability\/public\/components\/shared\/exploratory_view/, - /^x-pack\/plugins\/exploratory_view/, - ]) - ) { + if (await doAnyChangesMatch([/^x-pack\/plugins\/ux/, /^x-pack\/plugins\/exploratory_view/])) { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/ux_plugin_e2e.yml')); } diff --git a/packages/kbn-ts-projects/ts_projects.ts b/packages/kbn-ts-projects/ts_projects.ts index e7e2efb30c0ab..9eea1e2227ba5 100644 --- a/packages/kbn-ts-projects/ts_projects.ts +++ b/packages/kbn-ts-projects/ts_projects.ts @@ -21,7 +21,6 @@ export const TS_PROJECTS = TsProject.loadAll({ 'x-pack/plugins/synthetics/e2e/tsconfig.json', 'x-pack/plugins/ux/e2e/tsconfig.json', - 'x-pack/plugins/observability/e2e/tsconfig.json', 'x-pack/plugins/exploratory_view/e2e/tsconfig.json', 'x-pack/plugins/threat_intelligence/cypress/tsconfig.json', ], diff --git a/x-pack/plugins/observability/e2e/README.md b/x-pack/plugins/observability/e2e/README.md deleted file mode 100644 index 292e38a208d2d..0000000000000 --- a/x-pack/plugins/observability/e2e/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## How to run these tests - -These tests rely on the Kibana functional test runner. There is a Kibana config in this directory, and a dedicated -script for standing up the test server. - -### Start the server - -From `~/x-pack/plugins/observability/scripts`, run `node e2e.js --server`. Wait for the server to startup. It will provide you -with an example run command when it finishes. - -### Run the tests - -From this directory, `~/x-pack/plugins/observability/e2e`, you can now run `node ../../../../scripts/functional_test_runner --config synthetics_run.ts`. - -In addition to the usual flags like `--grep`, you can also specify `--no-headless` in order to view your tests as you debug/develop. diff --git a/x-pack/plugins/observability/e2e/journeys/exploratory_view.ts b/x-pack/plugins/observability/e2e/journeys/exploratory_view.ts deleted file mode 100644 index 877c13e93c373..0000000000000 --- a/x-pack/plugins/observability/e2e/journeys/exploratory_view.ts +++ /dev/null @@ -1,108 +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 { journey, step, before } from '@elastic/synthetics'; -import { recordVideo } from '../record_video'; -import { createExploratoryViewUrl } from '../../public/components/shared/exploratory_view/configurations/exploratory_view_url'; -import { loginToKibana, TIMEOUT_60_SEC, waitForLoadingToFinish } from '../utils'; - -journey('Exploratory view', async ({ page, params }) => { - recordVideo(page); - - before(async () => { - await waitForLoadingToFinish({ page }); - }); - - const expUrl = createExploratoryViewUrl({ - reportType: 'kpi-over-time', - allSeries: [ - { - name: 'Elastic page views', - time: { - from: '2021-01-18T12:20:01.682Z', - to: '2021-01-18T12:25:27.484Z', - }, - selectedMetricField: '___records___', - reportDefinitions: { 'service.name': [] }, - dataType: 'ux', - }, - ], - }); - - const baseUrl = `${params.kibanaUrl}${expUrl}`; - - step('Go to Exploratory view', async () => { - await page.goto(baseUrl, { - waitUntil: 'networkidle', - }); - await loginToKibana({ - page, - user: { username: 'elastic', password: 'changeme' }, - }); - }); - - step('renders as expected', async () => { - await Promise.all([page.waitForNavigation(TIMEOUT_60_SEC), page.click('text=Explore data')]); - await page.click('text=User experience (RUM)'); - await page.click('[aria-label="Toggle series information"] >> text=Page views', TIMEOUT_60_SEC); - await page.click('[aria-label="Edit series"]', TIMEOUT_60_SEC); - await page.click('button:has-text("No breakdown")'); - await page.click('button[role="option"]:has-text("Operating system")', TIMEOUT_60_SEC); - await page.click('button:has-text("Apply changes")'); - - await page.click('text=Chrome OS'); - await page.click('text=iOS'); - await page.click('text=iOS'); - await page.click('text=Chrome OS'); - await page.click('text=Ubuntu'); - await page.click('text=Android'); - await page.click('text=Linux'); - await page.click('text=Mac OS X'); - await page.click('text=Windows'); - await page.click('h1:has-text("Explore data")'); - }); - - step('Edit and change the series to distribution', async () => { - await page.click('[aria-label="View series actions"]'); - await page.click('[aria-label="Remove series"]'); - await page.click('button:has-text("KPI over time")'); - await page.click('button[role="option"]:has-text("Performance distribution")'); - await page.click('button:has-text("Add series")'); - await page.click('button:has-text("Select data type")'); - await page.click('button:has-text("User experience (RUM)")'); - await page.click('button:has-text("Select report metric")'); - await page.click('button:has-text("Page load time")'); - await page.click('.euiComboBox__inputWrap'); - await page.click('[aria-label="Date quick select"]'); - await page.click('text=Last 1 year'); - await page.click('[aria-label="Date quick select"]'); - await page.click('[aria-label="Time value"]'); - await page.fill('[aria-label="Time value"]', '010'); - await page.selectOption('[aria-label="Time unit"]', 'y'); - - await page.click('div[role="dialog"] button:has-text("Apply")'); - await page.click('.euiComboBox__inputWrap'); - await page.click('button[role="option"]:has-text("elastic-co-frontend")'); - await page.click('button:has-text("Apply changes")'); - await page.click('text=ux-series-1'); - await page.click('text=User experience (RUM)'); - await page.click('text=Page load time'); - await page.click('text=Pages loaded'); - await page.click('button:has-text("95th")'); - await page.click('button:has-text("90th")'); - await page.click('button:has-text("99th")'); - await page.click('[aria-label="Edit series"]'); - await page.click('button:has-text("No breakdown")'); - await page.click('button[role="option"]:has-text("Browser family")'); - await page.click('button:has-text("Apply changes")'); - await page.click('text=Edge'); - await page.click('text=Opera'); - await page.click('text=Safari'); - await page.click('text=HeadlessChrome'); - await page.click('[aria-label="Firefox; Activate to hide series in graph"]'); - }); -}); diff --git a/x-pack/plugins/observability/e2e/journeys/index.ts b/x-pack/plugins/observability/e2e/journeys/index.ts deleted file mode 100644 index 3f0bc8128434c..0000000000000 --- a/x-pack/plugins/observability/e2e/journeys/index.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 * from './exploratory_view'; -export * from './step_duration.journey'; -// export * from './single_metric.journey'; diff --git a/x-pack/plugins/observability/e2e/journeys/single_metric.journey.ts b/x-pack/plugins/observability/e2e/journeys/single_metric.journey.ts deleted file mode 100644 index 6fcdb71ccffa2..0000000000000 --- a/x-pack/plugins/observability/e2e/journeys/single_metric.journey.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { journey, step, before } from '@elastic/synthetics'; -import { recordVideo } from '../record_video'; -import { createExploratoryViewUrl } from '../../public/components/shared/exploratory_view/configurations/exploratory_view_url'; -import { loginToKibana, TIMEOUT_60_SEC, waitForLoadingToFinish } from '../utils'; - -journey('SingleMetric', async ({ page, params }) => { - recordVideo(page); - - before(async () => { - await waitForLoadingToFinish({ page }); - }); - - const expUrl = createExploratoryViewUrl({ - reportType: 'single-metric', - allSeries: [ - { - dataType: 'synthetics', - time: { - from: 'now-1y/d', - to: 'now', - }, - name: 'synthetics-series-1', - selectedMetricField: 'monitor_availability', - reportDefinitions: { - 'monitor.name': ['test-monitor - inline'], - 'url.full': ['https://www.elastic.co/'], - }, - }, - ], - }); - - const baseUrl = `${params.kibanaUrl}${expUrl}`; - - step('Go to Exploratory view', async () => { - await page.goto(baseUrl, { - waitUntil: 'networkidle', - }); - await loginToKibana({ - page, - user: { username: 'elastic', password: 'changeme' }, - }); - }); - - step('Open exploratory view with single metric', async () => { - await Promise.all([ - page.waitForNavigation(TIMEOUT_60_SEC), - page.click('text=Explore data', TIMEOUT_60_SEC), - ]); - - await waitForLoadingToFinish({ page }); - - await page.click('text=0.0%', TIMEOUT_60_SEC); - await page.click('text=0.0%Availability'); - await page.click( - 'text=Explore data Last Updated: a few seconds agoRefreshHide chart0.0%AvailabilityRep' - ); - }); -}); diff --git a/x-pack/plugins/observability/e2e/journeys/step_duration.journey.ts b/x-pack/plugins/observability/e2e/journeys/step_duration.journey.ts deleted file mode 100644 index 10cc98fa2da6a..0000000000000 --- a/x-pack/plugins/observability/e2e/journeys/step_duration.journey.ts +++ /dev/null @@ -1,96 +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 { journey, step, before, after } from '@elastic/synthetics'; -import moment from 'moment'; -import { recordVideo } from '../record_video'; -import { createExploratoryViewUrl } from '../../public/components/shared/exploratory_view/configurations/exploratory_view_url'; -import { loginToKibana, TIMEOUT_60_SEC, waitForLoadingToFinish } from '../utils'; - -journey('Exploratory view', async ({ page, params }) => { - recordVideo(page); - - before(async () => { - await waitForLoadingToFinish({ page }); - }); - - after(async () => { - // eslint-disable-next-line no-console - console.log(await page.video()?.path()); - }); - - const expUrl = createExploratoryViewUrl({ - reportType: 'kpi-over-time', - allSeries: [ - { - dataType: 'uptime', - time: { - from: moment().subtract(10, 'y').toISOString(), - to: moment().toISOString(), - }, - name: 'synthetics-series-1', - breakdown: 'monitor.type', - selectedMetricField: 'monitor.duration.us', - reportDefinitions: { - 'url.full': ['ALL_VALUES'], - }, - }, - ], - }); - - const baseUrl = `${params.kibanaUrl}${expUrl}`; - - step('Go to Exploratory view', async () => { - await page.goto(baseUrl, { - waitUntil: 'networkidle', - }); - await loginToKibana({ - page, - user: { username: 'elastic', password: 'changeme' }, - }); - }); - - step('Open exploratory view with monitor duration', async () => { - await page.waitForNavigation(TIMEOUT_60_SEC); - - await waitForLoadingToFinish({ page }); - await page.click('text=browser', TIMEOUT_60_SEC); - await page.click('text=http'); - await page.click('[aria-label="Remove report metric"]'); - await page.click('button:has-text("Select report metric")'); - await page.click('button:has-text("Step duration")'); - await page.click('button[data-test-subj="seriesBreakdown"]:has-text("Monitor type")'); - await page.click('button[role="option"]:has-text("Step name")'); - await page.click('.euiComboBox__inputWrap'); - await page.click( - 'text=Search Monitor nameCombo box. Selected. Combo box input. Search Monitor name. Ty' - ); - await page.click('button[role="option"]:has-text("test-monitor - inline")'); - await page.click('button:has-text("Apply changes")'); - - await waitForLoadingToFinish({ page }); - - await page.click('[aria-label="series color: #54b399"]'); - await page.click('[aria-label="series color: #6092c0"]'); - await page.click('[aria-label="series color: #d36086"] path'); - await page.click('[aria-label="series color: #9170b8"]'); - await page.click('[aria-label="series color: #ca8eae"]'); - await page.click('[aria-label="series color: #d6bf57"]'); - await page.click('text=load homepage'); - await page.click('text=load homepage'); - await page.click('text=load github'); - await page.click('text=load github'); - await page.click('text=load google'); - await page.click('text=load google'); - await page.click('text=hover over products menu'); - await page.click('text=hover over products menu'); - await page.click('text=load homepage 1'); - await page.click('text=load homepage 1'); - await page.click('text=load homepage 2'); - await page.click('text=load homepage 2'); - }); -}); diff --git a/x-pack/plugins/observability/e2e/synthetics_run.ts b/x-pack/plugins/observability/e2e/synthetics_run.ts deleted file mode 100644 index eadb62dc44a72..0000000000000 --- a/x-pack/plugins/observability/e2e/synthetics_run.ts +++ /dev/null @@ -1,45 +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 { FtrConfigProviderContext } from '@kbn/test'; -import path from 'path'; -import { SyntheticsRunner } from './synthetics_runner'; -import { argv } from './parse_args_params'; - -const { headless, grep, bail: pauseOnError } = argv; - -async function runE2ETests({ readConfigFile }: FtrConfigProviderContext) { - const kibanaConfig = await readConfigFile(require.resolve('@kbn/synthetics-plugin/e2e/config')); - - return { - ...kibanaConfig.getAll(), - testRunner: async ({ getService }: any) => { - const syntheticsRunner = new SyntheticsRunner(getService, { - headless, - match: grep, - pauseOnError, - }); - - await syntheticsRunner.setup(); - - await syntheticsRunner.loadTestData(path.join(__dirname, '../../ux/e2e/fixtures/'), [ - 'rum_8.0.0', - 'rum_test_data', - ]); - await syntheticsRunner.loadTestData( - path.join(__dirname, '../../synthetics/e2e/fixtures/es_archiver/'), - ['full_heartbeat', 'browser'] - ); - await syntheticsRunner.loadTestFiles(async () => { - require(path.join(__dirname, './journeys')); - }); - await syntheticsRunner.run(); - }, - }; -} - -// eslint-disable-next-line import/no-default-export -export default runE2ETests; diff --git a/x-pack/plugins/observability/e2e/tsconfig.json b/x-pack/plugins/observability/e2e/tsconfig.json deleted file mode 100644 index df3428e1aa0bf..0000000000000 --- a/x-pack/plugins/observability/e2e/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../../../tsconfig.base.json", - "exclude": ["tmp", "target/**/*"], - "include": ["./**/*"], - "compilerOptions": { - "outDir": "target/types", - "types": [ "node"], - }, -} diff --git a/x-pack/plugins/synthetics/e2e/config.ts b/x-pack/plugins/synthetics/e2e/config.ts index 04eccf0d85d2a..458fbd4c2dccd 100644 --- a/x-pack/plugins/synthetics/e2e/config.ts +++ b/x-pack/plugins/synthetics/e2e/config.ts @@ -6,8 +6,8 @@ */ import { FtrConfigProviderContext } from '@kbn/test'; -import { argv } from '@kbn/observability-plugin/e2e/parse_args_params'; import { CA_CERT_PATH } from '@kbn/dev-utils'; +import { argv } from './helpers/parse_args_params'; import { readKibanaConfig } from './tasks/read_kibana_config'; const MANIFEST_KEY = 'xpack.uptime.service.manifestUrl'; const SERVICE_PASSWORD = 'xpack.uptime.service.password'; diff --git a/x-pack/plugins/observability/e2e/parse_args_params.ts b/x-pack/plugins/synthetics/e2e/helpers/parse_args_params.ts similarity index 100% rename from x-pack/plugins/observability/e2e/parse_args_params.ts rename to x-pack/plugins/synthetics/e2e/helpers/parse_args_params.ts diff --git a/x-pack/plugins/observability/e2e/record_video.ts b/x-pack/plugins/synthetics/e2e/helpers/record_video.ts similarity index 100% rename from x-pack/plugins/observability/e2e/record_video.ts rename to x-pack/plugins/synthetics/e2e/helpers/record_video.ts diff --git a/x-pack/plugins/observability/e2e/synthetics_runner.ts b/x-pack/plugins/synthetics/e2e/helpers/synthetics_runner.ts similarity index 98% rename from x-pack/plugins/observability/e2e/synthetics_runner.ts rename to x-pack/plugins/synthetics/e2e/helpers/synthetics_runner.ts index 66183218780f2..2a1ebce4126f2 100644 --- a/x-pack/plugins/observability/e2e/synthetics_runner.ts +++ b/x-pack/plugins/synthetics/e2e/helpers/synthetics_runner.ts @@ -13,7 +13,7 @@ import { PromiseType } from 'utility-types'; import { createApmUsers } from '@kbn/apm-plugin/server/test_helpers/create_apm_users/create_apm_users'; import { EsArchiver } from '@kbn/es-archiver'; -import { esArchiverUnload } from './tasks/es_archiver'; +import { esArchiverUnload } from '../tasks/es_archiver'; import { TestReporter } from './test_reporter'; export interface ArgParams { diff --git a/x-pack/plugins/observability/e2e/test_reporter.ts b/x-pack/plugins/synthetics/e2e/helpers/test_reporter.ts similarity index 100% rename from x-pack/plugins/observability/e2e/test_reporter.ts rename to x-pack/plugins/synthetics/e2e/helpers/test_reporter.ts diff --git a/x-pack/plugins/observability/e2e/utils.ts b/x-pack/plugins/synthetics/e2e/helpers/utils.ts similarity index 100% rename from x-pack/plugins/observability/e2e/utils.ts rename to x-pack/plugins/synthetics/e2e/helpers/utils.ts diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/add_monitor.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/add_monitor.journey.ts index 6f80a115b3b6e..38f0399243ec5 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/add_monitor.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/add_monitor.journey.ts @@ -6,7 +6,7 @@ */ import { v4 as uuidv4 } from 'uuid'; import { journey, step, expect, Page } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../../helpers/record_video'; import { FormMonitorType } from '../../../common/runtime_types'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics/synthetics_app'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/alert_rules/default_status_alert.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/alert_rules/default_status_alert.journey.ts index ae8ffeed83431..b9fc467fc0bef 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/alert_rules/default_status_alert.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/alert_rules/default_status_alert.journey.ts @@ -9,7 +9,7 @@ import { journey, step, before, after, expect } from '@elastic/synthetics'; import { byTestId } from '@kbn/ux-plugin/e2e/journeys/utils'; import { RetryService } from '@kbn/ftr-common-functional-services'; import { v4 as uuidv4 } from 'uuid'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../../../helpers/record_video'; import { getReasonMessage } from '../../../../server/legacy_uptime/lib/alerts/status_check'; import { syntheticsAppPageProvider } from '../../../page_objects/synthetics/synthetics_app'; import { SyntheticsServices } from '../services/synthetics_services'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/alerting_default.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/alerting_default.journey.ts index 4b6bd772840e6..a5430ab4e62de 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/alerting_default.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/alerting_default.journey.ts @@ -6,8 +6,8 @@ */ import { journey, step, expect, before, after } from '@elastic/synthetics'; -import { byTestId } from '@kbn/observability-plugin/e2e/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId } from '../../helpers/utils'; +import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics/synthetics_app'; import { cleanSettings } from './services/settings'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/data_retention.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/data_retention.journey.ts index b6ff8dffbbf57..bbbf6a7e48aa9 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/data_retention.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/data_retention.journey.ts @@ -6,9 +6,9 @@ */ import { journey, step, expect, Page } from '@elastic/synthetics'; -import { assertText, byTestId } from '@kbn/observability-plugin/e2e/utils'; import { RetryService } from '@kbn/ftr-common-functional-services'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId, assertText } from '../../helpers/utils'; +import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics/synthetics_app'; let page1: Page; diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/detail_flyout.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/detail_flyout.ts index 66ca6c0ab4c25..2cd8c018e4032 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/detail_flyout.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/detail_flyout.ts @@ -6,7 +6,7 @@ */ import { expect, journey, step } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics/synthetics_app'; journey('TestMonitorDetailFlyout', async ({ page, params }) => { diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/getting_started.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/getting_started.journey.ts index 5b8f6670c2f21..c52b2b5f1aa48 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/getting_started.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/getting_started.journey.ts @@ -6,7 +6,7 @@ */ import { journey, step, expect, before, Page } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics/synthetics_app'; import { cleanTestMonitors } from './services/add_monitor'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/global_parameters.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/global_parameters.journey.ts index 59b103d047250..8af902caf07db 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/global_parameters.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/global_parameters.journey.ts @@ -6,8 +6,8 @@ */ import { journey, step, before, after, expect } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; -import { byTestId } from '@kbn/observability-plugin/e2e/utils'; +import { byTestId } from '../../helpers/utils'; +import { recordVideo } from '../../helpers/record_video'; import { cleanTestParams } from './services/add_monitor'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics/synthetics_app'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/management_list.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/management_list.journey.ts index 685fe3986f23f..357ab213819eb 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/management_list.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/management_list.journey.ts @@ -6,8 +6,8 @@ */ import { journey, step, expect, before, after } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; -import { byTestId } from '@kbn/ux-plugin/e2e/journeys/utils'; +import { byTestId } from '../../helpers/utils'; +import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/monitor_details_page/monitor_summary.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/monitor_details_page/monitor_summary.journey.ts index 67c227695d021..b450bd7983ff7 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/monitor_details_page/monitor_summary.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/monitor_details_page/monitor_summary.journey.ts @@ -8,8 +8,8 @@ import { journey, step, before, after } from '@elastic/synthetics'; import { byTestId } from '@kbn/ux-plugin/e2e/journeys/utils'; import { RetryService } from '@kbn/ftr-common-functional-services'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; import moment from 'moment'; +import { recordVideo } from '../../../helpers/record_video'; import { syntheticsAppPageProvider } from '../../../page_objects/synthetics/synthetics_app'; import { SyntheticsServices } from '../services/synthetics_services'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/monitor_selector.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/monitor_selector.journey.ts index 758a3823332f2..fe4a36ba6e9ba 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/monitor_selector.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/monitor_selector.journey.ts @@ -6,8 +6,8 @@ */ import { journey, step, expect, before, after } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; -import { byTestId } from '@kbn/observability-plugin/e2e/utils'; +import { byTestId } from '../../helpers/utils'; +import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_scrolling.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_scrolling.journey.ts index c592595e15eaf..29aeb474212c5 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_scrolling.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_scrolling.journey.ts @@ -6,8 +6,8 @@ */ import { before, after, expect, journey, step } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; import { RetryService } from '@kbn/ftr-common-functional-services'; +import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_search.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_search.journey.ts index 289b434b9e2c9..cb9983e830406 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_search.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_search.journey.ts @@ -6,8 +6,8 @@ */ import { before, expect, journey, step } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; import { RetryService } from '@kbn/ftr-common-functional-services'; +import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_sorting.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_sorting.journey.ts index bed3e5f55d056..cb8417450c96d 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_sorting.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/overview_sorting.journey.ts @@ -6,7 +6,7 @@ */ import { before, expect, journey, step } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/private_locations.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/private_locations.journey.ts index e9aec6dc3f1c4..a3ab5804db62c 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/private_locations.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/private_locations.journey.ts @@ -6,9 +6,9 @@ */ import { journey, step, before, after, expect } from '@elastic/synthetics'; -import { byTestId } from '@kbn/observability-plugin/e2e/utils'; import { waitForLoadingToFinish } from '@kbn/ux-plugin/e2e/journeys/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId } from '../../helpers/utils'; +import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanPrivateLocations, diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/project_api_keys.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/project_api_keys.journey.ts index ffe083fcec77f..2421a31037eb1 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/project_api_keys.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/project_api_keys.journey.ts @@ -6,7 +6,7 @@ */ import { journey, step, expect, before } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../../helpers/record_video'; journey('ProjectAPIKeys', async ({ page }) => { recordVideo(page); diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/project_monitor_read_only.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/project_monitor_read_only.journey.ts index 404f593bbdaa9..dcd9a53f45b3a 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/project_monitor_read_only.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/project_monitor_read_only.journey.ts @@ -6,7 +6,7 @@ */ import { after, before, expect, journey, step } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../../helpers/record_video'; import { cleanTestMonitors, enableMonitorManagedViaApi } from './services/add_monitor'; import { getMonitor } from './services/get_monitor'; import { addTestMonitorProject } from './services/add_monitor_project'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/step_details.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/step_details.journey.ts index 94172d2c2bde6..c11515d0a44da 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/step_details.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/step_details.journey.ts @@ -6,7 +6,7 @@ */ import { journey, step, before, after } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics/synthetics_app'; import { SyntheticsServices } from './services/synthetics_services'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/test_now_mode.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/test_now_mode.journey.ts index 6c61272b24b91..df7d3da5bd177 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/test_now_mode.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/test_now_mode.journey.ts @@ -7,8 +7,8 @@ import { journey, step, before, after, expect } from '@elastic/synthetics'; import { RetryService } from '@kbn/ftr-common-functional-services'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; -import { byTestId } from '@kbn/observability-plugin/e2e/utils'; +import { byTestId } from '../../helpers/utils'; +import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics/synthetics_app'; import { SyntheticsServices } from './services/synthetics_services'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/synthetics/test_run_details.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/synthetics/test_run_details.journey.ts index e9f931e64bf04..080ca78d71358 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/synthetics/test_run_details.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/synthetics/test_run_details.journey.ts @@ -6,8 +6,8 @@ */ import { journey, step, before, after } from '@elastic/synthetics'; -import { byTestId } from '@kbn/ux-plugin/e2e/journeys/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId } from '../../helpers/utils'; +import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics/synthetics_app'; import { SyntheticsServices } from './services/synthetics_services'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/alerts/default_email_settings.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/alerts/default_email_settings.ts index a2baf2f7b3371..17a6ee07aa1c7 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/alerts/default_email_settings.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/alerts/default_email_settings.ts @@ -13,13 +13,14 @@ */ import { journey, step, before } from '@elastic/synthetics'; + import { assertNotText, - assertText, byTestId, + assertText, waitForLoadingToFinish, -} from '@kbn/observability-plugin/e2e/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +} from '../../../helpers/utils'; +import { recordVideo } from '../../../helpers/record_video'; import { settingsPageProvider } from '../../../page_objects/uptime/settings'; journey('DefaultEmailSettings', async ({ page, params }) => { diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/alerts/status_alert_flyouts_in_alerting_app.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/alerts/status_alert_flyouts_in_alerting_app.ts index 6f058d33d7714..b3cdfbe134935 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/alerts/status_alert_flyouts_in_alerting_app.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/alerts/status_alert_flyouts_in_alerting_app.ts @@ -6,9 +6,9 @@ */ import { journey, step, expect, before } from '@elastic/synthetics'; -import { assertText, byTestId, waitForLoadingToFinish } from '@kbn/observability-plugin/e2e/utils'; import { RetryService } from '@kbn/ftr-common-functional-services'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId, assertText, waitForLoadingToFinish } from '../../../helpers/utils'; +import { recordVideo } from '../../../helpers/record_video'; import { loginPageProvider } from '../../../page_objects/login'; journey('StatusFlyoutInAlertingApp', async ({ page, params }) => { diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/alerts/tls_alert_flyouts_in_alerting_app.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/alerts/tls_alert_flyouts_in_alerting_app.ts index 8b36867fed7a5..f8ac13ff0361a 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/alerts/tls_alert_flyouts_in_alerting_app.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/alerts/tls_alert_flyouts_in_alerting_app.ts @@ -6,8 +6,8 @@ */ import { journey, step, before, expect } from '@elastic/synthetics'; -import { assertText, byTestId, waitForLoadingToFinish } from '@kbn/observability-plugin/e2e/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId, assertText, waitForLoadingToFinish } from '../../../helpers/utils'; +import { recordVideo } from '../../../helpers/record_video'; import { loginPageProvider } from '../../../page_objects/login'; journey('TlsFlyoutInAlertingApp', async ({ page, params }) => { diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/data_view_permissions.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/data_view_permissions.ts index 0955a1c73ba2a..f8b74dabc027b 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/data_view_permissions.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/data_view_permissions.ts @@ -6,13 +6,9 @@ */ import { journey, step, expect, before } from '@elastic/synthetics'; -import { - byTestId, - TIMEOUT_60_SEC, - waitForLoadingToFinish, -} from '@kbn/observability-plugin/e2e/utils'; import { callKibana } from '@kbn/apm-plugin/server/test_helpers/create_apm_users/helpers/call_kibana'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId, TIMEOUT_60_SEC, waitForLoadingToFinish } from '../../helpers/utils'; +import { recordVideo } from '../../helpers/record_video'; import { loginPageProvider } from '../../page_objects/login'; journey('DataViewPermissions', async ({ page, params }) => { diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/locations/locations.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/locations/locations.ts index 54f1c1568cc75..fc307f10d0c00 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/locations/locations.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/locations/locations.ts @@ -6,7 +6,7 @@ */ import { journey, step, before, Page } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../../../helpers/record_video'; import { makeChecksWithStatus } from '../../../helpers/make_checks'; import { monitorDetailsPageProvider } from '../../../page_objects/uptime/monitor_details'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details.journey.ts index e386c8717d39a..2265af524b2c2 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details.journey.ts @@ -7,7 +7,7 @@ import { v4 as uuidv4 } from 'uuid'; import { journey, step, expect, after, Page } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../../helpers/record_video'; import { monitorManagementPageProvider } from '../../page_objects/uptime/monitor_management'; journey('MonitorDetails', async ({ page, params }: { page: Page; params: any }) => { diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details/monitor_alerts.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details/monitor_alerts.journey.ts index 75f66f41b2dbe..41c51a9bba00e 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details/monitor_alerts.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details/monitor_alerts.journey.ts @@ -7,8 +7,8 @@ import { journey, step, expect, before, Page } from '@elastic/synthetics'; import { noop } from 'lodash'; -import { byTestId, delay } from '@kbn/observability-plugin/e2e/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId, delay } from '../../../helpers/utils'; +import { recordVideo } from '../../../helpers/record_video'; import { monitorDetailsPageProvider } from '../../../page_objects/uptime/monitor_details'; const dateRangeStart = '2019-09-10T12:40:08.078Z'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details/monitor_details.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details/monitor_details.journey.ts index a71a4526db063..e395fdae45713 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details/monitor_details.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details/monitor_details.journey.ts @@ -6,8 +6,8 @@ */ import { journey, step, before, Page } from '@elastic/synthetics'; -import { byTestId } from '@kbn/observability-plugin/e2e/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId } from '../../../helpers/utils'; +import { recordVideo } from '../../../helpers/record_video'; import { monitorDetailsPageProvider } from '../../../page_objects/uptime/monitor_details'; const dateRangeStart = '2019-09-10T12:40:08.078Z'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details/ping_redirects.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details/ping_redirects.journey.ts index c7ca067a234de..9d753a75c92d7 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details/ping_redirects.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_details/ping_redirects.journey.ts @@ -6,8 +6,8 @@ */ import { journey, step, expect, before, Page } from '@elastic/synthetics'; -import { byTestId, delay } from '@kbn/observability-plugin/e2e/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId, delay } from '../../../helpers/utils'; +import { recordVideo } from '../../../helpers/record_video'; import { makeChecksWithStatus } from '../../../helpers/make_checks'; import { monitorDetailsPageProvider } from '../../../page_objects/uptime/monitor_details'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_management.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_management.journey.ts index f5a5ed151c056..df39cf7cc52eb 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_management.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_management.journey.ts @@ -7,8 +7,8 @@ import { v4 as uuidv4 } from 'uuid'; import { journey, step, expect, after, Page } from '@elastic/synthetics'; -import { byTestId } from '@kbn/observability-plugin/e2e/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId } from '../../helpers/utils'; +import { recordVideo } from '../../helpers/record_video'; import { monitorManagementPageProvider } from '../../page_objects/uptime/monitor_management'; import { DataStream } from '../../../common/runtime_types/monitor_management'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_management_enablement.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_management_enablement.journey.ts index 6d4d522364d4c..b62f6b3f5a3f4 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_management_enablement.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_management_enablement.journey.ts @@ -5,7 +5,7 @@ * 2.0. */ import { journey, step, expect, after, Page } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../../helpers/record_video'; import { monitorManagementPageProvider } from '../../page_objects/uptime/monitor_management'; journey( diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_name.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_name.journey.ts index 972b64d39217b..5c35c7de082a0 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_name.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/monitor_name.journey.ts @@ -6,8 +6,8 @@ */ import { v4 as uuidv4 } from 'uuid'; import { journey, step, expect, Page } from '@elastic/synthetics'; -import { byTestId } from '@kbn/observability-plugin/e2e/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId } from '../../helpers/utils'; +import { recordVideo } from '../../helpers/record_video'; import { monitorManagementPageProvider } from '../../page_objects/uptime/monitor_management'; journey(`MonitorName`, async ({ page, params }: { page: Page; params: any }) => { diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/private_locations/add_monitor_private_location.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/private_locations/add_monitor_private_location.ts index 45a47f8280f42..6a1329fd241b9 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/private_locations/add_monitor_private_location.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/private_locations/add_monitor_private_location.ts @@ -6,8 +6,8 @@ */ import { v4 as uuidv4 } from 'uuid'; import { journey, step, expect, before } from '@elastic/synthetics'; -import { byTestId, TIMEOUT_60_SEC } from '@kbn/observability-plugin/e2e/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { TIMEOUT_60_SEC, byTestId } from '../../../helpers/utils'; +import { recordVideo } from '../../../helpers/record_video'; import { cleanTestMonitors } from '../../synthetics/services/add_monitor'; import { monitorManagementPageProvider } from '../../../page_objects/uptime/monitor_management'; diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/private_locations/manage_locations.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/private_locations/manage_locations.ts index 703b416156d13..c5b5195f076d6 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/private_locations/manage_locations.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/private_locations/manage_locations.ts @@ -5,8 +5,8 @@ * 2.0. */ import { journey, step, expect } from '@elastic/synthetics'; -import { byTestId, TIMEOUT_60_SEC } from '@kbn/observability-plugin/e2e/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId, TIMEOUT_60_SEC } from '../../../helpers/utils'; +import { recordVideo } from '../../../helpers/record_video'; import { monitorManagementPageProvider } from '../../../page_objects/uptime/monitor_management'; journey('ManagePrivateLocation', async ({ page, params: { kibanaUrl } }) => { diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/read_only_user/monitor_management.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/read_only_user/monitor_management.ts index 85a17b56299ff..c7c44d2e6bc0e 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/read_only_user/monitor_management.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/read_only_user/monitor_management.ts @@ -6,8 +6,8 @@ */ import { expect, journey, Page, step } from '@elastic/synthetics'; -import { byTestId } from '@kbn/observability-plugin/e2e/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId } from '../../../helpers/utils'; +import { recordVideo } from '../../../helpers/record_video'; import { monitorManagementPageProvider } from '../../../page_objects/uptime/monitor_management'; journey( diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/step_duration.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/step_duration.journey.ts index 4ced9a3db25cf..f77b0ef4e88f5 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/step_duration.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/step_duration.journey.ts @@ -6,8 +6,8 @@ */ import { journey, step, expect } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; import { RetryService } from '@kbn/ftr-common-functional-services'; +import { recordVideo } from '../../helpers/record_video'; import { loginPageProvider } from '../../page_objects/login'; journey('StepsDuration', async ({ page, params }) => { diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/uptime.journey.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/uptime.journey.ts index 67dd0d8e5b68f..f3916cd4eab12 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/uptime.journey.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/uptime.journey.ts @@ -6,8 +6,8 @@ */ import { journey, step, before } from '@elastic/synthetics'; -import { byTestId, waitForLoadingToFinish } from '@kbn/observability-plugin/e2e/utils'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { byTestId, waitForLoadingToFinish } from '../../helpers/utils'; +import { recordVideo } from '../../helpers/record_video'; journey('uptime', ({ page, params }) => { recordVideo(page); diff --git a/x-pack/plugins/synthetics/e2e/page_objects/uptime/monitor_details.tsx b/x-pack/plugins/synthetics/e2e/page_objects/uptime/monitor_details.tsx index c67490e2e817a..150f6992da81e 100644 --- a/x-pack/plugins/synthetics/e2e/page_objects/uptime/monitor_details.tsx +++ b/x-pack/plugins/synthetics/e2e/page_objects/uptime/monitor_details.tsx @@ -6,7 +6,7 @@ */ import { Page } from '@elastic/synthetics'; -import { byTestId, delay } from '@kbn/observability-plugin/e2e/utils'; +import { byTestId, delay } from '../../helpers/utils'; import { monitorManagementPageProvider } from './monitor_management'; interface AlertType { diff --git a/x-pack/plugins/synthetics/e2e/page_objects/uptime/monitor_management.tsx b/x-pack/plugins/synthetics/e2e/page_objects/uptime/monitor_management.tsx index 4d89150143344..5bbe3bd8e8def 100644 --- a/x-pack/plugins/synthetics/e2e/page_objects/uptime/monitor_management.tsx +++ b/x-pack/plugins/synthetics/e2e/page_objects/uptime/monitor_management.tsx @@ -5,7 +5,7 @@ * 2.0. */ import { expect, Page } from '@elastic/synthetics'; -import { getQuerystring, TIMEOUT_60_SEC } from '@kbn/observability-plugin/e2e/utils'; +import { getQuerystring, TIMEOUT_60_SEC } from '../../helpers/utils'; import { DataStream } from '../../../common/runtime_types/monitor_management'; import { loginPageProvider } from '../login'; import { utilsPageProvider } from '../utils'; diff --git a/x-pack/plugins/synthetics/e2e/page_objects/uptime/settings.tsx b/x-pack/plugins/synthetics/e2e/page_objects/uptime/settings.tsx index 16e1e59f85a5b..a94f122e4f718 100644 --- a/x-pack/plugins/synthetics/e2e/page_objects/uptime/settings.tsx +++ b/x-pack/plugins/synthetics/e2e/page_objects/uptime/settings.tsx @@ -6,7 +6,7 @@ */ import { expect, Page } from '@elastic/synthetics'; -import { byTestId } from '@kbn/observability-plugin/e2e/utils'; +import { byTestId } from '../../helpers/utils'; import { loginPageProvider } from '../login'; import { utilsPageProvider } from '../utils'; diff --git a/x-pack/plugins/synthetics/e2e/synthetics_run.ts b/x-pack/plugins/synthetics/e2e/synthetics_run.ts index 75c8a440c2aae..b03ff899fcf5f 100644 --- a/x-pack/plugins/synthetics/e2e/synthetics_run.ts +++ b/x-pack/plugins/synthetics/e2e/synthetics_run.ts @@ -6,8 +6,8 @@ */ import { FtrConfigProviderContext } from '@kbn/test'; import path from 'path'; -import { argv } from '@kbn/observability-plugin/e2e/parse_args_params'; -import { SyntheticsRunner } from '@kbn/observability-plugin/e2e/synthetics_runner'; +import { argv } from './helpers/parse_args_params'; +import { SyntheticsRunner } from './helpers/synthetics_runner'; const { headless, grep, bail: pauseOnError } = argv; diff --git a/x-pack/plugins/observability/e2e/tasks/es_archiver.ts b/x-pack/plugins/synthetics/e2e/tasks/es_archiver.ts similarity index 100% rename from x-pack/plugins/observability/e2e/tasks/es_archiver.ts rename to x-pack/plugins/synthetics/e2e/tasks/es_archiver.ts diff --git a/x-pack/plugins/ux/e2e/helpers/parse_args_params.ts b/x-pack/plugins/ux/e2e/helpers/parse_args_params.ts new file mode 100644 index 0000000000000..41100ba2ec295 --- /dev/null +++ b/x-pack/plugins/ux/e2e/helpers/parse_args_params.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import yargs from 'yargs'; + +const { argv } = yargs(process.argv.slice(2)) + .option('headless', { + default: true, + type: 'boolean', + description: 'Start in headless mode', + }) + .option('bail', { + default: false, + type: 'boolean', + description: 'Pause on error', + }) + .option('watch', { + default: false, + type: 'boolean', + description: 'Runs the server in watch mode, restarting on changes', + }) + .option('grep', { + default: undefined, + type: 'string', + description: 'run only journeys with a name or tags that matches the glob', + }) + .help(); + +export { argv }; diff --git a/x-pack/plugins/ux/e2e/helpers/record_video.ts b/x-pack/plugins/ux/e2e/helpers/record_video.ts new file mode 100644 index 0000000000000..251dc441d8d9a --- /dev/null +++ b/x-pack/plugins/ux/e2e/helpers/record_video.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 fs from 'fs'; +import Runner from '@elastic/synthetics/dist/core/runner'; +import { after, Page } from '@elastic/synthetics'; + +const SYNTHETICS_RUNNER = Symbol.for('SYNTHETICS_RUNNER'); + +// @ts-ignore +export const runner: Runner = global[SYNTHETICS_RUNNER]; + +export const recordVideo = (page: Page, postfix = '') => { + after(async () => { + try { + const videoFilePath = await page.video()?.path(); + const pathToVideo = videoFilePath + ?.replace('.journeys/videos/', '') + .replace('.webm', ''); + const newVideoPath = videoFilePath?.replace( + pathToVideo!, + postfix + ? runner.currentJourney!.name + `-${postfix}` + : runner.currentJourney!.name + ); + fs.renameSync(videoFilePath!, newVideoPath!); + } catch (e) { + // eslint-disable-next-line no-console + console.log('Error while renaming video file', e); + } + }); +}; diff --git a/x-pack/plugins/ux/e2e/helpers/synthetics_runner.ts b/x-pack/plugins/ux/e2e/helpers/synthetics_runner.ts new file mode 100644 index 0000000000000..820db788ea270 --- /dev/null +++ b/x-pack/plugins/ux/e2e/helpers/synthetics_runner.ts @@ -0,0 +1,164 @@ +/* + * 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. + */ + +/* eslint-disable no-console */ + +import Url from 'url'; +import { run as syntheticsRun } from '@elastic/synthetics'; +import { PromiseType } from 'utility-types'; +import { createApmUsers } from '@kbn/apm-plugin/server/test_helpers/create_apm_users/create_apm_users'; + +import { EsArchiver } from '@kbn/es-archiver'; +import { esArchiverUnload } from '../tasks/es_archiver'; +import { TestReporter } from './test_reporter'; + +export interface ArgParams { + headless: boolean; + match?: string; + pauseOnError: boolean; +} + +export class SyntheticsRunner { + public getService: any; + public kibanaUrl: string; + private elasticsearchUrl: string; + + public testFilesLoaded: boolean = false; + + public params: ArgParams; + + private loadTestFilesCallback?: (reload?: boolean) => Promise; + + constructor(getService: any, params: ArgParams) { + this.getService = getService; + this.kibanaUrl = this.getKibanaUrl(); + this.elasticsearchUrl = this.getElasticsearchUrl(); + this.params = params; + } + + async setup() { + await this.createTestUsers(); + } + + async createTestUsers() { + await createApmUsers({ + elasticsearch: { + node: this.elasticsearchUrl, + username: 'elastic', + password: 'changeme', + }, + kibana: { hostname: this.kibanaUrl }, + }); + } + + async loadTestFiles( + callback: (reload?: boolean) => Promise, + reload = false + ) { + console.log('Loading test files'); + await callback(reload); + this.loadTestFilesCallback = callback; + this.testFilesLoaded = true; + console.log('Successfully loaded test files'); + } + + async loadTestData(e2eDir: string, dataArchives: string[]) { + try { + console.log('Loading esArchiver...'); + + const esArchiver: EsArchiver = this.getService('esArchiver'); + + const promises = dataArchives.map((archive) => { + if (archive === 'synthetics_data') { + return esArchiver.load(e2eDir + archive, { + docsOnly: true, + skipExisting: true, + }); + } + return esArchiver.load(e2eDir + archive, { skipExisting: true }); + }); + + await Promise.all([...promises]); + } catch (e) { + console.log(e); + } + } + + getKibanaUrl() { + const config = this.getService('config'); + + return Url.format({ + protocol: config.get('servers.kibana.protocol'), + hostname: config.get('servers.kibana.hostname'), + port: config.get('servers.kibana.port'), + }); + } + + getElasticsearchUrl() { + const config = this.getService('config'); + + return Url.format({ + protocol: config.get('servers.elasticsearch.protocol'), + hostname: config.get('servers.elasticsearch.hostname'), + port: config.get('servers.elasticsearch.port'), + }); + } + + async run() { + if (!this.testFilesLoaded) { + throw new Error('Test files not loaded'); + } + const { headless, match, pauseOnError } = this.params; + const noOfRuns = process.env.NO_OF_RUNS + ? Number(process.env.NO_OF_RUNS) + : 1; + console.log(`Running ${noOfRuns} times`); + let results: PromiseType> = {}; + for (let i = 0; i < noOfRuns; i++) { + results = await syntheticsRun({ + params: { kibanaUrl: this.kibanaUrl, getService: this.getService }, + playwrightOptions: { + headless, + chromiumSandbox: false, + timeout: 60 * 1000, + viewport: { + height: 900, + width: 1600, + }, + recordVideo: { + dir: '.journeys/videos', + }, + }, + match: match === 'undefined' ? '' : match, + pauseOnError, + screenshots: 'only-on-failure', + reporter: TestReporter, + }); + if (noOfRuns > 1) { + // need to reload again since runner resets the journeys + await this.loadTestFiles(this.loadTestFilesCallback!, true); + } + } + + await this.assertResults(results); + } + + assertResults(results: PromiseType>) { + Object.entries(results).forEach(([_journey, result]) => { + if (result.status !== 'succeeded') { + process.exitCode = 1; + process.exit(); + } + }); + } + + cleanUp() { + console.log('Removing esArchiver...'); + esArchiverUnload('full_heartbeat'); + esArchiverUnload('browser'); + } +} diff --git a/x-pack/plugins/ux/e2e/helpers/test_reporter.ts b/x-pack/plugins/ux/e2e/helpers/test_reporter.ts new file mode 100644 index 0000000000000..c04ee8ec26f5f --- /dev/null +++ b/x-pack/plugins/ux/e2e/helpers/test_reporter.ts @@ -0,0 +1,246 @@ +/* + * 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 { Journey, Step } from '@elastic/synthetics/dist/dsl'; +import { Reporter, ReporterOptions } from '@elastic/synthetics'; +import { + JourneyEndResult, + JourneyStartResult, + StepEndResult, +} from '@elastic/synthetics/dist/common_types'; + +import { yellow, green, cyan, red, bold } from 'chalk'; + +// eslint-disable-next-line no-console +const log = console.log; + +import { performance } from 'perf_hooks'; +import * as fs from 'fs'; +import { gatherScreenshots } from '@elastic/synthetics/dist/reporters/json'; +import { CACHE_PATH } from '@elastic/synthetics/dist/helpers'; +import { join } from 'path'; + +function renderError(error: any) { + let output = ''; + const outer = indent(''); + const inner = indent(outer); + const container = outer + '---\n'; + output += container; + let stack = error.stack; + if (stack) { + output += inner + 'stack: |-\n'; + stack = rewriteErrorStack(stack, findPWLogsIndexes(stack)); + const lines = String(stack).split('\n'); + for (const line of lines) { + output += inner + ' ' + line + '\n'; + } + } + output += container; + return red(output); +} + +function renderDuration(durationMs: number) { + return Number(durationMs).toFixed(0); +} + +export class TestReporter implements Reporter { + metrics = { + succeeded: 0, + failed: 0, + skipped: 0, + }; + + journeys: Map> = new Map(); + + constructor(options: ReporterOptions = {}) {} + + onJourneyStart(journey: Journey, {}: JourneyStartResult) { + if (process.env.CI) { + this.write(`\n--- Journey: ${journey.name}`); + } else { + this.write(bold(`\n Journey: ${journey.name}`)); + } + } + + onStepEnd(journey: Journey, step: Step, result: StepEndResult) { + const { status, end, start, error } = result; + const message = `${symbols[status]} Step: '${ + step.name + }' ${status} (${renderDuration((end - start) * 1000)} ms)`; + this.write(indent(message)); + if (error) { + this.write(renderError(error)); + } + this.metrics[status]++; + if (!this.journeys.has(journey.name)) { + this.journeys.set(journey.name, []); + } + this.journeys.get(journey.name)?.push({ name: step.name, ...result }); + } + + async onJourneyEnd( + journey: Journey, + { error, start, end, status }: JourneyEndResult + ) { + const { failed, succeeded, skipped } = this.metrics; + const total = failed + succeeded + skipped; + if (total === 0 && error) { + this.write(renderError(error)); + } + const message = `${symbols[status]} Took (${renderDuration( + end - start + )} seconds)`; + this.write(message); + + await fs.promises.mkdir('.journeys/failed_steps', { recursive: true }); + + await gatherScreenshots( + join(CACHE_PATH, 'screenshots'), + async (screenshot) => { + const { data, step } = screenshot; + + if (status === 'failed') { + await (async () => { + await fs.promises.writeFile( + join('.journeys/failed_steps/', `${step.name}.jpg`), + data, + { + encoding: 'base64', + } + ); + })(); + } + } + ); + } + + onEnd() { + const failedJourneys = Array.from(this.journeys.entries()).filter( + ([, steps]) => steps.some((step) => step.status === 'failed') + ); + + if (failedJourneys.length > 0) { + failedJourneys.forEach(([journeyName, steps]) => { + if (process.env.CI) { + const name = red(`Journey: ${journeyName} 🥵`); + this.write(`\n+++ ${name}`); + steps.forEach((stepResult) => { + const { status, end, start, error, name: stepName } = stepResult; + const message = `${ + symbols[status] + } Step: '${stepName}' ${status} (${renderDuration( + (end - start) * 1000 + )} ms)`; + this.write(indent(message)); + if (error) { + this.write(renderError(error)); + } + }); + } + }); + } + + const successfulJourneys = Array.from(this.journeys.entries()).filter( + ([, steps]) => steps.every((step) => step.status === 'succeeded') + ); + + successfulJourneys.forEach(([journeyName, steps]) => { + try { + fs.unlinkSync('.journeys/videos/' + journeyName + '.webm'); + } catch (e) { + // eslint-disable-next-line no-console + console.log( + 'Failed to delete video file for path ' + + '.journeys/videos/' + + journeyName + + '.webm' + ); + } + }); + + const { failed, succeeded, skipped } = this.metrics; + const total = failed + succeeded + skipped; + + let message = '\n'; + if (total === 0) { + message = 'No tests found!'; + message += ` (${renderDuration(now())} ms) \n`; + this.write(message); + return; + } + + message += succeeded > 0 ? green(` ${succeeded} passed`) : ''; + message += failed > 0 ? red(` ${failed} failed`) : ''; + message += skipped > 0 ? cyan(` ${skipped} skipped`) : ''; + message += ` (${renderDuration(now() / 1000)} seconds) \n`; + this.write(message); + } + + write(message: any) { + if (typeof message === 'object') { + message = JSON.stringify(message); + } + log(message + '\n'); + } +} + +const SEPARATOR = '\n'; + +function indent(lines: string, tab = ' ') { + return lines.replace(/^/gm, tab); +} + +const NO_UTF8_SUPPORT = process.platform === 'win32'; +const symbols = { + warning: yellow(NO_UTF8_SUPPORT ? '!' : '⚠'), + skipped: cyan('-'), + progress: cyan('>'), + succeeded: green(NO_UTF8_SUPPORT ? 'ok' : '✓'), + failed: red(NO_UTF8_SUPPORT ? 'x' : '✖'), +}; + +function now() { + return performance.now(); +} + +function findPWLogsIndexes(msgOrStack: string): [number, number] { + let startIndex = 0; + let endIndex = 0; + if (!msgOrStack) { + return [startIndex, endIndex]; + } + const lines = String(msgOrStack).split(SEPARATOR); + const logStart = /[=]{3,} logs [=]{3,}/; + const logEnd = /[=]{10,}/; + lines.forEach((line, index) => { + if (logStart.test(line)) { + startIndex = index; + } else if (logEnd.test(line)) { + endIndex = index; + } + }); + return [startIndex, endIndex]; +} + +function rewriteErrorStack(stack: string, indexes: [number, number]) { + const [start, end] = indexes; + /** + * Do not rewrite if its not a playwright error + */ + if (start === 0 && end === 0) { + return stack; + } + const linesToKeep = start + 3; + if (start > 0 && linesToKeep < end) { + const lines = stack.split(SEPARATOR); + return lines + .slice(0, linesToKeep) + .concat(...lines.slice(end)) + .join(SEPARATOR); + } + return stack; +} diff --git a/x-pack/plugins/ux/e2e/journeys/core_web_vitals.ts b/x-pack/plugins/ux/e2e/journeys/core_web_vitals.ts index 1df99e789af21..6aeebbb913b13 100644 --- a/x-pack/plugins/ux/e2e/journeys/core_web_vitals.ts +++ b/x-pack/plugins/ux/e2e/journeys/core_web_vitals.ts @@ -6,7 +6,7 @@ */ import { journey, step, expect, before } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../helpers/record_video'; import { UXDashboardDatePicker } from '../page_objects/date_picker'; import { loginToKibana, waitForLoadingToFinish } from './utils'; diff --git a/x-pack/plugins/ux/e2e/journeys/page_views.ts b/x-pack/plugins/ux/e2e/journeys/page_views.ts index d32fe0d2ccca5..904934d0f7018 100644 --- a/x-pack/plugins/ux/e2e/journeys/page_views.ts +++ b/x-pack/plugins/ux/e2e/journeys/page_views.ts @@ -6,7 +6,7 @@ */ import { journey, step, expect, before } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../helpers/record_video'; import { UXDashboardDatePicker } from '../page_objects/date_picker'; import { byTestId, loginToKibana, waitForLoadingToFinish } from './utils'; diff --git a/x-pack/plugins/ux/e2e/journeys/url_ux_query.journey.ts b/x-pack/plugins/ux/e2e/journeys/url_ux_query.journey.ts index 42088fb31a79e..24c1847b5cd06 100644 --- a/x-pack/plugins/ux/e2e/journeys/url_ux_query.journey.ts +++ b/x-pack/plugins/ux/e2e/journeys/url_ux_query.journey.ts @@ -6,7 +6,7 @@ */ import { journey, step, expect, before } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../helpers/record_video'; import { UXDashboardDatePicker } from '../page_objects/date_picker'; import { byTestId, loginToKibana, waitForLoadingToFinish } from './utils'; diff --git a/x-pack/plugins/ux/e2e/journeys/ux_client_metrics.journey.ts b/x-pack/plugins/ux/e2e/journeys/ux_client_metrics.journey.ts index ffcb016351bee..97cad67b91a7f 100644 --- a/x-pack/plugins/ux/e2e/journeys/ux_client_metrics.journey.ts +++ b/x-pack/plugins/ux/e2e/journeys/ux_client_metrics.journey.ts @@ -6,7 +6,7 @@ */ import { journey, step, expect, before } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../helpers/record_video'; import { UXDashboardDatePicker } from '../page_objects/date_picker'; import { byTestId, loginToKibana, waitForLoadingToFinish } from './utils'; diff --git a/x-pack/plugins/ux/e2e/journeys/ux_js_errors.journey.ts b/x-pack/plugins/ux/e2e/journeys/ux_js_errors.journey.ts index 248af00d60105..b6d84a2f509d9 100644 --- a/x-pack/plugins/ux/e2e/journeys/ux_js_errors.journey.ts +++ b/x-pack/plugins/ux/e2e/journeys/ux_js_errors.journey.ts @@ -6,7 +6,7 @@ */ import { journey, step, expect, before } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../helpers/record_video'; import { UXDashboardDatePicker } from '../page_objects/date_picker'; import { byTestId, loginToKibana, waitForLoadingToFinish } from './utils'; diff --git a/x-pack/plugins/ux/e2e/journeys/ux_long_task_metric_journey.ts b/x-pack/plugins/ux/e2e/journeys/ux_long_task_metric_journey.ts index e8c00a2b755af..57f9b98dcbb2d 100644 --- a/x-pack/plugins/ux/e2e/journeys/ux_long_task_metric_journey.ts +++ b/x-pack/plugins/ux/e2e/journeys/ux_long_task_metric_journey.ts @@ -6,7 +6,7 @@ */ import { journey, step, before, expect } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../helpers/record_video'; import { UXDashboardDatePicker } from '../page_objects/date_picker'; import { byTestId, loginToKibana, waitForLoadingToFinish } from './utils'; diff --git a/x-pack/plugins/ux/e2e/journeys/ux_visitor_breakdown.journey.ts b/x-pack/plugins/ux/e2e/journeys/ux_visitor_breakdown.journey.ts index a13a45d0f1b9a..86e804f947515 100644 --- a/x-pack/plugins/ux/e2e/journeys/ux_visitor_breakdown.journey.ts +++ b/x-pack/plugins/ux/e2e/journeys/ux_visitor_breakdown.journey.ts @@ -6,7 +6,7 @@ */ import { journey, step, before } from '@elastic/synthetics'; -import { recordVideo } from '@kbn/observability-plugin/e2e/record_video'; +import { recordVideo } from '../helpers/record_video'; import { UXDashboardDatePicker } from '../page_objects/date_picker'; import { byLensTestId, loginToKibana, waitForLoadingToFinish } from './utils'; diff --git a/x-pack/plugins/ux/e2e/synthetics_run.ts b/x-pack/plugins/ux/e2e/synthetics_run.ts index 0fe1f67c1bb11..2e7ff314d0201 100644 --- a/x-pack/plugins/ux/e2e/synthetics_run.ts +++ b/x-pack/plugins/ux/e2e/synthetics_run.ts @@ -5,9 +5,9 @@ * 2.0. */ import { FtrConfigProviderContext } from '@kbn/test'; -import { argv } from '@kbn/observability-plugin/e2e/parse_args_params'; import path from 'path'; -import { SyntheticsRunner } from '@kbn/observability-plugin/e2e/synthetics_runner'; +import { argv } from './helpers/parse_args_params'; +import { SyntheticsRunner } from './helpers/synthetics_runner'; const { headless, grep, bail: pauseOnError } = argv; diff --git a/x-pack/plugins/ux/e2e/tasks/es_archiver.ts b/x-pack/plugins/ux/e2e/tasks/es_archiver.ts new file mode 100644 index 0000000000000..bbb66b19f5a5e --- /dev/null +++ b/x-pack/plugins/ux/e2e/tasks/es_archiver.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import Path from 'path'; +import { execSync } from 'child_process'; + +const ES_ARCHIVE_DIR = './fixtures/es_archiver'; + +// Otherwise execSync would inject NODE_TLS_REJECT_UNAUTHORIZED=0 and node would abort if used over https +const NODE_TLS_REJECT_UNAUTHORIZED = '1'; + +export const esArchiverLoad = (folder: string) => { + const path = Path.join(ES_ARCHIVE_DIR, folder); + execSync( + `node ../../../../scripts/es_archiver load "${path}" --config ../../../test/functional/config.base.js`, + { env: { ...process.env, NODE_TLS_REJECT_UNAUTHORIZED }, stdio: 'inherit' } + ); +}; + +export const esArchiverUnload = (folder: string) => { + const path = Path.join(ES_ARCHIVE_DIR, folder); + execSync( + `node ../../../../scripts/es_archiver unload "${path}" --config ../../../test/functional/config.base.js`, + { env: { ...process.env, NODE_TLS_REJECT_UNAUTHORIZED }, stdio: 'inherit' } + ); +}; + +export const esArchiverResetKibana = () => { + execSync( + `node ../../../../scripts/es_archiver empty-kibana-index --config ../../../test/functional/config.base.js`, + { env: { ...process.env, NODE_TLS_REJECT_UNAUTHORIZED }, stdio: 'inherit' } + ); +}; From 01098bc47903931df003e84a9eaf44e35bbc3659 Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Wed, 29 Mar 2023 17:03:49 +0200 Subject: [PATCH 10/10] [APM] Migrate remaining tx-based visualisations (#153375) --- .../failed_transaction_chart.tsx | 17 +++- .../throughput_chart.tsx | 18 +++- .../service_overview_throughput_chart.tsx | 16 +++- .../failed_transaction_rate_chart/index.tsx | 16 +++- .../get_failed_transaction_rate.ts | 47 +++------- .../get_service_map_service_node_info.ts | 19 +++- .../server/routes/services/get_throughput.ts | 28 +++--- .../apm/server/routes/services/route.ts | 26 +++--- .../get_failed_transaction_rate_periods.ts | 14 ++- .../apm/server/routes/transactions/route.ts | 27 +++--- .../tests/error_rate/service_apis.spec.ts | 10 +++ .../tests/services/throughput.spec.ts | 20 ++++- .../tests/throughput/service_apis.spec.ts | 18 +++- .../tests/transactions/error_rate.spec.ts | 88 +++++++++++++++---- 14 files changed, 253 insertions(+), 111 deletions(-) diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/failed_transaction_chart.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/failed_transaction_chart.tsx index 688a3635b942f..6cb3e8232455c 100644 --- a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/failed_transaction_chart.tsx +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/failed_transaction_chart.tsx @@ -24,6 +24,8 @@ import { APIReturnType } from '../../../../services/rest/create_call_apm_api'; import { errorRateI18n } from '../../../shared/charts/failed_transaction_rate_chart'; import { TimeseriesChart } from '../../../shared/charts/timeseries_chart'; import { yLabelFormat } from './helpers'; +import { usePreferredDataSourceAndBucketSize } from '../../../../hooks/use_preferred_data_source_and_bucket_size'; +import { ApmDocumentType } from '../../../../../common/document_type'; type ErrorRate = APIReturnType<'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate'>; @@ -59,9 +61,17 @@ function FailedTransactionChart({ const { currentPeriodColor: currentPeriodColorErrorRate } = get_timeseries_color.getTimeSeriesColor(ChartType.FAILED_TRANSACTION_RATE); + const preferred = usePreferredDataSourceAndBucketSize({ + start, + end, + kuery: '', + numBuckets: 100, + type: ApmDocumentType.ServiceTransactionMetric, + }); + const { data: dataErrorRate = INITIAL_STATE_ERROR_RATE, status } = useFetcher( (callApmApi) => { - if (transactionType && serviceName && start && end) { + if (transactionType && serviceName && start && end && preferred) { return callApmApi( 'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate', { @@ -76,13 +86,16 @@ function FailedTransactionChart({ end, transactionType, transactionName: undefined, + documentType: preferred.source.documentType, + rollupInterval: preferred.source.rollupInterval, + bucketSizeInSeconds: preferred.bucketSizeInSeconds, }, }, } ); } }, - [environment, serviceName, start, end, transactionType] + [environment, serviceName, start, end, transactionType, preferred] ); const timeseriesErrorRate = [ { diff --git a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/throughput_chart.tsx b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/throughput_chart.tsx index 60b6997ad78d0..f9891eea5f13f 100644 --- a/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/throughput_chart.tsx +++ b/x-pack/plugins/apm/public/components/alerting/ui_components/alert_details_app_section/throughput_chart.tsx @@ -25,6 +25,8 @@ import { import { useFetcher } from '../../../../hooks/use_fetcher'; import { TimeseriesChart } from '../../../shared/charts/timeseries_chart'; import { getResponseTimeTickFormatter } from '../../../shared/charts/transaction_charts/helper'; +import { usePreferredDataSourceAndBucketSize } from '../../../../hooks/use_preferred_data_source_and_bucket_size'; +import { ApmDocumentType } from '../../../../../common/document_type'; const INITIAL_STATE = { currentPeriod: [], @@ -54,10 +56,19 @@ function ThroughputChart({ timeZone: string; }) { /* Throughput Chart */ + + const preferred = usePreferredDataSourceAndBucketSize({ + start, + end, + numBuckets: 100, + kuery: '', + type: ApmDocumentType.ServiceTransactionMetric, + }); + const { data: dataThroughput = INITIAL_STATE, status: statusThroughput } = useFetcher( (callApmApi) => { - if (serviceName && transactionType && start && end) { + if (serviceName && transactionType && start && end && preferred) { return callApmApi( 'GET /internal/apm/services/{serviceName}/throughput', { @@ -72,13 +83,16 @@ function ThroughputChart({ end, transactionType, transactionName: undefined, + documentType: preferred.source.documentType, + rollupInterval: preferred.source.rollupInterval, + bucketSizeInSeconds: preferred.bucketSizeInSeconds, }, }, } ); } }, - [environment, serviceName, start, end, transactionType] + [environment, serviceName, start, end, transactionType, preferred] ); const { currentPeriodColor, previousPeriodColor } = getTimeSeriesColor( ChartType.THROUGHPUT 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 30df64a9b4d0c..d1dd659ea5e19 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 @@ -30,6 +30,8 @@ import { ChartType, getTimeSeriesColor, } from '../../shared/charts/helper/get_timeseries_color'; +import { usePreferredDataSourceAndBucketSize } from '../../../hooks/use_preferred_data_source_and_bucket_size'; +import { ApmDocumentType } from '../../../../common/document_type'; const INITIAL_STATE = { currentPeriod: [], @@ -60,6 +62,14 @@ export function ServiceOverviewThroughputChart({ const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + const preferred = usePreferredDataSourceAndBucketSize({ + start, + end, + numBuckets: 100, + kuery, + type: ApmDocumentType.ServiceTransactionMetric, + }); + const { transactionType, serviceName, transactionTypeStatus } = useApmServiceContext(); @@ -71,7 +81,7 @@ export function ServiceOverviewThroughputChart({ return Promise.resolve(INITIAL_STATE); } - if (serviceName && transactionType && start && end) { + if (serviceName && transactionType && start && end && preferred) { return callApmApi( 'GET /internal/apm/services/{serviceName}/throughput', { @@ -90,6 +100,9 @@ export function ServiceOverviewThroughputChart({ ? offset : undefined, transactionName, + documentType: preferred.source.documentType, + rollupInterval: preferred.source.rollupInterval, + bucketSizeInSeconds: preferred.bucketSizeInSeconds, }, }, } @@ -107,6 +120,7 @@ export function ServiceOverviewThroughputChart({ offset, transactionName, comparisonEnabled, + preferred, ] ); diff --git a/x-pack/plugins/apm/public/components/shared/charts/failed_transaction_rate_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/failed_transaction_rate_chart/index.tsx index 620dacfd19f40..563e041224b87 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/failed_transaction_rate_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/failed_transaction_rate_chart/index.tsx @@ -24,6 +24,8 @@ import { useEnvironmentsContext } from '../../../../context/environments_context import { ApmMlDetectorType } from '../../../../../common/anomaly_detection/apm_ml_detectors'; import { usePreferredServiceAnomalyTimeseries } from '../../../../hooks/use_preferred_service_anomaly_timeseries'; import { ChartType, getTimeSeriesColor } from '../helper/get_timeseries_color'; +import { usePreferredDataSourceAndBucketSize } from '../../../../hooks/use_preferred_data_source_and_bucket_size'; +import { ApmDocumentType } from '../../../../../common/document_type'; function yLabelFormat(y?: number | null) { return asPercent(y || 0, 1); @@ -71,6 +73,14 @@ export function FailedTransactionRateChart({ const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + const preferred = usePreferredDataSourceAndBucketSize({ + start, + end, + numBuckets: 100, + kuery, + type: ApmDocumentType.ServiceTransactionMetric, + }); + const { environment } = useEnvironmentsContext(); const preferredAnomalyTimeseries = usePreferredServiceAnomalyTimeseries( @@ -88,7 +98,7 @@ export function FailedTransactionRateChart({ return Promise.resolve(INITIAL_STATE); } - if (transactionType && serviceName && start && end) { + if (transactionType && serviceName && start && end && preferred) { return callApmApi( 'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate', { @@ -107,6 +117,9 @@ export function FailedTransactionRateChart({ comparisonEnabled && isTimeComparison(offset) ? offset : undefined, + documentType: preferred.source.documentType, + rollupInterval: preferred.source.rollupInterval, + bucketSizeInSeconds: preferred.bucketSizeInSeconds, }, }, } @@ -124,6 +137,7 @@ export function FailedTransactionRateChart({ transactionName, offset, comparisonEnabled, + preferred, ] ); diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_failed_transaction_rate.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_failed_transaction_rate.ts index 0f646be6e0581..ce836c70fb676 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/get_failed_transaction_rate.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_failed_transaction_rate.ts @@ -10,28 +10,22 @@ import { rangeQuery, termQuery, } from '@kbn/observability-plugin/server'; +import { ApmServiceTransactionDocumentType } from '../../../common/document_type'; import { - EVENT_OUTCOME, SERVICE_NAME, TRANSACTION_NAME, TRANSACTION_TYPE, } from '../../../common/es_fields/apm'; -import { EventOutcome } from '../../../common/event_outcome'; +import { RollupInterval } from '../../../common/rollup'; import { environmentQuery } from '../../../common/utils/environment_query'; +import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms'; import { Coordinate } from '../../../typings/timeseries'; -import { - getDocumentTypeFilterForTransactions, - getProcessorEventForTransactions, -} from '../helpers/transactions'; -import { getBucketSizeForAggregatedTransactions } from '../helpers/get_bucket_size_for_aggregated_transactions'; +import { APMEventClient } from '../helpers/create_es_client/create_apm_event_client'; import { calculateFailedTransactionRate, - getOutcomeAggregation, getFailedTransactionRateTimeSeries, + getOutcomeAggregation, } from '../helpers/transaction_error_rate'; -import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms'; -import { APMEventClient } from '../helpers/create_es_client/create_apm_event_client'; -import { ApmDocumentType } from '../../../common/document_type'; export async function getFailedTransactionRate({ environment, @@ -40,11 +34,12 @@ export async function getFailedTransactionRate({ transactionTypes, transactionName, apmEventClient, - searchAggregatedTransactions, start, end, - numBuckets, offset, + documentType, + rollupInterval, + bucketSizeInSeconds, }: { environment: string; kuery: string; @@ -52,11 +47,12 @@ export async function getFailedTransactionRate({ transactionTypes: string[]; transactionName?: string; apmEventClient: APMEventClient; - searchAggregatedTransactions: boolean; start: number; end: number; - numBuckets?: number; offset?: string; + documentType: ApmServiceTransactionDocumentType; + rollupInterval: RollupInterval; + bucketSizeInSeconds: number; }): Promise<{ timeseries: Coordinate[]; average: number | null; @@ -69,28 +65,18 @@ export async function getFailedTransactionRate({ const filter = [ { term: { [SERVICE_NAME]: serviceName } }, - { - terms: { - [EVENT_OUTCOME]: [EventOutcome.failure, EventOutcome.success], - }, - }, { terms: { [TRANSACTION_TYPE]: transactionTypes } }, ...termQuery(TRANSACTION_NAME, transactionName), - ...getDocumentTypeFilterForTransactions(searchAggregatedTransactions), ...rangeQuery(startWithOffset, endWithOffset), ...environmentQuery(environment), ...kqlQuery(kuery), ]; - const outcomes = getOutcomeAggregation( - searchAggregatedTransactions - ? ApmDocumentType.TransactionMetric - : ApmDocumentType.TransactionEvent - ); + const outcomes = getOutcomeAggregation(documentType); const params = { apm: { - events: [getProcessorEventForTransactions(searchAggregatedTransactions)], + sources: [{ documentType, rollupInterval }], }, body: { track_total_hits: false, @@ -101,12 +87,7 @@ export async function getFailedTransactionRate({ timeseries: { date_histogram: { field: '@timestamp', - fixed_interval: getBucketSizeForAggregatedTransactions({ - start: startWithOffset, - end: endWithOffset, - searchAggregatedTransactions, - numBuckets, - }).intervalString, + fixed_interval: `${bucketSizeInSeconds}s`, min_doc_count: 0, extended_bounds: { min: startWithOffset, max: endWithOffset }, }, diff --git a/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.ts index 78a544c70b4d2..87963c19e6e08 100644 --- a/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.ts +++ b/x-pack/plugins/apm/server/routes/service_map/get_service_map_service_node_info.ts @@ -33,6 +33,8 @@ import { percentSystemMemoryUsedScript, } from '../metrics/by_agent/shared/memory'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; +import { ApmDocumentType } from '../../../common/document_type'; +import { RollupInterval } from '../../../common/rollup'; interface Options { apmEventClient: APMEventClient; @@ -136,12 +138,25 @@ async function getFailedTransactionsRateStats({ environment, apmEventClient, serviceName, - searchAggregatedTransactions, start, end, kuery: '', - numBuckets, transactionTypes: defaultTransactionTypes, + bucketSizeInSeconds: getBucketSizeForAggregatedTransactions({ + start, + end, + numBuckets, + searchAggregatedTransactions, + }).bucketSize, + ...(searchAggregatedTransactions + ? { + documentType: ApmDocumentType.TransactionMetric, + rollupInterval: RollupInterval.OneMinute, + } + : { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + }), }); return { value: average, diff --git a/x-pack/plugins/apm/server/routes/services/get_throughput.ts b/x-pack/plugins/apm/server/routes/services/get_throughput.ts index c73a6856a1104..6a33c979531e7 100644 --- a/x-pack/plugins/apm/server/routes/services/get_throughput.ts +++ b/x-pack/plugins/apm/server/routes/services/get_throughput.ts @@ -10,25 +10,21 @@ import { rangeQuery, termQuery, } from '@kbn/observability-plugin/server'; +import { ApmServiceTransactionDocumentType } from '../../../common/document_type'; import { SERVICE_NAME, TRANSACTION_NAME, TRANSACTION_TYPE, } from '../../../common/es_fields/apm'; +import { RollupInterval } from '../../../common/rollup'; import { environmentQuery } from '../../../common/utils/environment_query'; -import { - getDocumentTypeFilterForTransactions, - getProcessorEventForTransactions, -} from '../../lib/helpers/transactions'; import { getOffsetInMs } from '../../../common/utils/get_offset_in_ms'; -import { getBucketSizeForAggregatedTransactions } from '../../lib/helpers/get_bucket_size_for_aggregated_transactions'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; import { Maybe } from '../../../typings/common'; interface Options { environment: string; kuery: string; - searchAggregatedTransactions: boolean; serviceName: string; apmEventClient: APMEventClient; transactionType: string; @@ -36,6 +32,9 @@ interface Options { start: number; end: number; offset?: string; + documentType: ApmServiceTransactionDocumentType; + rollupInterval: RollupInterval; + bucketSizeInSeconds: number; } export type ServiceThroughputResponse = Array<{ x: number; y: Maybe }>; @@ -43,7 +42,6 @@ export type ServiceThroughputResponse = Array<{ x: number; y: Maybe }>; export async function getThroughput({ environment, kuery, - searchAggregatedTransactions, serviceName, apmEventClient, transactionType, @@ -51,6 +49,9 @@ export async function getThroughput({ start, end, offset, + documentType, + rollupInterval, + bucketSizeInSeconds, }: Options): Promise { const { startWithOffset, endWithOffset } = getOffsetInMs({ start, @@ -58,15 +59,9 @@ export async function getThroughput({ offset, }); - const { intervalString } = getBucketSizeForAggregatedTransactions({ - start: startWithOffset, - end: endWithOffset, - searchAggregatedTransactions, - }); - const params = { apm: { - events: [getProcessorEventForTransactions(searchAggregatedTransactions)], + sources: [{ documentType, rollupInterval }], }, body: { track_total_hits: false, @@ -76,9 +71,6 @@ export async function getThroughput({ filter: [ { term: { [SERVICE_NAME]: serviceName } }, { term: { [TRANSACTION_TYPE]: transactionType } }, - ...getDocumentTypeFilterForTransactions( - searchAggregatedTransactions - ), ...rangeQuery(startWithOffset, endWithOffset), ...environmentQuery(environment), ...kqlQuery(kuery), @@ -90,7 +82,7 @@ export async function getThroughput({ timeseries: { date_histogram: { field: '@timestamp', - fixed_interval: intervalString, + fixed_interval: `${bucketSizeInSeconds}s`, min_doc_count: 0, extended_bounds: { min: startWithOffset, max: endWithOffset }, }, diff --git a/x-pack/plugins/apm/server/routes/services/route.ts b/x-pack/plugins/apm/server/routes/services/route.ts index 2db05a6846735..2b31be70ad6ba 100644 --- a/x-pack/plugins/apm/server/routes/services/route.ts +++ b/x-pack/plugins/apm/server/routes/services/route.ts @@ -523,9 +523,15 @@ const serviceThroughputRoute = createApmServerRoute({ serviceName: t.string, }), query: t.intersection([ - t.type({ transactionType: t.string }), + t.type({ transactionType: t.string, bucketSizeInSeconds: toNumberRt }), t.partial({ transactionName: t.string }), - t.intersection([environmentRt, kueryRt, rangeRt, offsetRt]), + t.intersection([ + environmentRt, + kueryRt, + rangeRt, + offsetRt, + serviceTransactionDataSourceRt, + ]), ]), }), options: { tags: ['access:apm'] }, @@ -536,7 +542,7 @@ const serviceThroughputRoute = createApmServerRoute({ previousPeriod: ServiceThroughputResponse; }> => { const apmEventClient = await getApmEventClient(resources); - const { params, config } = resources; + const { params } = resources; const { serviceName } = params.path; const { environment, @@ -546,23 +552,21 @@ const serviceThroughputRoute = createApmServerRoute({ offset, start, end, + documentType, + rollupInterval, + bucketSizeInSeconds, } = params.query; - const searchAggregatedTransactions = await getSearchTransactionsEvents({ - config, - apmEventClient, - kuery, - start, - end, - }); const commonProps = { environment, kuery, - searchAggregatedTransactions, serviceName, apmEventClient, transactionType, transactionName, + documentType, + rollupInterval, + bucketSizeInSeconds, }; const [currentPeriod, previousPeriod] = await Promise.all([ diff --git a/x-pack/plugins/apm/server/routes/transactions/get_failed_transaction_rate_periods.ts b/x-pack/plugins/apm/server/routes/transactions/get_failed_transaction_rate_periods.ts index 3b0bc38d1ad8d..5b77a780bce6a 100644 --- a/x-pack/plugins/apm/server/routes/transactions/get_failed_transaction_rate_periods.ts +++ b/x-pack/plugins/apm/server/routes/transactions/get_failed_transaction_rate_periods.ts @@ -8,6 +8,8 @@ import { getFailedTransactionRate } from '../../lib/transaction_groups/get_faile import { offsetPreviousPeriodCoordinates } from '../../../common/utils/offset_previous_period_coordinate'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; import { Coordinate } from '../../../typings/timeseries'; +import { ApmServiceTransactionDocumentType } from '../../../common/document_type'; +import { RollupInterval } from '../../../common/rollup'; export interface FailedTransactionRateResponse { currentPeriod: { @@ -27,10 +29,12 @@ export async function getFailedTransactionRatePeriods({ transactionType, transactionName, apmEventClient, - searchAggregatedTransactions, start, end, offset, + documentType, + rollupInterval, + bucketSizeInSeconds, }: { environment: string; kuery: string; @@ -38,10 +42,12 @@ export async function getFailedTransactionRatePeriods({ transactionType: string; transactionName?: string; apmEventClient: APMEventClient; - searchAggregatedTransactions: boolean; start: number; end: number; offset?: string; + documentType: ApmServiceTransactionDocumentType; + rollupInterval: RollupInterval; + bucketSizeInSeconds: number; }): Promise { const commonProps = { environment, @@ -50,7 +56,9 @@ export async function getFailedTransactionRatePeriods({ transactionTypes: [transactionType], transactionName, apmEventClient, - searchAggregatedTransactions, + documentType, + rollupInterval, + bucketSizeInSeconds, }; const currentPeriodPromise = getFailedTransactionRate({ diff --git a/x-pack/plugins/apm/server/routes/transactions/route.ts b/x-pack/plugins/apm/server/routes/transactions/route.ts index 55ad9685e984d..2783d3465c87f 100644 --- a/x-pack/plugins/apm/server/routes/transactions/route.ts +++ b/x-pack/plugins/apm/server/routes/transactions/route.ts @@ -334,16 +334,22 @@ const transactionChartsErrorRateRoute = createApmServerRoute({ serviceName: t.string, }), query: t.intersection([ - t.type({ transactionType: t.string }), + t.type({ transactionType: t.string, bucketSizeInSeconds: toNumberRt }), t.partial({ transactionName: t.string }), - t.intersection([environmentRt, kueryRt, rangeRt, offsetRt]), + t.intersection([ + environmentRt, + kueryRt, + rangeRt, + offsetRt, + serviceTransactionDataSourceRt, + ]), ]), }), options: { tags: ['access:apm'] }, handler: async (resources): Promise => { const apmEventClient = await getApmEventClient(resources); - const { params, config } = resources; + const { params } = resources; const { serviceName } = params.path; const { environment, @@ -353,16 +359,11 @@ const transactionChartsErrorRateRoute = createApmServerRoute({ start, end, offset, + documentType, + rollupInterval, + bucketSizeInSeconds, } = params.query; - const searchAggregatedTransactions = await getSearchTransactionsEvents({ - config, - apmEventClient, - kuery, - start, - end, - }); - return getFailedTransactionRatePeriods({ environment, kuery, @@ -370,10 +371,12 @@ const transactionChartsErrorRateRoute = createApmServerRoute({ transactionType, transactionName, apmEventClient, - searchAggregatedTransactions, start, end, offset, + documentType, + rollupInterval, + bucketSizeInSeconds, }); }, }); diff --git a/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts b/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts index e4b44a289535d..472b5e6097d54 100644 --- a/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts +++ b/x-pack/test/apm_api_integration/tests/error_rate/service_apis.spec.ts @@ -67,6 +67,16 @@ export default function ApiTest({ getService }: FtrProviderContext) { ...commonQuery, kuery: `processor.event : "${processorEvent}"`, transactionType: 'request', + bucketSizeInSeconds: 60, + ...(processorEvent === ProcessorEvent.metric + ? { + documentType: ApmDocumentType.TransactionMetric, + rollupInterval: RollupInterval.OneMinute, + } + : { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + }), }, }, }), diff --git a/x-pack/test/apm_api_integration/tests/services/throughput.spec.ts b/x-pack/test/apm_api_integration/tests/services/throughput.spec.ts index 97236b6d0b6f4..b6f78addd7aca 100644 --- a/x-pack/test/apm_api_integration/tests/services/throughput.spec.ts +++ b/x-pack/test/apm_api_integration/tests/services/throughput.spec.ts @@ -15,6 +15,8 @@ import { APIReturnType, } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; +import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; +import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { roundNumber } from '../../utils'; @@ -32,7 +34,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { async function callApi( overrides?: RecursivePartial< APIClientRequestParamsOf<'GET /internal/apm/services/{serviceName}/throughput'>['params'] - > + >, + processorEvent: 'transaction' | 'metric' = 'metric' ) { const response = await apmApiClient.readUser({ endpoint: 'GET /internal/apm/services/{serviceName}/throughput', @@ -48,6 +51,17 @@ export default function ApiTest({ getService }: FtrProviderContext) { environment: 'ENVIRONMENT_ALL', kuery: '', ...overrides?.query, + ...(processorEvent === 'metric' + ? { + documentType: ApmDocumentType.TransactionMetric, + rollupInterval: RollupInterval.OneMinute, + bucketSizeInSeconds: 60, + } + : { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + bucketSizeInSeconds: 30, + }), }, }, }); @@ -120,8 +134,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { before(async () => { const [throughputMetricsResponse, throughputTransactionsResponse] = await Promise.all([ - callApi({ query: { kuery: 'processor.event : "metric"' } }), - callApi({ query: { kuery: 'processor.event : "transaction"' } }), + callApi({}, 'metric'), + callApi({}, 'transaction'), ]); throughputMetrics = throughputMetricsResponse.body; throughputTransactions = throughputTransactionsResponse.body; diff --git a/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts b/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts index 255638f50b85b..c1746975da533 100644 --- a/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts +++ b/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts @@ -4,13 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import expect from '@kbn/expect'; -import { meanBy, sumBy } from 'lodash'; -import { LatencyAggregationType } from '@kbn/apm-plugin/common/latency_aggregation_types'; import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; +import { LatencyAggregationType } from '@kbn/apm-plugin/common/latency_aggregation_types'; import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; +import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import expect from '@kbn/expect'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { meanBy, sumBy } from 'lodash'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { roundNumber } from '../../utils'; @@ -63,6 +63,16 @@ export default function ApiTest({ getService }: FtrProviderContext) { ...commonQuery, kuery: `processor.event : "${processorEvent}"`, transactionType: 'request', + bucketSizeInSeconds: 60, + ...(processorEvent === ProcessorEvent.metric + ? { + documentType: ApmDocumentType.TransactionMetric, + rollupInterval: RollupInterval.OneMinute, + } + : { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + }), }, }, }), diff --git a/x-pack/test/apm_api_integration/tests/transactions/error_rate.spec.ts b/x-pack/test/apm_api_integration/tests/transactions/error_rate.spec.ts index 60c980db5d148..3ae8731e8a16a 100644 --- a/x-pack/test/apm_api_integration/tests/transactions/error_rate.spec.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/error_rate.spec.ts @@ -13,6 +13,8 @@ import { APIReturnType, } from '@kbn/apm-plugin/public/services/rest/create_call_apm_api'; import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; +import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; +import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; import { FtrProviderContext } from '../../common/ftr_provider_context'; type ErrorRate = @@ -42,6 +44,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { transactionType: 'request', environment: 'ENVIRONMENT_ALL', kuery: '', + documentType: ApmDocumentType.TransactionMetric, + rollupInterval: RollupInterval.OneMinute, + bucketSizeInSeconds: 60, ...overrides?.query, }, }, @@ -84,13 +89,18 @@ export default function ApiTest({ getService }: FtrProviderContext) { successRate: 50, failureRate: 50, }, + secondTransaction: { + name: 'GET /pear 🍎 ', + successRate: 25, + failureRate: 75, + }, }; before(async () => { const serviceGoProdInstance = apm .service({ name: 'opbeans-go', environment: 'production', agentName: 'go' }) .instance('instance-a'); - const { firstTransaction } = config; + const { firstTransaction, secondTransaction } = config; const documents = [ timerange(start, end) @@ -102,21 +112,29 @@ export default function ApiTest({ getService }: FtrProviderContext) { .duration(1000) .success() ), - timerange(start, end) .ratePerMinute(firstTransaction.failureRate) .generator((timestamp) => serviceGoProdInstance .transaction({ transactionName: firstTransaction.name }) - .errors( - serviceGoProdInstance - .error({ - message: 'Error 1', - type: firstTransaction.name, - groupingName: 'Error test', - }) - .timestamp(timestamp) - ) + .duration(1000) + .timestamp(timestamp) + .failure() + ), + timerange(start, end) + .ratePerMinute(secondTransaction.successRate) + .generator((timestamp) => + serviceGoProdInstance + .transaction({ transactionName: secondTransaction.name }) + .timestamp(timestamp) + .duration(1000) + .success() + ), + timerange(start, end) + .ratePerMinute(secondTransaction.failureRate) + .generator((timestamp) => + serviceGoProdInstance + .transaction({ transactionName: secondTransaction.name }) .duration(1000) .timestamp(timestamp) .failure() @@ -174,17 +192,17 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); - describe('returns the transaction error rate with comparison data', () => { + describe('returns the transaction error rate with comparison data per transaction name', () => { let errorRateResponse: ErrorRate; before(async () => { - const response = await fetchErrorCharts({ - query: { - transactionName: config.firstTransaction.name, - start: moment(end).subtract(7, 'minutes').toISOString(), - offset: '7m', - }, - }); + const query = { + transactionName: config.firstTransaction.name, + start: moment(end).subtract(7, 'minutes').toISOString(), + offset: '7m', + }; + + const response = await fetchErrorCharts({ query }); errorRateResponse = response.body; }); @@ -246,5 +264,37 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); }); }); + + describe('returns the same error rate for tx metrics and service tx metrics ', () => { + let txMetricsErrorRateResponse: ErrorRate; + let serviceTxMetricsErrorRateResponse: ErrorRate; + + before(async () => { + const [txMetricsResponse, serviceTxMetricsResponse] = await Promise.all([ + fetchErrorCharts(), + fetchErrorCharts({ + query: { documentType: ApmDocumentType.ServiceTransactionMetric }, + }), + ]); + + txMetricsErrorRateResponse = txMetricsResponse.body; + serviceTxMetricsErrorRateResponse = serviceTxMetricsResponse.body; + }); + + describe('has the correct calculation for average', () => { + const expectedFailureRate = + (config.firstTransaction.failureRate + config.secondTransaction.failureRate) / 2 / 100; + + it('for tx metrics', () => { + expect(txMetricsErrorRateResponse.currentPeriod.average).to.eql(expectedFailureRate); + }); + + it('for service tx metrics', () => { + expect(serviceTxMetricsErrorRateResponse.currentPeriod.average).to.eql( + expectedFailureRate + ); + }); + }); + }); }); }