mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Adds contact point sorting and searching (#77390)
This commit is contained in:
parent
60546a9f5a
commit
be436ec6f6
@ -7,6 +7,7 @@ import { TestProvider } from 'test/helpers/TestProvider';
|
|||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { AccessControlAction } from 'app/types';
|
import { AccessControlAction } from 'app/types';
|
||||||
|
|
||||||
|
import { setupMswServer } from '../../mockApi';
|
||||||
import { grantUserPermissions, mockDataSource } from '../../mocks';
|
import { grantUserPermissions, mockDataSource } from '../../mocks';
|
||||||
import { AlertmanagerProvider } from '../../state/AlertmanagerContext';
|
import { AlertmanagerProvider } from '../../state/AlertmanagerContext';
|
||||||
import { setupDataSources } from '../../testSetup/datasources';
|
import { setupDataSources } from '../../testSetup/datasources';
|
||||||
@ -29,10 +30,16 @@ import setupMimirFlavoredServer, { MIMIR_DATASOURCE_UID } from './__mocks__/mimi
|
|||||||
*
|
*
|
||||||
* 3. Write tests for the hooks we call in the "container" components
|
* 3. Write tests for the hooks we call in the "container" components
|
||||||
* if those have any logic or data structure transformations in them.
|
* if those have any logic or data structure transformations in them.
|
||||||
|
*
|
||||||
|
* ⚠️ Always set up the MSW server only once – MWS does not support multiple calls to setupServer(); and causes all sorts of weird issues
|
||||||
*/
|
*/
|
||||||
describe('ContactPoints', () => {
|
const server = setupMswServer();
|
||||||
describe('Grafana managed alertmanager', () => {
|
|
||||||
setupGrafanaManagedServer();
|
describe('contact points', () => {
|
||||||
|
describe('Contact points with Grafana managed alertmanager', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setupGrafanaManagedServer(server);
|
||||||
|
});
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
grantUserPermissions([
|
grantUserPermissions([
|
||||||
@ -124,10 +131,35 @@ describe('ContactPoints', () => {
|
|||||||
const deleteButton = screen.getByRole('menuitem', { name: /delete/i });
|
const deleteButton = screen.getByRole('menuitem', { name: /delete/i });
|
||||||
expect(deleteButton).toBeDisabled();
|
expect(deleteButton).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be able to search', async () => {
|
||||||
|
render(
|
||||||
|
<AlertmanagerProvider accessType={'notification'}>
|
||||||
|
<ContactPoints />
|
||||||
|
</AlertmanagerProvider>,
|
||||||
|
{ wrapper: TestProvider }
|
||||||
|
);
|
||||||
|
|
||||||
|
const searchInput = screen.getByRole('textbox', { name: 'search contact points' });
|
||||||
|
await userEvent.type(searchInput, 'slack');
|
||||||
|
expect(searchInput).toHaveValue('slack');
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Slack with multiple channels')).toBeInTheDocument();
|
||||||
|
expect(screen.getAllByTestId('contact-point')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Mimir-flavored alertmanager', () => {
|
// ⚠️ for some reason, the query params are preserved for all tests so don't forget to clear the input
|
||||||
setupMimirFlavoredServer();
|
const clearButton = screen.getByRole('button', { name: 'clear' });
|
||||||
|
await userEvent.click(clearButton);
|
||||||
|
expect(searchInput).toHaveValue('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Contact points with Mimir-flavored alertmanager', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setupMimirFlavoredServer(server);
|
||||||
|
});
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
grantUserPermissions([
|
grantUserPermissions([
|
||||||
@ -145,10 +177,11 @@ describe('ContactPoints', () => {
|
|||||||
|
|
||||||
it('should show / hide loading states', async () => {
|
it('should show / hide loading states', async () => {
|
||||||
render(
|
render(
|
||||||
|
<TestProvider>
|
||||||
<AlertmanagerProvider accessType={'notification'} alertmanagerSourceName={MIMIR_DATASOURCE_UID}>
|
<AlertmanagerProvider accessType={'notification'} alertmanagerSourceName={MIMIR_DATASOURCE_UID}>
|
||||||
<ContactPoints />
|
<ContactPoints />
|
||||||
</AlertmanagerProvider>,
|
</AlertmanagerProvider>
|
||||||
{ wrapper: TestProvider }
|
</TestProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor(async () => {
|
await waitFor(async () => {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
|
import uFuzzy from '@leeoniya/ufuzzy';
|
||||||
import { SerializedError } from '@reduxjs/toolkit';
|
import { SerializedError } from '@reduxjs/toolkit';
|
||||||
import { groupBy, size, upperFirst } from 'lodash';
|
import { groupBy, size, uniq, upperFirst } from 'lodash';
|
||||||
import pluralize from 'pluralize';
|
import pluralize from 'pluralize';
|
||||||
import React, { Fragment, ReactNode, useCallback, useMemo, useState } from 'react';
|
import React, { Fragment, ReactNode, useCallback, useMemo, useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
@ -8,13 +9,6 @@ import { useToggle } from 'react-use';
|
|||||||
|
|
||||||
import { dateTime, GrafanaTheme2 } from '@grafana/data';
|
import { dateTime, GrafanaTheme2 } from '@grafana/data';
|
||||||
import {
|
import {
|
||||||
Alert,
|
|
||||||
Dropdown,
|
|
||||||
Icon,
|
|
||||||
LoadingPlaceholder,
|
|
||||||
Menu,
|
|
||||||
Tooltip,
|
|
||||||
useStyles2,
|
|
||||||
Text,
|
Text,
|
||||||
LinkButton,
|
LinkButton,
|
||||||
TabsBar,
|
TabsBar,
|
||||||
@ -23,6 +17,13 @@ import {
|
|||||||
Pagination,
|
Pagination,
|
||||||
Button,
|
Button,
|
||||||
Stack,
|
Stack,
|
||||||
|
Alert,
|
||||||
|
LoadingPlaceholder,
|
||||||
|
useStyles2,
|
||||||
|
Menu,
|
||||||
|
Dropdown,
|
||||||
|
Tooltip,
|
||||||
|
Icon,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
import ConditionalWrap from 'app/features/alerting/components/ConditionalWrap';
|
import ConditionalWrap from 'app/features/alerting/components/ConditionalWrap';
|
||||||
import { receiverTypeNames } from 'app/plugins/datasource/alertmanager/consts';
|
import { receiverTypeNames } from 'app/plugins/datasource/alertmanager/consts';
|
||||||
@ -31,6 +32,7 @@ import { GrafanaNotifierType, NotifierStatus } from 'app/types/alerting';
|
|||||||
|
|
||||||
import { AlertmanagerAction, useAlertmanagerAbility } from '../../hooks/useAbilities';
|
import { AlertmanagerAction, useAlertmanagerAbility } from '../../hooks/useAbilities';
|
||||||
import { usePagination } from '../../hooks/usePagination';
|
import { usePagination } from '../../hooks/usePagination';
|
||||||
|
import { useURLSearchParams } from '../../hooks/useURLSearchParams';
|
||||||
import { useAlertmanager } from '../../state/AlertmanagerContext';
|
import { useAlertmanager } from '../../state/AlertmanagerContext';
|
||||||
import { INTEGRATION_ICONS } from '../../types/contact-points';
|
import { INTEGRATION_ICONS } from '../../types/contact-points';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
|
||||||
@ -48,6 +50,7 @@ import { UnusedContactPointBadge } from '../receivers/ReceiversTable';
|
|||||||
import { ReceiverMetadataBadge } from '../receivers/grafanaAppReceivers/ReceiverMetadataBadge';
|
import { ReceiverMetadataBadge } from '../receivers/grafanaAppReceivers/ReceiverMetadataBadge';
|
||||||
import { ReceiverPluginMetadata } from '../receivers/grafanaAppReceivers/useReceiversMetadata';
|
import { ReceiverPluginMetadata } from '../receivers/grafanaAppReceivers/useReceiversMetadata';
|
||||||
|
|
||||||
|
import { ContactPointsFilter } from './ContactPointsFilter';
|
||||||
import { useDeleteContactPointModal } from './Modals';
|
import { useDeleteContactPointModal } from './Modals';
|
||||||
import { NotificationTemplates } from './NotificationTemplates';
|
import { NotificationTemplates } from './NotificationTemplates';
|
||||||
import {
|
import {
|
||||||
@ -82,6 +85,9 @@ const ContactPoints = () => {
|
|||||||
const [DeleteModal, showDeleteModal] = useDeleteContactPointModal(deleteTrigger, updateAlertmanagerState.isLoading);
|
const [DeleteModal, showDeleteModal] = useDeleteContactPointModal(deleteTrigger, updateAlertmanagerState.isLoading);
|
||||||
const [ExportDrawer, showExportDrawer] = useExportContactPoint();
|
const [ExportDrawer, showExportDrawer] = useExportContactPoint();
|
||||||
|
|
||||||
|
const [searchParams] = useURLSearchParams();
|
||||||
|
const { search } = getContactPointsFilters(searchParams);
|
||||||
|
|
||||||
const showingContactPoints = activeTab === ActiveTab.ContactPoints;
|
const showingContactPoints = activeTab === ActiveTab.ContactPoints;
|
||||||
const showNotificationTemplates = activeTab === ActiveTab.NotificationTemplates;
|
const showNotificationTemplates = activeTab === ActiveTab.NotificationTemplates;
|
||||||
|
|
||||||
@ -122,10 +128,8 @@ const ContactPoints = () => {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{/* TODO we can add some additional info here with a ToggleTip */}
|
{/* TODO we can add some additional info here with a ToggleTip */}
|
||||||
<Stack direction="row" alignItems="center">
|
<Stack direction="row" alignItems="end">
|
||||||
<Text variant="body" color="secondary">
|
<ContactPointsFilter />
|
||||||
Define where notifications are sent, a contact point can contain multiple integrations.
|
|
||||||
</Text>
|
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<Stack direction="row" gap={1}>
|
<Stack direction="row" gap={1}>
|
||||||
{addContactPointSupported && (
|
{addContactPointSupported && (
|
||||||
@ -152,6 +156,7 @@ const ContactPoints = () => {
|
|||||||
</Stack>
|
</Stack>
|
||||||
<ContactPointsList
|
<ContactPointsList
|
||||||
contactPoints={contactPoints}
|
contactPoints={contactPoints}
|
||||||
|
search={search}
|
||||||
pageSize={DEFAULT_PAGE_SIZE}
|
pageSize={DEFAULT_PAGE_SIZE}
|
||||||
onDelete={(name) => showDeleteModal(name)}
|
onDelete={(name) => showDeleteModal(name)}
|
||||||
disabled={updateAlertmanagerState.isLoading}
|
disabled={updateAlertmanagerState.isLoading}
|
||||||
@ -189,6 +194,7 @@ const ContactPoints = () => {
|
|||||||
|
|
||||||
interface ContactPointsListProps {
|
interface ContactPointsListProps {
|
||||||
contactPoints: ContactPointWithMetadata[];
|
contactPoints: ContactPointWithMetadata[];
|
||||||
|
search?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
onDelete: (name: string) => void;
|
onDelete: (name: string) => void;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
@ -197,20 +203,23 @@ interface ContactPointsListProps {
|
|||||||
const ContactPointsList = ({
|
const ContactPointsList = ({
|
||||||
contactPoints,
|
contactPoints,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
search,
|
||||||
pageSize = DEFAULT_PAGE_SIZE,
|
pageSize = DEFAULT_PAGE_SIZE,
|
||||||
onDelete,
|
onDelete,
|
||||||
}: ContactPointsListProps) => {
|
}: ContactPointsListProps) => {
|
||||||
const { page, pageItems, numberOfPages, onPageChange } = usePagination(contactPoints, 1, pageSize);
|
const searchResults = useContactPointsSearch(contactPoints, search);
|
||||||
|
const { page, pageItems, numberOfPages, onPageChange } = usePagination(searchResults, 1, pageSize);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{pageItems.map((contactPoint, index) => {
|
{pageItems.map((contactPoint, index) => {
|
||||||
const provisioned = isProvisioned(contactPoint);
|
const provisioned = isProvisioned(contactPoint);
|
||||||
const policies = contactPoint.numberOfPolicies;
|
const policies = contactPoint.numberOfPolicies;
|
||||||
|
const key = `${contactPoint.name}-${index}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContactPoint
|
<ContactPoint
|
||||||
key={`${contactPoint.name}-${index}`}
|
key={key}
|
||||||
name={contactPoint.name}
|
name={contactPoint.name}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
@ -225,6 +234,42 @@ const ContactPointsList = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fuzzyFinder = new uFuzzy({
|
||||||
|
intraMode: 1,
|
||||||
|
intraIns: 1,
|
||||||
|
intraSub: 1,
|
||||||
|
intraDel: 1,
|
||||||
|
intraTrn: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
// let's search in two different haystacks, the name of the contact point and the type of the receiver(s)
|
||||||
|
function useContactPointsSearch(
|
||||||
|
contactPoints: ContactPointWithMetadata[],
|
||||||
|
search?: string
|
||||||
|
): ContactPointWithMetadata[] {
|
||||||
|
const nameHaystack = useMemo(() => {
|
||||||
|
return contactPoints.map((contactPoint) => contactPoint.name);
|
||||||
|
}, [contactPoints]);
|
||||||
|
|
||||||
|
const typeHaystack = useMemo(() => {
|
||||||
|
return contactPoints.map((contactPoint) =>
|
||||||
|
// we're using the resolved metadata key here instead of the "type" property – ex. we alias "teams" to "microsoft teams"
|
||||||
|
contactPoint.grafana_managed_receiver_configs.map((receiver) => receiver[RECEIVER_META_KEY].name).join(' ')
|
||||||
|
);
|
||||||
|
}, [contactPoints]);
|
||||||
|
|
||||||
|
if (!search) {
|
||||||
|
return contactPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nameHits = fuzzyFinder.filter(nameHaystack, search) ?? [];
|
||||||
|
const typeHits = fuzzyFinder.filter(typeHaystack, search) ?? [];
|
||||||
|
|
||||||
|
const hits = [...nameHits, ...typeHits];
|
||||||
|
|
||||||
|
return uniq(hits).map((id) => contactPoints[id]) ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
interface ContactPointProps {
|
interface ContactPointProps {
|
||||||
name: string;
|
name: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
@ -571,6 +616,10 @@ const useExportContactPoint = (): ExportProps => {
|
|||||||
return [drawer, handleOpen];
|
return [drawer, handleOpen];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getContactPointsFilters = (searchParams: URLSearchParams) => ({
|
||||||
|
search: searchParams.get('search') ?? undefined,
|
||||||
|
});
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => ({
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
contactPointWrapper: css({
|
contactPointWrapper: css({
|
||||||
borderRadius: `${theme.shape.radius.default}`,
|
borderRadius: `${theme.shape.radius.default}`,
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import React, { useCallback, useState } from 'react';
|
||||||
|
import { useDebounce } from 'react-use';
|
||||||
|
|
||||||
|
import { Stack } from '@grafana/experimental';
|
||||||
|
import { Button, Field, Icon, Input, useStyles2 } from '@grafana/ui';
|
||||||
|
|
||||||
|
import { useURLSearchParams } from '../../hooks/useURLSearchParams';
|
||||||
|
|
||||||
|
const ContactPointsFilter = () => {
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
|
const [searchParams, setSearchParams] = useURLSearchParams();
|
||||||
|
|
||||||
|
const defaultValue = searchParams.get('search') ?? '';
|
||||||
|
const [searchValue, setSearchValue] = useState(defaultValue);
|
||||||
|
|
||||||
|
const [_, cancel] = useDebounce(
|
||||||
|
() => {
|
||||||
|
setSearchParams({ search: searchValue }, true);
|
||||||
|
},
|
||||||
|
300,
|
||||||
|
[setSearchParams, searchValue]
|
||||||
|
);
|
||||||
|
|
||||||
|
const clear = useCallback(() => {
|
||||||
|
cancel();
|
||||||
|
setSearchValue('');
|
||||||
|
setSearchParams({ search: '' }, true);
|
||||||
|
}, [cancel, setSearchParams]);
|
||||||
|
|
||||||
|
const hasInput = Boolean(defaultValue);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack direction="row" alignItems="end" gap={0.5}>
|
||||||
|
<Field className={styles.noBottom} label="Search by name or type">
|
||||||
|
<Input
|
||||||
|
aria-label="search contact points"
|
||||||
|
placeholder="Search"
|
||||||
|
width={46}
|
||||||
|
prefix={<Icon name="search" />}
|
||||||
|
onChange={(event) => {
|
||||||
|
setSearchValue(event.currentTarget.value);
|
||||||
|
}}
|
||||||
|
value={searchValue}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
<Button variant="secondary" icon="times" onClick={() => clear()} disabled={!hasInput} aria-label="clear">
|
||||||
|
Clear
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStyles = () => ({
|
||||||
|
noBottom: css({
|
||||||
|
marginBottom: 0,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export { ContactPointsFilter };
|
@ -1,18 +1,17 @@
|
|||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
|
import { SetupServer } from 'msw/lib/node';
|
||||||
|
|
||||||
import { AlertmanagerChoice, AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';
|
import { AlertmanagerChoice, AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';
|
||||||
import { ReceiversStateDTO } from 'app/types';
|
import { ReceiversStateDTO } from 'app/types';
|
||||||
|
|
||||||
import { mockApi, setupMswServer } from '../../../mockApi';
|
import { mockApi } from '../../../mockApi';
|
||||||
import { mockAlertmanagerChoiceResponse } from '../../../mocks/alertmanagerApi';
|
import { mockAlertmanagerChoiceResponse } from '../../../mocks/alertmanagerApi';
|
||||||
import { grafanaNotifiersMock } from '../../../mocks/grafana-notifiers';
|
import { grafanaNotifiersMock } from '../../../mocks/grafana-notifiers';
|
||||||
|
|
||||||
import alertmanagerMock from './alertmanager.config.mock.json';
|
import alertmanagerMock from './alertmanager.config.mock.json';
|
||||||
import receiversMock from './receivers.mock.json';
|
import receiversMock from './receivers.mock.json';
|
||||||
|
|
||||||
export default () => {
|
export default (server: SetupServer) => {
|
||||||
const server = setupMswServer();
|
|
||||||
|
|
||||||
server.use(
|
server.use(
|
||||||
// this endpoint is a grafana built-in alertmanager
|
// this endpoint is a grafana built-in alertmanager
|
||||||
rest.get('/api/alertmanager/grafana/config/api/v1/alerts', (_req, res, ctx) =>
|
rest.get('/api/alertmanager/grafana/config/api/v1/alerts', (_req, res, ctx) =>
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
|
import { SetupServer } from 'msw/lib/node';
|
||||||
|
|
||||||
import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';
|
import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';
|
||||||
|
|
||||||
import { setupMswServer } from '../../../mockApi';
|
|
||||||
|
|
||||||
import mimirAlertmanagerMock from './alertmanager.mimir.config.mock.json';
|
import mimirAlertmanagerMock from './alertmanager.mimir.config.mock.json';
|
||||||
|
|
||||||
// this one emulates a mimir server setup
|
// this one emulates a mimir server setup
|
||||||
export const MIMIR_DATASOURCE_UID = 'mimir';
|
export const MIMIR_DATASOURCE_UID = 'mimir';
|
||||||
|
|
||||||
export default () => {
|
export default (server: SetupServer) => {
|
||||||
const server = setupMswServer();
|
|
||||||
|
|
||||||
server.use(
|
server.use(
|
||||||
rest.get(`/api/alertmanager/${MIMIR_DATASOURCE_UID}/config/api/v1/alerts`, (_req, res, ctx) =>
|
rest.get(`/api/alertmanager/${MIMIR_DATASOURCE_UID}/config/api/v1/alerts`, (_req, res, ctx) =>
|
||||||
res(ctx.json<AlertManagerCortexConfig>(mimirAlertmanagerMock))
|
res(ctx.json<AlertManagerCortexConfig>(mimirAlertmanagerMock))
|
||||||
@ -22,4 +19,6 @@ export default () => {
|
|||||||
// this endpoint will respond if the OnCall plugin is installed
|
// this endpoint will respond if the OnCall plugin is installed
|
||||||
rest.get('/api/plugins/grafana-oncall-app/settings', (_req, res, ctx) => res(ctx.status(404)))
|
rest.get('/api/plugins/grafana-oncall-app/settings', (_req, res, ctx) => res(ctx.status(404)))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return server;
|
||||||
};
|
};
|
||||||
|
@ -32,6 +32,34 @@ exports[`useContactPoints should return contact points with status 1`] = `
|
|||||||
"name": "grafana-default-email",
|
"name": "grafana-default-email",
|
||||||
"numberOfPolicies": 0,
|
"numberOfPolicies": 0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"grafana_managed_receiver_configs": [
|
||||||
|
{
|
||||||
|
"disableResolveMessage": false,
|
||||||
|
"name": "lotsa-emails",
|
||||||
|
"secureFields": {},
|
||||||
|
"settings": {
|
||||||
|
"addresses": "gilles.demey+1@grafana.com, gilles.demey+2@grafana.com, gilles.demey+3@grafana.com, gilles.demey+4@grafana.com",
|
||||||
|
"singleEmail": false,
|
||||||
|
},
|
||||||
|
"type": "email",
|
||||||
|
"uid": "af306c96-35a2-4d6e-908a-4993e245dbb2",
|
||||||
|
Symbol(receiver_status): {
|
||||||
|
"lastNotifyAttempt": "",
|
||||||
|
"lastNotifyAttemptDuration": "",
|
||||||
|
"name": "email",
|
||||||
|
"sendResolved": true,
|
||||||
|
},
|
||||||
|
Symbol(receiver_metadata): {
|
||||||
|
"description": "Sends notifications using Grafana server configured SMTP settings",
|
||||||
|
"name": "Email",
|
||||||
|
},
|
||||||
|
Symbol(receiver_plugin_metadata): undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "lotsa-emails",
|
||||||
|
"numberOfPolicies": 0,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"grafana_managed_receiver_configs": [
|
"grafana_managed_receiver_configs": [
|
||||||
{
|
{
|
||||||
@ -61,34 +89,6 @@ exports[`useContactPoints should return contact points with status 1`] = `
|
|||||||
"name": "provisioned-contact-point",
|
"name": "provisioned-contact-point",
|
||||||
"numberOfPolicies": 0,
|
"numberOfPolicies": 0,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"grafana_managed_receiver_configs": [
|
|
||||||
{
|
|
||||||
"disableResolveMessage": false,
|
|
||||||
"name": "lotsa-emails",
|
|
||||||
"secureFields": {},
|
|
||||||
"settings": {
|
|
||||||
"addresses": "gilles.demey+1@grafana.com, gilles.demey+2@grafana.com, gilles.demey+3@grafana.com, gilles.demey+4@grafana.com",
|
|
||||||
"singleEmail": false,
|
|
||||||
},
|
|
||||||
"type": "email",
|
|
||||||
"uid": "af306c96-35a2-4d6e-908a-4993e245dbb2",
|
|
||||||
Symbol(receiver_status): {
|
|
||||||
"lastNotifyAttempt": "",
|
|
||||||
"lastNotifyAttemptDuration": "",
|
|
||||||
"name": "email",
|
|
||||||
"sendResolved": true,
|
|
||||||
},
|
|
||||||
Symbol(receiver_metadata): {
|
|
||||||
"description": "Sends notifications using Grafana server configured SMTP settings",
|
|
||||||
"name": "Email",
|
|
||||||
},
|
|
||||||
Symbol(receiver_plugin_metadata): undefined,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"name": "lotsa-emails",
|
|
||||||
"numberOfPolicies": 0,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"grafana_managed_receiver_configs": [
|
"grafana_managed_receiver_configs": [
|
||||||
{
|
{
|
||||||
|
@ -4,14 +4,19 @@ import { TestProvider } from 'test/helpers/TestProvider';
|
|||||||
|
|
||||||
import { AccessControlAction } from 'app/types';
|
import { AccessControlAction } from 'app/types';
|
||||||
|
|
||||||
|
import { setupMswServer } from '../../mockApi';
|
||||||
import { grantUserPermissions } from '../../mocks';
|
import { grantUserPermissions } from '../../mocks';
|
||||||
import { AlertmanagerProvider } from '../../state/AlertmanagerContext';
|
import { AlertmanagerProvider } from '../../state/AlertmanagerContext';
|
||||||
|
|
||||||
import setupGrafanaManagedServer from './__mocks__/grafanaManagedServer';
|
import setupGrafanaManagedServer from './__mocks__/grafanaManagedServer';
|
||||||
import { useContactPointsWithStatus } from './useContactPoints';
|
import { useContactPointsWithStatus } from './useContactPoints';
|
||||||
|
|
||||||
|
const server = setupMswServer();
|
||||||
|
|
||||||
describe('useContactPoints', () => {
|
describe('useContactPoints', () => {
|
||||||
setupGrafanaManagedServer();
|
beforeEach(() => {
|
||||||
|
setupGrafanaManagedServer(server);
|
||||||
|
});
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
grantUserPermissions([AccessControlAction.AlertingNotificationsRead]);
|
grantUserPermissions([AccessControlAction.AlertingNotificationsRead]);
|
||||||
|
@ -91,7 +91,7 @@ export function useContactPointsWithStatus() {
|
|||||||
onCallPluginStatusLoading ||
|
onCallPluginStatusLoading ||
|
||||||
onCallPluginIntegrationsLoading;
|
onCallPluginIntegrationsLoading;
|
||||||
|
|
||||||
const contactPoints = fetchAlertmanagerConfiguration.contactPoints;
|
const contactPoints = fetchAlertmanagerConfiguration.contactPoints.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
error,
|
error,
|
||||||
|
@ -87,7 +87,15 @@ const rulerRuleIdentifier = ruleId.fromRulerRule('prometheus', 'ns-default', 'gr
|
|||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
setBackendSrv(backendSrv);
|
setBackendSrv(backendSrv);
|
||||||
|
const promDsSettings = mockDataSource({
|
||||||
|
name: dsName,
|
||||||
|
uid: dsName,
|
||||||
|
});
|
||||||
|
|
||||||
|
setupDataSources(promDsSettings);
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
// some action buttons need to check what Alertmanager setup we have for Grafana managed rules
|
// some action buttons need to check what Alertmanager setup we have for Grafana managed rules
|
||||||
mockAlertmanagerChoiceResponse(server, {
|
mockAlertmanagerChoiceResponse(server, {
|
||||||
alertmanagersChoice: AlertmanagerChoice.Internal,
|
alertmanagersChoice: AlertmanagerChoice.Internal,
|
||||||
@ -96,13 +104,6 @@ beforeAll(() => {
|
|||||||
// we need to mock this one for the "declare incident" button
|
// we need to mock this one for the "declare incident" button
|
||||||
mockPluginSettings(server, SupportedPlugin.Incident);
|
mockPluginSettings(server, SupportedPlugin.Incident);
|
||||||
|
|
||||||
const promDsSettings = mockDataSource({
|
|
||||||
name: dsName,
|
|
||||||
uid: dsName,
|
|
||||||
});
|
|
||||||
|
|
||||||
setupDataSources(promDsSettings);
|
|
||||||
|
|
||||||
mockAlertRuleApi(server).rulerRules('grafana', {
|
mockAlertRuleApi(server).rulerRules('grafana', {
|
||||||
[mockGrafanaRule.namespace.name]: [
|
[mockGrafanaRule.namespace.name]: [
|
||||||
{ name: mockGrafanaRule.group.name, interval: '1m', rules: [mockGrafanaRule.rulerRule!] },
|
{ name: mockGrafanaRule.group.name, interval: '1m', rules: [mockGrafanaRule.rulerRule!] },
|
||||||
|
@ -427,6 +427,10 @@ export function setupMswServer() {
|
|||||||
server.listen({ onUnhandledRequest: 'error' });
|
server.listen({ onUnhandledRequest: 'error' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
server.resetHandlers();
|
||||||
|
});
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
server.close();
|
server.close();
|
||||||
});
|
});
|
||||||
|
@ -65,6 +65,7 @@ const fakeResponse: PromRulesResponse = {
|
|||||||
|
|
||||||
const server = setupMswServer();
|
const server = setupMswServer();
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
mockPromRulesApiResponse(server, fakeResponse);
|
mockPromRulesApiResponse(server, fakeResponse);
|
||||||
const originRule: RulerGrafanaRuleDTO = mockRulerGrafanaRule(
|
const originRule: RulerGrafanaRuleDTO = mockRulerGrafanaRule(
|
||||||
{
|
{
|
||||||
@ -77,6 +78,7 @@ const originRule: RulerGrafanaRuleDTO = mockRulerGrafanaRule(
|
|||||||
mockRulerRulesApiResponse(server, 'grafana', {
|
mockRulerRulesApiResponse(server, 'grafana', {
|
||||||
'folder-one': [{ name: 'group1', interval: '20s', rules: [originRule] }],
|
'folder-one': [{ name: 'group1', interval: '20s', rules: [originRule] }],
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const defaultOptions: UnifiedAlertListOptions = {
|
const defaultOptions: UnifiedAlertListOptions = {
|
||||||
maxItems: 2,
|
maxItems: 2,
|
||||||
|
Loading…
Reference in New Issue
Block a user