diff --git a/x-pack/plugins/search_notebooks/kibana.jsonc b/x-pack/plugins/search_notebooks/kibana.jsonc index d702cfb020275..e9432a5559ce9 100644 --- a/x-pack/plugins/search_notebooks/kibana.jsonc +++ b/x-pack/plugins/search_notebooks/kibana.jsonc @@ -15,7 +15,9 @@ "requiredPlugins": [ "console" ], - "optionalPlugins": [], + "optionalPlugins": [ + "usageCollection" + ], "requiredBundles": [ "kibanaReact" ] diff --git a/x-pack/plugins/search_notebooks/public/components/notebooks_view.tsx b/x-pack/plugins/search_notebooks/public/components/notebooks_view.tsx index a20c3b9ddaf2d..b6c837f70ba37 100644 --- a/x-pack/plugins/search_notebooks/public/components/notebooks_view.tsx +++ b/x-pack/plugins/search_notebooks/public/components/notebooks_view.tsx @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + import React from 'react'; import { CoreStart } from '@kbn/core/public'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; @@ -11,24 +12,32 @@ import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { SearchNotebooks } from './search_notebooks'; -import { NotebookListValue } from '../types'; +import { AppMetricsTracker, NotebookListValue } from '../types'; export interface SearchNotebooksViewProps { core: CoreStart; queryClient: QueryClient; + usageTracker: AppMetricsTracker; getNotebookList: () => NotebookListValue; } export const SearchNotebooksView = ({ core, queryClient, + usageTracker, getNotebookList, -}: SearchNotebooksViewProps) => ( - - - - - - - -); +}: SearchNotebooksViewProps) => { + React.useEffect(() => { + usageTracker.count('opened_notebooks_view'); + }, [usageTracker]); + + return ( + + + + + + + + ); +}; diff --git a/x-pack/plugins/search_notebooks/public/components/search_notebook.tsx b/x-pack/plugins/search_notebooks/public/components/search_notebook.tsx index 9c78344c15bab..e075c52c53e80 100644 --- a/x-pack/plugins/search_notebooks/public/components/search_notebook.tsx +++ b/x-pack/plugins/search_notebooks/public/components/search_notebook.tsx @@ -4,48 +4,31 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; -import { EuiPanel, EuiEmptyPrompt, EuiCodeBlock } from '@elastic/eui'; +import React, { useEffect } from 'react'; +import { EuiPanel } from '@elastic/eui'; import { NotebookRenderer } from '@kbn/ipynb'; -import { FormattedMessage } from '@kbn/i18n-react'; import { useNotebook } from '../hooks/use_notebook'; +import { useUsageTracker } from '../hooks/use_usage_tracker'; import { LoadingPanel } from './loading_panel'; +import { SearchNotebookError } from './search_notebook_error'; export interface SearchNotebookProps { notebookId: string; } export const SearchNotebook = ({ notebookId }: SearchNotebookProps) => { + const usageTracker = useUsageTracker(); const { data, isLoading, error } = useNotebook(notebookId); + useEffect(() => { + usageTracker.count(['view-notebook', `nb-${notebookId}`]); + }, [usageTracker, notebookId]); + if (isLoading) { return ; } if (!data || error) { return ( - - - - } - titleSize="l" - body={ - <> -

- -

- {JSON.stringify(error)} - - } - /> + ); } return ( diff --git a/x-pack/plugins/search_notebooks/public/components/search_notebook_error.tsx b/x-pack/plugins/search_notebooks/public/components/search_notebook_error.tsx new file mode 100644 index 0000000000000..87c0bf74fe941 --- /dev/null +++ b/x-pack/plugins/search_notebooks/public/components/search_notebook_error.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiEmptyPrompt, EuiCodeBlock } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import type { AppMetricsTracker } from '../types'; +import { getErrorMessage } from '../utils/get_error_message'; + +export interface SearchNotebookErrorProps { + error: unknown; + notebookId: string; + usageTracker: AppMetricsTracker; +} + +export const SearchNotebookError = ({ + error, + notebookId, + usageTracker, +}: SearchNotebookErrorProps) => { + React.useEffect(() => { + usageTracker.count(['notebookViewError', `error-${notebookId}`]); + }, [usageTracker, notebookId]); + + return ( + + + + } + titleSize="l" + body={ + <> +

+ +

+ {error !== undefined && typeof error === 'object' ? ( + {JSON.stringify(error)} + ) : ( +

+ {getErrorMessage( + error, + i18n.translate('xpack.searchNotebooks.notebook.fetchError.unknownError', { + defaultMessage: 'Unknown error fetching notebook', + }) + )} +

+ )} + + } + /> + ); +}; diff --git a/x-pack/plugins/search_notebooks/public/console_view.tsx b/x-pack/plugins/search_notebooks/public/console_view.tsx index 99575c8e93948..0f64ed989f5e2 100644 --- a/x-pack/plugins/search_notebooks/public/console_view.tsx +++ b/x-pack/plugins/search_notebooks/public/console_view.tsx @@ -14,7 +14,7 @@ import type { import { dynamic } from '@kbn/shared-ux-utility'; import { QueryClient } from '@tanstack/react-query'; -import { NotebookListValue } from './types'; +import { NotebookListValue, AppMetricsTracker } from './types'; const SearchNotebooksButton = dynamic(async () => ({ default: (await import('./components/notebooks_button')).SearchNotebooksButton, @@ -26,6 +26,7 @@ const SearchNotebooksView = dynamic(async () => ({ export const notebooksConsoleView = ( core: CoreStart, queryClient: QueryClient, + usageTracker: AppMetricsTracker, clearNotebookList: () => void, getNotebookListValue: () => NotebookListValue ): EmbeddedConsoleView => { @@ -37,6 +38,7 @@ export const notebooksConsoleView = ( ), diff --git a/x-pack/plugins/search_notebooks/public/hooks/use_kibana.ts b/x-pack/plugins/search_notebooks/public/hooks/use_kibana.ts index b490e61f41490..fc2a5511b9336 100644 --- a/x-pack/plugins/search_notebooks/public/hooks/use_kibana.ts +++ b/x-pack/plugins/search_notebooks/public/hooks/use_kibana.ts @@ -9,13 +9,14 @@ import type { ConsolePluginStart } from '@kbn/console-plugin/public'; import type { CoreStart } from '@kbn/core/public'; import { useKibana as useKibanaBase } from '@kbn/kibana-react-plugin/public'; -import { NotebookListValue } from '../types'; +import { NotebookListValue, AppMetricsTracker } from '../types'; export interface SearchNotebooksContext { console: ConsolePluginStart; notebooks: { getNotebookList: () => NotebookListValue; }; + usageTracker: AppMetricsTracker; } type ServerlessSearchKibanaContext = CoreStart & SearchNotebooksContext; diff --git a/x-pack/plugins/search_notebooks/public/hooks/use_usage_tracker.ts b/x-pack/plugins/search_notebooks/public/hooks/use_usage_tracker.ts new file mode 100644 index 0000000000000..52e583d6af358 --- /dev/null +++ b/x-pack/plugins/search_notebooks/public/hooks/use_usage_tracker.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useKibanaServices } from './use_kibana'; +import { AppMetricsTracker } from '../types'; + +export const useUsageTracker = (): AppMetricsTracker => { + const { usageTracker } = useKibanaServices(); + + return usageTracker; +}; diff --git a/x-pack/plugins/search_notebooks/public/plugin.ts b/x-pack/plugins/search_notebooks/public/plugin.ts index 3b71193ed3add..e4e1c2b4f0d00 100644 --- a/x-pack/plugins/search_notebooks/public/plugin.ts +++ b/x-pack/plugins/search_notebooks/public/plugin.ts @@ -14,14 +14,17 @@ import { SearchNotebooksPluginStart, SearchNotebooksPluginStartDependencies, NotebookListValue, + AppMetricsTracker, } from './types'; import { getErrorCode, getErrorMessage, isKibanaServerError } from './utils/get_error_message'; +import { createUsageTracker } from './utils/usage_tracker'; export class SearchNotebooksPlugin implements Plugin { private notebooksList: NotebookListValue = null; private queryClient: QueryClient | undefined; + private usageTracker: AppMetricsTracker | undefined; public setup(core: CoreSetup): SearchNotebooksPluginSetup { this.queryClient = new QueryClient({ @@ -56,11 +59,13 @@ export class SearchNotebooksPlugin core: CoreStart, deps: SearchNotebooksPluginStartDependencies ): SearchNotebooksPluginStart { + this.usageTracker = createUsageTracker(deps.usageCollection); if (deps.console?.registerEmbeddedConsoleAlternateView) { deps.console.registerEmbeddedConsoleAlternateView( notebooksConsoleView( core, this.queryClient!, + this.usageTracker, this.clearNotebookList.bind(this), this.getNotebookList.bind(this) ) diff --git a/x-pack/plugins/search_notebooks/public/types.ts b/x-pack/plugins/search_notebooks/public/types.ts index 150d08a5d463f..63955ceb85029 100644 --- a/x-pack/plugins/search_notebooks/public/types.ts +++ b/x-pack/plugins/search_notebooks/public/types.ts @@ -6,6 +6,7 @@ */ import type { ConsolePluginStart } from '@kbn/console-plugin/public'; +import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface SearchNotebooksPluginSetup {} @@ -16,6 +17,13 @@ export interface SearchNotebooksPluginStart { export interface SearchNotebooksPluginStartDependencies { console: ConsolePluginStart; + usageCollection?: UsageCollectionStart; } export type NotebookListValue = string | null; + +export interface AppMetricsTracker { + click: (eventName: string | string[]) => void; + count: (eventName: string | string[]) => void; + load: (eventName: string | string[]) => void; +} diff --git a/x-pack/plugins/search_notebooks/public/utils/get_error_message.ts b/x-pack/plugins/search_notebooks/public/utils/get_error_message.ts index 0a73af9e544ce..4625b2cf5240c 100644 --- a/x-pack/plugins/search_notebooks/public/utils/get_error_message.ts +++ b/x-pack/plugins/search_notebooks/public/utils/get_error_message.ts @@ -7,7 +7,7 @@ import { KibanaServerError } from '@kbn/kibana-utils-plugin/common'; -export function getErrorMessage(error: unknown): string { +export function getErrorMessage(error: unknown, defaultMessage?: string): string { if (typeof error === 'string') { return error; } @@ -19,7 +19,7 @@ export function getErrorMessage(error: unknown): string { return (error as { name: string }).name; } - return ''; + return defaultMessage ?? ''; } export function getErrorCode(error: unknown): number | undefined { diff --git a/x-pack/plugins/search_notebooks/public/utils/usage_tracker.ts b/x-pack/plugins/search_notebooks/public/utils/usage_tracker.ts new file mode 100644 index 0000000000000..9a85626c73608 --- /dev/null +++ b/x-pack/plugins/search_notebooks/public/utils/usage_tracker.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; +import type { + UsageCollectionSetup, + UsageCollectionStart, +} from '@kbn/usage-collection-plugin/public'; +import { AppMetricsTracker } from '../types'; + +const APP_TRACKER_NAME = 'searchNotebooks'; + +export function createUsageTracker( + usageCollection?: UsageCollectionSetup | UsageCollectionStart +): AppMetricsTracker { + const track = (type: UiCounterMetricType, name: string | string[]) => + usageCollection?.reportUiCounter(APP_TRACKER_NAME, type, name); + return { + click: (eventName: string | string[]) => { + track(METRIC_TYPE.CLICK, eventName); + }, + count: (eventName: string | string[]) => { + track(METRIC_TYPE.COUNT, eventName); + }, + load: (eventName: string | string[]) => { + track(METRIC_TYPE.LOADED, eventName); + }, + }; +} diff --git a/x-pack/plugins/search_notebooks/tsconfig.json b/x-pack/plugins/search_notebooks/tsconfig.json index b50ec60a5bed5..5f9d5efaa0308 100644 --- a/x-pack/plugins/search_notebooks/tsconfig.json +++ b/x-pack/plugins/search_notebooks/tsconfig.json @@ -15,6 +15,7 @@ "target/**/*" ], "kbn_references": [ + "@kbn/analytics", "@kbn/config-schema", "@kbn/core", "@kbn/i18n", @@ -26,5 +27,6 @@ "@kbn/shared-ux-utility", "@kbn/kibana-utils-plugin", "@kbn/ipynb", + "@kbn/usage-collection-plugin", ] }