diff --git a/packages/deeplinks/observability/index.ts b/packages/deeplinks/observability/index.ts index 9292cdf77d70c..39608b4b7f7a9 100644 --- a/packages/deeplinks/observability/index.ts +++ b/packages/deeplinks/observability/index.ts @@ -9,3 +9,5 @@ export { OBSERVABILITY_ONBOARDING_APP_ID } from './constants'; export type { AppId, DeepLinkId } from './deep_links'; + +export * from './locators'; diff --git a/packages/deeplinks/observability/locators/index.ts b/packages/deeplinks/observability/locators/index.ts new file mode 100644 index 0000000000000..8761b00b7a159 --- /dev/null +++ b/packages/deeplinks/observability/locators/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './log_explorer'; +export * from './observability_log_explorer'; +export * from './observability_onboarding'; diff --git a/packages/deeplinks/observability/locators/log_explorer.ts b/packages/deeplinks/observability/locators/log_explorer.ts new file mode 100644 index 0000000000000..752ae3d79bee8 --- /dev/null +++ b/packages/deeplinks/observability/locators/log_explorer.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import type { SerializableRecord } from '@kbn/utility-types'; +import type { Filter, TimeRange, Query, AggregateQuery } from '@kbn/es-query'; + +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type RefreshInterval = { + pause: boolean; + value: number; +}; + +export const LOG_EXPLORER_LOCATOR_ID = 'LOG_EXPLORER_LOCATOR'; + +export interface LogExplorerNavigationParams extends SerializableRecord { + /** + * Optionally set the time range in the time picker. + */ + timeRange?: TimeRange; + /** + * Optionally set the refresh interval. + */ + refreshInterval?: RefreshInterval; + /** + * Optionally set a query. + */ + query?: Query | AggregateQuery; + /** + * Columns displayed in the table + */ + columns?: string[]; + /** + * Array of the used sorting [[field,direction],...] + */ + sort?: string[][]; + /** + * Optionally apply filters. + */ + filters?: Filter[]; +} + +export interface LogExplorerLocatorParams extends LogExplorerNavigationParams { + /** + * Dataset name to be selected. + */ + dataset?: string; +} diff --git a/packages/deeplinks/observability/locators/observability_log_explorer.ts b/packages/deeplinks/observability/locators/observability_log_explorer.ts new file mode 100644 index 0000000000000..e5392712f76b5 --- /dev/null +++ b/packages/deeplinks/observability/locators/observability_log_explorer.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { LogExplorerNavigationParams } from './log_explorer'; + +export type DatasetLocatorParams = LogExplorerNavigationParams; + +// All datasets locator +export const ALL_DATASETS_LOCATOR_ID = 'ALL_DATASETS_LOCATOR'; + +export type AllDatasetsLocatorParams = DatasetLocatorParams; + +// Single dataset locator +export const SINGLE_DATASET_LOCATOR_ID = 'SINGLE_DATASET_LOCATOR'; + +export interface SingleDatasetLocatorParams extends DatasetLocatorParams { + /** + * Integration name to be selected. + */ + integration?: string; + /** + * Dataset name to be selected. + * ex: system.syslog + */ + dataset: string; +} diff --git a/packages/deeplinks/observability/locators/observability_onboarding.ts b/packages/deeplinks/observability/locators/observability_onboarding.ts new file mode 100644 index 0000000000000..08bde8331c045 --- /dev/null +++ b/packages/deeplinks/observability/locators/observability_onboarding.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { SerializableRecord } from '@kbn/utility-types'; + +export const OBSERVABILITY_ONBOARDING_LOCATOR = 'OBSERVABILITY_ONBOARDING_LOCATOR' as const; + +export interface ObservabilityOnboardingLocatorParams extends SerializableRecord { + /** If given, it will load the given map else will load the create a new map page. */ + source?: 'customLogs' | 'systemLogs'; +} diff --git a/packages/deeplinks/observability/tsconfig.json b/packages/deeplinks/observability/tsconfig.json index 94b099694eaf4..425a5a8612854 100644 --- a/packages/deeplinks/observability/tsconfig.json +++ b/packages/deeplinks/observability/tsconfig.json @@ -16,5 +16,7 @@ "target/**/*" ], "kbn_references": [ + "@kbn/utility-types", + "@kbn/es-query", ] } diff --git a/x-pack/plugins/apm/public/application/index.tsx b/x-pack/plugins/apm/public/application/index.tsx index a79be6c310be5..52020aed93453 100644 --- a/x-pack/plugins/apm/public/application/index.tsx +++ b/x-pack/plugins/apm/public/application/index.tsx @@ -58,6 +58,7 @@ export const renderApp = ({ lens: pluginsStart.lens, uiActions: pluginsStart.uiActions, observabilityAIAssistant: pluginsStart.observabilityAIAssistant, + share: pluginsSetup.share, }; // render APM feedback link in global help menu diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/instance_actions_menu/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/instance_actions_menu/index.tsx index d2e8034cab32f..b6d51aec3268e 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/instance_actions_menu/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/instance_actions_menu/index.tsx @@ -15,6 +15,10 @@ import { SectionSubtitle, SectionTitle, } from '@kbn/observability-shared-plugin/public'; +import { + AllDatasetsLocatorParams, + ALL_DATASETS_LOCATOR_ID, +} from '@kbn/deeplinks-observability/locators'; import { isJavaAgentName } from '../../../../../../common/agent_name'; import { SERVICE_NODE_NAME } from '../../../../../../common/es_fields/apm'; import { useApmPluginContext } from '../../../../../context/apm_plugin/use_apm_plugin_context'; @@ -40,7 +44,7 @@ export function InstanceActionsMenu({ kuery, onClose, }: Props) { - const { core, infra } = useApmPluginContext(); + const { core, infra, share } = useApmPluginContext(); const { data, status } = useInstanceDetailsFetcher({ serviceName, serviceNodeName, @@ -52,6 +56,10 @@ export function InstanceActionsMenu({ const metricOverviewHref = useMetricOverviewHref(serviceName); const history = useHistory(); + const allDatasetsLocator = share.url.locators.get( + ALL_DATASETS_LOCATOR_ID + )!; + if (isPending(status)) { return (
void; metricsHref: string; - infraLocators: InfraLocators; + infraLocators?: InfraLocators; + allDatasetsLocator: LocatorPublic; }) { const podId = instanceDetails.kubernetes?.pod?.uid; const containerId = instanceDetails.container?.id; @@ -54,69 +59,72 @@ export function getMenuSections({ const infraMetricsQuery = getInfraMetricsQuery(instanceDetails['@timestamp']); const infraNodeLocator = infraLocators?.nodeLogsLocator; - const podActions: Action[] = infraNodeLocator - ? [ - { - key: 'podLogs', - label: i18n.translate( - 'xpack.apm.serviceOverview.instancesTable.actionMenus.podLogs', - { defaultMessage: 'Pod logs' } - ), - href: infraNodeLocator?.getRedirectUrl({ - nodeId: podId!, - nodeType: 'pod', - time, - }), - condition: !!podId, - }, - { - key: 'podMetrics', - label: i18n.translate( - 'xpack.apm.serviceOverview.instancesTable.actionMenus.podMetrics', - { defaultMessage: 'Pod metrics' } - ), - href: getInfraHref({ - app: 'metrics', - basePath, - path: `/link-to/pod-detail/${podId}`, - query: infraMetricsQuery, - }), - condition: !!podId, - }, - ] - : []; + const podLogsHref = getNodeLogsHref( + 'pod', + podId!, + time, + allDatasetsLocator, + infraNodeLocator + ); + const containerLogsHref = getNodeLogsHref( + 'container', + containerId!, + time, + allDatasetsLocator, + infraNodeLocator + ); - const containerActions: Action[] = infraNodeLocator - ? [ - { - key: 'containerLogs', - label: i18n.translate( - 'xpack.apm.serviceOverview.instancesTable.actionMenus.containerLogs', - { defaultMessage: 'Container logs' } - ), - href: infraNodeLocator?.getRedirectUrl({ - nodeId: containerId!, - nodeType: 'container', - time, - }), - condition: !!containerId, - }, - { - key: 'containerMetrics', - label: i18n.translate( - 'xpack.apm.serviceOverview.instancesTable.actionMenus.containerMetrics', - { defaultMessage: 'Container metrics' } - ), - href: getInfraHref({ - app: 'metrics', - basePath, - path: `/link-to/container-detail/${containerId}`, - query: infraMetricsQuery, - }), - condition: !!containerId, - }, - ] - : []; + const podActions: Action[] = [ + { + key: 'podLogs', + label: i18n.translate( + 'xpack.apm.serviceOverview.instancesTable.actionMenus.podLogs', + { defaultMessage: 'Pod logs' } + ), + href: podLogsHref, + condition: !!podId, + }, + { + key: 'podMetrics', + label: i18n.translate( + 'xpack.apm.serviceOverview.instancesTable.actionMenus.podMetrics', + { defaultMessage: 'Pod metrics' } + ), + href: getInfraHref({ + app: 'metrics', + basePath, + path: `/link-to/pod-detail/${podId}`, + query: infraMetricsQuery, + }), + condition: !!podId && !!infraLocators, + }, + ]; + + const containerActions: Action[] = [ + { + key: 'containerLogs', + label: i18n.translate( + 'xpack.apm.serviceOverview.instancesTable.actionMenus.containerLogs', + { defaultMessage: 'Container logs' } + ), + href: containerLogsHref, + condition: !!containerId, + }, + { + key: 'containerMetrics', + label: i18n.translate( + 'xpack.apm.serviceOverview.instancesTable.actionMenus.containerMetrics', + { defaultMessage: 'Container metrics' } + ), + href: getInfraHref({ + app: 'metrics', + basePath, + path: `/link-to/container-detail/${containerId}`, + query: infraMetricsQuery, + }), + condition: !!containerId && !!infraLocators, + }, + ]; const apmActions: Action[] = [ { diff --git a/x-pack/plugins/apm/public/components/shared/links/observability_logs_link.ts b/x-pack/plugins/apm/public/components/shared/links/observability_logs_link.ts new file mode 100644 index 0000000000000..6909d0035b2f0 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/links/observability_logs_link.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + NodeLogsLocator, + DiscoverNodeLogsLocator, + LogsLocator, + DiscoverLogsLocator, +} from '@kbn/infra-plugin/common/locators'; +import { AllDatasetsLocatorParams } from '@kbn/deeplinks-observability/locators'; +import { LocatorPublic } from '@kbn/share-plugin/common'; +import moment from 'moment'; +import { DurationInputObject } from 'moment'; + +type NodeType = 'host' | 'pod' | 'container'; + +const NodeTypeMapping: Record = { + host: 'host.name', + container: 'container.id', + pod: 'kubernetes.pod.uid', +}; + +export const getNodeLogsHref = ( + nodeType: NodeType, + id: string, + time: number | undefined, + allDatasetsLocator: LocatorPublic, + infraNodeLocator?: NodeLogsLocator | DiscoverNodeLogsLocator +): string => { + if (infraNodeLocator) + return infraNodeLocator?.getRedirectUrl({ + nodeId: id!, + nodeType, + time, + }); + + return allDatasetsLocator.getRedirectUrl({ + query: getNodeQuery(nodeType, id), + ...(time + ? { + timeRange: { + from: getTimeRangeStartFromTime(time), + to: getTimeRangeEndFromTime(time), + }, + } + : {}), + }); +}; + +export const getTraceLogsHref = ( + traceId: string, + time: number | undefined, + allDatasetsLocator: LocatorPublic, + infraLogsLocator?: LogsLocator | DiscoverLogsLocator +): string => { + const query = `trace.id:"${traceId}" OR (not trace.id:* AND "${traceId}")`; + + if (infraLogsLocator) + return infraLogsLocator.getRedirectUrl({ + filter: query, + time, + }); + + return allDatasetsLocator.getRedirectUrl({ + query: { language: 'kuery', query }, + ...(time + ? { + timeRange: { + from: getTimeRangeStartFromTime(time), + to: getTimeRangeEndFromTime(time), + }, + } + : {}), + }); +}; + +const getNodeQuery = (type: NodeType, id: string) => { + return { language: 'kuery', query: `${NodeTypeMapping[type]}: ${id}` }; +}; + +const defaultTimeRangeFromPositionOffset: DurationInputObject = { hours: 1 }; + +const getTimeRangeStartFromTime = (time: number): string => + moment(time).subtract(defaultTimeRangeFromPositionOffset).toISOString(); + +const getTimeRangeEndFromTime = (time: number): string => + moment(time).add(defaultTimeRangeFromPositionOffset).toISOString(); diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.test.ts b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.test.ts index 3ed8c4c1f0ffc..803a69bd44e21 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.test.ts +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.test.ts @@ -13,7 +13,10 @@ import { apmRouter as apmRouterBase, ApmRouter, } from '../../routing/apm_route_config'; -import { infraLocatorsMock } from '../../../context/apm_plugin/mock_apm_plugin_context'; +import { + infraLocatorsMock, + observabilityLogExplorerLocatorsMock, +} from '../../../context/apm_plugin/mock_apm_plugin_context'; const apmRouter = { ...apmRouterBase, @@ -22,6 +25,7 @@ const apmRouter = { } as ApmRouter; const infraLocators = infraLocatorsMock; +const observabilityLogExplorerLocators = observabilityLogExplorerLocatorsMock; const expectInfraLocatorsToBeCalled = () => { expect(infraLocators.nodeLogsLocator.getRedirectUrl).toBeCalledTimes(3); @@ -61,6 +65,7 @@ describe('Transaction action menu', () => { location, apmRouter, infraLocators, + observabilityLogExplorerLocators, infraLinksAvailable: false, rangeFrom: 'now-24h', rangeTo: 'now', @@ -126,6 +131,7 @@ describe('Transaction action menu', () => { location, apmRouter, infraLocators, + observabilityLogExplorerLocators, infraLinksAvailable: true, rangeFrom: 'now-24h', rangeTo: 'now', @@ -210,6 +216,7 @@ describe('Transaction action menu', () => { location, apmRouter, infraLocators, + observabilityLogExplorerLocators, infraLinksAvailable: true, rangeFrom: 'now-24h', rangeTo: 'now', diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.ts b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.ts index 8f230ba94abe8..c17bedeb8635a 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.ts +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.ts @@ -13,6 +13,8 @@ import moment from 'moment'; import url from 'url'; import type { InfraLocators } from '@kbn/infra-plugin/common/locators'; import type { ProfilingLocators } from '@kbn/profiling-plugin/public'; +import { LocatorPublic } from '@kbn/share-plugin/common'; +import { AllDatasetsLocatorParams } from '@kbn/deeplinks-observability/locators'; import { Environment } from '../../../../common/environment_rt'; import type { Transaction } from '../../../../typings/es_schemas/ui/transaction'; import { getDiscoverHref } from '../links/discover_links/discover_link'; @@ -22,6 +24,10 @@ import { fromQuery } from '../links/url_helpers'; import { SectionRecord, getNonEmptySections, Action } from './sections_helper'; import { HOST_NAME, TRACE_ID } from '../../../../common/es_fields/apm'; import { ApmRouter } from '../../routing/apm_route_config'; +import { + getNodeLogsHref, + getTraceLogsHref, +} from '../links/observability_logs_link'; function getInfraMetricsQuery(transaction: Transaction) { const timestamp = new Date(transaction['@timestamp']).getTime(); @@ -44,6 +50,7 @@ export const getSections = ({ rangeFrom, rangeTo, environment, + allDatasetsLocator, }: { transaction?: Transaction; basePath: IBasePath; @@ -55,8 +62,10 @@ export const getSections = ({ rangeFrom: string; rangeTo: string; environment: Environment; + allDatasetsLocator: LocatorPublic; }) => { if (!transaction) return []; + const hostName = transaction.host?.hostname; const podId = transaction.kubernetes?.pod?.uid; const containerId = transaction.container?.id; @@ -79,102 +88,111 @@ export const getSections = ({ )}`, }); - const podActions: Action[] = nodeLogsLocator - ? [ - { - key: 'podLogs', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showPodLogsLinkLabel', - { defaultMessage: 'Pod logs' } - ), - href: nodeLogsLocator.getRedirectUrl({ - nodeId: podId!, - nodeType: 'pod', - time, - }), - condition: !!podId, - }, - { - key: 'podMetrics', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showPodMetricsLinkLabel', - { defaultMessage: 'Pod metrics' } - ), - href: getInfraHref({ - app: 'metrics', - basePath, - path: `/link-to/pod-detail/${podId}`, - query: infraMetricsQuery, - }), - condition: !!podId, - }, - ] - : []; + // Logs hrefs + const podLogsHref = getNodeLogsHref( + 'pod', + podId!, + time, + allDatasetsLocator, + nodeLogsLocator + ); + const containerLogsHref = getNodeLogsHref( + 'container', + containerId!, + time, + allDatasetsLocator, + nodeLogsLocator + ); + const hostLogsHref = getNodeLogsHref( + 'host', + hostName!, + time, + allDatasetsLocator, + nodeLogsLocator + ); + const traceLogsHref = getTraceLogsHref( + transaction.trace.id!, + time, + allDatasetsLocator, + logsLocator + ); - const containerActions: Action[] = nodeLogsLocator - ? [ - { - key: 'containerLogs', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showContainerLogsLinkLabel', - { defaultMessage: 'Container logs' } - ), - href: nodeLogsLocator.getRedirectUrl({ - nodeId: containerId!, - nodeType: 'container', - time, - }), - condition: !!containerId, - }, - { - key: 'containerMetrics', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showContainerMetricsLinkLabel', - { defaultMessage: 'Container metrics' } - ), - href: getInfraHref({ - app: 'metrics', - basePath, - path: `/link-to/container-detail/${containerId}`, - query: infraMetricsQuery, - }), - condition: !!containerId, - }, - ] - : []; + const podActions: Action[] = [ + { + key: 'podLogs', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showPodLogsLinkLabel', + { defaultMessage: 'Pod logs' } + ), + href: podLogsHref, + condition: !!podId, + }, + { + key: 'podMetrics', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showPodMetricsLinkLabel', + { defaultMessage: 'Pod metrics' } + ), + href: getInfraHref({ + app: 'metrics', + basePath, + path: `/link-to/pod-detail/${podId}`, + query: infraMetricsQuery, + }), + condition: !!podId && infraLinksAvailable, + }, + ]; + + const containerActions: Action[] = [ + { + key: 'containerLogs', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showContainerLogsLinkLabel', + { defaultMessage: 'Container logs' } + ), + href: containerLogsHref, + condition: !!containerId, + }, + { + key: 'containerMetrics', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showContainerMetricsLinkLabel', + { defaultMessage: 'Container metrics' } + ), + href: getInfraHref({ + app: 'metrics', + basePath, + path: `/link-to/container-detail/${containerId}`, + query: infraMetricsQuery, + }), + condition: !!containerId && infraLinksAvailable, + }, + ]; const hostActions: Action[] = [ - ...(nodeLogsLocator - ? [ - { - key: 'hostLogs', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showHostLogsLinkLabel', - { defaultMessage: 'Host logs' } - ), - href: nodeLogsLocator.getRedirectUrl({ - nodeId: hostName!, - nodeType: 'host', - time, - }), - condition: !!hostName, - }, - { - key: 'hostMetrics', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showHostMetricsLinkLabel', - { defaultMessage: 'Host metrics' } - ), - href: getInfraHref({ - app: 'metrics', - basePath, - path: `/link-to/host-detail/${hostName}`, - query: infraMetricsQuery, - }), - condition: !!hostName, - }, - ] - : []), + { + key: 'hostLogs', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showHostLogsLinkLabel', + { defaultMessage: 'Host logs' } + ), + href: hostLogsHref, + condition: !!hostName, + }, + { + key: 'hostMetrics', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showHostMetricsLinkLabel', + { defaultMessage: 'Host metrics' } + ), + href: getInfraHref({ + app: 'metrics', + basePath, + path: `/link-to/host-detail/${hostName}`, + query: infraMetricsQuery, + }), + condition: !!hostName && infraLinksAvailable, + }, { key: 'hostProfilingFlamegraph', label: i18n.translate( @@ -213,22 +231,17 @@ export const getSections = ({ }, ]; - const logActions: Action[] = logsLocator - ? [ - { - key: 'traceLogs', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showTraceLogsLinkLabel', - { defaultMessage: 'Trace logs' } - ), - href: logsLocator.getRedirectUrl({ - filter: `trace.id:"${transaction.trace.id}" OR (not trace.id:* AND "${transaction.trace.id}")`, - time, - }), - condition: true, - }, - ] - : []; + const logActions: Action[] = [ + { + key: 'traceLogs', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showTraceLogsLinkLabel', + { defaultMessage: 'Trace logs' } + ), + href: traceLogsHref, + condition: true, + }, + ]; const uptimeActions: Action[] = [ { @@ -283,82 +296,64 @@ export const getSections = ({ const sectionRecord: SectionRecord = { observability: [ - ...(infraLinksAvailable && infraLocators - ? [ - { - key: 'podDetails', - title: i18n.translate( - 'xpack.apm.transactionActionMenu.pod.title', - { - defaultMessage: 'Pod details', - } - ), - subtitle: i18n.translate( - 'xpack.apm.transactionActionMenu.pod.subtitle', - { - defaultMessage: - 'View logs and metrics for this pod to get further details.', - } - ), - actions: podActions, - }, - { - key: 'containerDetails', - title: i18n.translate( - 'xpack.apm.transactionActionMenu.container.title', - { - defaultMessage: 'Container details', - } - ), - subtitle: i18n.translate( - 'xpack.apm.transactionActionMenu.container.subtitle', - { - defaultMessage: - 'View logs and metrics for this container to get further details.', - } - ), - actions: containerActions, - }, - { - key: 'hostDetails', - title: i18n.translate( - 'xpack.apm.transactionActionMenu.host.title', - { - defaultMessage: 'Host details', - } - ), - subtitle: i18n.translate( - 'xpack.apm.transactionActionMenu.host.subtitle', - { - defaultMessage: - 'View host logs and metrics to get further details.', - } - ), - actions: hostActions, - }, - ] - : []), - - ...(infraLocators - ? [ - { - key: 'traceDetails', - title: i18n.translate( - 'xpack.apm.transactionActionMenu.trace.title', - { - defaultMessage: 'Trace details', - } - ), - subtitle: i18n.translate( - 'xpack.apm.transactionActionMenu.trace.subtitle', - { - defaultMessage: 'View trace logs to get further details.', - } - ), - actions: logActions, - }, - ] - : []), + { + key: 'podDetails', + title: i18n.translate('xpack.apm.transactionActionMenu.pod.title', { + defaultMessage: 'Pod details', + }), + subtitle: i18n.translate( + 'xpack.apm.transactionActionMenu.pod.subtitle', + { + defaultMessage: + 'View logs and metrics for this pod to get further details.', + } + ), + actions: podActions, + }, + { + key: 'containerDetails', + title: i18n.translate( + 'xpack.apm.transactionActionMenu.container.title', + { + defaultMessage: 'Container details', + } + ), + subtitle: i18n.translate( + 'xpack.apm.transactionActionMenu.container.subtitle', + { + defaultMessage: + 'View logs and metrics for this container to get further details.', + } + ), + actions: containerActions, + }, + { + key: 'hostDetails', + title: i18n.translate('xpack.apm.transactionActionMenu.host.title', { + defaultMessage: 'Host details', + }), + subtitle: i18n.translate( + 'xpack.apm.transactionActionMenu.host.subtitle', + { + defaultMessage: + 'View host logs and metrics to get further details.', + } + ), + actions: hostActions, + }, + { + key: 'traceDetails', + title: i18n.translate('xpack.apm.transactionActionMenu.trace.title', { + defaultMessage: 'Trace details', + }), + subtitle: i18n.translate( + 'xpack.apm.transactionActionMenu.trace.subtitle', + { + defaultMessage: 'View trace logs to get further details.', + } + ), + actions: logActions, + }, { key: 'statusDetails', title: i18n.translate('xpack.apm.transactionActionMenu.status.title', { diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx index 3f88469664baa..555217d1c0b7b 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx @@ -69,9 +69,11 @@ const renderTransaction = async (transaction: Record) => { const expectInfraLocatorsToBeCalled = () => { expect( - apmContextMock.infra.locators.nodeLogsLocator.getRedirectUrl + apmContextMock.infra?.locators.nodeLogsLocator.getRedirectUrl + ).toBeCalled(); + expect( + apmContextMock.infra?.locators.logsLocator.getRedirectUrl ).toBeCalled(); - expect(apmContextMock.infra.locators.logsLocator.getRedirectUrl).toBeCalled(); }; describe('TransactionActionMenu component', () => { diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.tsx b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.tsx index 14cae53ba28cf..162fdf8e90b85 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.tsx +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.tsx @@ -22,6 +22,10 @@ import { ProfilingLocators } from '@kbn/profiling-plugin/public'; import React, { useState } from 'react'; import { useLocation } from 'react-router-dom'; import useAsync from 'react-use/lib/useAsync'; +import { + AllDatasetsLocatorParams, + ALL_DATASETS_LOCATOR_ID, +} from '@kbn/deeplinks-observability/locators'; import { useAnyOfApmParams } from '../../../hooks/use_apm_params'; import { ApmFeatureFlagName } from '../../../../common/apm_feature_flags'; import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; @@ -125,10 +129,14 @@ function ActionMenuSections({ transaction?: Transaction; profilingLocators?: ProfilingLocators; }) { - const { core, uiActions, infra } = useApmPluginContext(); + const { core, uiActions, infra, share } = useApmPluginContext(); const location = useLocation(); const apmRouter = useApmRouter(); + const allDatasetsLocator = share.url.locators.get( + ALL_DATASETS_LOCATOR_ID + )!; + const infraLinksAvailable = useApmFeatureFlag( ApmFeatureFlagName.InfraUiAvailable ); @@ -153,6 +161,7 @@ function ActionMenuSections({ rangeFrom, rangeTo, environment, + allDatasetsLocator, }); const externalMenuItems = useAsync(() => { diff --git a/x-pack/plugins/apm/public/context/apm_plugin/apm_plugin_context.tsx b/x-pack/plugins/apm/public/context/apm_plugin/apm_plugin_context.tsx index 6bc5fd0ca2eb0..efe88ced91d4a 100644 --- a/x-pack/plugins/apm/public/context/apm_plugin/apm_plugin_context.tsx +++ b/x-pack/plugins/apm/public/context/apm_plugin/apm_plugin_context.tsx @@ -17,6 +17,7 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { InfraClientStartExports } from '@kbn/infra-plugin/public'; import type { ObservabilityAIAssistantPluginStart } from '@kbn/observability-ai-assistant-plugin/public'; +import { SharePluginSetup } from '@kbn/share-plugin/public'; import type { ApmPluginSetupDeps } from '../../plugin'; import type { ConfigSchema } from '../..'; @@ -28,12 +29,13 @@ export interface ApmPluginContextValue { plugins: ApmPluginSetupDeps & { maps?: MapsStartApi }; observabilityRuleTypeRegistry: ObservabilityRuleTypeRegistry; observability: ObservabilityPublicStart; - infra: InfraClientStartExports; + infra?: InfraClientStartExports; dataViews: DataViewsPublicPluginStart; data: DataPublicPluginStart; unifiedSearch: UnifiedSearchPublicPluginStart; uiActions: UiActionsStart; observabilityAIAssistant: ObservabilityAIAssistantPluginStart; + share: SharePluginSetup; } export const ApmPluginContext = createContext({} as ApmPluginContextValue); diff --git a/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx b/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx index 3e3a811504a61..919e98267c8b2 100644 --- a/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx +++ b/x-pack/plugins/apm/public/context/apm_plugin/mock_apm_plugin_context.tsx @@ -141,6 +141,7 @@ export const mockApmPluginContextValue = { locators: infraLocatorsMock, }, deps: {}, + share: sharePluginMock.createSetupContract(), unifiedSearch: mockUnifiedSearch, uiActions: { getTriggerCompatibleActions: () => Promise.resolve([]), diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index fe859abb02ec4..7a22ac6e4a4c2 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -101,7 +101,8 @@ "@kbn/profiling-utils", "@kbn/core-analytics-server", "@kbn/analytics-client", - "@kbn/monaco" + "@kbn/monaco", + "@kbn/deeplinks-observability" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/log_explorer/public/utils/dataset_selection/all_dataset_selection.ts b/x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts similarity index 95% rename from x-pack/plugins/log_explorer/public/utils/dataset_selection/all_dataset_selection.ts rename to x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts index ebe3254968fb0..c505a07da7768 100644 --- a/x-pack/plugins/log_explorer/public/utils/dataset_selection/all_dataset_selection.ts +++ b/x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Dataset } from '../../../common/datasets'; +import { Dataset } from '../datasets'; import { encodeDatasetSelection } from './encoding'; import { DatasetSelectionStrategy } from './types'; diff --git a/x-pack/plugins/log_explorer/public/utils/dataset_selection/encoding.test.ts b/x-pack/plugins/log_explorer/common/dataset_selection/encoding.test.ts similarity index 100% rename from x-pack/plugins/log_explorer/public/utils/dataset_selection/encoding.test.ts rename to x-pack/plugins/log_explorer/common/dataset_selection/encoding.test.ts diff --git a/x-pack/plugins/log_explorer/public/utils/dataset_selection/encoding.ts b/x-pack/plugins/log_explorer/common/dataset_selection/encoding.ts similarity index 95% rename from x-pack/plugins/log_explorer/public/utils/dataset_selection/encoding.ts rename to x-pack/plugins/log_explorer/common/dataset_selection/encoding.ts index 5a84c398e7d4e..83a7c5357fde5 100644 --- a/x-pack/plugins/log_explorer/public/utils/dataset_selection/encoding.ts +++ b/x-pack/plugins/log_explorer/common/dataset_selection/encoding.ts @@ -7,7 +7,7 @@ import { decode, encode, RisonValue } from '@kbn/rison'; import * as lz from 'lz-string'; -import { decodeOrThrow } from '../../../common/runtime_types'; +import { decodeOrThrow } from '../runtime_types'; import { DatasetEncodingError } from './errors'; import { DatasetSelectionPlain, datasetSelectionPlainRT } from './types'; diff --git a/x-pack/plugins/log_explorer/public/utils/dataset_selection/errors.ts b/x-pack/plugins/log_explorer/common/dataset_selection/errors.ts similarity index 100% rename from x-pack/plugins/log_explorer/public/utils/dataset_selection/errors.ts rename to x-pack/plugins/log_explorer/common/dataset_selection/errors.ts diff --git a/x-pack/plugins/log_explorer/public/utils/dataset_selection/hydrate_dataset_selection.ts.ts b/x-pack/plugins/log_explorer/common/dataset_selection/hydrate_dataset_selection.ts.ts similarity index 77% rename from x-pack/plugins/log_explorer/public/utils/dataset_selection/hydrate_dataset_selection.ts.ts rename to x-pack/plugins/log_explorer/common/dataset_selection/hydrate_dataset_selection.ts.ts index 8a855a46412f3..43faebc618140 100644 --- a/x-pack/plugins/log_explorer/public/utils/dataset_selection/hydrate_dataset_selection.ts.ts +++ b/x-pack/plugins/log_explorer/common/dataset_selection/hydrate_dataset_selection.ts.ts @@ -8,6 +8,7 @@ import { AllDatasetSelection } from './all_dataset_selection'; import { SingleDatasetSelection } from './single_dataset_selection'; import { DatasetSelectionPlain } from './types'; +import { UnresolvedDatasetSelection } from './unresolved_dataset_selection'; export const hydrateDatasetSelection = (datasetSelection: DatasetSelectionPlain) => { if (datasetSelection.selectionType === 'all') { @@ -16,4 +17,7 @@ export const hydrateDatasetSelection = (datasetSelection: DatasetSelectionPlain) if (datasetSelection.selectionType === 'single') { return SingleDatasetSelection.fromSelection(datasetSelection.selection); } + if (datasetSelection.selectionType === 'unresolved') { + return UnresolvedDatasetSelection.fromSelection(datasetSelection.selection); + } }; diff --git a/x-pack/plugins/log_explorer/public/utils/dataset_selection/index.ts b/x-pack/plugins/log_explorer/common/dataset_selection/index.ts similarity index 66% rename from x-pack/plugins/log_explorer/public/utils/dataset_selection/index.ts rename to x-pack/plugins/log_explorer/common/dataset_selection/index.ts index a3c430df1d356..3284610f53bcc 100644 --- a/x-pack/plugins/log_explorer/public/utils/dataset_selection/index.ts +++ b/x-pack/plugins/log_explorer/common/dataset_selection/index.ts @@ -7,16 +7,25 @@ import { AllDatasetSelection } from './all_dataset_selection'; import { SingleDatasetSelection } from './single_dataset_selection'; +import { UnresolvedDatasetSelection } from './unresolved_dataset_selection'; -export type DatasetSelection = AllDatasetSelection | SingleDatasetSelection; +export type DatasetSelection = + | AllDatasetSelection + | SingleDatasetSelection + | UnresolvedDatasetSelection; export type DatasetSelectionChange = (datasetSelection: DatasetSelection) => void; export const isDatasetSelection = (input: any): input is DatasetSelection => { - return input instanceof AllDatasetSelection || input instanceof SingleDatasetSelection; + return ( + input instanceof AllDatasetSelection || + input instanceof SingleDatasetSelection || + input instanceof UnresolvedDatasetSelection + ); }; export * from './all_dataset_selection'; export * from './single_dataset_selection'; +export * from './unresolved_dataset_selection'; export * from './encoding'; export * from './errors'; export * from './hydrate_dataset_selection.ts'; diff --git a/x-pack/plugins/log_explorer/public/utils/dataset_selection/single_dataset_selection.ts b/x-pack/plugins/log_explorer/common/dataset_selection/single_dataset_selection.ts similarity index 97% rename from x-pack/plugins/log_explorer/public/utils/dataset_selection/single_dataset_selection.ts rename to x-pack/plugins/log_explorer/common/dataset_selection/single_dataset_selection.ts index 6f772200cf6c0..21c788579ed70 100644 --- a/x-pack/plugins/log_explorer/public/utils/dataset_selection/single_dataset_selection.ts +++ b/x-pack/plugins/log_explorer/common/dataset_selection/single_dataset_selection.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Dataset } from '../../../common/datasets'; +import { Dataset } from '../datasets'; import { encodeDatasetSelection } from './encoding'; import { DatasetSelectionStrategy, SingleDatasetSelectionPayload } from './types'; diff --git a/x-pack/plugins/log_explorer/public/utils/dataset_selection/types.ts b/x-pack/plugins/log_explorer/common/dataset_selection/types.ts similarity index 74% rename from x-pack/plugins/log_explorer/public/utils/dataset_selection/types.ts rename to x-pack/plugins/log_explorer/common/dataset_selection/types.ts index a25f16cee7654..239bbc1108a29 100644 --- a/x-pack/plugins/log_explorer/public/utils/dataset_selection/types.ts +++ b/x-pack/plugins/log_explorer/common/dataset_selection/types.ts @@ -6,7 +6,7 @@ */ import { DataViewSpec } from '@kbn/data-views-plugin/common'; import * as rt from 'io-ts'; -import { datasetRT } from '../../../common/datasets'; +import { datasetRT } from '../datasets'; export const allDatasetSelectionPlainRT = rt.type({ selectionType: rt.literal('all'), @@ -33,17 +33,33 @@ const singleDatasetSelectionPayloadRT = rt.intersection([ }), ]); +const unresolvedDatasetSelectionPayloadRT = rt.intersection([ + integrationNameRT, + rt.type({ + dataset: datasetRT, + }), +]); + export const singleDatasetSelectionPlainRT = rt.type({ selectionType: rt.literal('single'), selection: singleDatasetSelectionPayloadRT, }); +export const unresolvedDatasetSelectionPlainRT = rt.type({ + selectionType: rt.literal('unresolved'), + selection: unresolvedDatasetSelectionPayloadRT, +}); + export const datasetSelectionPlainRT = rt.union([ allDatasetSelectionPlainRT, singleDatasetSelectionPlainRT, + unresolvedDatasetSelectionPlainRT, ]); export type SingleDatasetSelectionPayload = rt.TypeOf; +export type UnresolvedDatasetSelectionPayload = rt.TypeOf< + typeof unresolvedDatasetSelectionPayloadRT +>; export type DatasetSelectionPlain = rt.TypeOf; export interface DatasetSelectionStrategy { diff --git a/x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts b/x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts new file mode 100644 index 0000000000000..acfd5180f0ed3 --- /dev/null +++ b/x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Dataset } from '../datasets'; +import { encodeDatasetSelection } from './encoding'; +import { DatasetSelectionStrategy, UnresolvedDatasetSelectionPayload } from './types'; + +export class UnresolvedDatasetSelection implements DatasetSelectionStrategy { + selectionType: 'unresolved'; + selection: { + name?: string; + dataset: Dataset; + }; + + private constructor(dataset: Dataset) { + this.selectionType = 'unresolved'; + this.selection = { + name: dataset.parentIntegration?.name, + dataset, + }; + } + + toDataviewSpec() { + const { name, title } = this.selection.dataset.toDataviewSpec(); + return { + id: this.toURLSelectionId(), + name, + title, + }; + } + + toURLSelectionId() { + return encodeDatasetSelection({ + selectionType: this.selectionType, + selection: { + name: this.selection.name, + dataset: this.selection.dataset.toPlain(), + }, + }); + } + + public static fromSelection(selection: UnresolvedDatasetSelectionPayload) { + const { name, dataset } = selection; + + // Attempt reconstructing the integration object + const integration = name ? { name } : undefined; + const datasetInstance = Dataset.create(dataset, integration); + + return new UnresolvedDatasetSelection(datasetInstance); + } + + public static create(dataset: Dataset) { + return new UnresolvedDatasetSelection(dataset); + } +} diff --git a/x-pack/plugins/log_explorer/common/datasets/models/dataset.ts b/x-pack/plugins/log_explorer/common/datasets/models/dataset.ts index 5a817e69a191c..a07220ff77d71 100644 --- a/x-pack/plugins/log_explorer/common/datasets/models/dataset.ts +++ b/x-pack/plugins/log_explorer/common/datasets/models/dataset.ts @@ -11,7 +11,7 @@ import { IndexPattern } from '@kbn/io-ts-utils'; import { TIMESTAMP_FIELD } from '../../constants'; import { DatasetId, DatasetType, IntegrationType } from '../types'; -type IntegrationBase = Pick; +type IntegrationBase = Partial>; interface DatasetDeps extends DatasetType { iconType?: IconType; @@ -31,7 +31,7 @@ export class Dataset { this.title = dataset.title ?? dataset.name; this.parentIntegration = parentIntegration && { name: parentIntegration.name, - title: parentIntegration.title, + title: parentIntegration.title ?? parentIntegration.name, icons: parentIntegration.icons, version: parentIntegration.version, }; diff --git a/x-pack/plugins/log_explorer/common/index.ts b/x-pack/plugins/log_explorer/common/index.ts new file mode 100644 index 0000000000000..989f981879ac0 --- /dev/null +++ b/x-pack/plugins/log_explorer/common/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { AllDatasetSelection, UnresolvedDatasetSelection } from './dataset_selection'; diff --git a/x-pack/plugins/log_explorer/common/locators/index.ts b/x-pack/plugins/log_explorer/common/locators/index.ts new file mode 100644 index 0000000000000..ebcd27baa5543 --- /dev/null +++ b/x-pack/plugins/log_explorer/common/locators/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LogExplorerLocator } from './log_explorer/log_explorer_locator'; + +export * from './log_explorer'; + +export interface LogExplorerLocators { + logExplorerLocator: LogExplorerLocator; +} diff --git a/x-pack/plugins/log_explorer/common/locators/log_explorer/index.ts b/x-pack/plugins/log_explorer/common/locators/log_explorer/index.ts new file mode 100644 index 0000000000000..738ad7cea2f39 --- /dev/null +++ b/x-pack/plugins/log_explorer/common/locators/log_explorer/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './log_explorer_locator'; diff --git a/x-pack/plugins/log_explorer/common/locators/log_explorer/log_explorer_locator.test.ts b/x-pack/plugins/log_explorer/common/locators/log_explorer/log_explorer_locator.test.ts new file mode 100644 index 0000000000000..2785def1f2927 --- /dev/null +++ b/x-pack/plugins/log_explorer/common/locators/log_explorer/log_explorer_locator.test.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { sharePluginMock } from '@kbn/share-plugin/public/mocks'; +import { LogExplorerLocatorDefinition } from './log_explorer_locator'; +import { LogExplorerLocatorDependencies } from './types'; + +const setup = async () => { + const discoverSetupContract: LogExplorerLocatorDependencies = { + discover: { + locator: sharePluginMock.createLocator(), + }, + }; + const logExplorerLocator = new LogExplorerLocatorDefinition(discoverSetupContract); + + return { + logExplorerLocator, + discoverGetLocation: discoverSetupContract.discover.locator?.getLocation, + }; +}; + +describe('Logs Explorer Locators', () => { + const dataset = 'logs-*-*'; + it('should call discover locator with empty params', async () => { + const { logExplorerLocator, discoverGetLocation } = await setup(); + await logExplorerLocator.getLocation({}); + + expect(discoverGetLocation).toBeCalledWith({}); + }); + + it('should call discover locator with correct dataViewId if dataset is sent', async () => { + const { logExplorerLocator, discoverGetLocation } = await setup(); + await logExplorerLocator.getLocation({ dataset }); + + expect(discoverGetLocation).toBeCalledWith( + expect.objectContaining({ + dataViewId: 'logs-*-*', + }) + ); + }); + + it('should call discover locator with correct dataViewSpec if dataset is sent', async () => { + const { logExplorerLocator, discoverGetLocation } = await setup(); + await logExplorerLocator.getLocation({ dataset }); + + expect(discoverGetLocation).toBeCalledWith( + expect.objectContaining({ + dataViewId: 'logs-*-*', + dataViewSpec: { + id: 'logs-*-*', + title: 'logs-*-*', + }, + }) + ); + }); +}); diff --git a/x-pack/plugins/log_explorer/common/locators/log_explorer/log_explorer_locator.ts b/x-pack/plugins/log_explorer/common/locators/log_explorer/log_explorer_locator.ts new file mode 100644 index 0000000000000..f0265e088a206 --- /dev/null +++ b/x-pack/plugins/log_explorer/common/locators/log_explorer/log_explorer_locator.ts @@ -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 type { DataViewSpec } from '@kbn/data-views-plugin/common'; +import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public'; +import { + LogExplorerLocatorParams, + LOG_EXPLORER_LOCATOR_ID, +} from '@kbn/deeplinks-observability/locators'; +import { LogExplorerLocatorDependencies } from './types'; + +export type LogExplorerLocator = LocatorPublic; + +export class LogExplorerLocatorDefinition implements LocatorDefinition { + public readonly id = LOG_EXPLORER_LOCATOR_ID; + + constructor(protected readonly deps: LogExplorerLocatorDependencies) {} + + public readonly getLocation = (params: LogExplorerLocatorParams) => { + const { dataset } = params; + const dataViewSpec: DataViewSpec | undefined = dataset + ? { + id: dataset, + title: dataset, + } + : undefined; + + return this.deps.discover.locator?.getLocation({ + ...params, + dataViewId: dataset, + dataViewSpec, + })!; + }; +} diff --git a/x-pack/plugins/log_explorer/common/locators/log_explorer/types.ts b/x-pack/plugins/log_explorer/common/locators/log_explorer/types.ts new file mode 100644 index 0000000000000..f35a74088b60d --- /dev/null +++ b/x-pack/plugins/log_explorer/common/locators/log_explorer/types.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DiscoverSetup } from '@kbn/discover-plugin/public'; + +export interface LogExplorerLocatorDependencies { + discover: DiscoverSetup; +} diff --git a/x-pack/plugins/log_explorer/kibana.jsonc b/x-pack/plugins/log_explorer/kibana.jsonc index 612bd34859b98..969fa50c87a8c 100644 --- a/x-pack/plugins/log_explorer/kibana.jsonc +++ b/x-pack/plugins/log_explorer/kibana.jsonc @@ -19,9 +19,13 @@ "kibanaReact", "kibanaUtils", "controls", - "embeddable" + "embeddable", + "share", ], "optionalPlugins": [], - "requiredBundles": [] + "requiredBundles": [], + "extraPublicDirs": [ + "common", + ] } } diff --git a/x-pack/plugins/log_explorer/public/components/dataset_selector/dataset_selector.stories.tsx b/x-pack/plugins/log_explorer/public/components/dataset_selector/dataset_selector.stories.tsx index c1549bc899ab4..10bc958c8f2ce 100644 --- a/x-pack/plugins/log_explorer/public/components/dataset_selector/dataset_selector.stories.tsx +++ b/x-pack/plugins/log_explorer/public/components/dataset_selector/dataset_selector.stories.tsx @@ -11,14 +11,14 @@ import React, { useState } from 'react'; import { I18nProvider } from '@kbn/i18n-react'; import type { Meta, Story } from '@storybook/react'; import { IndexPattern } from '@kbn/io-ts-utils'; -import { Dataset, Integration } from '../../../common/datasets'; -import { DatasetSelector } from './dataset_selector'; -import { DatasetSelectorProps, DatasetsSelectorSearchParams } from './types'; import { AllDatasetSelection, DatasetSelection, DatasetSelectionChange, -} from '../../utils/dataset_selection'; +} from '../../../common/dataset_selection'; +import { Dataset, Integration } from '../../../common/datasets'; +import { DatasetSelector } from './dataset_selector'; +import { DatasetSelectorProps, DatasetsSelectorSearchParams } from './types'; const meta: Meta = { component: DatasetSelector, diff --git a/x-pack/plugins/log_explorer/public/components/dataset_selector/state_machine/defaults.ts b/x-pack/plugins/log_explorer/public/components/dataset_selector/state_machine/defaults.ts index a818c6645bda7..142908a782436 100644 --- a/x-pack/plugins/log_explorer/public/components/dataset_selector/state_machine/defaults.ts +++ b/x-pack/plugins/log_explorer/public/components/dataset_selector/state_machine/defaults.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { AllDatasetSelection } from '../../../utils/dataset_selection'; +import { AllDatasetSelection } from '../../../../common/dataset_selection'; import { HashedCache } from '../../../../common/hashed_cache'; import { INTEGRATION_PANEL_ID } from '../constants'; import { DatasetsSelectorSearchParams } from '../types'; diff --git a/x-pack/plugins/log_explorer/public/components/dataset_selector/state_machine/state_machine.ts b/x-pack/plugins/log_explorer/public/components/dataset_selector/state_machine/state_machine.ts index aea09e7c6f2e7..1361629c819d8 100644 --- a/x-pack/plugins/log_explorer/public/components/dataset_selector/state_machine/state_machine.ts +++ b/x-pack/plugins/log_explorer/public/components/dataset_selector/state_machine/state_machine.ts @@ -6,7 +6,7 @@ */ import { actions, assign, createMachine, raise } from 'xstate'; -import { AllDatasetSelection, SingleDatasetSelection } from '../../../utils/dataset_selection'; +import { AllDatasetSelection, SingleDatasetSelection } from '../../../../common/dataset_selection'; import { UNMANAGED_STREAMS_PANEL_ID } from '../constants'; import { defaultSearch, DEFAULT_CONTEXT } from './defaults'; import { diff --git a/x-pack/plugins/log_explorer/public/components/dataset_selector/state_machine/types.ts b/x-pack/plugins/log_explorer/public/components/dataset_selector/state_machine/types.ts index 17e526aea87f9..e434692a658b2 100644 --- a/x-pack/plugins/log_explorer/public/components/dataset_selector/state_machine/types.ts +++ b/x-pack/plugins/log_explorer/public/components/dataset_selector/state_machine/types.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { DatasetSelection, DatasetSelectionChange } from '../../../utils/dataset_selection'; +import { DatasetSelection, DatasetSelectionChange } from '../../../../common/dataset_selection'; import { Dataset } from '../../../../common/datasets/models/dataset'; import { ReloadDatasets, SearchDatasets } from '../../../hooks/use_datasets'; import { diff --git a/x-pack/plugins/log_explorer/public/components/dataset_selector/sub_components/datasets_popover.tsx b/x-pack/plugins/log_explorer/public/components/dataset_selector/sub_components/datasets_popover.tsx index 7428ffbd47612..652cbafdc35d9 100644 --- a/x-pack/plugins/log_explorer/public/components/dataset_selector/sub_components/datasets_popover.tsx +++ b/x-pack/plugins/log_explorer/public/components/dataset_selector/sub_components/datasets_popover.tsx @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import styled from '@emotion/styled'; import { PackageIcon } from '@kbn/fleet-plugin/public'; -import { DatasetSelection } from '../../../utils/dataset_selection'; +import { DatasetSelection } from '../../../../common/dataset_selection'; import { DATA_VIEW_POPOVER_CONTENT_WIDTH, POPOVER_ID, selectDatasetLabel } from '../constants'; import { getPopoverButtonStyles } from '../utils'; diff --git a/x-pack/plugins/log_explorer/public/components/dataset_selector/types.ts b/x-pack/plugins/log_explorer/public/components/dataset_selector/types.ts index 50cbcf6c1c5ba..9aed76eb602fa 100644 --- a/x-pack/plugins/log_explorer/public/components/dataset_selector/types.ts +++ b/x-pack/plugins/log_explorer/public/components/dataset_selector/types.ts @@ -6,6 +6,7 @@ */ import { EuiContextMenuPanelId } from '@elastic/eui/src/components/context_menu/context_menu'; +import type { DatasetSelection, DatasetSelectionChange } from '../../../common/dataset_selection'; import { SortOrder } from '../../../common/latest'; import { Dataset, Integration, IntegrationId } from '../../../common/datasets'; import { LoadDatasets, ReloadDatasets, SearchDatasets } from '../../hooks/use_datasets'; @@ -15,7 +16,6 @@ import { SearchIntegrations, } from '../../hooks/use_integrations'; import { INTEGRATION_PANEL_ID, UNMANAGED_STREAMS_PANEL_ID } from './constants'; -import type { DatasetSelection, DatasetSelectionChange } from '../../utils/dataset_selection'; export interface DatasetSelectorProps { /* The generic data stream list */ diff --git a/x-pack/plugins/log_explorer/public/components/log_explorer/log_explorer.tsx b/x-pack/plugins/log_explorer/public/components/log_explorer/log_explorer.tsx index 6a945afa19ab2..336c3782438c1 100644 --- a/x-pack/plugins/log_explorer/public/components/log_explorer/log_explorer.tsx +++ b/x-pack/plugins/log_explorer/public/components/log_explorer/log_explorer.tsx @@ -8,8 +8,9 @@ import React, { useMemo } from 'react'; import { ScopedHistory } from '@kbn/core-application-browser'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { DiscoverAppState, DiscoverStart } from '@kbn/discover-plugin/public'; +import { DiscoverStart } from '@kbn/discover-plugin/public'; import type { BehaviorSubject } from 'rxjs'; +import { DiscoverAppState } from '@kbn/discover-plugin/public/application/main/services/discover_app_state_container'; import { createLogExplorerProfileCustomizations, CreateLogExplorerProfileCustomizationsDeps, diff --git a/x-pack/plugins/log_explorer/public/customizations/log_explorer_profile.tsx b/x-pack/plugins/log_explorer/public/customizations/log_explorer_profile.tsx index 7e80fdd54f22f..ee8f7ffbc9779 100644 --- a/x-pack/plugins/log_explorer/public/customizations/log_explorer_profile.tsx +++ b/x-pack/plugins/log_explorer/public/customizations/log_explorer_profile.tsx @@ -33,11 +33,12 @@ export const createLogExplorerProfileCustomizations = const [{ DatasetsService }, { initializeLogExplorerProfileStateService, waitForState }] = await Promise.all([datasetServiceModuleLoadable, logExplorerMachineModuleLoadable]); - const datasetsService = new DatasetsService().start({ + const datasetsClient = new DatasetsService().start({ http: core.http, - }); + }).client; const logExplorerProfileStateService = initializeLogExplorerProfileStateService({ + datasetsClient, stateContainer, toasts: core.notifications.toasts, }); @@ -70,7 +71,7 @@ export const createLogExplorerProfileCustomizations = id: 'search_bar', CustomDataViewPicker: () => ( ), diff --git a/x-pack/plugins/log_explorer/public/hooks/use_dataset_selection.ts b/x-pack/plugins/log_explorer/public/hooks/use_dataset_selection.ts index 05fbc2bc81506..6bff4055f3635 100644 --- a/x-pack/plugins/log_explorer/public/hooks/use_dataset_selection.ts +++ b/x-pack/plugins/log_explorer/public/hooks/use_dataset_selection.ts @@ -7,8 +7,8 @@ import { useSelector } from '@xstate/react'; import { useCallback } from 'react'; +import { DatasetSelectionChange } from '../../common/dataset_selection'; import { LogExplorerProfileStateService } from '../state_machines/log_explorer_profile'; -import { DatasetSelectionChange } from '../utils/dataset_selection'; export const useDatasetSelection = ( logExplorerProfileStateService: LogExplorerProfileStateService diff --git a/x-pack/plugins/log_explorer/public/plugin.ts b/x-pack/plugins/log_explorer/public/plugin.ts index 5807e8260f326..57df6b8e1ef0e 100644 --- a/x-pack/plugins/log_explorer/public/plugin.ts +++ b/x-pack/plugins/log_explorer/public/plugin.ts @@ -6,6 +6,7 @@ */ import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; +import { LogExplorerLocatorDefinition, LogExplorerLocators } from '../common/locators'; import { createLogExplorer } from './components/log_explorer'; import { LogExplorerPluginSetup, @@ -15,9 +16,28 @@ import { } from './types'; export class LogExplorerPlugin implements Plugin { + private locators?: LogExplorerLocators; + constructor(context: PluginInitializerContext) {} - public setup(core: CoreSetup, plugins: LogExplorerSetupDeps) {} + public setup(core: CoreSetup, plugins: LogExplorerSetupDeps) { + const { share, discover } = plugins; + + // Register Locators + const logExplorerLocator = share.url.locators.create( + new LogExplorerLocatorDefinition({ + discover, + }) + ); + + this.locators = { + logExplorerLocator, + }; + + return { + locators: this.locators, + }; + } public start(core: CoreStart, plugins: LogExplorerStartDeps) { const { data, discover } = plugins; diff --git a/x-pack/plugins/log_explorer/public/services/datasets/datasets_client.ts b/x-pack/plugins/log_explorer/public/services/datasets/datasets_client.ts index 6afa8016781ab..5e746e6544e4a 100644 --- a/x-pack/plugins/log_explorer/public/services/datasets/datasets_client.ts +++ b/x-pack/plugins/log_explorer/public/services/datasets/datasets_client.ts @@ -6,6 +6,7 @@ */ import { HttpStart } from '@kbn/core/public'; + import { Dataset, Integration } from '../../../common/datasets'; import { DATASETS_URL, diff --git a/x-pack/plugins/log_explorer/public/services/datasets/datasets_service.ts b/x-pack/plugins/log_explorer/public/services/datasets/datasets_service.ts index 7e6c016f94ab9..fd8222dcaec28 100644 --- a/x-pack/plugins/log_explorer/public/services/datasets/datasets_service.ts +++ b/x-pack/plugins/log_explorer/public/services/datasets/datasets_service.ts @@ -6,7 +6,7 @@ */ import { DatasetsClient } from './datasets_client'; -import { DatasetsServiceStartDeps, DatasetsServiceSetup, DatasetsServiceStart } from './types'; +import { DatasetsServiceSetup, DatasetsServiceStart, DatasetsServiceStartDeps } from './types'; export class DatasetsService { constructor() {} diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/defaults.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/defaults.ts index fe19f34d7565c..2a058e2dde017 100644 --- a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/defaults.ts +++ b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/defaults.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { AllDatasetSelection } from '../../../utils/dataset_selection'; +import { AllDatasetSelection } from '../../../../common/dataset_selection'; import { ControlPanels, DefaultLogExplorerProfileState } from './types'; export const DEFAULT_CONTEXT: DefaultLogExplorerProfileState = { diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/selection_service.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/selection_service.ts new file mode 100644 index 0000000000000..6de3d24025802 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/selection_service.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { InvokeCreator } from 'xstate'; +import { Dataset } from '../../../../common/datasets'; +import { SingleDatasetSelection } from '../../../../common/dataset_selection'; +import { IDatasetsClient } from '../../../services/datasets'; +import { LogExplorerProfileContext, LogExplorerProfileEvent } from './types'; + +interface LogExplorerProfileUrlStateDependencies { + datasetsClient: IDatasetsClient; +} + +export const validateSelection = + ({ + datasetsClient, + }: LogExplorerProfileUrlStateDependencies): InvokeCreator< + LogExplorerProfileContext, + LogExplorerProfileEvent + > => + (context) => + async (send) => { + const unresolvedIntegrationName = + context.datasetSelection.selection.dataset.parentIntegration?.name; + const unresolvedDatasetName = context.datasetSelection.selection.dataset.name; + + if (context.datasetSelection.selectionType !== 'unresolved' || !unresolvedIntegrationName) { + return send('LISTEN_TO_CHANGES'); + } + + try { + const { items } = await datasetsClient.findIntegrations({ + nameQuery: unresolvedIntegrationName, + }); + + // There should only be one matching integration with the given name + // If no integration matches, skip the update and listen for user changes + const installedIntegration = items[0]; + if (!installedIntegration) { + return send('LISTEN_TO_CHANGES'); + } + + // If no dataset matches the passed name for the retrieved integration, + // skip the update and listen for user changes + const targetDataset = installedIntegration.datasets.find( + (d) => d.name === unresolvedDatasetName + ); + if (!targetDataset) { + return send('LISTEN_TO_CHANGES'); + } + + const dataset = Dataset.create(targetDataset, installedIntegration); + const datasetSelection = SingleDatasetSelection.create(dataset); + + send({ type: 'UPDATE_DATASET_SELECTION', data: datasetSelection }); + } catch (error) { + return send('DATASET_SELECTION_RESTORE_FAILURE'); + } + }; diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/state_machine.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/state_machine.ts index 617c66b10140f..4fa1673b5879d 100644 --- a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/state_machine.ts +++ b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/state_machine.ts @@ -8,8 +8,10 @@ import { IToasts } from '@kbn/core/public'; import { DiscoverStateContainer } from '@kbn/discover-plugin/public'; import { actions, createMachine, interpret, InterpreterFrom, raise } from 'xstate'; -import { isDatasetSelection } from '../../../utils/dataset_selection'; +import { IDatasetsClient } from '../../../services/datasets'; +import { isDatasetSelection } from '../../../../common/dataset_selection'; import { createAndSetDataView } from './data_view_service'; +import { validateSelection } from './selection_service'; import { DEFAULT_CONTEXT } from './defaults'; import { createCreateDataViewFailedNotifier, @@ -33,7 +35,7 @@ import { export const createPureLogExplorerProfileStateMachine = ( initialContext: LogExplorerProfileContext ) => - /** @xstate-layout N4IgpgJg5mDOIC5QBkD2UCiAPADgG1QCcxCAFQ1AMwEs8wA6AVwDtrWAXagQz2oC9IAYgDaABgC6iUDlSxqnVMykgsiAIwAOAMz0NorVoDsAVi0a1x0QE4ATADYANCACe6zfVMbjeu-rWi7DQAWAF8QpzRMXAJiMgoaOno2eW5ePjYoADEKAFsAVUI8QQhFBjYAN1QAawZI7HwiEnIqWjKOVP4M7NR8woQK1ABjLgVmMXFx5Rk5UeVVBEsNekMrQK0bYKC7fztHF3VDII8vDTXDNUNtu2MwiPR6mKb41qT2nk7mLNyCopIKQno+BGlCIOXodWijTiLUSyU473Sn26vTw-WYlWGo3GkyQIGmKUUc0Qi2Wq20GyCWx2e1cCHM9CCXmuekMokO2y0txAEIasWaCTaKQRGQAIiMuAA1ahgADuxVKr0qNXB90hfOesLeaVF4qlsrRGJG1EU2IkU1kBKUuPmdhsTlpxjUR00Nis+g0NjUVis3i5PMe0IFryF2s+YvYkulcr+REBeGBoJVUV5Txhgvhoag4cj+oGmONYwkOOkFtm1uJdv2CDUFzsHkOjpshhsWguahsftVKcDLzhHURUAAwop2BQ8KQuMwwHhYPKp4rqrUuwH+b2tR8hyOxxOpzODUMjSai2bcfiy6B5qZjAyjIzDOYbNYm-bEFYDPRrJZNJpvI+NJ3kxXDV037DJh2YUdUHHSdp1nGMASBdgQUIMF-ShVdNRDDdwMg6Dd1gfd8yPCYTxLGYCyJBYtGvIJbw0e92yfQwXwQIJvXoN9jCsIIAiMV1XQAh50OA4MM34SB6AgcVYDAdgAGVpzAQZRiSCA6EEPJSBFABBAAVDAAH0dN07S5IwXSDLM5AMEHXSAEkAHkADlizxUsKPLatrCOZt720LRRFdIIbErWlmzUZZjEMaLNHMbRzkEtVUyDPsEQkqSIxk+TFOUgtVPU4zTPMyyMGs2zHKcgyACUMDk3SHJqgzMm0uzkDyGrXLPDyL3UbzlibDR-MC7iQpY8xDF0LjqNsIx6LURLuwwkC0ogSTpNkhS6FyxQmBwDKdQjPU5RKecBmVND1TTUT+3S9bsq2lTGD2o0w11KNCMPQsSMkU93MJTz21Megm18QJLnbRkaVfNQdFWURggMKKtnvBagKu1K0luzKNpyx7ns4V7DqjQR4LjBMUKTITLpS9cBFWjKuCyzalLx-bCZzGUPqxY8frIy1KPbdxLC0WxaLY8WghY20lmuBtRB41tHw7cJuWXYT0dpiTBi3KCAHEKCe2AmFYTWIEEOynLs+ztOQOyAC1DMHZzdKqhzkAM3XXc0gztNIOzOr+q0eurQGPEZCwBqbIJLhY-xWQ4rRGUpa5PXWVH1ZprC6fobWILHfXUEN-KwA0rS9IwEUjL07SDIlOyMAAdQD8j-uDmHLg4wLG3sN0rCdWPqKWGtKW9Aw+9tdPqbXLOtZ1vAC6L6g1JLzTjMd53Xfd0htKc0q5Ob-mAYMHRtiCYxAlOMwgn7qsazsCb9DsWjH3htRdkn5Lp7E7Pc9whecCNk9Nmm485QR3LBOcbQlRLkAhnL+N1Vq-3zgbABu1gE4W3DBPceZPqml5m5FuQcVBuBsNeYw4djCR0ODHW+noJqUN8GfeGoh9AGA-j2TC39Z6gPnigwB+MwJz3ATOEmhB-hkyQomC6n9OEIJznPf+-D0FCKwQRHB3NvrmkIQLUO5DzCUPvFHGhtIYb+HoLaOwWhdjGG8CYfQYQVbMFQBAOAyhpEcLAFow+wcAC0UMEA+OvN6YJITQmchVu4paxsMbiQgF488xDWKhTcPQow3Fz4rBhi2dhUSYkDmRD8eJ3VEkDV0IcKw4VAotkCCxbiH5r7aECI6YI2hQgRLVlPWRwp2ZHSKa3RJKwrAeD7jWfwidgqrFqUceijpWQ-m-PfHJIk8mCJ4cI+Av1tGeXsDLXwFhyGGGoosSWVZGRDMCByYKVwmxWCWRrGecTNneIGZSegFg7H8SbCYfxZ8T6nBhlct+Ny7B3Mzlw+md1mbbSIV1fp8wVg2DeVFSwnzornxYuQiKbYEUGBbIcUF8CVprWxvdFmeUl50D6UQ+YwRRBIo+bYL56KqwpzeTWUhpg-zUXCXcWBnTlqYwhSSqFrMXpZjerKKlAsthDLfNsUWdgKnXysCxQ5EVtjeFMOPDJbTeVUxkQK2J8ieGKKlZ5BF9KUWMrRf4qw2gGRsU0MyGGXhbntL5Qa66RKkF6z4dE02Zrg4+iWIcRk6xvQsI0G+WOfUbA8W0BcEaFhdWqw9R4r1grjV-z9RSzxTyEnzDfmY9JrZmyWPPtHWOnprzaFMEClOrSCVdMzT63hhdUFALFRgsBqjA2JNpZa4aIUbWxzfkMzQjIPTNjjefZWYQgA */ + /** @xstate-layout N4IgpgJg5mDOIC5QBkD2UCiAPADgG1QCcxCAFQ1AMwEs8wA6AVwDtrWAXagQz2oC9IAYgDaABgC6iUDlSxqnVMykgsiAIwAOAMz0NagEwBWUdoBsWgOxaAnEYA0IAJ7rt9UxesWD+2xf2WAXwCHNExcAmIyCho6ejZ5bl4+NigAMQoAWwBVQjxBCEUGNgA3VABrBlDsfCIScipaIo5E-hT01GzchBLUAGMuBWYxcWHlGTlB5VUEYw1dfQ1RQ30LC0NrbX0HZwQ1CwAWemtDLTV991FTPWWtIJD0aoi66Ma45p5W5jTMnLySCkI9HwA0oRAy9Cq4VqUQasXinA+yS+7U6eG6zFK-UGw1GSBA4wSiimiFm80Wy1W60220QenoxlEjP2J32alEmmsdxAkJqkXqMSaCURKQAIgMuAA1ahgADu+UKb1KFQhDyhfJecPeSVF4qlsvRmIG1EUOIkY1khKUeOmPkM9D8pkMFm0bMpFhpM3Obi0tqdxk0Fi5PKeMIFbyF2q+YvYkulcv+RCBeBBYJVYV5z1hgoRkag0dj+p6WONQwkuOkFsm1sQtvt+kdztOojdHquonoah91h9WkWS1MQdVGdDr3hLSRUAAwop2BQ8KQuMwwHhYPKl4rypUhyH+aOtZ8pzO5wulyuDX0jSay2a8QSq6BphZTNZ6Po1O+jMy++YPScLEdLi0UwDG7Z9jkHdMdw1bNxxSadmFnVB50XZdVwTQFgXYUFCHBYNoV3TUIwPeDEOQ09YHPYsrxGG8KwmEtiQQJ8XzfD9DC-RkfycRB9msdt9kuBYNA0Dxlg0Adgm5bd8Og8McwPABlGN2DAEiuDYEg1yaJUt0gmSszk2CviUgZVJndSl0ISjL1LGjJFvSsGOrBAtC0Q4tFEc5DHE-YPI0XjTA9NRDCdI4rhEvR9HrEKIMefSwzHYVjOUsyEIszT0KTFMcLTOL1QMxLcxMlS1I0qyixs017Loy1GNc9zPMdHy-ICoLDDZehzg0Wx3z4vtbkkvD8oS-cBAgegIHFWAwHYBTlzAXpBnoYoPkmzhjPmxaS0EZAAEkFIAFQwAA5AB9A6AHlTsnAAJABBY6AHEMAU8t8UcolnOE-9fLWZ9NEsAwgtc9ttG6p0fQsQCNFitVMxGoixomqaZrmugtsUZbVqNDb0cGQQslIEU7qO07iYOu6FIwA7Tqp5AMEnA7dou463rvJyH1pETOssQx-u0Lwtm43Yzjtbz1jZGwDg2Ab7j04a90RyBkZjabZs2paVt4NaUjRhb8fJynqdpjB6cZ5mzoAJRey7rdO1I7t25AsmttmPqtTmEG+nm-usAHBba9rOp67trGffz-Nh4cCJgxFlbWrg1b1jHmDiCA6AJomSYwMmSaNmm6YZpmWbd+jPs9s5PFff19jWZsDH2IWdk7MP7UdUx-GbdxTGbKOoIK0b45R9W8ZLNOM8NqmC9NouLdO63Douu2Hadl2MFL2rnMr-8jHZWvjEFxvgcZehTn0fZIr48WYcG6SFcI+SkYTpONbHxgcB1qNdTjLSN2VIb4aK0fkPVWqNX6Y3fp-PM39CwYgvNia81V3plw9ioGsbI1CvnfJ5SwtdTB4OBmoF83k+KmHcCcNyN85Z5UAQ-ccIDE5gNHhAj+ONoExj1PGQgAIspYVTAAkcdC47jWfkw-Wb9WHrXYQWGU1kEF2XNCgxib52RYLZL9PBBDhadncPQEwfF-BXGWKIBYfd4pAPoSI4eyclqQLYcVVKMYyq-x6P-O+tDY5JAYS-Zhqc7FSIcaVSyciSxVUUZvT2KjMGsRwQcJ8Wjm7sUwdYEOlgnxOk5LfeWHjDLCJVowke4iWFQMCeZZxmVMLYVwu4wRnj+DeLESnJgkjdYpSCSQEJ1EN73jQQgFR+h6TnGsL5TQQlz5BWCnMEwEsTGeSipk6hcNam5K8eNXoR4kKPQoO-WATBWCDwgIIXax1dpMzuntAAWjnScLMDqWwusgU6j17mE1OndUgu1ukc16bYQ4ngyHuDDs2ICgVtFrB+syICfse5QxsGY++dSkbrIQnOLZqAdnjzAIIQ2p0JS7QwAAdVOoTcmGARRfPLr098T5T6+T4s6RYWhvIen0J5O0TLgpkPWN3FY8KcmFXqWsjZeA0UYuoOnLFJLs7XVufcx5pAHqm1erRZBESqWdh0EsWu-kiG1zWEFDQJxdAbHaqIYZVxnQSUWdHWSAqkXCtFTgXZ-i4LCpPKhFxcC3HZOWXa5WyLSKOudS0r4JFjwoTPBVeRFLUHTF+UcJ8ZCPAwpBUFPi7kNjnGlm+c1fLfUHPoAG1F2ynXNKgWGpC7qVyCAqcmPhOUBExxWYKwtDqS3BvLW6iNFEo2hMQeEnpcbz4JoBcm4FZC01V0NWoHuYd8F9SCJJZgqAIBwGUI26CA7vnTAALSgp2Duu0KTj0npPVFPNTaWB+ogFuyl0wj7aI0AMwFBxOx8ydG+C9trRptB+LkW9saazOmNf5aZ+CfTMjai+PYngjCdgOJ5bQX6B6Ix1BwuMAHGLuFbPg0+6xTV6EuMyK1UkfVNrta6lFlbu2YecsYAZjciGms8qyVlhhfxRQ7F2KW58Fh+2QwjR+rTTLtMILRz22HtHtRYhyN87gpZMoExY4R4nensR0E6PQz5WTsm+h6Tw7ZvIwuZBSZ8stSM0PzUrKxoDCkp1U9MCW9JnQzuGRgvT2ilhTMNSk8+axOz7CU0I1Z+SfFFNTlrcV9jwGoPZnexA5hiEue0+5kSx9-xgxScJXiWqFkWaWeRgtoi7NLXFXQBz6gjDPq8H7KkAkbDpd0DYfs6wTGdiC4ihpJWJFQPzJwiruwRIMf8rxXyzZmSdmPi+VyfFJawfEoYDrzan7WJi2W+xbSymWQG4Y0wnURvNYg0yiZ-ksHn20LxXsIUSMbpQ8AoVVGRXtoG05zTrmdOaDS9oshL5hkUL9D3KKVD8s2ru5Y1tj2g17OvQNtYcwZ2smy+Qp8WggrOnbCHYCLmNX6CW9eiHgb22YoG2cDqrJWR+Dwf6DQaaTG6GluJNy13nR44LUWzZROXWhq7eRAbiXnNabc7pr7zdDV7cQ+1CbthnyBkXUAA */ createMachine( { context: initialContext, @@ -96,8 +98,26 @@ export const createPureLogExplorerProfileStateMachine = ( type: 'parallel', states: { datasetSelection: { - initial: 'idle', + initial: 'validatingSelection', states: { + validatingSelection: { + invoke: { + src: 'validateSelection', + }, + on: { + LISTEN_TO_CHANGES: { + target: 'idle', + }, + UPDATE_DATASET_SELECTION: { + target: 'updatingDataView', + actions: ['storeDatasetSelection'], + }, + DATASET_SELECTION_RESTORE_FAILURE: { + target: 'updatingDataView', + actions: ['notifyDatasetSelectionRestoreFailed'], + }, + }, + }, idle: { invoke: { src: 'listenUrlChange', @@ -218,12 +238,14 @@ export const createPureLogExplorerProfileStateMachine = ( export interface LogExplorerProfileStateMachineDependencies { initialContext?: LogExplorerProfileContext; + datasetsClient: IDatasetsClient; stateContainer: DiscoverStateContainer; toasts: IToasts; } export const createLogExplorerProfileStateMachine = ({ initialContext = DEFAULT_CONTEXT, + datasetsClient, stateContainer, toasts, }: LogExplorerProfileStateMachineDependencies) => @@ -240,6 +262,7 @@ export const createLogExplorerProfileStateMachine = ({ subscribeControlGroup: subscribeControlGroup({ stateContainer }), updateControlPanels: updateControlPanels({ stateContainer }), updateStateContainer: updateStateContainer({ stateContainer }), + validateSelection: validateSelection({ datasetsClient }), }, }); diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/types.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/types.ts index 621f1c8f56b76..fe4323fac0cd4 100644 --- a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/types.ts +++ b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/types.ts @@ -8,7 +8,7 @@ import * as rt from 'io-ts'; import { ControlGroupAPI } from '@kbn/controls-plugin/public'; import { DoneInvokeEvent } from 'xstate'; -import type { DatasetEncodingError, DatasetSelection } from '../../../utils/dataset_selection'; +import type { DatasetEncodingError, DatasetSelection } from '../../../../common/dataset_selection'; export interface WithDatasetSelection { datasetSelection: DatasetSelection; @@ -49,6 +49,10 @@ export type LogExplorerProfileTypeState = value: 'initialized'; context: WithDatasetSelection & WithControlPanels; } + | { + value: 'initialized.datasetSelection.validatingSelection'; + context: WithDatasetSelection & WithControlPanels; + } | { value: 'initialized.datasetSelection.idle'; context: WithDatasetSelection & WithControlPanels; @@ -79,6 +83,9 @@ export type LogExplorerProfileContext = LogExplorerProfileTypeState['context']; export type LogExplorerProfileStateValue = LogExplorerProfileTypeState['value']; export type LogExplorerProfileEvent = + | { + type: 'LISTEN_TO_CHANGES'; + } | { type: 'UPDATE_DATASET_SELECTION'; data: DatasetSelection; diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/url_state_storage_service.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/url_state_storage_service.ts index 1864a4a558dac..5fd46dec9bf0b 100644 --- a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/url_state_storage_service.ts +++ b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/url_state_storage_service.ts @@ -10,16 +10,16 @@ import deepEqual from 'fast-deep-equal'; import { DiscoverAppState, DiscoverStateContainer } from '@kbn/discover-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; import { ROWS_HEIGHT_OPTIONS } from '@kbn/unified-data-table'; -import { - DATA_GRID_COLUMNS_PREFERENCES, - DATA_GRID_DEFAULT_COLUMNS, -} from '../../../../common/constants'; import { AllDatasetSelection, decodeDatasetSelectionId, hydrateDatasetSelection, isDatasetSelection, -} from '../../../utils/dataset_selection'; +} from '../../../../common/dataset_selection'; +import { + DATA_GRID_COLUMNS_PREFERENCES, + DATA_GRID_DEFAULT_COLUMNS, +} from '../../../../common/constants'; import { ControlPanelRT, ControlPanels, diff --git a/x-pack/plugins/log_explorer/public/types.ts b/x-pack/plugins/log_explorer/public/types.ts index d0b488950fee4..07a345bc661d2 100644 --- a/x-pack/plugins/log_explorer/public/types.ts +++ b/x-pack/plugins/log_explorer/public/types.ts @@ -5,17 +5,23 @@ * 2.0. */ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import type { DiscoverStart } from '@kbn/discover-plugin/public'; +import type { DiscoverSetup, DiscoverStart } from '@kbn/discover-plugin/public'; +import { SharePluginSetup } from '@kbn/share-plugin/public'; import type { ComponentType } from 'react'; +import { LogExplorerLocators } from '../common/locators'; import type { LogExplorerProps } from './components/log_explorer'; -export type LogExplorerPluginSetup = void; +export interface LogExplorerPluginSetup { + locators: LogExplorerLocators; +} export interface LogExplorerPluginStart { LogExplorer: ComponentType; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface LogExplorerSetupDeps {} +export interface LogExplorerSetupDeps { + share: SharePluginSetup; + discover: DiscoverSetup; +} export interface LogExplorerStartDeps { data: DataPublicPluginStart; diff --git a/x-pack/plugins/log_explorer/tsconfig.json b/x-pack/plugins/log_explorer/tsconfig.json index 9cfb123160983..8845fdb4c3b0c 100644 --- a/x-pack/plugins/log_explorer/tsconfig.json +++ b/x-pack/plugins/log_explorer/tsconfig.json @@ -20,7 +20,9 @@ "@kbn/data-plugin", "@kbn/unified-field-list", "@kbn/core-application-browser", + "@kbn/share-plugin", "@kbn/unified-data-table", + "@kbn/deeplinks-observability" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability/public/pages/landing/landing.tsx b/x-pack/plugins/observability/public/pages/landing/landing.tsx index 9568af1a0e0b8..cbeebf0edab1e 100644 --- a/x-pack/plugins/observability/public/pages/landing/landing.tsx +++ b/x-pack/plugins/observability/public/pages/landing/landing.tsx @@ -4,8 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { DISCOVER_APP_ID } from '@kbn/deeplinks-analytics'; import React, { useEffect } from 'react'; +import { + AllDatasetsLocatorParams, + ALL_DATASETS_LOCATOR_ID, +} from '@kbn/deeplinks-observability/locators'; import { useHasData } from '../../hooks/use_has_data'; import { useKibana } from '../../utils/kibana_react'; @@ -14,6 +17,7 @@ export function LandingPage() { const { application: { navigateToUrl, navigateToApp }, http: { basePath }, + share: { url }, } = useKibana().services; useEffect(() => { @@ -23,16 +27,17 @@ export function LandingPage() { const hasLogsData = logs?.hasData; if (hasLogsData) { - navigateToApp(DISCOVER_APP_ID, { - deepLinkId: 'log-explorer', - }); + const allDataSetsLocator = + url.locators.get(ALL_DATASETS_LOCATOR_ID); + + allDataSetsLocator?.navigate({}); } else if (hasApmData) { navigateToUrl(basePath.prepend('/app/apm/services')); } else { navigateToUrl(basePath.prepend('/app/observabilityOnboarding')); } } - }, [basePath, hasDataMap, isAllRequestsComplete, navigateToApp, navigateToUrl]); + }, [basePath, hasDataMap, isAllRequestsComplete, navigateToApp, navigateToUrl, url.locators]); return <>; } diff --git a/x-pack/plugins/observability/tsconfig.json b/x-pack/plugins/observability/tsconfig.json index 35137bbddb002..53a2fd3815170 100644 --- a/x-pack/plugins/observability/tsconfig.json +++ b/x-pack/plugins/observability/tsconfig.json @@ -82,11 +82,11 @@ "@kbn/data-view-editor-plugin", "@kbn/actions-plugin", "@kbn/core-capabilities-common", - "@kbn/deeplinks-analytics", "@kbn/observability-ai-assistant-plugin", "@kbn/osquery-plugin", "@kbn/aiops-plugin", - "@kbn/content-management-plugin" + "@kbn/content-management-plugin", + "@kbn/deeplinks-observability" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/observability_log_explorer/common/index.ts b/x-pack/plugins/observability_log_explorer/common/index.ts new file mode 100644 index 0000000000000..40e3f1373687d --- /dev/null +++ b/x-pack/plugins/observability_log_explorer/common/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { SingleDatasetLocatorDefinition, AllDatasetsLocatorDefinition } from './locators'; diff --git a/x-pack/plugins/observability_log_explorer/common/locators/all_datasets/all_datasets_locator.ts b/x-pack/plugins/observability_log_explorer/common/locators/all_datasets/all_datasets_locator.ts new file mode 100644 index 0000000000000..17c8b2ae02047 --- /dev/null +++ b/x-pack/plugins/observability_log_explorer/common/locators/all_datasets/all_datasets_locator.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public'; +import { AllDatasetSelection } from '@kbn/log-explorer-plugin/common'; +import { + AllDatasetsLocatorParams, + ALL_DATASETS_LOCATOR_ID, +} from '@kbn/deeplinks-observability/locators'; +import { DatasetLocatorDependencies } from '../types'; +import { constructLocatorPath } from '../utils'; + +export type AllDatasetsLocator = LocatorPublic; + +export class AllDatasetsLocatorDefinition implements LocatorDefinition { + public readonly id = ALL_DATASETS_LOCATOR_ID; + + constructor(protected readonly deps: DatasetLocatorDependencies) {} + + public readonly getLocation = (params: AllDatasetsLocatorParams) => { + const { useHash } = this.deps; + const index = AllDatasetSelection.create().toDataviewSpec().id; + + return constructLocatorPath({ + locatorParams: params, + index, + useHash, + }); + }; +} diff --git a/x-pack/plugins/observability_log_explorer/common/locators/all_datasets/index.ts b/x-pack/plugins/observability_log_explorer/common/locators/all_datasets/index.ts new file mode 100644 index 0000000000000..078549b8593b1 --- /dev/null +++ b/x-pack/plugins/observability_log_explorer/common/locators/all_datasets/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './all_datasets_locator'; diff --git a/x-pack/plugins/observability_log_explorer/common/locators/index.ts b/x-pack/plugins/observability_log_explorer/common/locators/index.ts new file mode 100644 index 0000000000000..7571731a22221 --- /dev/null +++ b/x-pack/plugins/observability_log_explorer/common/locators/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AllDatasetsLocator } from './all_datasets'; +import { SingleDatasetLocator } from './single_dataset'; + +export * from './single_dataset'; +export * from './all_datasets'; +export * from './utils'; + +export interface ObservabilityLogExplorerLocators { + allDatasetsLocator: AllDatasetsLocator; + singleDatasetLocator: SingleDatasetLocator; +} diff --git a/x-pack/plugins/observability_log_explorer/common/locators/locators.test.ts b/x-pack/plugins/observability_log_explorer/common/locators/locators.test.ts new file mode 100644 index 0000000000000..fa4fea9baf7a8 --- /dev/null +++ b/x-pack/plugins/observability_log_explorer/common/locators/locators.test.ts @@ -0,0 +1,333 @@ +/* + * 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 { FilterStateStore } from '@kbn/es-query'; +import { getStatesFromKbnUrl } from '@kbn/kibana-utils-plugin/public'; +import { + AllDatasetsLocatorParams, + SingleDatasetLocatorParams, +} from '@kbn/deeplinks-observability/locators'; +import { OBSERVABILITY_LOG_EXPLORER_APP_ID } from '../constants'; +import { AllDatasetsLocatorDefinition } from './all_datasets/all_datasets_locator'; +import { SingleDatasetLocatorDefinition } from './single_dataset'; +import { DatasetLocatorDependencies } from './types'; + +const setup = async () => { + const dep: DatasetLocatorDependencies = { + useHash: false, + }; + const allDatasetsLocator = new AllDatasetsLocatorDefinition(dep); + const singleDatasetLocator = new SingleDatasetLocatorDefinition(dep); + + return { + allDatasetsLocator, + singleDatasetLocator, + }; +}; + +describe('Observability Logs Explorer Locators', () => { + const timeRange = { to: 'now', from: 'now-30m' }; + + describe('All Dataset Locator', () => { + it('should create a link with no state', async () => { + const { allDatasetsLocator } = await setup(); + const location = await allDatasetsLocator.getLocation({}); + + expect(location).toMatchObject({ + app: OBSERVABILITY_LOG_EXPLORER_APP_ID, + path: '/?_a=(index:BQZwpgNmDGAuCWB7AdgFQJ4AcwC4CGEEAlEA)', + state: {}, + }); + }); + + it('should allow specifiying time range', async () => { + const params: AllDatasetsLocatorParams = { + timeRange, + }; + + const { allDatasetsLocator } = await setup(); + const location = await allDatasetsLocator.getLocation(params); + + expect(location).toMatchObject({ + app: OBSERVABILITY_LOG_EXPLORER_APP_ID, + path: '/?_g=(time:(from:now-30m,to:now))&_a=(index:BQZwpgNmDGAuCWB7AdgFQJ4AcwC4CGEEAlEA)', + state: {}, + }); + }); + it('should allow specifiying query', async () => { + const params: AllDatasetsLocatorParams = { + query: { + language: 'kuery', + query: 'foo', + }, + }; + + const { allDatasetsLocator } = await setup(); + const location = await allDatasetsLocator.getLocation(params); + + expect(location).toMatchObject({ + app: OBSERVABILITY_LOG_EXPLORER_APP_ID, + path: '/?_a=(index:BQZwpgNmDGAuCWB7AdgFQJ4AcwC4CGEEAlEA,query:(language:kuery,query:foo))', + state: {}, + }); + }); + + it('should allow specifiying refresh interval', async () => { + const params: AllDatasetsLocatorParams = { + refreshInterval: { + pause: false, + value: 666, + }, + }; + + const { allDatasetsLocator } = await setup(); + const location = await allDatasetsLocator.getLocation(params); + + expect(location).toMatchObject({ + app: OBSERVABILITY_LOG_EXPLORER_APP_ID, + path: '/?_g=(refreshInterval:(pause:!f,value:666))&_a=(index:BQZwpgNmDGAuCWB7AdgFQJ4AcwC4CGEEAlEA)', + state: {}, + }); + }); + + it('should allow specifiying columns and sort', async () => { + const params: AllDatasetsLocatorParams = { + columns: ['_source'], + sort: [['timestamp, asc']] as string[][], + }; + + const { allDatasetsLocator } = await setup(); + const location = await allDatasetsLocator.getLocation(params); + + expect(location).toMatchObject({ + app: OBSERVABILITY_LOG_EXPLORER_APP_ID, + path: `/?_a=(columns:!(_source),index:BQZwpgNmDGAuCWB7AdgFQJ4AcwC4CGEEAlEA,sort:!(!('timestamp,%20asc')))`, + state: {}, + }); + }); + + it('should allow specifiying filters', async () => { + const params: AllDatasetsLocatorParams = { + filters: [ + { + meta: { + alias: 'foo', + disabled: false, + negate: false, + }, + $state: { + store: FilterStateStore.APP_STATE, + }, + }, + { + meta: { + alias: 'bar', + disabled: false, + negate: false, + }, + $state: { + store: FilterStateStore.GLOBAL_STATE, + }, + }, + ], + }; + + const { allDatasetsLocator } = await setup(); + const { path } = await allDatasetsLocator.getLocation(params); + + const { _a, _g } = getStatesFromKbnUrl(path, ['_a', '_g'], { getFromHashQuery: false }); + + expect(_a).toEqual({ + filters: [ + { + $state: { + store: 'appState', + }, + meta: { + alias: 'foo', + disabled: false, + negate: false, + }, + }, + ], + index: 'BQZwpgNmDGAuCWB7AdgFQJ4AcwC4CGEEAlEA', + }); + expect(_g).toEqual({ + filters: [ + { + $state: { + store: 'globalState', + }, + meta: { + alias: 'bar', + disabled: false, + negate: false, + }, + }, + ], + }); + }); + }); + + describe('Single Dataset Locator', () => { + const integration = 'Test'; + const dataset = 'test-*'; + it('should create a link with correct index', async () => { + const { singleDatasetLocator } = await setup(); + const location = await singleDatasetLocator.getLocation({ + integration, + dataset, + }); + + expect(location).toMatchObject({ + app: OBSERVABILITY_LOG_EXPLORER_APP_ID, + path: `/?_a=(index:BQZwpgNmDGAuCWB7AdgLmAEwIay%2BW6yWAtmKgOQSIDmIAtLGCLHQFRvkA0CsUqjzAJScipVABUmsYeChwkycQE8ADmQCuyAE5NEEAG5gMgoA)`, + state: {}, + }); + }); + + it('should allow specifiying time range', async () => { + const params: SingleDatasetLocatorParams = { + integration, + dataset, + timeRange, + }; + + const { singleDatasetLocator } = await setup(); + const location = await singleDatasetLocator.getLocation(params); + + expect(location).toMatchObject({ + app: OBSERVABILITY_LOG_EXPLORER_APP_ID, + path: `/?_g=(time:(from:now-30m,to:now))&_a=(index:BQZwpgNmDGAuCWB7AdgLmAEwIay%2BW6yWAtmKgOQSIDmIAtLGCLHQFRvkA0CsUqjzAJScipVABUmsYeChwkycQE8ADmQCuyAE5NEEAG5gMgoA)`, + state: {}, + }); + }); + + it('should allow specifiying query', async () => { + const params: SingleDatasetLocatorParams = { + integration, + dataset, + query: { + language: 'kuery', + query: 'foo', + }, + }; + + const { singleDatasetLocator } = await setup(); + const location = await singleDatasetLocator.getLocation(params); + + expect(location).toMatchObject({ + app: OBSERVABILITY_LOG_EXPLORER_APP_ID, + path: `/?_a=(index:BQZwpgNmDGAuCWB7AdgLmAEwIay%2BW6yWAtmKgOQSIDmIAtLGCLHQFRvkA0CsUqjzAJScipVABUmsYeChwkycQE8ADmQCuyAE5NEEAG5gMgoA,query:(language:kuery,query:foo))`, + state: {}, + }); + }); + + it('should allow specifiying refresh interval', async () => { + const params: SingleDatasetLocatorParams = { + integration, + dataset, + refreshInterval: { + pause: false, + value: 666, + }, + }; + + const { singleDatasetLocator } = await setup(); + const location = await singleDatasetLocator.getLocation(params); + + expect(location).toMatchObject({ + app: OBSERVABILITY_LOG_EXPLORER_APP_ID, + path: `/?_g=(refreshInterval:(pause:!f,value:666))&_a=(index:BQZwpgNmDGAuCWB7AdgLmAEwIay%2BW6yWAtmKgOQSIDmIAtLGCLHQFRvkA0CsUqjzAJScipVABUmsYeChwkycQE8ADmQCuyAE5NEEAG5gMgoA)`, + state: {}, + }); + }); + + it('should allow specifiying columns and sort', async () => { + const params: SingleDatasetLocatorParams = { + integration, + dataset, + columns: ['_source'], + sort: [['timestamp, asc']] as string[][], + }; + + const { singleDatasetLocator } = await setup(); + const location = await singleDatasetLocator.getLocation(params); + + expect(location).toMatchObject({ + app: OBSERVABILITY_LOG_EXPLORER_APP_ID, + path: `/?_a=(columns:!(_source),index:BQZwpgNmDGAuCWB7AdgLmAEwIay%2BW6yWAtmKgOQSIDmIAtLGCLHQFRvkA0CsUqjzAJScipVABUmsYeChwkycQE8ADmQCuyAE5NEEAG5gMgoA,sort:!(!('timestamp,%20asc')))`, + state: {}, + }); + }); + + it('should allow specifiying filters', async () => { + const params: SingleDatasetLocatorParams = { + integration, + dataset, + filters: [ + { + meta: { + alias: 'foo', + disabled: false, + negate: false, + }, + $state: { + store: FilterStateStore.APP_STATE, + }, + }, + { + meta: { + alias: 'bar', + disabled: false, + negate: false, + }, + $state: { + store: FilterStateStore.GLOBAL_STATE, + }, + }, + ], + }; + + const { singleDatasetLocator } = await setup(); + const { path } = await singleDatasetLocator.getLocation(params); + + const { _a, _g } = getStatesFromKbnUrl(path, ['_a', '_g'], { getFromHashQuery: false }); + + expect(_a).toEqual({ + filters: [ + { + $state: { + store: 'appState', + }, + meta: { + alias: 'foo', + disabled: false, + negate: false, + }, + }, + ], + index: + 'BQZwpgNmDGAuCWB7AdgLmAEwIay+W6yWAtmKgOQSIDmIAtLGCLHQFRvkA0CsUqjzAJScipVABUmsYeChwkycQE8ADmQCuyAE5NEEAG5gMgoA', + }); + expect(_g).toEqual({ + filters: [ + { + $state: { + store: 'globalState', + }, + meta: { + alias: 'bar', + disabled: false, + negate: false, + }, + }, + ], + }); + }); + }); +}); diff --git a/x-pack/plugins/observability_log_explorer/common/locators/single_dataset/index.ts b/x-pack/plugins/observability_log_explorer/common/locators/single_dataset/index.ts new file mode 100644 index 0000000000000..ae8e21e7c8296 --- /dev/null +++ b/x-pack/plugins/observability_log_explorer/common/locators/single_dataset/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './single_dataset_locator'; diff --git a/x-pack/plugins/observability_log_explorer/common/locators/single_dataset/single_dataset_locator.ts b/x-pack/plugins/observability_log_explorer/common/locators/single_dataset/single_dataset_locator.ts new file mode 100644 index 0000000000000..632d0d8d93de6 --- /dev/null +++ b/x-pack/plugins/observability_log_explorer/common/locators/single_dataset/single_dataset_locator.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public'; +import { UnresolvedDatasetSelection } from '@kbn/log-explorer-plugin/common'; +import type { IndexPattern } from '@kbn/io-ts-utils'; +import { + SingleDatasetLocatorParams, + SINGLE_DATASET_LOCATOR_ID, +} from '@kbn/deeplinks-observability/locators'; +import { DatasetLocatorDependencies } from '../types'; +import { constructLocatorPath } from '../utils'; + +export type SingleDatasetLocator = LocatorPublic; + +export class SingleDatasetLocatorDefinition + implements LocatorDefinition +{ + public readonly id = SINGLE_DATASET_LOCATOR_ID; + + constructor(protected readonly deps: DatasetLocatorDependencies) {} + + public readonly getLocation = (params: SingleDatasetLocatorParams) => { + const { useHash } = this.deps; + const { integration, dataset } = params; + + const unresolvedDatasetSelection = UnresolvedDatasetSelection.fromSelection({ + name: integration, + dataset: { + name: this.composeIndexPattern(dataset), + }, + }); + + const index = unresolvedDatasetSelection.toDataviewSpec().id; + + return constructLocatorPath({ + locatorParams: params, + index, + useHash, + }); + }; + + private composeIndexPattern(datasetName: SingleDatasetLocatorParams['dataset']) { + return `logs-${datasetName}-*` as IndexPattern; + } +} diff --git a/x-pack/plugins/observability_log_explorer/common/locators/types.ts b/x-pack/plugins/observability_log_explorer/common/locators/types.ts new file mode 100644 index 0000000000000..a181fdce10a02 --- /dev/null +++ b/x-pack/plugins/observability_log_explorer/common/locators/types.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AggregateQuery, Filter, Query } from '@kbn/es-query'; + +export interface AppState { + index?: string; + query?: Query | AggregateQuery; + filters?: Filter[]; + columns?: string[]; + sort?: string[][]; +} + +export interface DatasetLocatorDependencies { + useHash: boolean; +} diff --git a/x-pack/plugins/observability_log_explorer/common/locators/utils/helpers.ts b/x-pack/plugins/observability_log_explorer/common/locators/utils/helpers.ts new file mode 100644 index 0000000000000..c22c000732839 --- /dev/null +++ b/x-pack/plugins/observability_log_explorer/common/locators/utils/helpers.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { GlobalQueryStateFromUrl } from '@kbn/data-plugin/public'; +import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/common'; +import { DatasetLocatorParams } from '@kbn/deeplinks-observability/locators'; +import { AppState } from '../types'; + +interface LocatorPathCosntructionParams { + locatorParams: DatasetLocatorParams; + index: string; + useHash: boolean; +} + +export const constructLocatorPath = async (params: LocatorPathCosntructionParams) => { + const { isFilterPinned } = await import('@kbn/es-query'); + + const { + locatorParams: { filters, query, refreshInterval, timeRange, columns, sort }, + index, + useHash, + } = params; + const appState: AppState = {}; + const queryState: GlobalQueryStateFromUrl = {}; + + // App state + if (index) appState.index = index; + if (query) appState.query = query; + if (filters && filters.length) appState.filters = filters?.filter((f) => !isFilterPinned(f)); + if (columns) appState.columns = columns; + if (sort) appState.sort = sort; + + // Global State + if (timeRange) queryState.time = timeRange; + if (filters && filters.length) queryState.filters = filters?.filter((f) => isFilterPinned(f)); + if (refreshInterval) queryState.refreshInterval = refreshInterval; + + let path = '/'; + + if (Object.keys(queryState).length) { + path = setStateToKbnUrl( + '_g', + queryState, + { useHash, storeInHashQuery: false }, + path + ); + } + + path = setStateToKbnUrl('_a', appState, { useHash, storeInHashQuery: false }, path); + + return { + app: 'observability-log-explorer', + path, + state: {}, + }; +}; diff --git a/x-pack/plugins/observability_log_explorer/common/locators/utils/index.ts b/x-pack/plugins/observability_log_explorer/common/locators/utils/index.ts new file mode 100644 index 0000000000000..6c315f929b9bb --- /dev/null +++ b/x-pack/plugins/observability_log_explorer/common/locators/utils/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './helpers'; diff --git a/x-pack/plugins/observability_log_explorer/kibana.jsonc b/x-pack/plugins/observability_log_explorer/kibana.jsonc index 15beb1ed3c8d2..7ac940de86dd4 100644 --- a/x-pack/plugins/observability_log_explorer/kibana.jsonc +++ b/x-pack/plugins/observability_log_explorer/kibana.jsonc @@ -16,11 +16,15 @@ "discover", "logExplorer", "observabilityShared", - "share" + "share", + "kibanaUtils", ], "optionalPlugins": [ "serverless" ], - "requiredBundles": ["kibanaReact", "observabilityOnboarding"] + "requiredBundles": ["kibanaReact"], + "extraPublicDirs": [ + "common", + ] } } diff --git a/x-pack/plugins/observability_log_explorer/public/components/log_explorer_top_nav_menu.tsx b/x-pack/plugins/observability_log_explorer/public/components/log_explorer_top_nav_menu.tsx index dab2ddb772010..d9456e6ed253b 100644 --- a/x-pack/plugins/observability_log_explorer/public/components/log_explorer_top_nav_menu.tsx +++ b/x-pack/plugins/observability_log_explorer/public/components/log_explorer_top_nav_menu.tsx @@ -25,7 +25,7 @@ import { LogExplorerStateContainer } from '@kbn/log-explorer-plugin/public'; import { OBSERVABILITY_ONBOARDING_LOCATOR, ObservabilityOnboardingLocatorParams, -} from '@kbn/observability-onboarding-plugin/public'; +} from '@kbn/deeplinks-observability/locators'; import { KibanaReactContextValue } from '@kbn/kibana-react-plugin/public'; import { toMountPoint } from '@kbn/react-kibana-mount'; import { css } from '@emotion/react'; diff --git a/x-pack/plugins/observability_log_explorer/public/plugin.ts b/x-pack/plugins/observability_log_explorer/public/plugin.ts index 8ca79e4ec7be4..e82d863727a60 100644 --- a/x-pack/plugins/observability_log_explorer/public/plugin.ts +++ b/x-pack/plugins/observability_log_explorer/public/plugin.ts @@ -13,6 +13,11 @@ import { Plugin, PluginInitializerContext, } from '@kbn/core/public'; +import { + ObservabilityLogExplorerLocators, + SingleDatasetLocatorDefinition, + AllDatasetsLocatorDefinition, +} from '../common/locators'; import { type ObservabilityLogExplorerConfig } from '../common/plugin_config'; import { OBSERVABILITY_LOG_EXPLORER_APP_ID } from '../common/constants'; import { logExplorerAppTitle } from '../common/translations'; @@ -28,6 +33,7 @@ export class ObservabilityLogExplorerPlugin implements Plugin { private config: ObservabilityLogExplorerConfig; + private locators?: ObservabilityLogExplorerLocators; constructor(context: PluginInitializerContext) { this.config = context.config.get(); @@ -37,6 +43,9 @@ export class ObservabilityLogExplorerPlugin core: CoreSetup, _pluginsSetup: ObservabilityLogExplorerSetupDeps ) { + const { share } = _pluginsSetup; + const useHash = core.uiSettings.get('state:storeInSessionStorage'); + core.application.register({ id: OBSERVABILITY_LOG_EXPLORER_APP_ID, title: logExplorerAppTitle, @@ -58,7 +67,26 @@ export class ObservabilityLogExplorerPlugin }, }); - return {}; + // Register Locators + const singleDatasetLocator = share.url.locators.create( + new SingleDatasetLocatorDefinition({ + useHash, + }) + ); + const allDatasetsLocator = share.url.locators.create( + new AllDatasetsLocatorDefinition({ + useHash, + }) + ); + + this.locators = { + singleDatasetLocator, + allDatasetsLocator, + }; + + return { + locators: this.locators, + }; } public start(_core: CoreStart, _pluginsStart: ObservabilityLogExplorerStartDeps) { diff --git a/x-pack/plugins/observability_log_explorer/public/types.ts b/x-pack/plugins/observability_log_explorer/public/types.ts index 48b2ad624796a..a4596995a4a7b 100644 --- a/x-pack/plugins/observability_log_explorer/public/types.ts +++ b/x-pack/plugins/observability_log_explorer/public/types.ts @@ -6,20 +6,23 @@ */ import { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { DiscoverStart } from '@kbn/discover-plugin/public'; import { LogExplorerPluginStart } from '@kbn/log-explorer-plugin/public'; +import { DiscoverStart } from '@kbn/discover-plugin/public'; import { ObservabilitySharedPluginStart } from '@kbn/observability-shared-plugin/public'; import { ServerlessPluginStart } from '@kbn/serverless/public'; -import { SharePluginStart } from '@kbn/share-plugin/public'; +import { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; +import { ObservabilityLogExplorerLocators } from '../common/locators'; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ObservabilityLogExplorerPluginSetup {} +export interface ObservabilityLogExplorerPluginSetup { + locators: ObservabilityLogExplorerLocators; +} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ObservabilityLogExplorerPluginStart {} export interface ObservabilityLogExplorerSetupDeps { serverless?: ServerlessPluginStart; + share: SharePluginSetup; } export interface ObservabilityLogExplorerStartDeps { diff --git a/x-pack/plugins/observability_log_explorer/tsconfig.json b/x-pack/plugins/observability_log_explorer/tsconfig.json index 0100aa5abb37a..9a0fb5d43e7b9 100644 --- a/x-pack/plugins/observability_log_explorer/tsconfig.json +++ b/x-pack/plugins/observability_log_explorer/tsconfig.json @@ -22,11 +22,14 @@ "@kbn/serverless", "@kbn/core-chrome-browser", "@kbn/config-schema", + "@kbn/kibana-utils-plugin", "@kbn/core-application-browser", "@kbn/discover-plugin", - "@kbn/observability-onboarding-plugin", + "@kbn/es-query", "@kbn/react-kibana-mount", "@kbn/share-plugin", + "@kbn/io-ts-utils", + "@kbn/deeplinks-observability" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/observability_onboarding/e2e/cypress/e2e/logs/custom_logs/install_elastic_agent.cy.ts b/x-pack/plugins/observability_onboarding/e2e/cypress/e2e/logs/custom_logs/install_elastic_agent.cy.ts index 1307b4da93100..0843423f10b1b 100644 --- a/x-pack/plugins/observability_onboarding/e2e/cypress/e2e/logs/custom_logs/install_elastic_agent.cy.ts +++ b/x-pack/plugins/observability_onboarding/e2e/cypress/e2e/logs/custom_logs/install_elastic_agent.cy.ts @@ -618,12 +618,12 @@ describe('[Logs onboarding] Custom logs - install elastic agent', () => { .should('exist'); }); - it('when user clicks on Explore Logs it navigates to discover', () => { + it('when user clicks on Explore Logs it navigates to observability log explorer', () => { cy.wait('@checkOnboardingProgress'); cy.getByTestSubj('obltOnboardingExploreLogs').should('exist').click(); - cy.url().should('include', '/app/discover'); - cy.get('button[title="logs-*"]').should('exist'); + cy.url().should('include', '/app/observability-log-explorer'); + cy.get('button').contains('[mylogs] mylogs').should('exist'); }); }); }); diff --git a/x-pack/plugins/observability_onboarding/e2e/cypress/e2e/logs/system_logs.cy.ts b/x-pack/plugins/observability_onboarding/e2e/cypress/e2e/logs/system_logs.cy.ts index 3497ac145eaf3..41347191561ca 100644 --- a/x-pack/plugins/observability_onboarding/e2e/cypress/e2e/logs/system_logs.cy.ts +++ b/x-pack/plugins/observability_onboarding/e2e/cypress/e2e/logs/system_logs.cy.ts @@ -645,13 +645,13 @@ describe('[Logs onboarding] System logs', () => { .should('exist'); }); - it('when user clicks on Explore Logs it navigates to discover', () => { + it('when user clicks on Explore Logs it navigates to observability log explorer', () => { cy.wait('@systemIntegrationInstall'); cy.wait('@checkOnboardingProgress'); cy.getByTestSubj('obltOnboardingExploreLogs').should('exist').click(); - cy.url().should('include', '/app/discover'); - cy.get('button[title="logs-*"]').should('exist'); + cy.url().should('include', '/app/observability-log-explorer'); + cy.get('button').contains('[System] syslog').should('exist'); }); }); }); diff --git a/x-pack/plugins/observability_onboarding/public/components/app/custom_logs/install_elastic_agent.tsx b/x-pack/plugins/observability_onboarding/public/components/app/custom_logs/install_elastic_agent.tsx index fc8e36626c4cd..aea68cdd6711c 100644 --- a/x-pack/plugins/observability_onboarding/public/components/app/custom_logs/install_elastic_agent.tsx +++ b/x-pack/plugins/observability_onboarding/public/components/app/custom_logs/install_elastic_agent.tsx @@ -15,6 +15,10 @@ import { import { i18n } from '@kbn/i18n'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { default as React, useCallback, useEffect, useState } from 'react'; +import { + SingleDatasetLocatorParams, + SINGLE_DATASET_LOCATOR_ID, +} from '@kbn/deeplinks-observability/locators'; import { ObservabilityOnboardingPluginSetupDeps } from '../../../plugin'; import { useWizard } from '.'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; @@ -34,25 +38,38 @@ import { } from '../../shared/step_panel'; import { ApiKeyBanner } from './api_key_banner'; import { BackButton } from './back_button'; -import { getDiscoverNavigationParams } from '../utils'; import { WindowsInstallStep } from '../../shared/windows_install_step'; import { TroubleshootingLink } from '../../shared/troubleshooting_link'; export function InstallElasticAgent() { const { - services: { - discover: { locator }, - }, + services: { share }, } = useKibana(); + + const singleDatasetLocator = + share.url.locators.get( + SINGLE_DATASET_LOCATOR_ID + ); + const { goBack, getState, setState } = useWizard(); const wizardState = getState(); + const { + integrationName: integration, + datasetName: dataset, + autoDownloadConfig, + } = wizardState; + const [elasticAgentPlatform, setElasticAgentPlatform] = useState('linux-tar'); + const enforcedDatasetName = + integration === dataset ? dataset : `${integration}.${dataset}`; + async function onContinue() { - await locator?.navigate( - getDiscoverNavigationParams([wizardState.datasetName]) - ); + await singleDatasetLocator!.navigate({ + integration, + dataset: enforcedDatasetName, + }); } function onAutoDownloadConfig() { @@ -258,7 +275,7 @@ export function InstallElasticAgent() {

- {wizardState.integrationName && ( + {integration && ( <> (); + const singleDatasetLocator = + share.url.locators.get( + SINGLE_DATASET_LOCATOR_ID + ); + const { navigateToKibanaUrl } = useKibanaNavigation(); const { getState, setState } = useWizard(); const wizardState = getState(); @@ -60,11 +65,10 @@ export function InstallElasticAgent() { navigateToKibanaUrl('/app/observabilityOnboarding'); } async function onContinue() { - const dataStreams = getSystemLogsDataStreams(); - const dataSets = dataStreams.map( - (dataSream) => dataSream.data_stream.dataset - ); - await locator?.navigate(getDiscoverNavigationParams(dataSets)); + await singleDatasetLocator!.navigate({ + integration: 'system', + dataset: 'system.syslog', + }); } function onAutoDownloadConfig() { diff --git a/x-pack/plugins/observability_onboarding/public/components/app/utils.ts b/x-pack/plugins/observability_onboarding/public/components/app/utils.ts deleted file mode 100644 index 843002cb1fcc6..0000000000000 --- a/x-pack/plugins/observability_onboarding/public/components/app/utils.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { DataViewSpec } from '@kbn/data-views-plugin/common'; -import { DiscoverAppLocatorParams } from '@kbn/discover-plugin/common'; -import { Filter, FilterStateStore } from '@kbn/es-query'; - -type DiscoverPropertiesToPick = 'dataViewId' | 'dataViewSpec' | 'filters'; - -type DiscoverNavigationParams = Pick< - DiscoverAppLocatorParams, - DiscoverPropertiesToPick ->; - -const defaultFilterKey = 'data_stream.dataset'; -const defaultLogsDataViewId = 'logs-*'; -const defaultLogsDataView: DataViewSpec = { - id: defaultLogsDataViewId, - title: defaultLogsDataViewId, -}; - -const getDefaultDatasetFilter = (datasets: string[]): Filter[] => [ - { - meta: { - index: defaultLogsDataViewId, - key: defaultFilterKey, - params: datasets, - type: 'phrases', - }, - query: { - bool: { - minimum_should_match: 1, - should: datasets.map((dataset) => ({ - match_phrase: { - [defaultFilterKey]: dataset, - }, - })), - }, - }, - $state: { - store: FilterStateStore.APP_STATE, - }, - }, -]; - -export const getDiscoverNavigationParams = ( - datasets: string[] -): DiscoverNavigationParams => ({ - dataViewId: defaultLogsDataViewId, - dataViewSpec: defaultLogsDataView, - filters: getDefaultDatasetFilter(datasets), -}); diff --git a/x-pack/plugins/observability_onboarding/public/locators/onboarding_locator/locator_definition.ts b/x-pack/plugins/observability_onboarding/public/locators/onboarding_locator/locator_definition.ts index 0c18e06cb92f5..a4f8965dda5a3 100644 --- a/x-pack/plugins/observability_onboarding/public/locators/onboarding_locator/locator_definition.ts +++ b/x-pack/plugins/observability_onboarding/public/locators/onboarding_locator/locator_definition.ts @@ -6,10 +6,10 @@ */ import type { LocatorDefinition } from '@kbn/share-plugin/public'; -import type { ObservabilityOnboardingLocatorParams } from './types'; - -export const OBSERVABILITY_ONBOARDING_LOCATOR = - 'OBSERVABILITY_ONBOARDING_LOCATOR' as const; +import { + ObservabilityOnboardingLocatorParams, + OBSERVABILITY_ONBOARDING_LOCATOR, +} from '@kbn/deeplinks-observability/locators'; export class ObservabilityOnboardingLocatorDefinition implements LocatorDefinition diff --git a/x-pack/plugins/observability_onboarding/public/locators/onboarding_locator/types.ts b/x-pack/plugins/observability_onboarding/public/locators/onboarding_locator/types.ts index b8709f6af361d..61f6d923db49d 100644 --- a/x-pack/plugins/observability_onboarding/public/locators/onboarding_locator/types.ts +++ b/x-pack/plugins/observability_onboarding/public/locators/onboarding_locator/types.ts @@ -6,13 +6,7 @@ */ import type { LocatorPublic } from '@kbn/share-plugin/public'; -import { SerializableRecord } from '@kbn/utility-types'; - -export interface ObservabilityOnboardingLocatorParams - extends SerializableRecord { - /** If given, it will load the given map else will load the create a new map page. */ - source?: 'customLogs' | 'systemLogs'; -} +import type { ObservabilityOnboardingLocatorParams } from '@kbn/deeplinks-observability/locators'; export type ObservabilityOnboardingLocator = LocatorPublic; diff --git a/x-pack/plugins/observability_onboarding/public/plugin.ts b/x-pack/plugins/observability_onboarding/public/plugin.ts index 45fa8a640c02f..05806059b12e3 100644 --- a/x-pack/plugins/observability_onboarding/public/plugin.ts +++ b/x-pack/plugins/observability_onboarding/public/plugin.ts @@ -23,7 +23,6 @@ import { DataPublicPluginSetup, DataPublicPluginStart, } from '@kbn/data-plugin/public'; -import type { DiscoverSetup } from '@kbn/discover-plugin/public'; import { SharePluginSetup } from '@kbn/share-plugin/public'; import type { ObservabilityOnboardingConfig } from '../server'; import { PLUGIN_ID } from '../common'; @@ -35,7 +34,6 @@ export type ObservabilityOnboardingPluginStart = void; export interface ObservabilityOnboardingPluginSetupDeps { data: DataPublicPluginSetup; - discover: DiscoverSetup; observability: ObservabilityPublicSetup; share: SharePluginSetup; } diff --git a/x-pack/plugins/observability_onboarding/tsconfig.json b/x-pack/plugins/observability_onboarding/tsconfig.json index 3188e63982483..4e1e2bacdf437 100644 --- a/x-pack/plugins/observability_onboarding/tsconfig.json +++ b/x-pack/plugins/observability_onboarding/tsconfig.json @@ -14,7 +14,6 @@ "kbn_references": [ "@kbn/core", "@kbn/data-plugin", - "@kbn/discover-plugin", "@kbn/kibana-react-plugin", "@kbn/observability-plugin", "@kbn/i18n", @@ -30,12 +29,10 @@ "@kbn/core-http-server", "@kbn/security-plugin", "@kbn/std", - "@kbn/data-views-plugin", - "@kbn/es-query", "@kbn/use-tracked-promise", "@kbn/custom-integrations", "@kbn/share-plugin", - "@kbn/utility-types", + "@kbn/deeplinks-observability" ], "exclude": [ "target/**/*",