mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Refactor simplified routing contact points hook (#90762)
This commit is contained in:
parent
24c64fdffa
commit
9d639278f4
@ -17,7 +17,7 @@ import {
|
||||
} from '@grafana/runtime';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import * as ruleActionButtons from 'app/features/alerting/unified/components/rules/RuleActionsButtons';
|
||||
import { mockSearchApi, mockUserApi, setupMswServer } from 'app/features/alerting/unified/mockApi';
|
||||
import { mockUserApi, setupMswServer } from 'app/features/alerting/unified/mockApi';
|
||||
import { setAlertmanagerChoices } from 'app/features/alerting/unified/mocks/server/configure';
|
||||
import * as actions from 'app/features/alerting/unified/state/actions';
|
||||
import { getMockUser } from 'app/features/users/__mocks__/userMocks';
|
||||
@ -156,7 +156,6 @@ const ui = {
|
||||
const server = setupMswServer();
|
||||
|
||||
const configureMockServer = (server: SetupServer) => {
|
||||
mockSearchApi(server).search([]);
|
||||
mockUserApi(server).user(getMockUser());
|
||||
setAlertmanagerChoices(AlertmanagerChoice.All, 1);
|
||||
};
|
||||
|
@ -1,14 +1,10 @@
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { ContactPointsState, NotifierDTO, ReceiversStateDTO, ReceiverState } from 'app/types';
|
||||
import { ContactPointsState, ReceiversStateDTO, ReceiverState } from 'app/types';
|
||||
|
||||
import { getDatasourceAPIUid } from '../utils/datasource';
|
||||
|
||||
export function fetchNotifiers(): Promise<NotifierDTO[]> {
|
||||
return getBackendSrv().get(`/api/alert-notifiers`);
|
||||
}
|
||||
|
||||
interface IntegrationNameObject {
|
||||
type: string;
|
||||
index?: string;
|
||||
|
@ -1,6 +1,190 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`useContactPoints should return contact points with status 1`] = `
|
||||
{
|
||||
"contactPoints": [
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "grafana-default-email",
|
||||
"secureFields": {},
|
||||
"settings": {
|
||||
"addresses": "gilles.demey@grafana.com",
|
||||
"singleEmail": false,
|
||||
},
|
||||
"type": "email",
|
||||
"uid": "xeKQrBrnk",
|
||||
Symbol(receiver_status): {
|
||||
"lastNotifyAttempt": "2023-07-02T21:35:34.841+02:00",
|
||||
"lastNotifyAttemptDuration": "1ms",
|
||||
"lastNotifyAttemptError": "failed to send notification to email addresses: gilles.demey@grafana.com: dial tcp 192.168.1.21:1025: connect: connection refused",
|
||||
"name": "email",
|
||||
"sendResolved": true,
|
||||
},
|
||||
Symbol(receiver_metadata): {
|
||||
"description": "Sends notifications using Grafana server configured SMTP settings",
|
||||
"name": "Email",
|
||||
},
|
||||
Symbol(receiver_plugin_metadata): undefined,
|
||||
},
|
||||
],
|
||||
"name": "grafana-default-email",
|
||||
"policies": [
|
||||
{
|
||||
"receiver": "grafana-default-email",
|
||||
"route": {
|
||||
"type": "normal",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"policies": [],
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "Oncall-integration",
|
||||
"settings": {
|
||||
"url": "https://oncall-endpoint.example.com",
|
||||
},
|
||||
"type": "oncall",
|
||||
Symbol(receiver_status): undefined,
|
||||
Symbol(receiver_metadata): {
|
||||
"description": "Sends notifications to Grafana OnCall",
|
||||
"name": "Grafana OnCall",
|
||||
},
|
||||
Symbol(receiver_plugin_metadata): {
|
||||
"icon": "public/img/alerting/oncall_logo.svg",
|
||||
"title": "Grafana OnCall",
|
||||
},
|
||||
},
|
||||
],
|
||||
"name": "OnCall Conctact point",
|
||||
"policies": [],
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "provisioned-contact-point",
|
||||
"provenance": "api",
|
||||
"secureFields": {},
|
||||
"settings": {
|
||||
"addresses": "gilles.demey@grafana.com",
|
||||
"singleEmail": false,
|
||||
},
|
||||
"type": "email",
|
||||
"uid": "s8SdCVjnk",
|
||||
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": "provisioned-contact-point",
|
||||
"policies": [
|
||||
{
|
||||
"receiver": "provisioned-contact-point",
|
||||
"route": {
|
||||
"type": "normal",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "Slack with multiple channels",
|
||||
"secureFields": {
|
||||
"token": true,
|
||||
},
|
||||
"settings": {
|
||||
"recipient": "test-alerts",
|
||||
},
|
||||
"type": "slack",
|
||||
"uid": "c02ad56a-31da-46b9-becb-4348ec0890fd",
|
||||
Symbol(receiver_status): {
|
||||
"lastNotifyAttempt": "",
|
||||
"lastNotifyAttemptDuration": "",
|
||||
"name": "slack",
|
||||
"sendResolved": true,
|
||||
},
|
||||
Symbol(receiver_metadata): {
|
||||
"description": "Sends notifications to Slack",
|
||||
"name": "Slack",
|
||||
},
|
||||
Symbol(receiver_plugin_metadata): undefined,
|
||||
},
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "Slack with multiple channels",
|
||||
"secureFields": {
|
||||
"token": true,
|
||||
},
|
||||
"settings": {
|
||||
"recipient": "test-alerts2",
|
||||
},
|
||||
"type": "slack",
|
||||
"uid": "b286a3be-f690-49e2-8605-b075cbace2df",
|
||||
Symbol(receiver_status): {
|
||||
"lastNotifyAttempt": "",
|
||||
"lastNotifyAttemptDuration": "",
|
||||
"name": "slack",
|
||||
"sendResolved": true,
|
||||
},
|
||||
Symbol(receiver_metadata): {
|
||||
"description": "Sends notifications to Slack",
|
||||
"name": "Slack",
|
||||
},
|
||||
Symbol(receiver_plugin_metadata): undefined,
|
||||
},
|
||||
],
|
||||
"name": "Slack with multiple channels",
|
||||
"policies": [],
|
||||
},
|
||||
],
|
||||
"error": undefined,
|
||||
"isLoading": false,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`useContactPoints when having oncall plugin installed and no alert manager config data should return contact points with oncall metadata 1`] = `
|
||||
{
|
||||
"contactPoints": [
|
||||
{
|
||||
@ -184,177 +368,5 @@ exports[`useContactPoints should return contact points with status 1`] = `
|
||||
],
|
||||
"error": undefined,
|
||||
"isLoading": false,
|
||||
"refetchReceivers": [Function],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`useContactPoints when having oncall plugin installed and no alert manager config data should return contact points with oncall metadata 1`] = `
|
||||
{
|
||||
"contactPoints": [
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "grafana-default-email",
|
||||
"secureFields": {},
|
||||
"settings": {
|
||||
"addresses": "gilles.demey@grafana.com",
|
||||
"singleEmail": false,
|
||||
},
|
||||
"type": "email",
|
||||
"uid": "xeKQrBrnk",
|
||||
Symbol(receiver_status): {
|
||||
"lastNotifyAttempt": "2023-07-02T21:35:34.841+02:00",
|
||||
"lastNotifyAttemptDuration": "1ms",
|
||||
"lastNotifyAttemptError": "failed to send notification to email addresses: gilles.demey@grafana.com: dial tcp 192.168.1.21:1025: connect: connection refused",
|
||||
"name": "email",
|
||||
"sendResolved": true,
|
||||
},
|
||||
Symbol(receiver_metadata): {
|
||||
"description": "Sends notifications using Grafana server configured SMTP settings",
|
||||
"name": "Email",
|
||||
},
|
||||
Symbol(receiver_plugin_metadata): undefined,
|
||||
},
|
||||
],
|
||||
"name": "grafana-default-email",
|
||||
"policies": undefined,
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"policies": undefined,
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "Oncall-integration",
|
||||
"settings": {
|
||||
"url": "https://oncall-endpoint.example.com",
|
||||
},
|
||||
"type": "oncall",
|
||||
Symbol(receiver_status): undefined,
|
||||
Symbol(receiver_metadata): {
|
||||
"description": "Sends notifications to Grafana OnCall",
|
||||
"name": "Grafana OnCall",
|
||||
},
|
||||
Symbol(receiver_plugin_metadata): {
|
||||
"icon": "public/img/alerting/oncall_logo.svg",
|
||||
"title": "Grafana OnCall",
|
||||
},
|
||||
},
|
||||
],
|
||||
"name": "OnCall Conctact point",
|
||||
"policies": undefined,
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "provisioned-contact-point",
|
||||
"provenance": "api",
|
||||
"secureFields": {},
|
||||
"settings": {
|
||||
"addresses": "gilles.demey@grafana.com",
|
||||
"singleEmail": false,
|
||||
},
|
||||
"type": "email",
|
||||
"uid": "s8SdCVjnk",
|
||||
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": "provisioned-contact-point",
|
||||
"policies": undefined,
|
||||
},
|
||||
{
|
||||
"grafana_managed_receiver_configs": [
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "Slack with multiple channels",
|
||||
"secureFields": {
|
||||
"token": true,
|
||||
},
|
||||
"settings": {
|
||||
"recipient": "test-alerts",
|
||||
},
|
||||
"type": "slack",
|
||||
"uid": "c02ad56a-31da-46b9-becb-4348ec0890fd",
|
||||
Symbol(receiver_status): {
|
||||
"lastNotifyAttempt": "",
|
||||
"lastNotifyAttemptDuration": "",
|
||||
"name": "slack",
|
||||
"sendResolved": true,
|
||||
},
|
||||
Symbol(receiver_metadata): {
|
||||
"description": "Sends notifications to Slack",
|
||||
"name": "Slack",
|
||||
},
|
||||
Symbol(receiver_plugin_metadata): undefined,
|
||||
},
|
||||
{
|
||||
"disableResolveMessage": false,
|
||||
"name": "Slack with multiple channels",
|
||||
"secureFields": {
|
||||
"token": true,
|
||||
},
|
||||
"settings": {
|
||||
"recipient": "test-alerts2",
|
||||
},
|
||||
"type": "slack",
|
||||
"uid": "b286a3be-f690-49e2-8605-b075cbace2df",
|
||||
Symbol(receiver_status): {
|
||||
"lastNotifyAttempt": "",
|
||||
"lastNotifyAttemptDuration": "",
|
||||
"name": "slack",
|
||||
"sendResolved": true,
|
||||
},
|
||||
Symbol(receiver_metadata): {
|
||||
"description": "Sends notifications to Slack",
|
||||
"name": "Slack",
|
||||
},
|
||||
Symbol(receiver_plugin_metadata): undefined,
|
||||
},
|
||||
],
|
||||
"name": "Slack with multiple channels",
|
||||
"policies": undefined,
|
||||
},
|
||||
],
|
||||
"error": undefined,
|
||||
"isLoading": false,
|
||||
"refetchReceivers": [Function],
|
||||
}
|
||||
`;
|
||||
|
@ -1,17 +1,30 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
import { ReactNode } from 'react';
|
||||
import { getWrapper } from 'test/test-utils';
|
||||
|
||||
import alertmanagerMock from 'app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json';
|
||||
import { disablePlugin } from 'app/features/alerting/unified/mocks/server/configure';
|
||||
import { setOnCallIntegrations } from 'app/features/alerting/unified/mocks/server/handlers/plugins/configure-plugins';
|
||||
import { SupportedPlugin } from 'app/features/alerting/unified/types/pluginBridges';
|
||||
import { AccessControlAction } from 'app/types';
|
||||
|
||||
import { mockApi, setupMswServer } from '../../mockApi';
|
||||
import { setupMswServer } from '../../mockApi';
|
||||
import { grantUserPermissions } from '../../mocks';
|
||||
import { AlertmanagerProvider } from '../../state/AlertmanagerContext';
|
||||
|
||||
import { useContactPointsWithStatus } from './useContactPoints';
|
||||
|
||||
const server = setupMswServer();
|
||||
const wrapper = ({ children }: { children: ReactNode }) => {
|
||||
const ProviderWrapper = getWrapper({ renderWithRouter: true });
|
||||
return (
|
||||
<ProviderWrapper>
|
||||
<AlertmanagerProvider accessType="notification" alertmanagerSourceName="grafana">
|
||||
{children}
|
||||
</AlertmanagerProvider>
|
||||
</ProviderWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
setupMswServer();
|
||||
|
||||
describe('useContactPoints', () => {
|
||||
beforeAll(() => {
|
||||
@ -19,23 +32,10 @@ describe('useContactPoints', () => {
|
||||
});
|
||||
|
||||
it('should return contact points with status', async () => {
|
||||
setOnCallIntegrations([
|
||||
{
|
||||
display_name: 'grafana-integration',
|
||||
value: 'ABC123',
|
||||
integration_url: 'https://oncall-endpoint.example.com',
|
||||
},
|
||||
]);
|
||||
mockApi(server).getContactPointsList(receivers);
|
||||
disablePlugin(SupportedPlugin.OnCall);
|
||||
|
||||
const { result } = renderHook(() => useContactPointsWithStatus(), {
|
||||
wrapper: ({ children }) => (
|
||||
<TestProvider>
|
||||
<AlertmanagerProvider accessType={'notification'} alertmanagerSourceName={'grafana'}>
|
||||
{children}
|
||||
</AlertmanagerProvider>
|
||||
</TestProvider>
|
||||
),
|
||||
wrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
@ -53,20 +53,10 @@ describe('useContactPoints', () => {
|
||||
integration_url: 'https://oncall-endpoint.example.com',
|
||||
},
|
||||
]);
|
||||
mockApi(server).getContactPointsList(receivers);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => useContactPointsWithStatus({ includePoliciesCount: false, receiverStatusPollingInterval: 0 }),
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
<TestProvider>
|
||||
<AlertmanagerProvider accessType={'notification'} alertmanagerSourceName={'grafana'}>
|
||||
{children}
|
||||
</AlertmanagerProvider>
|
||||
</TestProvider>
|
||||
),
|
||||
}
|
||||
);
|
||||
const { result } = renderHook(() => useContactPointsWithStatus(), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
@ -75,5 +65,3 @@ describe('useContactPoints', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const receivers = JSON.parse(JSON.stringify(alertmanagerMock)).alertmanager_config.receivers;
|
||||
|
@ -5,9 +5,10 @@
|
||||
|
||||
import { produce } from 'immer';
|
||||
import { remove } from 'lodash';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { alertmanagerApi } from '../../api/alertmanagerApi';
|
||||
import { onCallApi, OnCallIntegrationDTO } from '../../api/onCallApi';
|
||||
import { onCallApi } from '../../api/onCallApi';
|
||||
import { usePluginBridge } from '../../hooks/usePluginBridge';
|
||||
import { useAlertmanager } from '../../state/AlertmanagerContext';
|
||||
import { SupportedPlugin } from '../../types/pluginBridges';
|
||||
@ -27,62 +28,116 @@ const RECEIVER_STATUS_POLLING_INTERVAL = 10 * 1000; // 10 seconds
|
||||
* 3. (if available) additional metadata about Grafana Managed contact points
|
||||
* 4. (if available) the OnCall plugin metadata
|
||||
*/
|
||||
interface UseContactPointsWithStatusOptions {
|
||||
includePoliciesCount: boolean;
|
||||
receiverStatusPollingInterval?: number;
|
||||
}
|
||||
|
||||
const defaultHookOptions = {
|
||||
includePoliciesCount: true,
|
||||
receiverStatusPollingInterval: RECEIVER_STATUS_POLLING_INTERVAL,
|
||||
const {
|
||||
useGetAlertmanagerConfigurationQuery,
|
||||
useGetContactPointsListQuery,
|
||||
useGetContactPointsStatusQuery,
|
||||
useGrafanaNotifiersQuery,
|
||||
useLazyGetAlertmanagerConfigurationQuery,
|
||||
useUpdateAlertmanagerConfigurationMutation,
|
||||
} = alertmanagerApi;
|
||||
|
||||
const { useGrafanaOnCallIntegrationsQuery } = onCallApi;
|
||||
|
||||
/**
|
||||
* Check if OnCall is installed, and fetch the list of integrations if so.
|
||||
*
|
||||
* Otherwise, returns no data
|
||||
*/
|
||||
const useOnCallIntegrations = ({ skip }: { skip?: boolean } = {}) => {
|
||||
const { installed, loading } = usePluginBridge(SupportedPlugin.OnCall);
|
||||
const oncallIntegrationsResponse = useGrafanaOnCallIntegrationsQuery(undefined, { skip: skip || !installed });
|
||||
|
||||
return useMemo(() => {
|
||||
if (installed) {
|
||||
return oncallIntegrationsResponse;
|
||||
}
|
||||
return {
|
||||
isLoading: loading,
|
||||
data: undefined,
|
||||
};
|
||||
}, [installed, loading, oncallIntegrationsResponse]);
|
||||
};
|
||||
|
||||
export function useContactPointsWithStatus({
|
||||
includePoliciesCount,
|
||||
receiverStatusPollingInterval,
|
||||
}: UseContactPointsWithStatusOptions = defaultHookOptions) {
|
||||
const { selectedAlertmanager, isGrafanaAlertmanager } = useAlertmanager();
|
||||
const { installed: onCallPluginInstalled, loading: onCallPluginStatusLoading } = usePluginBridge(
|
||||
SupportedPlugin.OnCall
|
||||
/**
|
||||
* Fetch contact points from separate endpoint (i.e. not the Alertmanager config) and combine with
|
||||
* OnCall integrations and any additional metadata from list of notifiers
|
||||
* (e.g. hydrate with additional names/descriptions)
|
||||
*/
|
||||
export const useGetContactPoints = () => {
|
||||
const onCallResponse = useOnCallIntegrations();
|
||||
const alertNotifiers = useGrafanaNotifiersQuery();
|
||||
const contactPointsListResponse = useGetContactPointsListQuery();
|
||||
|
||||
return useMemo(() => {
|
||||
const isLoading = onCallResponse.isLoading || alertNotifiers.isLoading || contactPointsListResponse.isLoading;
|
||||
|
||||
if (isLoading || !contactPointsListResponse.data) {
|
||||
return {
|
||||
...contactPointsListResponse,
|
||||
// If we're inside this block, it means that at least one of the endpoints we care about is still loading,
|
||||
// but the contactPointsListResponse may have in fact finished.
|
||||
// If we were to use _that_ loading state, it might be inaccurate elsewhere when consuming this hook,
|
||||
// so we explicitly say "yes, this is definitely still loading"
|
||||
isLoading: true,
|
||||
contactPoints: [],
|
||||
};
|
||||
}
|
||||
|
||||
const enhanced = enhanceContactPointsWithMetadata(
|
||||
[],
|
||||
alertNotifiers.data,
|
||||
onCallResponse?.data,
|
||||
contactPointsListResponse.data,
|
||||
undefined
|
||||
);
|
||||
|
||||
// fetch receiver status if we're dealing with a Grafana Managed Alertmanager
|
||||
const fetchContactPointsStatus = alertmanagerApi.endpoints.getContactPointsStatus.useQuery(undefined, {
|
||||
return {
|
||||
...contactPointsListResponse,
|
||||
contactPoints: enhanced,
|
||||
};
|
||||
}, [
|
||||
alertNotifiers.data,
|
||||
alertNotifiers.isLoading,
|
||||
contactPointsListResponse,
|
||||
onCallResponse?.data,
|
||||
onCallResponse.isLoading,
|
||||
]);
|
||||
};
|
||||
|
||||
export function useContactPointsWithStatus() {
|
||||
const { selectedAlertmanager, isGrafanaAlertmanager } = useAlertmanager();
|
||||
|
||||
const defaultOptions = {
|
||||
refetchOnFocus: true,
|
||||
refetchOnReconnect: true,
|
||||
// re-fetch status every so often for up-to-date information, allow disabling by passing "receiverStatusPollingInterval: 0"
|
||||
pollingInterval: receiverStatusPollingInterval,
|
||||
};
|
||||
|
||||
// fetch receiver status if we're dealing with a Grafana Managed Alertmanager
|
||||
const fetchContactPointsStatus = useGetContactPointsStatusQuery(undefined, {
|
||||
...defaultOptions,
|
||||
// re-fetch status every so often for up-to-date information
|
||||
pollingInterval: RECEIVER_STATUS_POLLING_INTERVAL,
|
||||
// skip fetching receiver statuses if not Grafana AM
|
||||
skip: !isGrafanaAlertmanager,
|
||||
});
|
||||
|
||||
// fetch notifier metadata from the Grafana API if we're using a Grafana AM – this will be used to add additional
|
||||
// metadata and canonical names to the receiver
|
||||
const fetchReceiverMetadata = alertmanagerApi.endpoints.grafanaNotifiers.useQuery(undefined, {
|
||||
const fetchReceiverMetadata = useGrafanaNotifiersQuery(undefined, {
|
||||
skip: !isGrafanaAlertmanager,
|
||||
});
|
||||
|
||||
// if the OnCall plugin is installed, fetch its list of integrations so we can match those to the Grafana Managed contact points
|
||||
const { data: onCallIntegrations, isLoading: onCallPluginIntegrationsLoading } =
|
||||
onCallApi.endpoints.grafanaOnCallIntegrations.useQuery(undefined, {
|
||||
skip: !onCallPluginInstalled || !isGrafanaAlertmanager,
|
||||
const { data: onCallMetadata, isLoading: onCallPluginIntegrationsLoading } = useOnCallIntegrations({
|
||||
skip: !isGrafanaAlertmanager,
|
||||
});
|
||||
|
||||
// null = no installed, undefined = loading, [n] is installed with integrations
|
||||
let onCallMetadata: null | undefined | OnCallIntegrationDTO[] = undefined;
|
||||
if (onCallPluginInstalled) {
|
||||
onCallMetadata = onCallIntegrations ?? [];
|
||||
} else if (onCallPluginInstalled === false) {
|
||||
onCallMetadata = null;
|
||||
}
|
||||
|
||||
// fetch the latest config from the Alertmanager
|
||||
// we use this endpoint only when we need to get the number of policies
|
||||
const fetchAlertmanagerConfiguration = alertmanagerApi.endpoints.getAlertmanagerConfiguration.useQuery(
|
||||
selectedAlertmanager!,
|
||||
{
|
||||
refetchOnFocus: true,
|
||||
refetchOnReconnect: true,
|
||||
const fetchAlertmanagerConfiguration = useGetAlertmanagerConfigurationQuery(selectedAlertmanager!, {
|
||||
...defaultOptions,
|
||||
selectFromResult: (result) => ({
|
||||
...result,
|
||||
contactPoints: result.data
|
||||
@ -95,55 +150,23 @@ export function useContactPointsWithStatus({
|
||||
)
|
||||
: [],
|
||||
}),
|
||||
skip: !includePoliciesCount,
|
||||
}
|
||||
);
|
||||
|
||||
// for Grafana Managed Alertmanager, we use the new read-only endpoint for getting the list of contact points
|
||||
const fetchGrafanaContactPoints = alertmanagerApi.endpoints.getContactPointsList.useQuery(undefined, {
|
||||
refetchOnFocus: true,
|
||||
refetchOnReconnect: true,
|
||||
selectFromResult: (result) => ({
|
||||
...result,
|
||||
contactPoints: result.data
|
||||
? enhanceContactPointsWithMetadata(
|
||||
fetchContactPointsStatus.data,
|
||||
fetchReceiverMetadata.data,
|
||||
onCallMetadata,
|
||||
result.data, // contact points from the new readonly endpoint
|
||||
undefined //no config data
|
||||
)
|
||||
: [],
|
||||
}),
|
||||
skip: includePoliciesCount || !isGrafanaAlertmanager,
|
||||
});
|
||||
|
||||
// we will fail silently for fetching OnCall plugin status and integrations
|
||||
const error =
|
||||
fetchAlertmanagerConfiguration.error || fetchGrafanaContactPoints.error || fetchContactPointsStatus.error;
|
||||
const error = fetchAlertmanagerConfiguration.error || fetchContactPointsStatus.error;
|
||||
const isLoading =
|
||||
fetchAlertmanagerConfiguration.isLoading ||
|
||||
fetchGrafanaContactPoints.isLoading ||
|
||||
fetchContactPointsStatus.isLoading ||
|
||||
onCallPluginStatusLoading ||
|
||||
onCallPluginIntegrationsLoading;
|
||||
fetchAlertmanagerConfiguration.isLoading || fetchContactPointsStatus.isLoading || onCallPluginIntegrationsLoading;
|
||||
|
||||
const unsortedContactPoints = includePoliciesCount
|
||||
? fetchAlertmanagerConfiguration.contactPoints
|
||||
: fetchGrafanaContactPoints.contactPoints;
|
||||
const contactPoints = unsortedContactPoints.sort((a, b) => a.name.localeCompare(b.name));
|
||||
return {
|
||||
error,
|
||||
isLoading,
|
||||
contactPoints,
|
||||
refetchReceivers: fetchGrafanaContactPoints.refetch,
|
||||
contactPoints: fetchAlertmanagerConfiguration.contactPoints,
|
||||
};
|
||||
}
|
||||
|
||||
export function useDeleteContactPoint(selectedAlertmanager: string) {
|
||||
const [fetchAlertmanagerConfig] = alertmanagerApi.endpoints.getAlertmanagerConfiguration.useLazyQuery();
|
||||
const [updateAlertManager, updateAlertmanagerState] =
|
||||
alertmanagerApi.endpoints.updateAlertmanagerConfiguration.useMutation();
|
||||
const [fetchAlertmanagerConfig] = useLazyGetAlertmanagerConfigurationQuery();
|
||||
const [updateAlertManager, updateAlertmanagerState] = useUpdateAlertmanagerConfigurationMutation();
|
||||
|
||||
const deleteTrigger = (contactPointName: string) => {
|
||||
return fetchAlertmanagerConfig(selectedAlertmanager).then(({ data }) => {
|
||||
|
@ -127,7 +127,7 @@ export function enhanceContactPointsWithMetadata(
|
||||
? (alertmanagerConfiguration?.alertmanager_config.receivers ?? [])
|
||||
: (contactPoints ?? []);
|
||||
|
||||
return contactPointsList.map((contactPoint) => {
|
||||
const enhanced = contactPointsList.map((contactPoint) => {
|
||||
const receivers = extractReceivers(contactPoint);
|
||||
const statusForReceiver = status.find((status) => status.name === contactPoint.name);
|
||||
|
||||
@ -152,6 +152,8 @@ export function enhanceContactPointsWithMetadata(
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
return enhanced.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
export function isAutoGeneratedPolicy(route: Route) {
|
||||
|
@ -4,9 +4,8 @@ import { Props } from 'react-virtualized-auto-sizer';
|
||||
import { render, waitFor, waitForElementToBeRemoved, userEvent } from 'test/test-utils';
|
||||
import { byRole, byTestId, byText } from 'testing-library-selector';
|
||||
|
||||
import { DashboardSearchItemType } from '../../../../search/types';
|
||||
import { mockExportApi, mockSearchApi, setupMswServer } from '../../mockApi';
|
||||
import { mockDashboardSearchItem, mockDataSource } from '../../mocks';
|
||||
import { mockExportApi, setupMswServer } from '../../mockApi';
|
||||
import { mockDataSource } from '../../mocks';
|
||||
import { grafanaRulerRule } from '../../mocks/grafanaRulerApi';
|
||||
import { setupDataSources } from '../../testSetup/datasources';
|
||||
|
||||
@ -67,15 +66,6 @@ describe('GrafanaModifyExport', () => {
|
||||
setupDataSources(dataSources.default);
|
||||
|
||||
it('Should render edit form for the specified rule', async () => {
|
||||
mockSearchApi(server).search([
|
||||
mockDashboardSearchItem({
|
||||
title: grafanaRulerRule.grafana_alert.title,
|
||||
uid: grafanaRulerRule.grafana_alert.namespace_uid,
|
||||
url: '',
|
||||
tags: [],
|
||||
type: DashboardSearchItemType.DashFolder,
|
||||
}),
|
||||
]);
|
||||
mockExportApi(server).modifiedExport(grafanaRulerRule.grafana_alert.namespace_uid, {
|
||||
yaml: 'Yaml Export Content',
|
||||
json: 'Json Export Content',
|
||||
|
@ -8,7 +8,7 @@ import { RuleFormValues } from 'app/features/alerting/unified/types/rule-form';
|
||||
import { AlertManagerDataSource } from 'app/features/alerting/unified/utils/datasource';
|
||||
|
||||
import { ContactPointReceiverSummary } from '../../../contact-points/ContactPoint';
|
||||
import { useContactPointsWithStatus } from '../../../contact-points/useContactPoints';
|
||||
import { useGetContactPoints } from '../../../contact-points/useContactPoints';
|
||||
import { ContactPointWithMetadata } from '../../../contact-points/utils';
|
||||
|
||||
import { ContactPointDetails } from './contactPoint/ContactPointDetails';
|
||||
@ -28,8 +28,9 @@ export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRo
|
||||
isLoading,
|
||||
error: errorInContactPointStatus,
|
||||
contactPoints,
|
||||
refetchReceivers,
|
||||
} = useContactPointsWithStatus({ includePoliciesCount: false, receiverStatusPollingInterval: 0 });
|
||||
refetch: refetchReceivers,
|
||||
} = useGetContactPoints();
|
||||
|
||||
const [selectedContactPointWithMetadata, setSelectedContactPointWithMetadata] = useState<
|
||||
ContactPointWithMetadata | undefined
|
||||
>();
|
||||
|
@ -1,72 +1,39 @@
|
||||
import { render, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import * as React from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { Route } from 'react-router-dom';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
import { ui } from 'test/helpers/alertingRuleEditor';
|
||||
import { clickSelectOption } from 'test/helpers/selectOptionInTest';
|
||||
import { render, screen, waitFor, waitForElementToBeRemoved, userEvent } from 'test/test-utils';
|
||||
import { byRole } from 'testing-library-selector';
|
||||
|
||||
import { config, locationService } from '@grafana/runtime';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import RuleEditor from 'app/features/alerting/unified/RuleEditor';
|
||||
import * as ruler from 'app/features/alerting/unified/api/ruler';
|
||||
import * as useContactPoints from 'app/features/alerting/unified/components/contact-points/useContactPoints';
|
||||
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
|
||||
import { grantUserPermissions, mockDataSource } from 'app/features/alerting/unified/mocks';
|
||||
import { setAlertmanagerChoices } from 'app/features/alerting/unified/mocks/server/configure';
|
||||
import { FOLDER_TITLE_HAPPY_PATH } from 'app/features/alerting/unified/mocks/server/handlers/search';
|
||||
import { AlertmanagerProvider } from 'app/features/alerting/unified/state/AlertmanagerContext';
|
||||
import * as utils_config from 'app/features/alerting/unified/utils/config';
|
||||
import {
|
||||
DataSourceType,
|
||||
GRAFANA_DATASOURCE_NAME,
|
||||
GRAFANA_RULES_SOURCE_NAME,
|
||||
useGetAlertManagerDataSourcesByPermissionAndConfig,
|
||||
} from 'app/features/alerting/unified/utils/datasource';
|
||||
import { getDefaultQueries } from 'app/features/alerting/unified/utils/rule-form';
|
||||
import { searchFolders } from 'app/features/manage-dashboards/state/actions';
|
||||
import { DashboardSearchHit, DashboardSearchItemType } from 'app/features/search/types';
|
||||
import { AlertmanagerChoice } from 'app/plugins/datasource/alertmanager/types';
|
||||
import { AccessControlAction } from 'app/types';
|
||||
import { GrafanaAlertStateDecision } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { grafanaRulerEmptyGroup, grafanaRulerNamespace2, grafanaRulerRule } from '../../../../mocks/grafanaRulerApi';
|
||||
import { grafanaRulerEmptyGroup, grafanaRulerNamespace2 } from '../../../../mocks/grafanaRulerApi';
|
||||
import { setupDataSources } from '../../../../testSetup/datasources';
|
||||
import { RECEIVER_META_KEY } from '../../../contact-points/useContactPoints';
|
||||
import { ContactPointWithMetadata } from '../../../contact-points/utils';
|
||||
import { ExpressionEditorProps } from '../../ExpressionEditor';
|
||||
|
||||
jest.mock('app/features/alerting/unified/components/rule-editor/ExpressionEditor', () => ({
|
||||
ExpressionEditor: ({ value, onChange }: ExpressionEditorProps) => (
|
||||
<input value={value} data-testid="expr" onChange={(e) => onChange(e.target.value)} />
|
||||
),
|
||||
}));
|
||||
|
||||
jest.mock('app/features/manage-dashboards/state/actions');
|
||||
|
||||
jest.mock('app/core/components/AppChrome/AppChromeUpdate', () => ({
|
||||
AppChromeUpdate: ({ actions }: { actions: React.ReactNode }) => <div>{actions}</div>,
|
||||
AppChromeUpdate: ({ actions }: { actions: ReactNode }) => <div>{actions}</div>,
|
||||
}));
|
||||
|
||||
// there's no angular scope in test and things go terribly wrong when trying to render the query editor row.
|
||||
// lets just skip it
|
||||
jest.mock('app/features/query/components/QueryEditorRow', () => ({
|
||||
QueryEditorRow: () => <p>hi</p>,
|
||||
}));
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
// jest.spyOn(utils_config, 'getAllDataSources');
|
||||
// jest.spyOn(dsByPermission, 'useAlertManagersByPermission');
|
||||
jest.spyOn(useContactPoints, 'useContactPointsWithStatus');
|
||||
|
||||
jest.setTimeout(60 * 1000);
|
||||
|
||||
const mocks = {
|
||||
getAllDataSources: jest.mocked(utils_config.getAllDataSources),
|
||||
searchFolders: jest.mocked(searchFolders),
|
||||
useContactPointsWithStatus: jest.mocked(useContactPoints.useContactPointsWithStatus),
|
||||
useGetAlertManagerDataSourcesByPermissionAndConfig: jest.mocked(useGetAlertManagerDataSourcesByPermissionAndConfig),
|
||||
api: {
|
||||
setRulerRuleGroup: jest.spyOn(ruler, 'setRulerRuleGroup'),
|
||||
},
|
||||
@ -74,11 +41,37 @@ const mocks = {
|
||||
|
||||
setupMswServer();
|
||||
|
||||
const dataSources = {
|
||||
default: mockDataSource(
|
||||
{
|
||||
type: 'prometheus',
|
||||
name: 'Prom',
|
||||
isDefault: true,
|
||||
},
|
||||
{ alerting: false }
|
||||
),
|
||||
am: mockDataSource({
|
||||
name: 'Alertmanager',
|
||||
type: DataSourceType.Alertmanager,
|
||||
}),
|
||||
};
|
||||
setupDataSources(dataSources.default, dataSources.am);
|
||||
|
||||
const selectFolderAndGroup = async () => {
|
||||
const user = userEvent.setup();
|
||||
const folderInput = await ui.inputs.folder.find();
|
||||
await clickSelectOption(folderInput, FOLDER_TITLE_HAPPY_PATH);
|
||||
const groupInput = await ui.inputs.group.find();
|
||||
await user.click(await byRole('combobox').find(groupInput));
|
||||
await clickSelectOption(groupInput, grafanaRulerEmptyGroup.name);
|
||||
};
|
||||
|
||||
describe('Can create a new grafana managed alert unsing simplified routing', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
contextSrv.isEditor = true;
|
||||
contextSrv.hasEditPermissionInFolders = true;
|
||||
config.featureToggles.alertingSimplifiedRouting = true;
|
||||
grantUserPermissions([
|
||||
AccessControlAction.AlertingRuleRead,
|
||||
AccessControlAction.AlertingRuleUpdate,
|
||||
@ -96,74 +89,26 @@ describe('Can create a new grafana managed alert unsing simplified routing', ()
|
||||
]);
|
||||
});
|
||||
|
||||
const dataSources = {
|
||||
default: mockDataSource(
|
||||
{
|
||||
type: 'prometheus',
|
||||
name: 'Prom',
|
||||
isDefault: true,
|
||||
},
|
||||
{ alerting: false }
|
||||
),
|
||||
am: mockDataSource({
|
||||
name: 'Alertmanager',
|
||||
type: DataSourceType.Alertmanager,
|
||||
}),
|
||||
};
|
||||
setupDataSources(dataSources.default, dataSources.am);
|
||||
|
||||
it('cannot create new grafana managed alert when using simplified routing and not selecting a contact point', async () => {
|
||||
// no contact points found
|
||||
mocks.useContactPointsWithStatus.mockReturnValue({
|
||||
contactPoints: [],
|
||||
isLoading: false,
|
||||
error: undefined,
|
||||
refetchReceivers: jest.fn(),
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
|
||||
mocks.api.setRulerRuleGroup.mockResolvedValue();
|
||||
mocks.searchFolders.mockResolvedValue([
|
||||
{
|
||||
title: 'Folder A',
|
||||
uid: grafanaRulerRule.grafana_alert.namespace_uid,
|
||||
id: 1,
|
||||
type: DashboardSearchItemType.DashDB,
|
||||
},
|
||||
{
|
||||
title: 'Folder B',
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
title: 'Folder / with slash',
|
||||
id: 2,
|
||||
uid: 'b',
|
||||
type: DashboardSearchItemType.DashDB,
|
||||
},
|
||||
] as DashboardSearchHit[]);
|
||||
|
||||
config.featureToggles.alertingSimplifiedRouting = true;
|
||||
renderSimplifiedRuleEditor();
|
||||
await waitForElementToBeRemoved(screen.getAllByTestId('Spinner'));
|
||||
|
||||
await user.type(await ui.inputs.name.find(), 'my great new rule');
|
||||
|
||||
const folderInput = await ui.inputs.folder.find();
|
||||
await clickSelectOption(folderInput, 'Folder A');
|
||||
const groupInput = await ui.inputs.group.find();
|
||||
await user.click(byRole('combobox').get(groupInput));
|
||||
await clickSelectOption(groupInput, grafanaRulerRule.grafana_alert.rule_group);
|
||||
await selectFolderAndGroup();
|
||||
|
||||
//select contact point routing
|
||||
await user.click(ui.inputs.simplifiedRouting.contactPointRouting.get());
|
||||
|
||||
// do not select a contact point
|
||||
// save and check that call to backend was not made
|
||||
await user.click(ui.buttons.saveAndExit.get());
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Contact point is required.')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Contact point is required.')).toBeInTheDocument();
|
||||
expect(mocks.api.setRulerRuleGroup).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('simplified routing is not available when Grafana AM is not enabled', async () => {
|
||||
config.featureToggles.alertingSimplifiedRouting = true;
|
||||
setAlertmanagerChoices(AlertmanagerChoice.External, 1);
|
||||
renderSimplifiedRuleEditor();
|
||||
await waitForElementToBeRemoved(screen.getAllByTestId('Spinner'));
|
||||
@ -172,69 +117,20 @@ describe('Can create a new grafana managed alert unsing simplified routing', ()
|
||||
});
|
||||
|
||||
it('can create new grafana managed alert when using simplified routing and selecting a contact point', async () => {
|
||||
const contactPointsAvailable: ContactPointWithMetadata[] = [
|
||||
{
|
||||
name: 'contact_point1',
|
||||
grafana_managed_receiver_configs: [
|
||||
{
|
||||
name: 'contact_point1',
|
||||
type: 'email',
|
||||
disableResolveMessage: false,
|
||||
[RECEIVER_META_KEY]: {
|
||||
name: 'contact_point1',
|
||||
description: 'contact_point1 description',
|
||||
},
|
||||
settings: {},
|
||||
},
|
||||
],
|
||||
policies: [],
|
||||
},
|
||||
];
|
||||
mocks.useContactPointsWithStatus.mockReturnValue({
|
||||
contactPoints: contactPointsAvailable,
|
||||
isLoading: false,
|
||||
error: undefined,
|
||||
refetchReceivers: jest.fn(),
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
const contactPointName = 'lotsa-emails';
|
||||
|
||||
mocks.api.setRulerRuleGroup.mockResolvedValue();
|
||||
mocks.searchFolders.mockResolvedValue([
|
||||
{
|
||||
title: 'Folder A',
|
||||
uid: grafanaRulerNamespace2.uid,
|
||||
id: 1,
|
||||
type: DashboardSearchItemType.DashDB,
|
||||
},
|
||||
{
|
||||
title: 'Folder B',
|
||||
id: 2,
|
||||
uid: 'b',
|
||||
type: DashboardSearchItemType.DashDB,
|
||||
},
|
||||
{
|
||||
title: 'Folder / with slash',
|
||||
uid: 'c',
|
||||
id: 2,
|
||||
type: DashboardSearchItemType.DashDB,
|
||||
},
|
||||
] as DashboardSearchHit[]);
|
||||
|
||||
config.featureToggles.alertingSimplifiedRouting = true;
|
||||
renderSimplifiedRuleEditor();
|
||||
await waitForElementToBeRemoved(screen.getAllByTestId('Spinner'));
|
||||
|
||||
await user.type(await ui.inputs.name.find(), 'my great new rule');
|
||||
|
||||
const folderInput = await ui.inputs.folder.find();
|
||||
await clickSelectOption(folderInput, 'Folder A');
|
||||
const groupInput = await ui.inputs.group.find();
|
||||
await user.click(byRole('combobox').get(groupInput));
|
||||
await clickSelectOption(groupInput, grafanaRulerEmptyGroup.name);
|
||||
await selectFolderAndGroup();
|
||||
|
||||
//select contact point routing
|
||||
await user.click(ui.inputs.simplifiedRouting.contactPointRouting.get());
|
||||
const contactPointInput = await ui.inputs.simplifiedRouting.contactPoint.find();
|
||||
await user.click(byRole('combobox').get(contactPointInput));
|
||||
await clickSelectOption(contactPointInput, 'contact_point1');
|
||||
await clickSelectOption(contactPointInput, contactPointName);
|
||||
|
||||
// save and check what was sent to backend
|
||||
await user.click(ui.buttons.saveAndExit.get());
|
||||
@ -262,7 +158,7 @@ describe('Can create a new grafana managed alert unsing simplified routing', ()
|
||||
group_interval: undefined,
|
||||
group_wait: undefined,
|
||||
mute_timings: undefined,
|
||||
receiver: 'contact_point1',
|
||||
receiver: contactPointName,
|
||||
repeat_interval: undefined,
|
||||
},
|
||||
},
|
||||
@ -274,13 +170,10 @@ describe('Can create a new grafana managed alert unsing simplified routing', ()
|
||||
});
|
||||
|
||||
function renderSimplifiedRuleEditor() {
|
||||
locationService.push(`/alerting/new/alerting`);
|
||||
|
||||
return render(
|
||||
<TestProvider>
|
||||
<AlertmanagerProvider alertmanagerSourceName={GRAFANA_DATASOURCE_NAME} accessType="notification">
|
||||
<Route path={['/alerting/new/:type', '/alerting/:id/edit']} component={RuleEditor} />
|
||||
</AlertmanagerProvider>
|
||||
</TestProvider>
|
||||
</AlertmanagerProvider>,
|
||||
{ historyOptions: { initialEntries: ['/alerting/new/alerting'] } }
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { TestProvider } from 'test/helpers/TestProvider';
|
||||
import { byLabelText, byRole } from 'testing-library-selector';
|
||||
|
||||
import { locationService, setDataSourceSrv } from '@grafana/runtime';
|
||||
import { mockSearchApi, setupMswServer } from 'app/features/alerting/unified/mockApi';
|
||||
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
|
||||
|
||||
import * as analytics from '../../Analytics';
|
||||
import { MockDataSourceSrv } from '../../mocks';
|
||||
@ -12,7 +12,7 @@ import { setupPluginsExtensionsHook } from '../../testSetup/plugins';
|
||||
|
||||
import RulesFilter from './RulesFilter';
|
||||
|
||||
const server = setupMswServer();
|
||||
setupMswServer();
|
||||
jest.spyOn(analytics, 'logInfo');
|
||||
|
||||
jest.mock('./MultipleDataSourcePicker', () => {
|
||||
@ -43,7 +43,6 @@ const ui = {
|
||||
|
||||
beforeEach(() => {
|
||||
locationService.replace({ search: '' });
|
||||
mockSearchApi(server).search([]);
|
||||
});
|
||||
|
||||
describe('RulesFilter', () => {
|
||||
|
@ -20,7 +20,6 @@ import {
|
||||
AlertManagerCortexConfig,
|
||||
AlertmanagerReceiver,
|
||||
EmailConfig,
|
||||
GrafanaManagedContactPoint,
|
||||
GrafanaManagedReceiverConfig,
|
||||
MatcherOperator,
|
||||
Route,
|
||||
@ -170,10 +169,6 @@ export function mockApi(server: SetupServer) {
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
getContactPointsList: (response: GrafanaManagedContactPoint[]) => {
|
||||
server.use(http.get(`/api/v1/notifications/receivers`, () => HttpResponse.json(response)));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -9,9 +9,12 @@ import evalHandlers from 'app/features/alerting/unified/mocks/server/handlers/ev
|
||||
import folderHandlers from 'app/features/alerting/unified/mocks/server/handlers/folders';
|
||||
import grafanaRulerHandlers from 'app/features/alerting/unified/mocks/server/handlers/grafanaRuler';
|
||||
import mimirRulerHandlers from 'app/features/alerting/unified/mocks/server/handlers/mimirRuler';
|
||||
import notificationsHandlers from 'app/features/alerting/unified/mocks/server/handlers/notifications';
|
||||
import pluginsHandlers from 'app/features/alerting/unified/mocks/server/handlers/plugins';
|
||||
import allPluginHandlers from 'app/features/alerting/unified/mocks/server/handlers/plugins/all-plugin-handlers';
|
||||
import searchHandlers from 'app/features/alerting/unified/mocks/server/handlers/search';
|
||||
import silenceHandlers from 'app/features/alerting/unified/mocks/server/handlers/silences';
|
||||
|
||||
/**
|
||||
* Array of all mock handlers that are required across Alerting tests
|
||||
*/
|
||||
@ -25,6 +28,8 @@ const allHandlers = [
|
||||
...folderHandlers,
|
||||
...pluginsHandlers,
|
||||
...silenceHandlers,
|
||||
...searchHandlers,
|
||||
...notificationsHandlers,
|
||||
|
||||
...allPluginHandlers,
|
||||
];
|
||||
|
@ -0,0 +1,13 @@
|
||||
import { HttpResponse, http } from 'msw';
|
||||
|
||||
import alertmanagerConfig from 'app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json';
|
||||
import { GrafanaManagedContactPoint } from 'app/plugins/datasource/alertmanager/types';
|
||||
|
||||
const defaultReceiversResponse: GrafanaManagedContactPoint[] = JSON.parse(JSON.stringify(alertmanagerConfig))
|
||||
.alertmanager_config.receivers;
|
||||
|
||||
const getNotificationReceiversHandler = (response = defaultReceiversResponse) =>
|
||||
http.get('/api/v1/notifications/receivers', () => HttpResponse.json(response));
|
||||
|
||||
const handlers = [getNotificationReceiversHandler()];
|
||||
export default handlers;
|
@ -0,0 +1,34 @@
|
||||
import { HttpResponse, http } from 'msw';
|
||||
|
||||
import { grafanaRulerNamespace2 } from 'app/features/alerting/unified/mocks/grafanaRulerApi';
|
||||
import { DashboardSearchItemType } from 'app/features/search/types';
|
||||
|
||||
export const FOLDER_TITLE_HAPPY_PATH = 'Folder A';
|
||||
|
||||
// TODO: Generalise/scaffold out default response for search
|
||||
// to be more multi purpose
|
||||
const defaultSearchResponse = [
|
||||
{
|
||||
title: FOLDER_TITLE_HAPPY_PATH,
|
||||
uid: grafanaRulerNamespace2.uid,
|
||||
id: 1,
|
||||
type: DashboardSearchItemType.DashFolder,
|
||||
},
|
||||
{
|
||||
title: 'Folder B',
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
title: 'Folder / with slash',
|
||||
id: 2,
|
||||
uid: 'b',
|
||||
type: DashboardSearchItemType.DashFolder,
|
||||
},
|
||||
];
|
||||
|
||||
export const searchHandler = (response = defaultSearchResponse) =>
|
||||
http.get(`/api/search`, () => HttpResponse.json(response));
|
||||
|
||||
const handlers = [searchHandler()];
|
||||
|
||||
export default handlers;
|
@ -10,7 +10,7 @@ import {
|
||||
Receiver,
|
||||
TestReceiversAlert,
|
||||
} from 'app/plugins/datasource/alertmanager/types';
|
||||
import { FolderDTO, NotifierDTO, StoreState, ThunkResult } from 'app/types';
|
||||
import { FolderDTO, StoreState, ThunkResult } from 'app/types';
|
||||
import {
|
||||
CombinedRuleGroup,
|
||||
CombinedRuleNamespace,
|
||||
@ -49,7 +49,6 @@ import {
|
||||
import { alertmanagerApi } from '../api/alertmanagerApi';
|
||||
import { fetchAnnotations } from '../api/annotations';
|
||||
import { discoverFeatures } from '../api/buildInfo';
|
||||
import { fetchNotifiers } from '../api/grafana';
|
||||
import { FetchPromRulesFilter, fetchRules } from '../api/prometheus';
|
||||
import { FetchRulerRulesFilter, deleteRulerRulesGroup, fetchRulerRules, setRulerRuleGroup } from '../api/ruler';
|
||||
import { RuleFormValues } from '../types/rule-form';
|
||||
@ -437,11 +436,6 @@ function reportSwitchingRoutingType(values: RuleFormValues, existingRule: RuleWi
|
||||
}
|
||||
}
|
||||
|
||||
export const fetchGrafanaNotifiersAction = createAsyncThunk(
|
||||
'unifiedalerting/fetchGrafanaNotifiers',
|
||||
(): Promise<NotifierDTO[]> => withSerializedError(fetchNotifiers())
|
||||
);
|
||||
|
||||
export const fetchGrafanaAnnotationsAction = createAsyncThunk(
|
||||
'unifiedalerting/fetchGrafanaAnnotations',
|
||||
(alertId: string): Promise<StateHistoryItem[]> => withSerializedError(fetchAnnotations(alertId))
|
||||
|
@ -7,7 +7,6 @@ import {
|
||||
fetchAlertGroupsAction,
|
||||
fetchFolderAction,
|
||||
fetchGrafanaAnnotationsAction,
|
||||
fetchGrafanaNotifiersAction,
|
||||
fetchPromRulesAction,
|
||||
fetchRulerRulesAction,
|
||||
fetchRulesSourceBuildInfoAction,
|
||||
@ -28,7 +27,6 @@ export const reducer = combineReducers({
|
||||
ruleForm: combineReducers({
|
||||
saveRule: createAsyncSlice('saveRule', saveRuleFormAction).reducer,
|
||||
}),
|
||||
grafanaNotifiers: createAsyncSlice('grafanaNotifiers', fetchGrafanaNotifiersAction).reducer,
|
||||
saveAMConfig: createAsyncSlice('saveAMConfig', updateAlertManagerConfigAction).reducer,
|
||||
deleteAMConfig: createAsyncSlice('deleteAMConfig', deleteAlertManagerConfigAction).reducer,
|
||||
folders: createAsyncMapSlice('folders', fetchFolderAction, (uid) => uid).reducer,
|
||||
|
Loading…
Reference in New Issue
Block a user