From ccd78c9f0e6898544a824682c47465a64e3632a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Thu, 16 Feb 2023 14:09:48 -0500 Subject: [PATCH] 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> --- .../public/alert_types/astros.tsx | 2 +- .../public/alert_types/index.ts | 2 +- .../server/alert_types/astros.ts | 3 ++ x-pack/plugins/alerting/README.md | 2 +- x-pack/plugins/alerting/common/index.ts | 1 - x-pack/plugins/alerting/common/rule.ts | 1 + .../alerting/common/rule_navigation.ts | 15 ------- .../alert_navigation_registry.test.ts | 10 ++--- .../public/alert_navigation_registry/types.ts | 3 +- .../public/lib/common_transformations.ts | 2 + x-pack/plugins/alerting/public/plugin.test.ts | 35 ++++++++++++++++ x-pack/plugins/alerting/public/plugin.ts | 19 ++++++--- .../alerting/server/routes/get_rule.ts | 2 + .../common/calculate_is_snoozed_until.ts | 17 -------- .../server/rules_client/common/index.ts | 1 - .../rules_client/lib/get_alert_from_raw.ts | 42 +++++++++++++------ .../task_runner/execution_handler.test.ts | 33 +++++++++++++++ .../server/task_runner/execution_handler.ts | 10 +++-- x-pack/plugins/alerting/server/types.ts | 8 ++++ .../ml/public/alerting/register_ml_alerts.ts | 2 +- .../public/rule_types/es_query/index.ts | 2 +- .../application/lib/action_variables.test.ts | 4 +- .../application/lib/action_variables.ts | 2 +- .../rule_details/components/view_in_app.tsx | 25 ++++------- .../plugins/alerts/public/plugin.ts | 2 +- 25 files changed, 156 insertions(+), 89 deletions(-) delete mode 100644 x-pack/plugins/alerting/common/rule_navigation.ts create mode 100644 x-pack/plugins/alerting/public/plugin.test.ts delete mode 100644 x-pack/plugins/alerting/server/rules_client/common/calculate_is_snoozed_until.ts diff --git a/x-pack/examples/alerting_example/public/alert_types/astros.tsx b/x-pack/examples/alerting_example/public/alert_types/astros.tsx index eee4e92b623f7..6155959024053 100644 --- a/x-pack/examples/alerting_example/public/alert_types/astros.tsx +++ b/x-pack/examples/alerting_example/public/alert_types/astros.tsx @@ -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}` ); } diff --git a/x-pack/examples/alerting_example/public/alert_types/index.ts b/x-pack/examples/alerting_example/public/alert_types/index.ts index 4cb30b9f4df02..44712b69fb34b 100644 --- a/x-pack/examples/alerting_example/public/alert_types/index.ts +++ b/x-pack/examples/alerting_example/public/alert_types/index.ts @@ -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); diff --git a/x-pack/examples/alerting_example/server/alert_types/astros.ts b/x-pack/examples/alerting_example/server/alert_types/astros.ts index 64accdc550298..c73ec8ecbc8bf 100644 --- a/x-pack/examples/alerting_example/server/alert_types/astros.ts +++ b/x-pack/examples/alerting_example/server/alert_types/astros.ts @@ -81,4 +81,7 @@ export const alertType: RuleType< }; }, producer: ALERTING_EXAMPLE_APP_ID, + getViewInAppRelativeUrl({ rule }) { + return `/app/${ALERTING_EXAMPLE_APP_ID}/astros/${rule.id}`; + }, }; diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index 858e79e30c7ae..f547c05e52e09 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -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`. diff --git a/x-pack/plugins/alerting/common/index.ts b/x-pack/plugins/alerting/common/index.ts index 9a977213d4446..fafadcdb99ee4 100644 --- a/x-pack/plugins/alerting/common/index.ts +++ b/x-pack/plugins/alerting/common/index.ts @@ -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'; diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index f548441713420..833d514df1f86 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -147,6 +147,7 @@ export interface Rule { lastRun?: RuleLastRun | null; nextRun?: Date | null; running?: boolean | null; + viewInAppRelativeUrl?: string; } export type SanitizedRule = Omit, 'apiKey'>; diff --git a/x-pack/plugins/alerting/common/rule_navigation.ts b/x-pack/plugins/alerting/common/rule_navigation.ts deleted file mode 100644 index abc109a31c432..0000000000000 --- a/x-pack/plugins/alerting/common/rule_navigation.ts +++ /dev/null @@ -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; diff --git a/x-pack/plugins/alerting/public/alert_navigation_registry/alert_navigation_registry.test.ts b/x-pack/plugins/alerting/public/alert_navigation_registry/alert_navigation_registry.test.ts index bedb17270a477..c7bb9a4feb8ba 100644 --- a/x-pack/plugins/alerting/public/alert_navigation_registry/alert_navigation_registry.test.ts +++ b/x-pack/plugins/alerting/public/alert_navigation_registry/alert_navigation_registry.test.ts @@ -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); diff --git a/x-pack/plugins/alerting/public/alert_navigation_registry/types.ts b/x-pack/plugins/alerting/public/alert_navigation_registry/types.ts index 3c7b7aa3c8c06..405f040d5b3ae 100644 --- a/x-pack/plugins/alerting/public/alert_navigation_registry/types.ts +++ b/x-pack/plugins/alerting/public/alert_navigation_registry/types.ts @@ -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; diff --git a/x-pack/plugins/alerting/public/lib/common_transformations.ts b/x-pack/plugins/alerting/public/lib/common_transformations.ts index 6a89b1ce9958b..b123962bb4ea7 100644 --- a/x-pack/plugins/alerting/public/lib/common_transformations.ts +++ b/x-pack/plugins/alerting/public/lib/common_transformations.ts @@ -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) } : {}), diff --git a/x-pack/plugins/alerting/public/plugin.test.ts b/x-pack/plugins/alerting/public/plugin.test.ts new file mode 100644 index 0000000000000..fce03fa48ce0e --- /dev/null +++ b/x-pack/plugins/alerting/public/plugin.test.ts @@ -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'); + }); + }); +}); diff --git a/x-pack/plugins/alerting/public/plugin.ts b/x-pack/plugins/alerting/public/plugin.ts index 57ab4172b4689..2e29a40b7dba0 100644 --- a/x-pack/plugins/alerting/public/plugin.ts +++ b/x-pack/plugins/alerting/public/plugin.ts @@ -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; + getNavigation: (ruleId: Rule['id']) => Promise; } export class AlertingPublicPlugin implements Plugin { 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> = ({ isSnoozedUntil, lastRun, nextRun, + viewInAppRelativeUrl, ...rest }) => ({ ...rest, @@ -74,6 +75,7 @@ const rewriteBodyRes: RewriteResponseCase> = ({ })), ...(lastRun ? { last_run: rewriteRuleLastRun(lastRun) } : {}), ...(nextRun ? { next_run: nextRun } : {}), + ...(viewInAppRelativeUrl ? { view_in_app_relative_url: viewInAppRelativeUrl } : {}), }); interface BuildGetRulesRouteParams { diff --git a/x-pack/plugins/alerting/server/rules_client/common/calculate_is_snoozed_until.ts b/x-pack/plugins/alerting/server/rules_client/common/calculate_is_snoozed_until.ts deleted file mode 100644 index d77a013ae3f6b..0000000000000 --- a/x-pack/plugins/alerting/server/rules_client/common/calculate_is_snoozed_until.ts +++ /dev/null @@ -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; -} diff --git a/x-pack/plugins/alerting/server/rules_client/common/index.ts b/x-pack/plugins/alerting/server/rules_client/common/index.ts index 6019eb0f4307e..ab380a6ca178b 100644 --- a/x-pack/plugins/alerting/server/rules_client/common/index.ts +++ b/x-pack/plugins/alerting/server/rules_client/common/index.ts @@ -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'; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/get_alert_from_raw.ts b/x-pack/plugins/alerting/server/rules_client/lib/get_alert_from_raw.ts index 41330fe9b7eb6..59ed14ecf2735 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/get_alert_from_raw.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/get_alert_from_raw.ts @@ -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( ...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 = { id, notifyWhen, ...omit(partialRawRule, excludeFromPublicApi ? [...context.fieldsToExcludeFromPublicApi] : ''), @@ -152,7 +152,23 @@ export function getPartialRuleFromRaw( : {}), }; - return includeLegacyId - ? ({ ...rule, legacyId } as PartialRuleWithLegacyId) - : (rule as PartialRule); + // Need the `rule` object to build a URL + if (!excludeFromPublicApi) { + const viewInAppRelativeUrl = + ruleType.getViewInAppRelativeUrl && + ruleType.getViewInAppRelativeUrl({ rule: rule as Rule }); + if (viewInAppRelativeUrl) { + rule.viewInAppRelativeUrl = viewInAppRelativeUrl; + } + } + + if (includeLegacyId) { + const result: PartialRuleWithLegacyId = { + ...rule, + legacyId, + }; + return result; + } + + return rule; } diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts index b41db1147f3b7..cc1741fe722fd 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts @@ -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", + }, + ] + `); + }); }); }); diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts index d0784805c5942..9975c8f9e6923 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts @@ -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 ); diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 09493e4357a15..1ecff391be4af 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -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; +export interface GetViewInAppRelativeUrlFnOpts { + rule: Omit, 'viewInAppRelativeUrl'>; +} +export type GetViewInAppRelativeUrlFn = ( + opts: GetViewInAppRelativeUrlFnOpts +) => 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; } export type UntypedRuleType = RuleType< RuleTypeParams, diff --git a/x-pack/plugins/ml/public/alerting/register_ml_alerts.ts b/x-pack/plugins/ml/public/alerting/register_ml_alerts.ts index 75aea773f662b..86446dfb1012e 100644 --- a/x-pack/plugins/ml/public/alerting/register_ml_alerts.ts +++ b/x-pack/plugins/ml/public/alerting/register_ml_alerts.ts @@ -149,6 +149,6 @@ export function registerNavigation(alerting: AlertingSetup) { ]), ]; - return formatExplorerUrl('', { jobIds }); + return formatExplorerUrl('/app/ml', { jobIds }); }); } diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/index.ts b/x-pack/plugins/stack_alerts/public/rule_types/es_query/index.ts index 87be947c22f90..b3358ffad5bc1 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/index.ts +++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/index.ts @@ -48,7 +48,7 @@ function registerNavigation(alerting: AlertingSetup) { PLUGIN_ID, ES_QUERY_ALERT_TYPE, (alert: SanitizedRule>) => { - return `#/viewAlert/${alert.id}`; + return `/app/discover#/viewAlert/${alert.id}`; } ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts index ac94547851155..7a58c9677ca5d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts @@ -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, }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts index 76b6aba407771..212c85cfe1aa7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts @@ -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, }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/view_in_app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/view_in_app.tsx index 3f4375aad9ac6..f364a9d984da5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/view_in_app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/view_in_app.tsx @@ -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 = ({ rule }) => { const { - application: { navigateToApp }, + application: { navigateToUrl }, + http: { basePath }, alerting: maybeAlerting, } = useKibana().services; @@ -62,7 +58,7 @@ export const ViewInApp: React.FunctionComponent = ({ rule }) => isLoading={ruleNavigation === null} disabled={!hasNavigation(ruleNavigation)} iconType="popout" - {...getNavigationHandler(ruleNavigation, rule, navigateToApp)} + {...getNavigationHandler(ruleNavigation, rule, navigateToUrl, basePath)} > = ({ 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)); }, } : {}; diff --git a/x-pack/test/functional_with_es_ssl/plugins/alerts/public/plugin.ts b/x-pack/test/functional_with_es_ssl/plugins/alerts/public/plugin.ts index 43fcb32a0ed20..511bc98f6e3a5 100644 --- a/x-pack/test/functional_with_es_ssl/plugins/alerts/public/plugin.ts +++ b/x-pack/test/functional_with_es_ssl/plugins/alerts/public/plugin.ts @@ -24,7 +24,7 @@ export class AlertingFixturePlugin implements Plugin `/rule/${alert.id}` + (alert: SanitizedRule) => `/app/alerting_fixture/rule/${alert.id}` ); triggersActionsUi.ruleTypeRegistry.register({