[ML] AI Ops: Adds discover link to analysis table (#139877)

* wip: create linksMenu component for discover url

* adds actions column to spikeanalysis table with link to discover

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* use EuiTable built in Actions column type for Discover link

* remove linkMenu

* fix localization messages

* set fixed width for actions column

* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'

* update link icon and simplify discoverUrlError setting

* Update x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx

Co-authored-by: Dima Arnautov <arnautov.dima@gmail.com>

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Dima Arnautov <arnautov.dima@gmail.com>
This commit is contained in:
Melissa Alvarez 2022-09-07 09:58:14 -06:00 committed by GitHub
parent 27d93fdf69
commit 7a83e87560
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 101 additions and 2 deletions

View file

@ -178,6 +178,7 @@ export const ExplainLogRateSpikesAnalysis: FC<ExplainLogRateSpikesAnalysisProps>
onPinnedChangePoint={onPinnedChangePoint}
onSelectedChangePoint={onSelectedChangePoint}
selectedChangePoint={selectedChangePoint}
dataViewId={dataView.id}
/>
)}
</div>

View file

@ -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<SpikeAnalysisTableProps> = ({
changePoints,
dataViewId,
loading,
onPinnedChangePoint,
onSelectedChangePoint,
@ -54,6 +64,67 @@ export const SpikeAnalysisTable: FC<SpikeAnalysisTableProps> = ({
const [sortField, setSortField] = useState<keyof ChangePoint>(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<EuiBasicTableColumn<ChangePoint>> = [
{
'data-test-subj': 'aiopsSpikeAnalysisTableColumnFieldName',
@ -163,6 +234,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="discoverApp" />
</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: ACTIONS_COLUMN_WIDTH,
},
];
const onChange = useCallback((tableSettings) => {

View file

@ -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<AiopsPluginSetup, AiopsPluginStart> {