From 647edf7f58d034a625e10234ccec52b4af47546c Mon Sep 17 00:00:00 2001 From: Gilles De Mey Date: Thu, 20 Apr 2023 11:29:37 +0200 Subject: [PATCH] Alerting: Fix share URL for Prometheus rules on subpath (#66752) (#66802) --- .../alerting/unified/RedirectToRuleViewer.tsx | 7 +++++-- .../components/rules/RuleActionsButtons.tsx | 18 ++++------------ .../rules/RuleDetailsActionButtons.tsx | 14 +++---------- .../features/alerting/unified/utils/misc.ts | 12 +++++++++-- .../features/alerting/unified/utils/url.ts | 21 ++++++++++++++++++- 5 files changed, 42 insertions(+), 30 deletions(-) diff --git a/public/app/features/alerting/unified/RedirectToRuleViewer.tsx b/public/app/features/alerting/unified/RedirectToRuleViewer.tsx index e7f10bd1aee..faecfd358ec 100644 --- a/public/app/features/alerting/unified/RedirectToRuleViewer.tsx +++ b/public/app/features/alerting/unified/RedirectToRuleViewer.tsx @@ -4,6 +4,7 @@ import { Redirect } from 'react-router-dom'; import { useLocation } from 'react-use'; import { GrafanaTheme2 } from '@grafana/data'; +import { config } from '@grafana/runtime'; import { Alert, Card, Icon, LoadingPlaceholder, useStyles2, withErrorBoundary } from '@grafana/ui'; import { AlertLabels } from './components/AlertLabels'; @@ -13,6 +14,7 @@ import { getRulesSourceByName } from './utils/datasource'; import { createViewLink } from './utils/misc'; const pageTitle = 'Find rule'; +const subUrl = config.appSubUrl; function useRuleFindParams() { // DO NOT USE REACT-ROUTER HOOKS FOR THIS CODE @@ -22,7 +24,7 @@ function useRuleFindParams() { // Relevant issue: https://github.com/remix-run/history/issues/505#issuecomment-453175833 // It was probably fixed in React-Router v6 const location = useLocation(); - const segments = location.pathname?.split('/') ?? []; // ["", "alerting", "{sourceName}", "{name}] + const segments = location.pathname?.replace(subUrl, '').split('/') ?? []; // ["", "alerting", "{sourceName}", "{name}] const name = decodeURIComponent(segments[3]); const sourceName = decodeURIComponent(segments[2]); @@ -76,7 +78,8 @@ export function RedirectToRuleViewer(): JSX.Element | null { if (rules.length === 1) { const [rule] = rules; - return ; + const to = createViewLink(rulesSource, rule, '/alerting/list').replace(subUrl, ''); + return ; } return ( diff --git a/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx b/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx index e912a06f172..a40c26cec1d 100644 --- a/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx +++ b/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx @@ -4,7 +4,6 @@ import { useLocation } from 'react-router-dom'; import { GrafanaTheme2 } from '@grafana/data'; import { Stack } from '@grafana/experimental'; -import { config } from '@grafana/runtime'; import { Button, ClipboardButton, ConfirmModal, LinkButton, Tooltip, useStyles2 } from '@grafana/ui'; import { useAppNotification } from 'app/core/copy/appNotification'; import { useDispatch } from 'app/types'; @@ -12,8 +11,8 @@ import { CombinedRule, RulesSource } from 'app/types/unified-alerting'; import { useIsRuleEditable } from '../../hooks/useIsRuleEditable'; import { deleteRuleAction } from '../../state/actions'; -import { getRulesSourceName, isCloudRulesSource } from '../../utils/datasource'; -import { createViewLink } from '../../utils/misc'; +import { getRulesSourceName } from '../../utils/datasource'; +import { createShareLink, createViewLink } from '../../utils/misc'; import * as ruleId from '../../utils/rule-id'; import { isFederatedRuleGroup, isGrafanaRulerRule } from '../../utils/rules'; import { createUrl } from '../../utils/url'; @@ -59,16 +58,7 @@ export const RuleActionsButtons: FC = ({ rule, rulesSource }) => { } }; - const buildShareUrl = () => { - if (isCloudRulesSource(rulesSource)) { - const { appUrl, appSubUrl } = config; - const baseUrl = appSubUrl !== '' ? `${appUrl}${appSubUrl}/` : config.appUrl; - const ruleUrl = `${encodeURIComponent(rulesSource.name)}/${encodeURIComponent(rule.name)}`; - return `${baseUrl}alerting/${ruleUrl}/find`; - } - - return window.location.href.split('?')[0]; - }; + const buildShareUrl = () => createShareLink(rulesSource, rule); const sourceName = getRulesSourceName(rulesSource); @@ -83,7 +73,7 @@ export const RuleActionsButtons: FC = ({ rule, rulesSource }) => { variant="secondary" icon="eye" href={createViewLink(rulesSource, rule, returnTo)} - > + /> ); } diff --git a/public/app/features/alerting/unified/components/rules/RuleDetailsActionButtons.tsx b/public/app/features/alerting/unified/components/rules/RuleDetailsActionButtons.tsx index f66b3eab7b7..cfbc23b4792 100644 --- a/public/app/features/alerting/unified/components/rules/RuleDetailsActionButtons.tsx +++ b/public/app/features/alerting/unified/components/rules/RuleDetailsActionButtons.tsx @@ -21,7 +21,7 @@ import { getRulesPermissions } from '../../utils/access-control'; import { getAlertmanagerByUid } from '../../utils/alertmanager'; import { Annotation } from '../../utils/constants'; import { getRulesSourceName, isCloudRulesSource, isGrafanaRulesSource } from '../../utils/datasource'; -import { createExploreLink, makeRuleBasedSilenceLink } from '../../utils/misc'; +import { createExploreLink, createShareLink, makeRuleBasedSilenceLink } from '../../utils/misc'; import * as ruleId from '../../utils/rule-id'; import { isAlertingRule, isFederatedRuleGroup, isGrafanaRulerRule } from '../../utils/rules'; import { DeclareIncident } from '../bridges/DeclareIncidentButton'; @@ -67,16 +67,6 @@ export const RuleDetailsActionButtons: FC = ({ rule, rulesSource, isViewM setRuleToDelete(undefined); } }; - const buildShareUrl = () => { - if (isCloudRulesSource(rulesSource)) { - const { appUrl, appSubUrl } = config; - const baseUrl = appSubUrl !== '' ? `${appUrl}${appSubUrl}/` : config.appUrl; - const ruleUrl = `${encodeURIComponent(rulesSource.name)}/${encodeURIComponent(rule.name)}`; - return `${baseUrl}alerting/${ruleUrl}/find`; - } - - return window.location.href.split('?')[0]; - }; const isFederated = isFederatedRuleGroup(group); const rulesSourceName = getRulesSourceName(rulesSource); @@ -89,6 +79,8 @@ export const RuleDetailsActionButtons: FC = ({ rule, rulesSource, isViewM const { isEditable, isRemovable } = useIsRuleEditable(rulesSourceName, rulerRule); const canSilence = useCanSilence(rule); + const buildShareUrl = () => createShareLink(rulesSource, rule); + const returnTo = location.pathname + location.search; // explore does not support grafana rule queries atm // neither do "federated rules" diff --git a/public/app/features/alerting/unified/utils/misc.ts b/public/app/features/alerting/unified/utils/misc.ts index 181ccd33dfe..35aa45348cd 100644 --- a/public/app/features/alerting/unified/utils/misc.ts +++ b/public/app/features/alerting/unified/utils/misc.ts @@ -11,10 +11,10 @@ import { } from 'app/types/unified-alerting-dto'; import { ALERTMANAGER_NAME_QUERY_KEY } from './constants'; -import { getRulesSourceName } from './datasource'; +import { getRulesSourceName, isCloudRulesSource } from './datasource'; import { getMatcherQueryParams } from './matchers'; import * as ruleId from './rule-id'; -import { createUrl } from './url'; +import { createAbsoluteUrl, createUrl } from './url'; export function createViewLink(ruleSource: RulesSource, rule: CombinedRule, returnTo: string): string { const sourceName = getRulesSourceName(ruleSource); @@ -35,6 +35,14 @@ export function createExploreLink(dataSourceName: string, query: string) { }); } +export function createShareLink(ruleSource: RulesSource, rule: CombinedRule): string { + if (isCloudRulesSource(ruleSource)) { + return createAbsoluteUrl(`/alerting/${encodeURIComponent(ruleSource.name)}/${encodeURIComponent(rule.name)}/find`); + } + + return window.location.href.split('?')[0]; +} + export function arrayToRecord(items: Array<{ key: string; value: string }>): Record { return items.reduce>((rec, { key, value }) => { rec[key] = value; diff --git a/public/app/features/alerting/unified/utils/url.ts b/public/app/features/alerting/unified/utils/url.ts index 8499598eaa0..b712e7ab1e6 100644 --- a/public/app/features/alerting/unified/utils/url.ts +++ b/public/app/features/alerting/unified/utils/url.ts @@ -2,5 +2,24 @@ import { config } from '@grafana/runtime'; export function createUrl(path: string, queryParams?: string[][] | Record | string | URLSearchParams) { const searchParams = new URLSearchParams(queryParams); - return `${config.appSubUrl}${path}?${searchParams.toString()}`; + const searchParamsString = searchParams.toString(); + + return `${config.appSubUrl}${path}${searchParamsString ? `?${searchParamsString}` : ''}`; +} + +export function createAbsoluteUrl( + path: string, + queryParams?: string[][] | Record | string | URLSearchParams +) { + const searchParams = new URLSearchParams(queryParams); + const searchParamsString = searchParams.toString(); + + try { + const baseUrl = new URL(config.appSubUrl, config.appUrl); + baseUrl.pathname = path; + + return `${baseUrl.href}${searchParamsString ? `?${searchParamsString}` : ''}`; + } catch (err) { + return createUrl(path, queryParams); + } }