diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx index 4c99c3bb9279d..063d27acff53e 100644 --- a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_analysis.tsx @@ -178,6 +178,7 @@ export const ExplainLogRateSpikesAnalysis: FC onPinnedChangePoint={onPinnedChangePoint} onSelectedChangePoint={onSelectedChangePoint} selectedChangePoint={selectedChangePoint} + dataViewId={dataView.id} /> )} diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx index 6ae778e48e3e0..b35fbe971b011 100644 --- a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx @@ -18,22 +18,31 @@ import { sortBy } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { escapeKuery } from '@kbn/es-query'; import type { ChangePoint } from '@kbn/ml-agg-utils'; - import { useEuiTheme } from '../../hooks/use_eui_theme'; - import { MiniHistogram } from '../mini_histogram'; +import { useAiOpsKibana } from '../../kibana_context'; +import { SEARCH_QUERY_LANGUAGE } from '../../application/utils/search_utils'; import { getFailedTransactionsCorrelationImpactLabel } from './get_failed_transactions_correlation_impact_label'; const NARROW_COLUMN_WIDTH = '120px'; +const ACTIONS_COLUMN_WIDTH = '60px'; const PAGINATION_SIZE_OPTIONS = [5, 10, 20, 50]; const DEFAULT_SORT_FIELD = 'pValue'; const DEFAULT_SORT_DIRECTION = 'asc'; +const viewInDiscoverMessage = i18n.translate( + 'xpack.aiops.spikeAnalysisTable.linksMenu.viewInDiscover', + { + defaultMessage: 'View in Discover', + } +); interface SpikeAnalysisTableProps { changePoints: ChangePoint[]; + dataViewId?: string; loading: boolean; onPinnedChangePoint?: (changePoint: ChangePoint | null) => void; onSelectedChangePoint?: (changePoint: ChangePoint | null) => void; @@ -42,6 +51,7 @@ interface SpikeAnalysisTableProps { export const SpikeAnalysisTable: FC = ({ changePoints, + dataViewId, loading, onPinnedChangePoint, onSelectedChangePoint, @@ -54,6 +64,67 @@ export const SpikeAnalysisTable: FC = ({ const [sortField, setSortField] = useState(DEFAULT_SORT_FIELD); const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>(DEFAULT_SORT_DIRECTION); + const aiOpsKibana = useAiOpsKibana(); + const { + services: { application, share, data }, + } = aiOpsKibana; + + const discoverLocator = useMemo( + () => share.url.locators.get('DISCOVER_APP_LOCATOR'), + [share.url.locators] + ); + + const discoverUrlError = useMemo(() => { + if (!application.capabilities.discover?.show) { + const discoverNotEnabled = i18n.translate( + 'xpack.aiops.spikeAnalysisTable.discoverNotEnabledErrorMessage', + { + defaultMessage: 'Discover is not enabled', + } + ); + + return discoverNotEnabled; + } + if (!discoverLocator) { + const discoverLocatorMissing = i18n.translate( + 'xpack.aiops.spikeAnalysisTable.discoverLocatorMissingErrorMessage', + { + defaultMessage: 'No locator for Discover detected', + } + ); + + return discoverLocatorMissing; + } + if (!dataViewId) { + const autoGeneratedDiscoverLinkError = i18n.translate( + 'xpack.aiops.spikeAnalysisTable.autoGeneratedDiscoverLinkErrorMessage', + { + defaultMessage: 'Unable to link to Discover; no data view exists for this index', + } + ); + + return autoGeneratedDiscoverLinkError; + } + }, [application.capabilities.discover?.show, dataViewId, discoverLocator]); + + const generateDiscoverUrl = async (changePoint: ChangePoint) => { + if (discoverLocator !== undefined) { + const url = await discoverLocator.getRedirectUrl({ + indexPatternId: dataViewId, + timeRange: data.query.timefilter.timefilter.getTime(), + filters: data.query.filterManager.getFilters(), + query: { + language: SEARCH_QUERY_LANGUAGE.KUERY, + query: `${escapeKuery(changePoint.fieldName)}:${escapeKuery( + String(changePoint.fieldValue) + )}`, + }, + }); + + return url; + } + }; + const columns: Array> = [ { 'data-test-subj': 'aiopsSpikeAnalysisTableColumnFieldName', @@ -163,6 +234,31 @@ export const SpikeAnalysisTable: FC = ({ }, sortable: true, }, + { + 'data-test-subj': 'aiOpsSpikeAnalysisTableColumnAction', + name: i18n.translate('xpack.aiops.spikeAnalysisTable.actionsColumnName', { + defaultMessage: 'Actions', + }), + actions: [ + { + name: () => ( + + + + ), + description: viewInDiscoverMessage, + type: 'button', + onClick: async (changePoint) => { + const openInDiscoverUrl = await generateDiscoverUrl(changePoint); + if (typeof openInDiscoverUrl === 'string') { + await application.navigateToUrl(openInDiscoverUrl); + } + }, + enabled: () => discoverUrlError === undefined, + }, + ], + width: ACTIONS_COLUMN_WIDTH, + }, ]; const onChange = useCallback((tableSettings) => { diff --git a/x-pack/plugins/aiops/public/plugin.ts b/x-pack/plugins/aiops/public/plugin.ts index dd903b08e2eb4..c6126ec2db7a9 100755 --- a/x-pack/plugins/aiops/public/plugin.ts +++ b/x-pack/plugins/aiops/public/plugin.ts @@ -10,6 +10,7 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { ChartsPluginStart } from '@kbn/charts-plugin/public'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { SharePluginStart } from '@kbn/share-plugin/public'; import { AiopsPluginSetup, AiopsPluginStart } from './types'; import { setStartServices } from './kibana_services'; @@ -19,6 +20,7 @@ export interface AiOpsStartDependencies { charts: ChartsPluginStart; fieldFormats: FieldFormatsStart; unifiedSearch: UnifiedSearchPublicPluginStart; + share: SharePluginStart; } export class AiopsPlugin implements Plugin {