Routing: Hoist alerting receivers routes (#94309)

* Separate receivers components

* Update exports

* Remove type assertion

* Add back test

* Format

* Update test

* Fix test import
This commit is contained in:
Alex Khomenko 2024-10-09 07:29:07 +03:00 committed by GitHub
parent a3764ebeba
commit be7b293b79
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 114 additions and 115 deletions

View File

@ -3,10 +3,7 @@ import { config } from 'app/core/config';
import { GrafanaRouteComponent, RouteDescriptor } from 'app/core/navigation/types';
import { AccessControlAction } from 'app/types';
import {
PERMISSIONS_CONTACT_POINTS,
PERMISSIONS_CONTACT_POINTS_MODIFY,
} from './unified/components/contact-points/permissions';
import { PERMISSIONS_CONTACT_POINTS } from './unified/components/contact-points/permissions';
import { evaluateAccess } from './unified/utils/access-control';
export function getAlertingRoutes(cfg = config): RouteDescriptor[] {
@ -104,7 +101,43 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] {
...PERMISSIONS_CONTACT_POINTS,
]),
component: importAlertingComponent(
() => import(/* webpackChunkName: "NotificationsListPage" */ 'app/features/alerting/unified/Receivers')
() =>
import(
/* webpackChunkName: "ContactPoints" */ 'app/features/alerting/unified/components/contact-points/ContactPoints'
)
),
},
{
path: '/alerting/notifications/receivers/new',
roles: evaluateAccess([
AccessControlAction.AlertingNotificationsRead,
AccessControlAction.AlertingNotificationsExternalRead,
...PERMISSIONS_CONTACT_POINTS,
]),
component: importAlertingComponent(
() =>
import(
/* webpackChunkName: "NewReceiverView" */ 'app/features/alerting/unified/components/receivers/NewReceiverView'
)
),
},
{
path: '/alerting/notifications/receivers/:name/edit',
roles: evaluateAccess([
AccessControlAction.AlertingNotificationsWrite,
AccessControlAction.AlertingNotificationsExternalWrite,
AccessControlAction.AlertingNotificationsRead,
AccessControlAction.AlertingNotificationsExternalRead,
// We check any contact point permission here because a user without edit permissions
// still has to be able to visit the "edit" page, because we don't have a separate view for edit vs view
// (we just disable the form instead)
...PERMISSIONS_CONTACT_POINTS,
]),
component: importAlertingComponent(
() =>
import(
/* webpackChunkName: "EditContactPoint" */ 'app/features/alerting/unified/components/contact-points/EditContactPoint'
)
),
},
{
@ -118,60 +151,16 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] {
),
},
{
path: '/alerting/notifications/:type/new',
roles: evaluateAccess([
AccessControlAction.AlertingNotificationsWrite,
AccessControlAction.AlertingNotificationsExternalWrite,
...PERMISSIONS_CONTACT_POINTS_MODIFY,
]),
component: importAlertingComponent(
() => import(/* webpackChunkName: "NotificationsListPage" */ 'app/features/alerting/unified/Receivers')
),
},
{
path: '/alerting/notifications/receivers/:id/edit',
roles: evaluateAccess([
AccessControlAction.AlertingNotificationsWrite,
AccessControlAction.AlertingNotificationsExternalWrite,
AccessControlAction.AlertingNotificationsRead,
AccessControlAction.AlertingNotificationsExternalRead,
// We check any contact point permission here because a user without edit permissions
// still has to be able to visit the "edit" page, because we don't have a separate view for edit vs view
// (we just disable the form instead)
...PERMISSIONS_CONTACT_POINTS,
]),
component: importAlertingComponent(
() => import(/* webpackChunkName: "NotificationsListPage" */ 'app/features/alerting/unified/Receivers')
),
},
{
path: '/alerting/notifications/:type/:id/edit',
path: '/alerting/notifications/global-config',
roles: evaluateAccess([
AccessControlAction.AlertingNotificationsWrite,
AccessControlAction.AlertingNotificationsExternalWrite,
]),
component: importAlertingComponent(
() => import(/* webpackChunkName: "NotificationsListPage" */ 'app/features/alerting/unified/Receivers')
),
},
{
path: '/alerting/notifications/:type/:id/duplicate',
roles: evaluateAccess([
AccessControlAction.AlertingNotificationsWrite,
AccessControlAction.AlertingNotificationsExternalWrite,
]),
component: importAlertingComponent(
() => import(/* webpackChunkName: "NotificationsListPage" */ 'app/features/alerting/unified/Receivers')
),
},
{
path: '/alerting/notifications/:type',
roles: evaluateAccess([
AccessControlAction.AlertingNotificationsWrite,
AccessControlAction.AlertingNotificationsExternalWrite,
]),
component: importAlertingComponent(
() => import(/* webpackChunkName: "NotificationsListPage" */ 'app/features/alerting/unified/Receivers')
() =>
import(
/* webpackChunkName: "GlobalConfig" */ 'app/features/alerting/unified/components/contact-points/components/GlobalConfig'
)
),
},
{

View File

@ -1,3 +1,4 @@
import { Route, Routes } from 'react-router-dom-v5-compat';
import { selectOptionInTest } from 'test/helpers/selectOptionInTest';
import { render, screen, waitFor, userEvent } from 'test/test-utils';
@ -11,9 +12,10 @@ import { grantUserPermissions } from 'app/features/alerting/unified/mocks';
import { setupDataSources } from 'app/features/alerting/unified/testSetup/datasources';
import { AccessControlAction } from 'app/types';
import ContactPoints from './Receivers';
import 'core-js/stable/structured-clone';
import ContactPoints from './components/contact-points/ContactPoints';
import EditContactPoint from './components/contact-points/EditContactPoint';
import NewReceiverView from './components/receivers/NewReceiverView';
const server = setupMswServer();
@ -28,6 +30,21 @@ const saveContactPoint = async () => {
return user.click(await screen.findByRole('button', { name: /save contact point/i }));
};
const setup = (location: string) => {
return render(
<Routes>
<Route path="/alerting/notifications" element={<ContactPoints />} />
<Route path="/alerting/notifications/receivers/new" element={<NewReceiverView />} />
<Route path="/alerting/notifications/receivers/:name/edit" element={<EditContactPoint />} />
</Routes>,
{
historyOptions: {
initialEntries: [location],
},
}
);
};
beforeEach(() => {
grantUserPermissions([
AccessControlAction.AlertingNotificationsRead,
@ -41,18 +58,7 @@ beforeEach(() => {
});
it('can save a contact point with a select dropdown', async () => {
const user = userEvent.setup();
render(<ContactPoints />, {
historyOptions: {
initialEntries: [
{
pathname: `/alerting/notifications/receivers/new`,
search: `?alertmanager=${PROVISIONED_MIMIR_ALERTMANAGER_UID}`,
},
],
},
});
const { user } = setup(`/alerting/notifications/receivers/new?alertmanager=${PROVISIONED_MIMIR_ALERTMANAGER_UID}`);
// Fill out contact point name
const contactPointName = await screen.findByPlaceholderText(/name/i);
@ -75,16 +81,7 @@ it('can save a contact point with a select dropdown', async () => {
});
it('can save existing Telegram contact point', async () => {
render(<ContactPoints />, {
historyOptions: {
initialEntries: [
{
pathname: `/alerting/notifications/receivers/Telegram/edit`,
search: `?alertmanager=${PROVISIONED_MIMIR_ALERTMANAGER_UID}`,
},
],
},
});
setup(`/alerting/notifications/receivers/Telegram/edit?alertmanager=${PROVISIONED_MIMIR_ALERTMANAGER_UID}`);
// Here, we're implicitly testing that our parsing of an existing Telegram integration works correctly
// Our mock server will reject a request if we've sent the Chat ID as `0`,

View File

@ -1,24 +0,0 @@
import { Route, Switch } from 'react-router-dom';
import { withErrorBoundary } from '@grafana/ui';
import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport';
import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
const ContactPointsV2 = SafeDynamicImport(() => import('./components/contact-points/ContactPoints'));
const EditContactPoint = SafeDynamicImport(() => import('./components/contact-points/EditContactPoint'));
const NewReceiverView = SafeDynamicImport(() => import('./components/receivers/NewReceiverView'));
const GlobalConfig = SafeDynamicImport(() => import('./components/contact-points/components/GlobalConfig'));
const ContactPoints = (): JSX.Element => (
<AlertmanagerPageWrapper navId="receivers" accessType="notification">
<Switch>
<Route exact={true} path="/alerting/notifications" component={ContactPointsV2} />
<Route exact={true} path="/alerting/notifications/receivers/new" component={NewReceiverView} />
<Route exact={true} path="/alerting/notifications/receivers/:name/edit" component={EditContactPoint} />
<Route exact={true} path="/alerting/notifications/global-config" component={GlobalConfig} />
</Switch>
</AlertmanagerPageWrapper>
);
export default withErrorBoundary(ContactPoints, { style: 'page' });

View File

@ -19,7 +19,7 @@ import { setupDataSources } from '../../testSetup/datasources';
import { DataSourceType, GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
import { ContactPoint } from './ContactPoint';
import ContactPointsPageContents from './ContactPoints';
import { ContactPointsPageContents } from './ContactPoints';
import setupMimirFlavoredServer, { MIMIR_DATASOURCE_UID } from './__mocks__/mimirFlavoredServer';
import setupVanillaAlertmanagerFlavoredServer, {
VANILLA_ALERTMANAGER_DATASOURCE_UID,

View File

@ -12,6 +12,7 @@ import {
TabContent,
TabsBar,
Text,
withErrorBoundary,
} from '@grafana/ui';
import { contextSrv } from 'app/core/core';
import { t, Trans } from 'app/core/internationalization';
@ -24,6 +25,7 @@ import { usePagination } from '../../hooks/usePagination';
import { useURLSearchParams } from '../../hooks/useURLSearchParams';
import { useAlertmanager } from '../../state/AlertmanagerContext';
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
import { AlertmanagerPageWrapper } from '../AlertingPageWrapper';
import { GrafanaAlertmanagerDeliveryWarning } from '../GrafanaAlertmanagerDeliveryWarning';
import { ContactPoint } from './ContactPoint';
@ -179,7 +181,7 @@ const useTabQueryParam = () => {
return [param, setParam] as const;
};
const ContactPointsPageContents = () => {
export const ContactPointsPageContents = () => {
const { selectedAlertmanager } = useAlertmanager();
const [activeTab, setActiveTab] = useTabQueryParam();
@ -242,4 +244,12 @@ const ContactPointsList = ({ contactPoints, search, pageSize = DEFAULT_PAGE_SIZE
);
};
export default ContactPointsPageContents;
function ContactPointsPage() {
return (
<AlertmanagerPageWrapper navId="receivers" accessType="notification">
<ContactPointsPageContents />
</AlertmanagerPageWrapper>
);
}
export default withErrorBoundary(ContactPointsPage, { style: 'page' });

View File

@ -1,18 +1,18 @@
import { RouteChildrenProps } from 'react-router-dom';
import { useParams } from 'react-router-dom-v5-compat';
import { Alert, LoadingPlaceholder } from '@grafana/ui';
import { Alert, LoadingPlaceholder, withErrorBoundary } from '@grafana/ui';
import { useGetContactPoint } from 'app/features/alerting/unified/components/contact-points/useContactPoints';
import { stringifyErrorLike } from 'app/features/alerting/unified/utils/misc';
import { useAlertmanager } from '../../state/AlertmanagerContext';
import { AlertmanagerPageWrapper } from '../AlertingPageWrapper';
import { EditReceiverView } from '../receivers/EditReceiverView';
type Props = RouteChildrenProps<{ name: string }>;
const EditContactPoint = ({ match }: Props) => {
const EditContactPoint = () => {
const { selectedAlertmanager } = useAlertmanager();
const { name = '' } = useParams();
const contactPointName = decodeURIComponent(match?.params.name!);
const contactPointName = decodeURIComponent(name);
const {
isLoading,
error,
@ -42,4 +42,12 @@ const EditContactPoint = ({ match }: Props) => {
return <EditReceiverView alertmanagerName={selectedAlertmanager!} contactPoint={contactPoint} />;
};
export default EditContactPoint;
function EditContactPointPage() {
return (
<AlertmanagerPageWrapper navId="receivers" accessType="notification">
<EditContactPoint />
</AlertmanagerPageWrapper>
);
}
export default withErrorBoundary(EditContactPointPage, { style: 'page' });

View File

@ -1,7 +1,8 @@
import { Alert } from '@grafana/ui';
import { Alert, withErrorBoundary } from '@grafana/ui';
import { useAlertmanagerConfig } from '../../../hooks/useAlertmanagerConfig';
import { useAlertmanager } from '../../../state/AlertmanagerContext';
import { AlertmanagerPageWrapper } from '../../AlertingPageWrapper';
import { GlobalConfigForm } from '../../receivers/GlobalConfigForm';
const NewMessageTemplate = () => {
@ -27,4 +28,12 @@ const NewMessageTemplate = () => {
return <GlobalConfigForm config={data} alertManagerSourceName={selectedAlertmanager!} />;
};
export default NewMessageTemplate;
function NewMessageTemplatePage() {
return (
<AlertmanagerPageWrapper navId="receivers" accessType="notification">
<NewMessageTemplate />
</AlertmanagerPageWrapper>
);
}
export default withErrorBoundary(NewMessageTemplatePage, { style: 'page' });

View File

@ -1,6 +1,8 @@
import { withErrorBoundary } from '@grafana/ui';
import { useAlertmanager } from 'app/features/alerting/unified/state/AlertmanagerContext';
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
import { AlertmanagerPageWrapper } from '../AlertingPageWrapper';
import { CloudReceiverForm } from './form/CloudReceiverForm';
import { GrafanaReceiverForm } from './form/GrafanaReceiverForm';
@ -14,4 +16,12 @@ const NewReceiverView = () => {
}
};
export default NewReceiverView;
function NewReceiverViewPage() {
return (
<AlertmanagerPageWrapper navId="receivers" accessType="notification">
<NewReceiverView />
</AlertmanagerPageWrapper>
);
}
export default withErrorBoundary(NewReceiverViewPage, { style: 'page' });