mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
Single view in app function for rule actions variables and UI page (#148671)
Resolves: https://github.com/elastic/kibana/issues/145132. In this PR, I'm adding a new function to the server-side rule type definition called `viewInAppRelativeUrl`. This function returns a relative path to view the rule in the proper application that will provide more context. This relative path is used to build the `rule.url` mustache variable for the actions (overriding the rule details page link when defined) as well as a fallback for the UI's `View in App` button if no navigation is registered on the front-end. Follow up issues: - https://github.com/elastic/kibana/issues/149608 - https://github.com/elastic/kibana/issues/151355 ## ML to verify 1. Create an anomaly detection rule from the ML application 2. Go to stack management rule details page 3. Click "View in App" 4. Ensure it brings you to the ML app properly. 5. Repeat step 1 to 4 in a space that isn't the default Note: ML won't take advantage of the new functionality yet, but we plan to help in a follow up https://github.com/elastic/kibana/issues/149608 so that ML anomaly detection rules can provide a view in app URL within the rule action messages. ## ResponseOps to verify 1. Set `server.publicBaseUrl` to the proper value in your kibana.yml 6. Modify the [index threshold rule type](https://github.com/elastic/kibana/blob/main/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.ts#L108-L136) to have a `getViewInAppRelativeUrl` function returning `/app/management/insightsAndAlerting/triggersActionsConnectors/connectors`. 7. Create an index threshold rule that always fires. Make sure to add a a server log action that contains the `{{rule.url}}` variable. 8. Pull the printed URL from the server logs and make sure it works and brings you to the connectors page. 9. Navigate to the rule details page, click the "View in App" button and ensure it also brings you to the connectors page. 10. Create a Kibana space. 11. Go into that created space and repeat step 3 to 5. Ensure the URL and View in App keep you in the same space. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
2406ada16a
commit
ccd78c9f0e
25 changed files with 156 additions and 89 deletions
|
@ -28,7 +28,7 @@ export function registerNavigation(alerting: AlertingSetup) {
|
|||
alerting.registerNavigation(
|
||||
ALERTING_EXAMPLE_APP_ID,
|
||||
'example.people-in-space',
|
||||
(rule: SanitizedRule) => `/astros/${rule.id}`
|
||||
(rule: SanitizedRule) => `/app/${ALERTING_EXAMPLE_APP_ID}/astros/${rule.id}`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ export function registerNavigation(alerting: AlertingSetup) {
|
|||
// register default navigation
|
||||
alerting.registerDefaultNavigation(
|
||||
ALERTING_EXAMPLE_APP_ID,
|
||||
(rule: SanitizedRule) => `/rule/${rule.id}`
|
||||
(rule: SanitizedRule) => `/app/${ALERTING_EXAMPLE_APP_ID}/rule/${rule.id}`
|
||||
);
|
||||
|
||||
registerPeopleInSpaceNavigation(alerting);
|
||||
|
|
|
@ -81,4 +81,7 @@ export const alertType: RuleType<
|
|||
};
|
||||
},
|
||||
producer: ALERTING_EXAMPLE_APP_ID,
|
||||
getViewInAppRelativeUrl({ rule }) {
|
||||
return `/app/${ALERTING_EXAMPLE_APP_ID}/astros/${rule.id}`;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -695,7 +695,7 @@ alerting.registerNavigation(
|
|||
|
||||
This tells the Alerting Framework that, given a rule of the RuleType whose ID is `my-application-id.my-unique-rule-type`, if that rule's `consumer` value (which is set when the rule is created by your plugin) is your application (whose id is `my-application-id`), then it will navigate to your application using the path `/my-unique-rule/${the id of the rule}`.
|
||||
|
||||
The navigation is handled using the `navigateToApp` API, meaning that the path will be automatically picked up by your `react-router-dom` **Route** component, so all you have top do is configure a Route that handles the path `/my-unique-rule/:id`.
|
||||
The navigation is handled using the `navigateToUrl` API, meaning that the path will be automatically picked up by your `react-router-dom` **Route** component, so all you have top do is configure a Route that handles the path `/my-unique-rule/:id`.
|
||||
|
||||
You can look at the `alerting-example` plugin to see an example of using this API, which is enabled using the `--run-examples` flag when you run `yarn start`.
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ export * from './rule';
|
|||
export * from './rules_settings';
|
||||
export * from './rule_type';
|
||||
export * from './rule_task_instance';
|
||||
export * from './rule_navigation';
|
||||
export * from './alert_instance';
|
||||
export * from './alert_summary';
|
||||
export * from './builtin_action_groups';
|
||||
|
|
|
@ -147,6 +147,7 @@ export interface Rule<Params extends RuleTypeParams = never> {
|
|||
lastRun?: RuleLastRun | null;
|
||||
nextRun?: Date | null;
|
||||
running?: boolean | null;
|
||||
viewInAppRelativeUrl?: string;
|
||||
}
|
||||
|
||||
export type SanitizedRule<Params extends RuleTypeParams = never> = Omit<Rule<Params>, 'apiKey'>;
|
||||
|
|
|
@ -1,15 +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 { JsonObject } from '@kbn/utility-types';
|
||||
export interface RuleUrlNavigation {
|
||||
path: string;
|
||||
}
|
||||
export interface RuleStateNavigation {
|
||||
state: JsonObject;
|
||||
}
|
||||
export type RuleNavigation = RuleUrlNavigation | RuleStateNavigation;
|
|
@ -31,7 +31,7 @@ const mockRuleType = (id: string): RuleType => ({
|
|||
|
||||
describe('AlertNavigationRegistry', () => {
|
||||
function handler(rule: SanitizedRule) {
|
||||
return {};
|
||||
return '';
|
||||
}
|
||||
|
||||
describe('has()', () => {
|
||||
|
@ -151,7 +151,7 @@ describe('AlertNavigationRegistry', () => {
|
|||
const registry = new AlertNavigationRegistry();
|
||||
|
||||
function indexThresholdHandler(rule: SanitizedRule) {
|
||||
return {};
|
||||
return '';
|
||||
}
|
||||
|
||||
const indexThresholdRuleType = mockRuleType('indexThreshold');
|
||||
|
@ -163,7 +163,7 @@ describe('AlertNavigationRegistry', () => {
|
|||
const registry = new AlertNavigationRegistry();
|
||||
|
||||
function defaultHandler(rule: SanitizedRule) {
|
||||
return {};
|
||||
return '';
|
||||
}
|
||||
|
||||
registry.registerDefault('siem', defaultHandler);
|
||||
|
@ -173,10 +173,10 @@ describe('AlertNavigationRegistry', () => {
|
|||
test('returns default handlers by consumer when there are other rule type handler', () => {
|
||||
const registry = new AlertNavigationRegistry();
|
||||
|
||||
registry.register('siem', mockRuleType('indexThreshold').id, () => ({}));
|
||||
registry.register('siem', mockRuleType('indexThreshold').id, () => '');
|
||||
|
||||
function defaultHandler(rule: SanitizedRule) {
|
||||
return {};
|
||||
return '';
|
||||
}
|
||||
|
||||
registry.registerDefault('siem', defaultHandler);
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { JsonObject } from '@kbn/utility-types';
|
||||
import { SanitizedRule } from '../../common';
|
||||
|
||||
/**
|
||||
|
@ -17,4 +16,4 @@ import { SanitizedRule } from '../../common';
|
|||
* originally registered to {@link PluginSetupContract.registerNavigation}.
|
||||
*
|
||||
*/
|
||||
export type AlertNavigationHandler = (rule: SanitizedRule) => JsonObject | string;
|
||||
export type AlertNavigationHandler = (rule: SanitizedRule) => string;
|
||||
|
|
|
@ -118,6 +118,7 @@ export function transformRule(input: ApiRule): Rule {
|
|||
next_run: nextRun,
|
||||
last_run: lastRun,
|
||||
monitoring: monitoring,
|
||||
view_in_app_relative_url: viewInAppRelativeUrl,
|
||||
...rest
|
||||
} = input;
|
||||
|
||||
|
@ -135,6 +136,7 @@ export function transformRule(input: ApiRule): Rule {
|
|||
executionStatus: transformExecutionStatus(executionStatusAPI),
|
||||
actions: actionsAPI ? actionsAPI.map((action) => transformAction(action)) : [],
|
||||
scheduledTaskId,
|
||||
...(viewInAppRelativeUrl ? { viewInAppRelativeUrl } : {}),
|
||||
...(nextRun ? { nextRun: new Date(nextRun) } : {}),
|
||||
...(monitoring ? { monitoring: transformMonitoring(monitoring) } : {}),
|
||||
...(lastRun ? { lastRun: transformLastRun(lastRun) } : {}),
|
||||
|
|
35
x-pack/plugins/alerting/public/plugin.test.ts
Normal file
35
x-pack/plugins/alerting/public/plugin.test.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 { AlertingPublicPlugin } from './plugin';
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
|
||||
jest.mock('./alert_api', () => ({
|
||||
loadRule: jest.fn(),
|
||||
loadRuleType: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('Alerting Public Plugin', () => {
|
||||
describe('start()', () => {
|
||||
it(`should fallback to the viewInAppRelativeUrl part of the rule object if navigation isn't registered`, async () => {
|
||||
const { loadRule, loadRuleType } = jest.requireMock('./alert_api');
|
||||
loadRule.mockResolvedValue({
|
||||
alertTypeId: 'foo',
|
||||
consumer: 'abc',
|
||||
viewInAppRelativeUrl: '/my/custom/path',
|
||||
});
|
||||
loadRuleType.mockResolvedValue({});
|
||||
|
||||
const plugin = new AlertingPublicPlugin();
|
||||
plugin.setup();
|
||||
const pluginStart = plugin.start(coreMock.createStart());
|
||||
|
||||
const navigationPath = await pluginStart.getNavigation('123');
|
||||
expect(navigationPath).toEqual('/my/custom/path');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,11 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CoreSetup, Plugin, CoreStart } from '@kbn/core/public';
|
||||
import { Plugin, CoreStart } from '@kbn/core/public';
|
||||
|
||||
import { AlertNavigationRegistry, AlertNavigationHandler } from './alert_navigation_registry';
|
||||
import { loadRule, loadRuleType } from './alert_api';
|
||||
import { Rule, RuleNavigation } from '../common';
|
||||
import { Rule } from '../common';
|
||||
|
||||
export interface PluginSetupContract {
|
||||
/**
|
||||
|
@ -26,6 +26,8 @@ export interface PluginSetupContract {
|
|||
* @param handler The navigation handler should return either a relative URL, or a state object. This information can be used,
|
||||
* in conjunction with the consumer id, to navigate the user to a custom URL to view a rule's details.
|
||||
* @throws an error if the given applicationId and ruleType combination has already been registered.
|
||||
*
|
||||
* @deprecated use "getViewInAppRelativeUrl" on the server side rule type instead.
|
||||
*/
|
||||
registerNavigation: (
|
||||
applicationId: string,
|
||||
|
@ -42,16 +44,18 @@ export interface PluginSetupContract {
|
|||
* @param applicationId The application id that the user should be navigated to, to view a particular rule in a custom way.
|
||||
* @param handler The navigation handler should return either a relative URL, or a state object. This information can be used,
|
||||
* in conjunction with the consumer id, to navigate the user to a custom URL to view a rule's details.
|
||||
*
|
||||
* @deprecated use "getViewInAppRelativeUrl" on the server side rule type instead.
|
||||
*/
|
||||
registerDefaultNavigation: (applicationId: string, handler: AlertNavigationHandler) => void;
|
||||
}
|
||||
export interface PluginStartContract {
|
||||
getNavigation: (ruleId: Rule['id']) => Promise<RuleNavigation | undefined>;
|
||||
getNavigation: (ruleId: Rule['id']) => Promise<string | undefined>;
|
||||
}
|
||||
|
||||
export class AlertingPublicPlugin implements Plugin<PluginSetupContract, PluginStartContract> {
|
||||
private alertNavigationRegistry?: AlertNavigationRegistry;
|
||||
public setup(core: CoreSetup) {
|
||||
public setup() {
|
||||
this.alertNavigationRegistry = new AlertNavigationRegistry();
|
||||
|
||||
const registerNavigation = async (
|
||||
|
@ -89,8 +93,11 @@ export class AlertingPublicPlugin implements Plugin<PluginSetupContract, PluginS
|
|||
|
||||
if (this.alertNavigationRegistry!.has(rule.consumer, ruleType)) {
|
||||
const navigationHandler = this.alertNavigationRegistry!.get(rule.consumer, ruleType);
|
||||
const state = navigationHandler(rule);
|
||||
return typeof state === 'string' ? { path: state } : { state };
|
||||
return navigationHandler(rule);
|
||||
}
|
||||
|
||||
if (rule.viewInAppRelativeUrl) {
|
||||
return rule.viewInAppRelativeUrl;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -39,6 +39,7 @@ const rewriteBodyRes: RewriteResponseCase<SanitizedRule<RuleTypeParams>> = ({
|
|||
isSnoozedUntil,
|
||||
lastRun,
|
||||
nextRun,
|
||||
viewInAppRelativeUrl,
|
||||
...rest
|
||||
}) => ({
|
||||
...rest,
|
||||
|
@ -74,6 +75,7 @@ const rewriteBodyRes: RewriteResponseCase<SanitizedRule<RuleTypeParams>> = ({
|
|||
})),
|
||||
...(lastRun ? { last_run: rewriteRuleLastRun(lastRun) } : {}),
|
||||
...(nextRun ? { next_run: nextRun } : {}),
|
||||
...(viewInAppRelativeUrl ? { view_in_app_relative_url: viewInAppRelativeUrl } : {}),
|
||||
});
|
||||
|
||||
interface BuildGetRulesRouteParams {
|
||||
|
|
|
@ -1,17 +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 { RuleSnooze } from '../../types';
|
||||
import { getRuleSnoozeEndTime } from '../../lib';
|
||||
|
||||
export function calculateIsSnoozedUntil(rule: {
|
||||
muteAll: boolean;
|
||||
snoozeSchedule?: RuleSnooze;
|
||||
}): string | null {
|
||||
const isSnoozedUntil = getRuleSnoozeEndTime(rule);
|
||||
return isSnoozedUntil ? isSnoozedUntil.toISOString() : null;
|
||||
}
|
|
@ -14,7 +14,6 @@ export { buildKueryNodeFilter } from './build_kuery_node_filter';
|
|||
export { generateAPIKeyName } from './generate_api_key_name';
|
||||
export * from './mapped_params_utils';
|
||||
export { apiKeyAsAlertAttributes } from './api_key_as_alert_attributes';
|
||||
export { calculateIsSnoozedUntil } from './calculate_is_snoozed_until';
|
||||
export * from './inject_references';
|
||||
export { parseDate } from './parse_date';
|
||||
export { includeFieldsRequiredForAuthentication } from './include_fields_required_for_authentication';
|
||||
|
|
|
@ -16,14 +16,14 @@ import {
|
|||
RuleWithLegacyId,
|
||||
PartialRuleWithLegacyId,
|
||||
} from '../../types';
|
||||
import { ruleExecutionStatusFromRaw, convertMonitoringFromRawAndVerify } from '../../lib';
|
||||
import {
|
||||
ruleExecutionStatusFromRaw,
|
||||
convertMonitoringFromRawAndVerify,
|
||||
getRuleSnoozeEndTime,
|
||||
} from '../../lib';
|
||||
import { UntypedNormalizedRuleType } from '../../rule_type_registry';
|
||||
import { getActiveScheduledSnoozes } from '../../lib/is_rule_snoozed';
|
||||
import {
|
||||
calculateIsSnoozedUntil,
|
||||
injectReferencesIntoActions,
|
||||
injectReferencesIntoParams,
|
||||
} from '../common';
|
||||
import { injectReferencesIntoActions, injectReferencesIntoParams } from '../common';
|
||||
import { RulesClientContext } from '../types';
|
||||
|
||||
export interface GetAlertFromRawParams {
|
||||
|
@ -98,20 +98,20 @@ export function getPartialRuleFromRaw<Params extends RuleTypeParams>(
|
|||
...s,
|
||||
rRule: {
|
||||
...s.rRule,
|
||||
dtstart: new Date(s.rRule.dtstart),
|
||||
...(s.rRule.until ? { until: new Date(s.rRule.until) } : {}),
|
||||
dtstart: new Date(s.rRule.dtstart).toISOString(),
|
||||
...(s.rRule.until ? { until: new Date(s.rRule.until).toISOString() } : {}),
|
||||
},
|
||||
}));
|
||||
const includeSnoozeSchedule =
|
||||
snoozeSchedule !== undefined && !isEmpty(snoozeSchedule) && !excludeFromPublicApi;
|
||||
const isSnoozedUntil = includeSnoozeSchedule
|
||||
? calculateIsSnoozedUntil({
|
||||
? getRuleSnoozeEndTime({
|
||||
muteAll: partialRawRule.muteAll ?? false,
|
||||
snoozeSchedule,
|
||||
})
|
||||
: null;
|
||||
const includeMonitoring = monitoring && !excludeFromPublicApi;
|
||||
const rule = {
|
||||
const rule: PartialRule<Params> = {
|
||||
id,
|
||||
notifyWhen,
|
||||
...omit(partialRawRule, excludeFromPublicApi ? [...context.fieldsToExcludeFromPublicApi] : ''),
|
||||
|
@ -152,7 +152,23 @@ export function getPartialRuleFromRaw<Params extends RuleTypeParams>(
|
|||
: {}),
|
||||
};
|
||||
|
||||
return includeLegacyId
|
||||
? ({ ...rule, legacyId } as PartialRuleWithLegacyId<Params>)
|
||||
: (rule as PartialRule<Params>);
|
||||
// Need the `rule` object to build a URL
|
||||
if (!excludeFromPublicApi) {
|
||||
const viewInAppRelativeUrl =
|
||||
ruleType.getViewInAppRelativeUrl &&
|
||||
ruleType.getViewInAppRelativeUrl({ rule: rule as Rule<Params> });
|
||||
if (viewInAppRelativeUrl) {
|
||||
rule.viewInAppRelativeUrl = viewInAppRelativeUrl;
|
||||
}
|
||||
}
|
||||
|
||||
if (includeLegacyId) {
|
||||
const result: PartialRuleWithLegacyId<Params> = {
|
||||
...rule,
|
||||
legacyId,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
|
|
@ -1472,5 +1472,38 @@ describe('Execution Handler', () => {
|
|||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('sets the rule.url to the value from getViewInAppRelativeUrl when the rule type has it defined', async () => {
|
||||
const execParams = {
|
||||
...defaultExecutionParams,
|
||||
rule: ruleWithUrl,
|
||||
taskRunnerContext: {
|
||||
...defaultExecutionParams.taskRunnerContext,
|
||||
kibanaBaseUrl: 'http://localhost:12345',
|
||||
},
|
||||
ruleType: {
|
||||
...ruleType,
|
||||
getViewInAppRelativeUrl() {
|
||||
return '/app/management/some/other/place';
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const executionHandler = new ExecutionHandler(generateExecutionParams(execParams));
|
||||
await executionHandler.run(generateAlert({ id: 1 }));
|
||||
|
||||
expect(injectActionParamsMock.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"actionParams": Object {
|
||||
"val": "rule url: http://localhost:12345/s/test1/app/management/some/other/place",
|
||||
},
|
||||
"actionTypeId": "test",
|
||||
"ruleId": "1",
|
||||
"spaceId": "test1",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -270,8 +270,8 @@ export class ExecutionHandler<
|
|||
kibanaBaseUrl: this.taskRunnerContext.kibanaBaseUrl,
|
||||
alertParams: this.rule.params,
|
||||
actionParams: action.params,
|
||||
ruleUrl: this.buildRuleUrl(spaceId),
|
||||
flapping: executableAlert.getFlapping(),
|
||||
ruleUrl: this.buildRuleUrl(spaceId),
|
||||
}),
|
||||
}),
|
||||
};
|
||||
|
@ -409,11 +409,13 @@ export class ExecutionHandler<
|
|||
return;
|
||||
}
|
||||
|
||||
const relativePath = this.ruleType.getViewInAppRelativeUrl
|
||||
? this.ruleType.getViewInAppRelativeUrl({ rule: this.rule })
|
||||
: `${triggersActionsRoute}${getRuleDetailsRoute(this.rule.id)}`;
|
||||
|
||||
try {
|
||||
const ruleUrl = new URL(
|
||||
`${
|
||||
spaceId !== 'default' ? `/s/${spaceId}` : ''
|
||||
}${triggersActionsRoute}${getRuleDetailsRoute(this.rule.id)}`,
|
||||
`${spaceId !== 'default' ? `/s/${spaceId}` : ''}${relativePath}`,
|
||||
this.taskRunnerContext.kibanaBaseUrl
|
||||
);
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ import {
|
|||
RuleSnooze,
|
||||
IntervalSchedule,
|
||||
RuleLastRun,
|
||||
SanitizedRule,
|
||||
} from '../common';
|
||||
import { PublicAlertFactory } from './alert/create_alert_factory';
|
||||
import { FieldMap } from '../common/alert_schema/field_maps/types';
|
||||
|
@ -161,6 +162,12 @@ export interface SummarizedAlerts {
|
|||
};
|
||||
}
|
||||
export type GetSummarizedAlertsFn = (opts: GetSummarizedAlertsFnOpts) => Promise<SummarizedAlerts>;
|
||||
export interface GetViewInAppRelativeUrlFnOpts<Params extends RuleTypeParams> {
|
||||
rule: Omit<SanitizedRule<Params>, 'viewInAppRelativeUrl'>;
|
||||
}
|
||||
export type GetViewInAppRelativeUrlFn<Params extends RuleTypeParams> = (
|
||||
opts: GetViewInAppRelativeUrlFnOpts<Params>
|
||||
) => string;
|
||||
export interface IRuleTypeAlerts {
|
||||
context: string;
|
||||
namespace?: string;
|
||||
|
@ -218,6 +225,7 @@ export interface RuleType<
|
|||
* automatically make recovery determination. Defaults to true.
|
||||
*/
|
||||
autoRecoverAlerts?: boolean;
|
||||
getViewInAppRelativeUrl?: GetViewInAppRelativeUrlFn<Params>;
|
||||
}
|
||||
export type UntypedRuleType = RuleType<
|
||||
RuleTypeParams,
|
||||
|
|
|
@ -149,6 +149,6 @@ export function registerNavigation(alerting: AlertingSetup) {
|
|||
]),
|
||||
];
|
||||
|
||||
return formatExplorerUrl('', { jobIds });
|
||||
return formatExplorerUrl('/app/ml', { jobIds });
|
||||
});
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ function registerNavigation(alerting: AlertingSetup) {
|
|||
PLUGIN_ID,
|
||||
ES_QUERY_ALERT_TYPE,
|
||||
(alert: SanitizedRule<EsQueryRuleParams<SearchType.searchSource>>) => {
|
||||
return `#/viewAlert/${alert.id}`;
|
||||
return `/app/discover#/viewAlert/${alert.id}`;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ const expectedTransformResult = [
|
|||
{ description: 'The type of rule.', name: 'rule.type' },
|
||||
{
|
||||
description:
|
||||
'The URL to the Stack Management rule page that generated the alert. This will be an empty string if the server.publicBaseUrl is not configured.',
|
||||
'The URL to the rule that generated the alert. This will be an empty string if the server.publicBaseUrl is not configured.',
|
||||
name: 'rule.url',
|
||||
usesPublicBaseUrl: true,
|
||||
},
|
||||
|
@ -171,7 +171,7 @@ const expectedSummaryTransformResult = [
|
|||
},
|
||||
{
|
||||
description:
|
||||
'The URL to the Stack Management rule page that generated the alert. This will be an empty string if the server.publicBaseUrl is not configured.',
|
||||
'The URL to the rule that generated the alert. This will be an empty string if the server.publicBaseUrl is not configured.',
|
||||
name: 'rule.url',
|
||||
usesPublicBaseUrl: true,
|
||||
},
|
||||
|
|
|
@ -114,7 +114,7 @@ const AlertProvidedActionVariableDescriptions = {
|
|||
name: AlertProvidedActionVariables.ruleUrl,
|
||||
description: i18n.translate('xpack.triggersActionsUI.actionVariables.ruleUrlLabel', {
|
||||
defaultMessage:
|
||||
'The URL to the Stack Management rule page that generated the alert. This will be an empty string if the server.publicBaseUrl is not configured.',
|
||||
'The URL to the rule that generated the alert. This will be an empty string if the server.publicBaseUrl is not configured.',
|
||||
}),
|
||||
usesPublicBaseUrl: true,
|
||||
},
|
||||
|
|
|
@ -12,11 +12,6 @@ import { CoreStart } from '@kbn/core/public';
|
|||
import { fromNullable, fold } from 'fp-ts/lib/Option';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
|
||||
import {
|
||||
RuleNavigation,
|
||||
RuleStateNavigation,
|
||||
RuleUrlNavigation,
|
||||
} from '@kbn/alerting-plugin/common';
|
||||
import { Rule } from '../../../../types';
|
||||
import { useKibana } from '../../../../common/lib/kibana';
|
||||
|
||||
|
@ -26,11 +21,12 @@ export interface ViewInAppProps {
|
|||
|
||||
const NO_NAVIGATION = false;
|
||||
|
||||
type RuleNavigationLoadingState = RuleNavigation | false | null;
|
||||
type RuleNavigationLoadingState = string | false | null;
|
||||
|
||||
export const ViewInApp: React.FunctionComponent<ViewInAppProps> = ({ rule }) => {
|
||||
const {
|
||||
application: { navigateToApp },
|
||||
application: { navigateToUrl },
|
||||
http: { basePath },
|
||||
alerting: maybeAlerting,
|
||||
} = useKibana().services;
|
||||
|
||||
|
@ -62,7 +58,7 @@ export const ViewInApp: React.FunctionComponent<ViewInAppProps> = ({ rule }) =>
|
|||
isLoading={ruleNavigation === null}
|
||||
disabled={!hasNavigation(ruleNavigation)}
|
||||
iconType="popout"
|
||||
{...getNavigationHandler(ruleNavigation, rule, navigateToApp)}
|
||||
{...getNavigationHandler(ruleNavigation, rule, navigateToUrl, basePath)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.triggersActionsUI.sections.ruleDetails.viewRuleInAppButtonLabel"
|
||||
|
@ -72,23 +68,20 @@ export const ViewInApp: React.FunctionComponent<ViewInAppProps> = ({ rule }) =>
|
|||
);
|
||||
};
|
||||
|
||||
function hasNavigation(
|
||||
ruleNavigation: RuleNavigationLoadingState
|
||||
): ruleNavigation is RuleStateNavigation | RuleUrlNavigation {
|
||||
return ruleNavigation
|
||||
? ruleNavigation.hasOwnProperty('state') || ruleNavigation.hasOwnProperty('path')
|
||||
: NO_NAVIGATION;
|
||||
function hasNavigation(ruleNavigation: RuleNavigationLoadingState): ruleNavigation is string {
|
||||
return typeof ruleNavigation === 'string';
|
||||
}
|
||||
|
||||
function getNavigationHandler(
|
||||
ruleNavigation: RuleNavigationLoadingState,
|
||||
rule: Rule,
|
||||
navigateToApp: CoreStart['application']['navigateToApp']
|
||||
navigateToUrl: CoreStart['application']['navigateToUrl'],
|
||||
basePath: CoreStart['http']['basePath']
|
||||
): object {
|
||||
return hasNavigation(ruleNavigation)
|
||||
? {
|
||||
onClick: () => {
|
||||
navigateToApp(rule.consumer, ruleNavigation);
|
||||
navigateToUrl(basePath.prepend(ruleNavigation));
|
||||
},
|
||||
}
|
||||
: {};
|
||||
|
|
|
@ -24,7 +24,7 @@ export class AlertingFixturePlugin implements Plugin<Setup, Start, AlertingExamp
|
|||
alerting.registerNavigation(
|
||||
'alerting_fixture',
|
||||
'test.noop',
|
||||
(alert: SanitizedRule) => `/rule/${alert.id}`
|
||||
(alert: SanitizedRule) => `/app/alerting_fixture/rule/${alert.id}`
|
||||
);
|
||||
|
||||
triggersActionsUi.ruleTypeRegistry.register({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue