Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] Explain Log Rate Spikes: Adds discover link to analysis table #139877

Merged
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export const ExplainLogRateSpikesAnalysis: FC<ExplainLogRateSpikesAnalysisProps>
onPinnedChangePoint={onPinnedChangePoint}
onSelectedChangePoint={onSelectedChangePoint}
selectedChangePoint={selectedChangePoint}
dataViewId={dataView.id}
/>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { FC, useCallback, useMemo, useState } from 'react';
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
import {
EuiBadge,
EuiBasicTable,
Expand All @@ -18,11 +18,12 @@ 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';

Expand All @@ -31,9 +32,16 @@ const NARROW_COLUMN_WIDTH = '120px';
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;
Expand All @@ -42,6 +50,7 @@ interface SpikeAnalysisTableProps {

export const SpikeAnalysisTable: FC<SpikeAnalysisTableProps> = ({
changePoints,
dataViewId,
loading,
onPinnedChangePoint,
onSelectedChangePoint,
Expand All @@ -53,6 +62,71 @@ export const SpikeAnalysisTable: FC<SpikeAnalysisTableProps> = ({
const [pageSize, setPageSize] = useState(10);
const [sortField, setSortField] = useState<keyof ChangePoint>(DEFAULT_SORT_FIELD);
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>(DEFAULT_SORT_DIRECTION);
const [discoverUrlError, setDiscoverUrlError] = useState<string | undefined>();

const aiOpsKibana = useAiOpsKibana();
const {
services: { application, share, data },
} = aiOpsKibana;

const discoverLocator = useMemo(
() => share.url.locators.get('DISCOVER_APP_LOCATOR'),
[share.url.locators]
);

useEffect(() => {
darnautov marked this conversation as resolved.
Show resolved Hide resolved
if (!application.capabilities.discover?.show) {
const discoverNotEnabled = i18n.translate(
'xpack.aiops.spikeAnalysisTable.discoverNotEnabledErrorMessage',
{
defaultMessage: 'Discover is not enabled',
}
);

setDiscoverUrlError(discoverNotEnabled);
return;
}
if (!discoverLocator) {
const discoverLocatorMissing = i18n.translate(
'xpack.aiops.spikeAnalysisTable.discoverLocatorMissingErrorMessage',
{
defaultMessage: 'No locator for Discover detected',
}
);

setDiscoverUrlError(discoverLocatorMissing);
return;
}
if (!dataViewId) {
const autoGeneratedDiscoverLinkError = i18n.translate(
'xpack.aiops.spikeAnalysisTable.autoGeneratedDiscoverLinkErrorMessage',
{
defaultMessage: 'Unable to link to Discover; no data view exists for this index',
}
);

setDiscoverUrlError(autoGeneratedDiscoverLinkError);
return;
}
}, [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<EuiBasicTableColumn<ChangePoint>> = [
{
Expand Down Expand Up @@ -163,6 +237,31 @@ export const SpikeAnalysisTable: FC<SpikeAnalysisTableProps> = ({
},
sortable: true,
},
{
'data-test-subj': 'aiOpsSpikeAnalysisTableColumnAction',
name: i18n.translate('xpack.aiops.spikeAnalysisTable.actionsColumnName', {
defaultMessage: 'Actions',
}),
actions: [
{
name: () => (
<EuiToolTip content={discoverUrlError ? discoverUrlError : viewInDiscoverMessage}>
<EuiIcon type="link" />
</EuiToolTip>
),
description: viewInDiscoverMessage,
type: 'button',
onClick: async (changePoint) => {
const openInDiscoverUrl = await generateDiscoverUrl(changePoint);
if (typeof openInDiscoverUrl === 'string') {
await application.navigateToUrl(openInDiscoverUrl);
}
},
enabled: () => discoverUrlError === undefined,
},
],
width: '60px',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: For the other columns we already have a constant with the custom width (NARROW_COLUMN_WIDTH). Even if this is just one other column, suggest to create another constant like ACTION_COLUMN_WIDTH for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 344f7b0

},
];

const onChange = useCallback((tableSettings) => {
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/aiops/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -19,6 +20,7 @@ export interface AiOpsStartDependencies {
charts: ChartsPluginStart;
fieldFormats: FieldFormatsStart;
unifiedSearch: UnifiedSearchPublicPluginStart;
share: SharePluginStart;
}

export class AiopsPlugin implements Plugin<AiopsPluginSetup, AiopsPluginStart> {
Expand Down