Alerting: Hoist routes for mute timings (#94201)

This commit is contained in:
Gilles De Mey 2024-10-03 14:41:11 +02:00 committed by GitHub
parent 87fd36aecf
commit 4224d05934
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 103 additions and 118 deletions

View File

@ -48,7 +48,10 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] {
AccessControlAction.AlertingNotificationsExternalWrite,
]),
component: importAlertingComponent(
() => import(/* webpackChunkName: "MuteTimings" */ 'app/features/alerting/unified/MuteTimings')
() =>
import(
/* webpackChunkName: "NewMuteTiming" */ 'app/features/alerting/unified/components/mute-timings/NewMuteTiming'
)
),
},
{
@ -58,7 +61,10 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] {
AccessControlAction.AlertingNotificationsExternalWrite,
]),
component: importAlertingComponent(
() => import(/* webpackChunkName: "MuteTimings" */ 'app/features/alerting/unified/MuteTimings')
() =>
import(
/* webpackChunkName: "EditMuteTiming" */ 'app/features/alerting/unified/components/mute-timings/EditMuteTiming'
)
),
},
{

View File

@ -1,7 +1,8 @@
import { InitialEntry } from 'history';
import { last } from 'lodash';
import { ReactNode } from 'react';
import { Route } from 'react-router';
import { render, within, userEvent, screen } from 'test/test-utils';
import { render, screen, userEvent, within } from 'test/test-utils';
import { byRole, byTestId, byText } from 'testing-library-selector';
import { config } from '@grafana/runtime';
@ -13,27 +14,28 @@ import {
import { captureRequests } from 'app/features/alerting/unified/mocks/server/events';
import { MOCK_DATASOURCE_EXTERNAL_VANILLA_ALERTMANAGER_UID } from 'app/features/alerting/unified/mocks/server/handlers/datasources';
import {
TIME_INTERVAL_NAME_HAPPY_PATH,
TIME_INTERVAL_NAME_FILE_PROVISIONED,
TIME_INTERVAL_NAME_HAPPY_PATH,
} from 'app/features/alerting/unified/mocks/server/handlers/k8s/timeIntervals.k8s';
import { setupDataSources } from 'app/features/alerting/unified/testSetup/datasources';
import { AlertManagerCortexConfig, MuteTimeInterval } from 'app/plugins/datasource/alertmanager/types';
import { AccessControlAction } from 'app/types';
import MuteTimings from './MuteTimings';
import EditMuteTimingPage from './components/mute-timings/EditMuteTiming';
import NewMuteTimingPage from './components/mute-timings/NewMuteTiming';
import { grantUserPermissions, mockDataSource } from './mocks';
import { DataSourceType, GRAFANA_RULES_SOURCE_NAME } from './utils/datasource';
const indexPageText = 'redirected routes page';
const renderMuteTimings = (location: InitialEntry = '/alerting/routes/mute-timing/new') => {
const renderMuteTimings = (component: ReactNode, location?: InitialEntry) => {
render(
<>
<Route path="/alerting/routes" exact>
{indexPageText}
</Route>
<MuteTimings />
{component}
</>,
{ historyOptions: { initialEntries: [location] } }
{ historyOptions: location ? { initialEntries: [location] } : undefined }
);
};
@ -210,9 +212,9 @@ describe('Mute timings', () => {
it('creates a new mute timing, with mute_time_intervals in config', async () => {
const capture = captureRequests();
renderMuteTimings();
renderMuteTimings(<NewMuteTimingPage />);
await screen.findByText(/create mute timing/i);
await screen.findByText(/add mute timing/i);
await fillOutForm({
name: 'maintenance period',
@ -237,8 +239,7 @@ describe('Mute timings', () => {
it('creates a new mute timing, with time_intervals in config', async () => {
const capture = captureRequests();
setAlertmanagerConfig(defaultConfigWithNewTimeIntervalsField);
renderMuteTimings({
pathname: '/alerting/routes/mute-timing/new',
renderMuteTimings(<NewMuteTimingPage />, {
search: `?alertmanager=${alertmanagerName}`,
});
@ -262,8 +263,7 @@ describe('Mute timings', () => {
it('creates a new mute timing, with time_intervals and mute_time_intervals in config', async () => {
setGrafanaAlertmanagerConfig(defaultConfigWithBothTimeIntervalsField);
renderMuteTimings({
pathname: '/alerting/routes/mute-timing/new',
renderMuteTimings(<NewMuteTimingPage />, {
search: `?alertmanager=${alertmanagerName}`,
});
@ -284,8 +284,7 @@ describe('Mute timings', () => {
it('prepopulates the form when editing a mute timing', async () => {
const capture = captureRequests();
renderMuteTimings({
pathname: '/alerting/routes/mute-timing/edit',
renderMuteTimings(<EditMuteTimingPage />, {
search: `?muteName=${encodeURIComponent(muteTimeInterval.name)}`,
});
@ -325,7 +324,7 @@ describe('Mute timings', () => {
});
it('form is invalid with duplicate mute timing name', async () => {
renderMuteTimings();
renderMuteTimings(<NewMuteTimingPage />);
await fillOutForm({ name: muteTimeInterval.name, days: '1' });
@ -335,8 +334,7 @@ describe('Mute timings', () => {
});
it('replaces mute timings in routes when the mute timing name is changed', async () => {
renderMuteTimings({
pathname: '/alerting/routes/mute-timing/edit',
renderMuteTimings(<EditMuteTimingPage />, {
search: `?muteName=${encodeURIComponent(muteTimeInterval.name)}`,
});
@ -351,8 +349,7 @@ describe('Mute timings', () => {
});
it('shows error when mute timing does not exist', async () => {
renderMuteTimings({
pathname: '/alerting/routes/mute-timing/edit',
renderMuteTimings(<EditMuteTimingPage />, {
search: `?alertmanager=${GRAFANA_RULES_SOURCE_NAME}&muteName=${'does not exist'}`,
});
@ -365,9 +362,7 @@ describe('Mute timings', () => {
});
it('allows creation of new mute timings', async () => {
renderMuteTimings({
pathname: '/alerting/routes/mute-timing/new',
});
renderMuteTimings(<NewMuteTimingPage />);
await fillOutForm({ name: 'a new mute timing' });
@ -376,8 +371,7 @@ describe('Mute timings', () => {
});
it('shows error when mute timing does not exist', async () => {
renderMuteTimings({
pathname: '/alerting/routes/mute-timing/edit',
renderMuteTimings(<EditMuteTimingPage />, {
search: `?alertmanager=${GRAFANA_RULES_SOURCE_NAME}&muteName=${TIME_INTERVAL_NAME_HAPPY_PATH + '_force_breakage'}`,
});
@ -385,8 +379,7 @@ describe('Mute timings', () => {
});
it('loads edit form correctly and allows saving', async () => {
renderMuteTimings({
pathname: '/alerting/routes/mute-timing/edit',
renderMuteTimings(<EditMuteTimingPage />, {
search: `?alertmanager=${GRAFANA_RULES_SOURCE_NAME}&muteName=${TIME_INTERVAL_NAME_HAPPY_PATH}`,
});
@ -395,8 +388,7 @@ describe('Mute timings', () => {
});
it('loads view form for provisioned interval', async () => {
renderMuteTimings({
pathname: '/alerting/routes/mute-timing/edit',
renderMuteTimings(<EditMuteTimingPage />, {
search: `?muteName=${TIME_INTERVAL_NAME_FILE_PROVISIONED}`,
});

View File

@ -1,87 +0,0 @@
import { useEffect, useState } from 'react';
import { Route, Switch, useRouteMatch } from 'react-router-dom';
import { Navigate } from 'react-router-dom-v5-compat';
import { NavModelItem } from '@grafana/data';
import { useGetMuteTiming } from 'app/features/alerting/unified/components/mute-timings/useMuteTimings';
import { useURLSearchParams } from 'app/features/alerting/unified/hooks/useURLSearchParams';
import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
import MuteTimingForm from './components/mute-timings/MuteTimingForm';
import { useAlertmanager } from './state/AlertmanagerContext';
const EditTimingRoute = () => {
const [queryParams] = useURLSearchParams();
const { selectedAlertmanager } = useAlertmanager();
const name = queryParams.get('muteName')!;
const {
isLoading,
data: timeInterval,
isError,
} = useGetMuteTiming({
alertmanager: selectedAlertmanager!,
name,
});
if (!name) {
return <Navigate replace to="/alerting/routes" />;
}
return (
<MuteTimingForm
editMode
loading={isLoading}
showError={isError}
muteTiming={timeInterval}
provisioned={timeInterval?.provisioned}
/>
);
};
const MuteTimings = () => {
return (
<>
<Switch>
<Route exact path="/alerting/routes/mute-timing/new">
<MuteTimingForm />
</Route>
<Route exact path="/alerting/routes/mute-timing/edit">
<EditTimingRoute />
</Route>
</Switch>
</>
);
};
const MuteTimingsPage = () => {
const pageNav = useMuteTimingNavData();
return (
<AlertmanagerPageWrapper navId="am-routes" pageNav={pageNav} accessType="notification">
<MuteTimings />
</AlertmanagerPageWrapper>
);
};
export function useMuteTimingNavData() {
const { isExact, path } = useRouteMatch();
const [pageNav, setPageNav] = useState<Pick<NavModelItem, 'id' | 'text' | 'icon'> | undefined>();
useEffect(() => {
if (path === '/alerting/routes/mute-timing/new') {
setPageNav({
id: 'alert-policy-new',
text: 'Add mute timing',
});
} else if (path === '/alerting/routes/mute-timing/edit') {
setPageNav({
id: 'alert-policy-edit',
text: 'Edit mute timing',
});
}
}, [path, isExact]);
return pageNav;
}
export default MuteTimingsPage;

View File

@ -0,0 +1,54 @@
import { Navigate } from 'react-router-dom-v5-compat';
import { withErrorBoundary } from '@grafana/ui';
import { useGetMuteTiming } from 'app/features/alerting/unified/components/mute-timings/useMuteTimings';
import { useURLSearchParams } from 'app/features/alerting/unified/hooks/useURLSearchParams';
import { useAlertmanager } from '../../state/AlertmanagerContext';
import { AlertmanagerPageWrapper } from '../AlertingPageWrapper';
import MuteTimingForm from './MuteTimingForm';
const EditTimingRoute = () => {
const [queryParams] = useURLSearchParams();
const { selectedAlertmanager } = useAlertmanager();
const name = queryParams.get('muteName')!;
const {
isLoading,
data: timeInterval,
isError,
} = useGetMuteTiming({
alertmanager: selectedAlertmanager!,
name,
});
if (!name) {
return <Navigate replace to="/alerting/routes" />;
}
return (
<MuteTimingForm
editMode
loading={isLoading}
showError={isError}
muteTiming={timeInterval}
provisioned={timeInterval?.provisioned}
/>
);
};
const EditMuteTimingPage = () => (
<AlertmanagerPageWrapper
navId="am-routes"
pageNav={{
id: 'alert-policy-edit',
text: 'Edit mute timing',
}}
accessType="notification"
>
<EditTimingRoute />
</AlertmanagerPageWrapper>
);
export default withErrorBoundary(EditMuteTimingPage, { style: 'page' });

View File

@ -100,7 +100,7 @@ const MuteTimingForm = ({ muteTiming, showError, loading, provisioned, editMode
{provisioned && <ProvisioningAlert resource={ProvisionedResource.MuteTiming} />}
<FormProvider {...formApi}>
<form onSubmit={formApi.handleSubmit(onSubmit)} data-testid="mute-timing-form">
<FieldSet label={'Create mute timing'} disabled={provisioned || updating}>
<FieldSet disabled={provisioned || updating}>
<Field
required
label="Name"

View File

@ -0,0 +1,20 @@
import { withErrorBoundary } from '@grafana/ui';
import { AlertmanagerPageWrapper } from '../AlertingPageWrapper';
import MuteTimingForm from './MuteTimingForm';
const NewMuteTimingPage = () => (
<AlertmanagerPageWrapper
navId="am-routes"
pageNav={{
id: 'alert-policy-new',
text: 'Add mute timing',
}}
accessType="notification"
>
<MuteTimingForm />
</AlertmanagerPageWrapper>
);
export default withErrorBoundary(NewMuteTimingPage, { style: 'page' });