Alerting: Refactor createUrl and createAbsoluteUrl (#90667)

* Refactor url creatUrl and add tests

* update createUrl to createRelativeUrl in GrafanaRules.tsx

* Add slash if not present in createAbsoluteUrl

* Use template type forcing url parameter to start with slash instead of adding slash if not present

* address review comment
This commit is contained in:
Sonia Aguilar
2024-07-31 15:30:35 +02:00
committed by GitHub
parent 230416cbb4
commit d080a91e8a
19 changed files with 137 additions and 62 deletions
@@ -12,7 +12,7 @@ import { generateCopiedName } from './utils/duplicate';
import { stringifyErrorLike } from './utils/misc';
import { rulerRuleToFormValues } from './utils/rule-form';
import { getRuleName, isAlertingRulerRule, isGrafanaRulerRule, isRecordingRulerRule } from './utils/rules';
import { createUrl } from './utils/url';
import { createRelativeUrl } from './utils/url';
export function CloneRuleEditor({ sourceRuleId }: { sourceRuleId: RuleIdentifier }) {
const { loading, result: rule, error } = useRuleWithLocation({ ruleIdentifier: sourceRuleId });
@@ -40,7 +40,7 @@ export function CloneRuleEditor({ sourceRuleId }: { sourceRuleId: RuleIdentifier
<Alert
title="Cannot copy the rule. The rule does not exist"
buttonContent="Go back to alert list"
onRemove={() => locationService.replace(createUrl('/alerting/list'))}
onRemove={() => locationService.replace(createRelativeUrl('/alerting/list'))}
/>
);
}
@@ -8,7 +8,7 @@ import ConditionalWrap from 'app/features/alerting/unified/components/Conditiona
import { useExportContactPoint } from 'app/features/alerting/unified/components/contact-points/useExportContactPoint';
import { AlertmanagerAction, useAlertmanagerAbility } from '../../hooks/useAbilities';
import { createUrl } from '../../utils/url';
import { createRelativeUrl } from '../../utils/url';
import MoreButton from '../MoreButton';
import { ProvisioningBadge } from '../Provisioning';
import { Spacer } from '../Spacer';
@@ -96,7 +96,7 @@ export const ContactPointHeader = (props: ContactPointHeaderProps) => {
</Stack>
{isReferencedByAnyPolicy && (
<TextLink
href={createUrl('/alerting/routes', { contactPoint: name })}
href={createRelativeUrl('/alerting/routes', { contactPoint: name })}
variant="bodySmall"
color="primary"
inline={false}
@@ -11,7 +11,7 @@ import { stringifyErrorLike } from '../../utils/misc';
import { formValuesFromExistingRule } from '../../utils/rule-form';
import * as ruleId from '../../utils/rule-id';
import { isGrafanaRulerRule } from '../../utils/rules';
import { createUrl } from '../../utils/url';
import { createRelativeUrl } from '../../utils/url';
import { AlertingPageWrapper } from '../AlertingPageWrapper';
import { ModifyExportRuleForm } from '../rule-editor/alert-rule-form/ModifyExportRuleForm';
@@ -79,7 +79,7 @@ function RuleModifyExport({ ruleIdentifier }: { ruleIdentifier: RuleIdentifier }
<Alert
title="Cannot load the rule. The rule does not exist"
buttonContent="Go back to alert list"
onRemove={() => locationService.replace(createUrl('/alerting/list'))}
onRemove={() => locationService.replace(createRelativeUrl('/alerting/list'))}
/>
);
}
@@ -90,7 +90,7 @@ function RuleModifyExport({ ruleIdentifier }: { ruleIdentifier: RuleIdentifier }
<Alert
title="This rule is not a Grafana-managed alert rule"
buttonContent="Go back to alert list"
onRemove={() => locationService.replace(createUrl('/alerting/list'))}
onRemove={() => locationService.replace(createRelativeUrl('/alerting/list'))}
/>
);
}
@@ -1,7 +1,7 @@
import { css, cx, keyframes } from '@emotion/css';
import { useCallback, useEffect, useState } from 'react';
import * as React from 'react';
import { useFormContext, Controller } from 'react-hook-form';
import { useCallback, useEffect, useState } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import {
@@ -15,7 +15,7 @@ import {
useStyles2,
} from '@grafana/ui';
import { RuleFormValues } from 'app/features/alerting/unified/types/rule-form';
import { createUrl } from 'app/features/alerting/unified/utils/url';
import { createRelativeUrl } from 'app/features/alerting/unified/utils/url';
import { ContactPointWithMetadata } from '../../../../contact-points/utils';
@@ -144,7 +144,7 @@ export function ContactPointSelector({
function LinkToContactPoints() {
const hrefToContactPoints = '/alerting/notifications';
return (
<TextLink external href={createUrl(hrefToContactPoints)} aria-label="View or create contact points">
<TextLink external href={createRelativeUrl(hrefToContactPoints)} aria-label="View or create contact points">
View or create contact points
</TextLink>
);
@@ -11,7 +11,7 @@ import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
import { AlertRuleAction, useAlertRuleAbility } from '../../hooks/useAbilities';
import { createShareLink, isLocalDevEnv, isOpenSourceEdition } from '../../utils/misc';
import * as ruleId from '../../utils/rule-id';
import { createUrl } from '../../utils/url';
import { createRelativeUrl } from '../../utils/url';
import { DeclareIncidentMenuItem } from '../bridges/DeclareIncidentButton';
interface Props {
@@ -123,9 +123,12 @@ type PropsWithIdentifier = { identifier: RuleIdentifier };
const ExportMenuItem = ({ identifier }: PropsWithIdentifier) => {
const returnTo = location.pathname + location.search;
const url = createUrl(`/alerting/${encodeURIComponent(ruleId.stringifyIdentifier(identifier))}/modify-export`, {
returnTo,
});
const url = createRelativeUrl(
`/alerting/${encodeURIComponent(ruleId.stringifyIdentifier(identifier))}/modify-export`,
{
returnTo,
}
);
return <Menu.Item key="with-modifications" label="With modifications" icon="file-edit-alt" url={url} />;
};
@@ -25,7 +25,7 @@ import {
isGrafanaRulerRulePaused,
isRecordingRule,
} from '../../utils/rules';
import { createUrl } from '../../utils/url';
import { createRelativeUrl } from '../../utils/url';
import { AlertLabels } from '../AlertLabels';
import { AlertingPageWrapper } from '../AlertingPageWrapper';
import { ProvisionedResource, ProvisioningAlert } from '../Provisioning';
@@ -222,7 +222,7 @@ const createMetadata = (rule: CombinedRule): PageInfoItem[] => {
// TODO move somewhere else
export const createListFilterLink = (values: Array<[string, string]>) => {
const params = new URLSearchParams([['search', values.map(([key, value]) => `${key}:"${value}"`).join(' ')]]);
return createUrl(`/alerting/list?` + params.toString());
return createRelativeUrl(`/alerting/list`, params);
};
interface TitleProps {
@@ -17,7 +17,7 @@ import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelect
import { getPaginationStyles } from '../../styles/pagination';
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
import { initialAsyncRequestState } from '../../utils/redux';
import { createUrl } from '../../utils/url';
import { createRelativeUrl } from '../../utils/url';
import { GrafanaRulesExporter } from '../export/GrafanaRulesExporter';
import { RulesGroup } from './RulesGroup';
@@ -86,7 +86,7 @@ export const GrafanaRules = ({ namespaces, expandAll }: Props) => {
)}
{grafanaRecordingRulesEnabled && (
<LinkButton
href={createUrl('/alerting/new/grafana-recording', {
href={createRelativeUrl('/alerting/new/grafana-recording', {
returnTo: '/alerting/list' + location.search,
})}
icon="plus"
@@ -19,7 +19,7 @@ import { GRAFANA_RULES_SOURCE_NAME, getRulesSourceName } from '../../utils/datas
import { createViewLink } from '../../utils/misc';
import * as ruleId from '../../utils/rule-id';
import { isGrafanaAlertingRule, isGrafanaRulerRule } from '../../utils/rules';
import { createUrl } from '../../utils/url';
import { createRelativeUrl } from '../../utils/url';
import { RedirectToCloneRule } from './CloneRule';
@@ -95,7 +95,7 @@ export const RuleActionsButtons = ({ compact, showViewButton, showCopyLinkButton
if (rulerRule && canEditRule) {
const identifier = ruleId.fromRulerRule(sourceName, namespace.name, group.name, rulerRule);
const editURL = createUrl(`/alerting/${encodeURIComponent(ruleId.stringifyIdentifier(identifier))}/edit`, {
const editURL = createRelativeUrl(`/alerting/${encodeURIComponent(ruleId.stringifyIdentifier(identifier))}/edit`, {
returnTo,
});
@@ -28,7 +28,7 @@ import { usePagination } from '../../../hooks/usePagination';
import { combineMatcherStrings, labelsMatchMatchers } from '../../../utils/alertmanager';
import { GRAFANA_RULES_SOURCE_NAME } from '../../../utils/datasource';
import { parsePromQLStyleMatcherLooseSafe } from '../../../utils/matchers';
import { createUrl } from '../../../utils/url';
import { createRelativeUrl } from '../../../utils/url';
import { AlertLabels } from '../../AlertLabels';
import { CollapseToggle } from '../../CollapseToggle';
import { LogRecord } from '../state-history/common';
@@ -240,7 +240,7 @@ function AlertRuleName({ labels, ruleUID }: AlertRuleNameProps) {
</Text>
);
}
const ruleViewUrl = createUrl(`/alerting/${GRAFANA_RULES_SOURCE_NAME}/${ruleUID}/view`, {
const ruleViewUrl = createRelativeUrl(`/alerting/${GRAFANA_RULES_SOURCE_NAME}/${ruleUID}/view`, {
tab: 'history',
returnTo,
});
@@ -8,7 +8,7 @@ import {
isProvisionedDataSource,
isVanillaPrometheusAlertManagerDataSource,
} from '../../utils/datasource';
import { createUrl } from '../../utils/url';
import { createRelativeUrl } from '../../utils/url';
import { AlertmanagerCard } from './AlertmanagerCard';
import { useSettings } from './SettingsContext';
@@ -50,8 +50,9 @@ export const ExternalAlertmanagers = ({ onEditConfiguration }: Props) => {
const isReceiving = isReceivingGrafanaAlerts(alertmanager);
const isProvisioned = isProvisionedDataSource(alertmanager.dataSourceSettings);
const isReadOnly = isVanillaPrometheusAlertManagerDataSource(alertmanager.dataSourceSettings.name);
const detailHref = createUrl(DATASOURCES_ROUTES.Edit.replace(/:uid/gi, uid));
// typescript on next line is wrong, as DATASOURCES_ROUTES.Edit is a RelativeUrl type, and replacing :uid with uid makes still a RelativeUrl
// @ts-ignore
const detailHref = createRelativeUrl(DATASOURCES_ROUTES.Edit.replace(/:uid/gi, uid));
const handleEditConfiguration = () => onEditConfiguration(name);
const handleEnable = forwardingDisabled ? undefined : () => enableAlertmanager(uid);
@@ -13,7 +13,7 @@ import { DataSourceRef } from '@grafana/schema';
import { Link, useStyles2 } from '@grafana/ui';
import { PANEL_STYLES } from '../../home/Insights';
import { createUrl } from '../../utils/url';
import { createRelativeUrl } from '../../utils/url';
import { InsightsRatingModal } from '../RatingModal';
export function getMostFiredInstancesScene(datasource: DataSourceRef, panelTitle: string) {
@@ -119,7 +119,7 @@ export function RuleLink({ value, ruleUID }: { value: string; ruleUID: string })
return (
<div className={styles.link}>
<Link target="_blank" href={createUrl(`/alerting/grafana/${ruleUID}/view`)}>
<Link target="_blank" href={createRelativeUrl(`/alerting/grafana/${ruleUID}/view`)}>
{value}
</Link>
</div>
@@ -3,7 +3,7 @@ import { lazy, Suspense } from 'react';
import { Drawer, LoadingPlaceholder, Stack, TextLink } from '@grafana/ui';
import { t } from '../../../../core/internationalization';
import { createUrl } from '../utils/url';
import { createRelativeUrl } from '../utils/url';
const AlertRulesDrawerContent = lazy(
() => import(/* webpackChunkName: "alert-rules-drawer-content" */ './AlertRulesDrawerContent')
@@ -30,7 +30,7 @@ function DrawerSubtitle({ dashboardUid }: { dashboardUid: string }) {
return (
<Stack gap={2}>
<div>{t('dashboard.alert-rules-drawer.subtitle', 'Alert rules related to this dashboard')}</div>
<TextLink href={createUrl(`/alerting/list/?${searchParams.toString()}`)}>
<TextLink href={createRelativeUrl(`/alerting/list/?${searchParams.toString()}`)}>
{t('dashboard.alert-rules-drawer.redirect-link', 'List in Grafana Alerting')}
</TextLink>
</Stack>
@@ -19,7 +19,7 @@ import { ALERTMANAGER_NAME_QUERY_KEY } from './constants';
import { getRulesSourceName, isCloudRulesSource } from './datasource';
import { getMatcherQueryParams } from './matchers';
import * as ruleId from './rule-id';
import { createAbsoluteUrl, createUrl } from './url';
import { createAbsoluteUrl, createRelativeUrl } from './url';
export function createViewLink(ruleSource: RulesSource, rule: CombinedRule, returnTo?: string): string {
const sourceName = getRulesSourceName(ruleSource);
@@ -27,13 +27,13 @@ export function createViewLink(ruleSource: RulesSource, rule: CombinedRule, retu
const paramId = encodeURIComponent(ruleId.stringifyIdentifier(identifier));
const paramSource = encodeURIComponent(sourceName);
return createUrl(`/alerting/${paramSource}/${paramId}/view`, returnTo ? { returnTo } : {});
return createRelativeUrl(`/alerting/${paramSource}/${paramId}/view`, returnTo ? { returnTo } : {});
}
export function createExploreLink(datasource: DataSourceRef, query: string) {
const { uid, type } = datasource;
return createUrl(`/explore`, {
return createRelativeUrl(`/explore`, {
left: JSON.stringify({
datasource: datasource.uid,
queries: [{ refId: 'A', datasource: { uid, type }, expr: query }],
@@ -43,13 +43,13 @@ export function createExploreLink(datasource: DataSourceRef, query: string) {
}
export function createContactPointLink(contactPoint: string, alertManagerSourceName = ''): string {
return createUrl(`/alerting/notifications/receivers/${encodeURIComponent(contactPoint)}/edit`, {
return createRelativeUrl(`/alerting/notifications/receivers/${encodeURIComponent(contactPoint)}/edit`, {
alertmanager: alertManagerSourceName,
});
}
export function createMuteTimingLink(muteTimingName: string, alertManagerSourceName = ''): string {
return createUrl('/alerting/routes/mute-timing/edit', {
return createRelativeUrl('/alerting/routes/mute-timing/edit', {
muteName: muteTimingName,
alertmanager: alertManagerSourceName,
});
@@ -125,32 +125,32 @@ export function makeLabelBasedSilenceLink(alertManagerSourceName: string, labels
const matcherParams = getMatcherQueryParams(labels);
matcherParams.forEach((value, key) => silenceUrlParams.append(key, value));
return createUrl('/alerting/silence/new', silenceUrlParams);
return createRelativeUrl('/alerting/silence/new', silenceUrlParams);
}
export function makeDataSourceLink(uid: string) {
return createUrl(`/datasources/edit/${uid}`);
return createRelativeUrl(`/datasources/edit/${uid}`);
}
export function makeFolderLink(folderUID: string): string {
return createUrl(`/dashboards/f/${folderUID}`);
return createRelativeUrl(`/dashboards/f/${folderUID}`);
}
export function makeFolderAlertsLink(folderUID: string, title: string): string {
return createUrl(`/dashboards/f/${folderUID}/${title}/alerting`);
return createRelativeUrl(`/dashboards/f/${folderUID}/${title}/alerting`);
}
export function makeFolderSettingsLink(uid: string): string {
return createUrl(`/dashboards/f/${uid}/settings`);
return createRelativeUrl(`/dashboards/f/${uid}/settings`);
}
export function makeDashboardLink(dashboardUID: string): string {
return createUrl(`/d/${encodeURIComponent(dashboardUID)}`);
return createRelativeUrl(`/d/${encodeURIComponent(dashboardUID)}`);
}
export function makePanelLink(dashboardUID: string, panelId: string): string {
const panelParams = new URLSearchParams({ viewPanel: panelId });
return createUrl(`/d/${encodeURIComponent(dashboardUID)}`, panelParams);
return createRelativeUrl(`/d/${encodeURIComponent(dashboardUID)}`, panelParams);
}
// keep retrying fn if it's error passes shouldRetry(error) and timeout has not elapsed yet
@@ -0,0 +1,65 @@
import { config } from '@grafana/runtime';
import { createAbsoluteUrl, createRelativeUrl } from './url';
config.appSubUrl = '/base';
config.appUrl = 'https://example.com';
describe('createRelativeUrl', () => {
it('should create a relative URL with no query parameters', () => {
expect(createRelativeUrl('/test')).toBe('/base/test');
});
it('should create a relative URL with string query parameters', () => {
expect(createRelativeUrl('/test', 'param1=value1&param2=value2')).toBe('/base/test?param1=value1&param2=value2');
});
it('should create a relative URL with object query parameters', () => {
expect(createRelativeUrl('/test', { param1: 'value1', param2: 'value2' })).toBe(
'/base/test?param1=value1&param2=value2'
);
});
it('should create a relative URL with array query parameters', () => {
expect(
createRelativeUrl('/test', [
['param1', 'value1'],
['param2', 'value2'],
])
).toBe('/base/test?param1=value1&param2=value2');
});
});
describe('createAbsoluteUrl', () => {
it('should create an absolute URL with no query parameters', () => {
expect(createAbsoluteUrl('/test')).toBe('https://example.com/base/test');
});
it('should create an absolute URL with string query parameters', () => {
expect(createAbsoluteUrl('/test', 'param1=value1&param2=value2')).toBe(
'https://example.com/base/test?param1=value1&param2=value2'
);
});
it('should create an absolute URL with object query parameters', () => {
expect(createAbsoluteUrl('/test', { param1: 'value1', param2: 'value2' })).toBe(
'https://example.com/base/test?param1=value1&param2=value2'
);
});
it('should create an absolute URL with array query parameters', () => {
expect(
createAbsoluteUrl('/test', [
['param1', 'value1'],
['param2', 'value2'],
])
).toBe('https://example.com/base/test?param1=value1&param2=value2');
});
it('should fallback to relative URL if base URL is invalid', () => {
const originalAppUrl = config.appUrl;
config.appUrl = 'invalid-url';
expect(createAbsoluteUrl('/test')).toBe('/base/test');
config.appUrl = originalAppUrl;
});
});
@@ -1,6 +1,10 @@
import { config } from '@grafana/runtime';
export function createUrl(path: string, queryParams?: string[][] | Record<string, string> | string | URLSearchParams) {
export type RelativeUrl = `/${string}`;
export function createRelativeUrl(
path: RelativeUrl,
queryParams?: string[][] | Record<string, string> | string | URLSearchParams
) {
const searchParams = new URLSearchParams(queryParams);
const searchParamsString = searchParams.toString();
@@ -8,7 +12,7 @@ export function createUrl(path: string, queryParams?: string[][] | Record<string
}
export function createAbsoluteUrl(
path: string,
path: RelativeUrl,
queryParams?: string[][] | Record<string, string> | string | URLSearchParams
) {
const searchParams = new URLSearchParams(queryParams);
@@ -18,6 +22,6 @@ export function createAbsoluteUrl(
const baseUrl = new URL(config.appSubUrl + path, config.appUrl);
return `${baseUrl.href}${searchParamsString ? `?${searchParamsString}` : ''}`;
} catch (err) {
return createUrl(path, queryParams);
return createRelativeUrl(path, queryParams);
}
}
+6 -4
View File
@@ -1,5 +1,7 @@
import { DataQuery, DataSourceApi, DataSourceJsonData, DataSourcePlugin } from '@grafana/data';
import { RelativeUrl } from '../alerting/unified/utils/url';
export type GenericDataSourcePlugin = DataSourcePlugin<DataSourceApi<DataQuery, DataSourceJsonData>>;
export type DataSourceRights = {
@@ -9,10 +11,10 @@ export type DataSourceRights = {
};
export type DataSourcesRoutes = {
New: string;
Edit: string;
List: string;
Dashboards: string;
New: RelativeUrl;
Edit: RelativeUrl;
List: RelativeUrl;
Dashboards: RelativeUrl;
};
export type DataSourceTestStatus = 'success' | 'warning' | 'error';
@@ -2,7 +2,7 @@ import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import { Button, Drawer, Dropdown, Icon, LinkButton, Menu, Stack, Text, Tooltip, useStyles2 } from '@grafana/ui';
import { createUrl } from 'app/features/alerting/unified/utils/url';
import { RelativeUrl, createRelativeUrl } from 'app/features/alerting/unified/utils/url';
import { SectionDto, SectionDtoStep, SectionsDto, StepButtonDto } from '../irmHooks';
@@ -86,22 +86,22 @@ function Step({ step }: StepProps) {
}
interface LinkButtonProps {
urlLink?: { url: string; queryParams?: Record<string, string> };
urlLink?: { url: RelativeUrl; queryParams?: Record<string, string> };
label: string;
urlLinkOnDone?: { url: string; queryParams?: Record<string, string> };
urlLinkOnDone?: { url: RelativeUrl; queryParams?: Record<string, string> };
labelOnDone?: string;
done?: boolean;
}
function OpenLinkButton(props: LinkButtonProps) {
const { urlLink, label, urlLinkOnDone, labelOnDone, done } = props;
const urlToGoWhenNotDone = urlLink?.url
? createUrl(urlLink.url, {
? createRelativeUrl(urlLink.url, {
returnTo: location.pathname + location.search,
...urlLink.queryParams,
})
: '';
const urlToGoWhenDone = urlLinkOnDone?.url
? createUrl(urlLinkOnDone.url, {
? createRelativeUrl(urlLinkOnDone.url, {
returnTo: location.pathname + location.search,
...urlLinkOnDone.queryParams,
})
@@ -1,7 +1,7 @@
import { useMemo } from 'react';
import { locationService } from '@grafana/runtime';
import { createUrl } from 'app/features/alerting/unified/utils/url';
import { RelativeUrl, createRelativeUrl } from 'app/features/alerting/unified/utils/url';
import {
isOnCallContactPointReady,
@@ -16,7 +16,7 @@ import { useOnCallChatOpsConnections, useOnCallOptions } from './onCall/hooks';
import { useSloChecks } from './slo/hooks';
interface UrlLink {
url: string;
url: RelativeUrl;
queryParams?: Record<string, string>;
}
export interface StepButtonDto {
@@ -113,8 +113,8 @@ export function useGetEssentialsConfiguration(): EssentialsConfigurationData {
isLoading,
} = useGetConfigurationForApps();
function onIntegrationClick(integrationId: string, url: string) {
const urlToGoWithIntegration = createUrl(url + integrationId, {
function onIntegrationClick(integrationId: string, url: RelativeUrl) {
const urlToGoWithIntegration = createRelativeUrl(`${url} + ${integrationId}`, {
returnTo: location.pathname + location.search,
});
locationService.push(urlToGoWithIntegration);
@@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css';
import { useLocation } from 'react-use';
import { GrafanaTheme2, intervalToAbbreviatedDurationString } from '@grafana/data';
import { Icon, useStyles2, Stack } from '@grafana/ui';
import { Icon, Stack, useStyles2 } from '@grafana/ui';
import alertDef from 'app/features/alerting/state/alertDef';
import { Spacer } from 'app/features/alerting/unified/components/Spacer';
import { fromCombinedRule, stringifyIdentifier } from 'app/features/alerting/unified/utils/rule-id';
@@ -12,11 +12,11 @@ import {
getFirstActiveAt,
isAlertingRule,
} from 'app/features/alerting/unified/utils/rules';
import { createUrl } from 'app/features/alerting/unified/utils/url';
import { createRelativeUrl } from 'app/features/alerting/unified/utils/url';
import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
import { GRAFANA_RULES_SOURCE_NAME } from '../../../../features/alerting/unified/utils/datasource';
import { AlertingRule, AlertInstanceTotalState, CombinedRuleWithLocation } from '../../../../types/unified-alerting';
import { AlertInstanceTotalState, AlertingRule, CombinedRuleWithLocation } from '../../../../types/unified-alerting';
import { AlertInstances } from '../AlertInstances';
import { getStyles } from '../UnifiedAlertList';
import { UnifiedAlertListOptions } from '../types';
@@ -63,7 +63,7 @@ const UngroupedModeView = ({ rules, options, handleInstancesLimit, limitInstance
? getGrafanaInstancesTotal(ruleWithLocation.filteredInstanceTotals)
: undefined;
const href = createUrl(
const href = createRelativeUrl(
`/alerting/${encodeURIComponent(dataSourceName)}/${encodeURIComponent(strIndentifier)}/view`,
{ returnTo: returnTo ?? '' }
);