mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Make alert rule policies preview use k8s API (#97070)
* Add translations for notification preview * Make notifications endpoints use alertmanager config mock entity * Fix translations and error handling in preview component * Update preview hook to use new k8s APIs * Move receivers k8s mock logic so it always comes from the mock config * Fix test that wasn't using the correct receiver * Fix object_matchers * Remove mockApi method and update tests * Update translation for error case * Remove useMemo
This commit is contained in:
parent
1c60d51905
commit
a2c407854f
@ -1443,16 +1443,6 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"]
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"]
|
||||||
],
|
],
|
||||||
"public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationPreview.tsx:5381": [
|
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
|
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"],
|
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"],
|
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "3"],
|
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"]
|
|
||||||
],
|
|
||||||
"public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationPreviewByAlertManager.tsx:5381": [
|
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"]
|
|
||||||
],
|
|
||||||
"public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRoute.tsx:5381": [
|
"public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRoute.tsx:5381": [
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
|
||||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"],
|
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"],
|
||||||
|
@ -240,7 +240,7 @@ describe.each([
|
|||||||
setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, {
|
setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, {
|
||||||
alertmanager_config: {
|
alertmanager_config: {
|
||||||
route: {},
|
route: {},
|
||||||
receivers: [{ name: 'grafana-default-email' }],
|
receivers: [{ name: 'lotsa-emails' }],
|
||||||
},
|
},
|
||||||
template_files: {},
|
template_files: {},
|
||||||
});
|
});
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import { render, screen, userEvent, waitFor, within } from 'test/test-utils';
|
import { render, screen, waitFor, within } from 'test/test-utils';
|
||||||
import { byRole, byTestId, byText } from 'testing-library-selector';
|
import { byRole, byTestId, byText } from 'testing-library-selector';
|
||||||
|
|
||||||
|
import { setAlertmanagerConfig } from 'app/features/alerting/unified/mocks/server/entities/alertmanagers';
|
||||||
|
import { testWithFeatureToggles } from 'app/features/alerting/unified/test/test-utils';
|
||||||
import { AccessControlAction } from 'app/types/accessControl';
|
import { AccessControlAction } from 'app/types/accessControl';
|
||||||
|
|
||||||
import { MatcherOperator } from '../../../../../../plugins/datasource/alertmanager/types';
|
import { MatcherOperator } from '../../../../../../plugins/datasource/alertmanager/types';
|
||||||
import { Labels } from '../../../../../../types/unified-alerting-dto';
|
import { Labels } from '../../../../../../types/unified-alerting-dto';
|
||||||
import { mockApi, setupMswServer } from '../../../mockApi';
|
import { getMockConfig, setupMswServer } from '../../../mockApi';
|
||||||
import { grantUserPermissions, mockAlertQuery } from '../../../mocks';
|
import { grantUserPermissions, mockAlertQuery } from '../../../mocks';
|
||||||
import { mockPreviewApiResponse } from '../../../mocks/grafanaRulerApi';
|
import { mockPreviewApiResponse } from '../../../mocks/grafanaRulerApi';
|
||||||
import { Folder } from '../../../types/rule-form';
|
import { Folder } from '../../../types/rule-form';
|
||||||
@ -36,7 +38,7 @@ const ui = {
|
|||||||
route: byTestId('matching-policy-route'),
|
route: byTestId('matching-policy-route'),
|
||||||
routeButton: byRole('button', { name: /Expand policy route/ }),
|
routeButton: byRole('button', { name: /Expand policy route/ }),
|
||||||
routeMatchingInstances: byTestId('route-matching-instance'),
|
routeMatchingInstances: byTestId('route-matching-instance'),
|
||||||
loadingIndicator: byText(/Loading/),
|
loadingIndicator: byText(/Loading routing preview/i),
|
||||||
previewButton: byRole('button', { name: /preview routing/i }),
|
previewButton: byRole('button', { name: /preview routing/i }),
|
||||||
grafanaAlertManagerLabel: byText(/alertmanager:grafana/i),
|
grafanaAlertManagerLabel: byText(/alertmanager:grafana/i),
|
||||||
otherAlertManagerLabel: byText(/alertmanager:other_am/i),
|
otherAlertManagerLabel: byText(/alertmanager:other_am/i),
|
||||||
@ -62,52 +64,33 @@ const grafanaAlertManagerDataSource: AlertManagerDataSource = {
|
|||||||
hasConfigurationAPI: true,
|
hasConfigurationAPI: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockConfig = getMockConfig((amConfigBuilder) =>
|
||||||
|
amConfigBuilder
|
||||||
|
.withRoute((routeBuilder) =>
|
||||||
|
routeBuilder
|
||||||
|
.withReceiver('email')
|
||||||
|
.addRoute((rb) => rb.withReceiver('slack').addMatcher('tomato', MatcherOperator.equal, 'red'))
|
||||||
|
.addRoute((rb) => rb.withReceiver('opsgenie').addMatcher('team', MatcherOperator.equal, 'operations'))
|
||||||
|
)
|
||||||
|
.addReceivers((b) => b.withName('email').addEmailConfig((eb) => eb.withTo('test@example.com')))
|
||||||
|
.addReceivers((b) => b.withName('slack'))
|
||||||
|
.addReceivers((b) => b.withName('opsgenie'))
|
||||||
|
);
|
||||||
|
|
||||||
function mockOneAlertManager() {
|
function mockOneAlertManager() {
|
||||||
getAlertManagerDataSourcesByPermissionAndConfigMock.mockReturnValue([grafanaAlertManagerDataSource]);
|
getAlertManagerDataSourcesByPermissionAndConfigMock.mockReturnValue([grafanaAlertManagerDataSource]);
|
||||||
mockApi(server).getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, (amConfigBuilder) =>
|
|
||||||
amConfigBuilder
|
setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, mockConfig);
|
||||||
.withRoute((routeBuilder) =>
|
|
||||||
routeBuilder
|
|
||||||
.withReceiver('email')
|
|
||||||
.addRoute((rb) => rb.withReceiver('slack').addMatcher('tomato', MatcherOperator.equal, 'red'))
|
|
||||||
.addRoute((rb) => rb.withReceiver('opsgenie').addMatcher('team', MatcherOperator.equal, 'operations'))
|
|
||||||
)
|
|
||||||
.addReceivers((b) => b.withName('email').addEmailConfig((eb) => eb.withTo('test@example.com')))
|
|
||||||
.addReceivers((b) => b.withName('slack'))
|
|
||||||
.addReceivers((b) => b.withName('opsgenie'))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mockTwoAlertManagers() {
|
function mockTwoAlertManagers() {
|
||||||
getAlertManagerDataSourcesByPermissionAndConfigMock.mockReturnValue([
|
getAlertManagerDataSourcesByPermissionAndConfigMock.mockReturnValue([
|
||||||
{ name: 'OTHER_AM', imgUrl: '', hasConfigurationAPI: true },
|
|
||||||
grafanaAlertManagerDataSource,
|
grafanaAlertManagerDataSource,
|
||||||
|
{ name: 'OTHER_AM', imgUrl: '', hasConfigurationAPI: true },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
mockApi(server).getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, (amConfigBuilder) =>
|
setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, mockConfig);
|
||||||
amConfigBuilder
|
setAlertmanagerConfig('OTHER_AM', mockConfig);
|
||||||
.withRoute((routeBuilder) =>
|
|
||||||
routeBuilder
|
|
||||||
.withReceiver('email')
|
|
||||||
.addRoute((rb) => rb.withReceiver('slack').addMatcher('tomato', MatcherOperator.equal, 'red'))
|
|
||||||
.addRoute((rb) => rb.withReceiver('opsgenie').addMatcher('team', MatcherOperator.equal, 'operations'))
|
|
||||||
)
|
|
||||||
.addReceivers((b) => b.withName('email').addEmailConfig((eb) => eb.withTo('test@example.com')))
|
|
||||||
.addReceivers((b) => b.withName('slack'))
|
|
||||||
.addReceivers((b) => b.withName('opsgenie'))
|
|
||||||
);
|
|
||||||
mockApi(server).getAlertmanagerConfig('OTHER_AM', (amConfigBuilder) =>
|
|
||||||
amConfigBuilder
|
|
||||||
.withRoute((routeBuilder) =>
|
|
||||||
routeBuilder
|
|
||||||
.withReceiver('email')
|
|
||||||
.addRoute((rb) => rb.withReceiver('slack').addMatcher('tomato', MatcherOperator.equal, 'red'))
|
|
||||||
.addRoute((rb) => rb.withReceiver('opsgenie').addMatcher('team', MatcherOperator.equal, 'operations'))
|
|
||||||
)
|
|
||||||
.addReceivers((b) => b.withName('email').addEmailConfig((eb) => eb.withTo('test@example.com')))
|
|
||||||
.addReceivers((b) => b.withName('slack'))
|
|
||||||
.addReceivers((b) => b.withName('opsgenie'))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mockHasEditPermission(enabled: boolean) {
|
function mockHasEditPermission(enabled: boolean) {
|
||||||
@ -131,14 +114,22 @@ const folder: Folder = {
|
|||||||
title: 'title',
|
title: 'title',
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('NotificationPreview', () => {
|
describe.each([
|
||||||
|
// k8s API enabled
|
||||||
|
true,
|
||||||
|
// k8s API disabled
|
||||||
|
false,
|
||||||
|
])('NotificationPreview with alertingApiServer=%p', (apiServerEnabled) => {
|
||||||
|
apiServerEnabled ? testWithFeatureToggles(['alertingApiServer']) : testWithFeatureToggles([]);
|
||||||
it('should render notification preview without alert manager label, when having only one alert manager configured to receive alerts', async () => {
|
it('should render notification preview without alert manager label, when having only one alert manager configured to receive alerts', async () => {
|
||||||
mockOneAlertManager();
|
mockOneAlertManager();
|
||||||
mockPreviewApiResponse(server, [{ labels: [{ tomato: 'red', avocate: 'green' }] }]);
|
mockPreviewApiResponse(server, [{ labels: [{ tomato: 'red', avocate: 'green' }] }]);
|
||||||
|
|
||||||
render(<NotificationPreview alertQueries={[alertQuery]} customLabels={[]} condition="A" folder={folder} />);
|
const { user } = render(
|
||||||
|
<NotificationPreview alertQueries={[alertQuery]} customLabels={[]} condition="A" folder={folder} />
|
||||||
|
);
|
||||||
|
|
||||||
await userEvent.click(ui.previewButton.get());
|
await user.click(ui.previewButton.get());
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(ui.loadingIndicator.query()).not.toBeInTheDocument();
|
expect(ui.loadingIndicator.query()).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
@ -160,23 +151,18 @@ describe('NotificationPreview', () => {
|
|||||||
mockTwoAlertManagers();
|
mockTwoAlertManagers();
|
||||||
mockPreviewApiResponse(server, [{ labels: [{ tomato: 'red', avocate: 'green' }] }]);
|
mockPreviewApiResponse(server, [{ labels: [{ tomato: 'red', avocate: 'green' }] }]);
|
||||||
|
|
||||||
render(<NotificationPreview alertQueries={[alertQuery]} customLabels={[]} condition="A" folder={folder} />);
|
const { user } = render(
|
||||||
await waitFor(() => {
|
<NotificationPreview alertQueries={[alertQuery]} customLabels={[]} condition="A" folder={folder} />
|
||||||
expect(ui.loadingIndicator.query()).not.toBeInTheDocument();
|
);
|
||||||
});
|
|
||||||
|
|
||||||
await userEvent.click(ui.previewButton.get());
|
await user.click(await ui.previewButton.find());
|
||||||
await waitFor(() => {
|
|
||||||
expect(ui.loadingIndicator.query()).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
// we expect the alert manager label to be present as there is more than one alert manager configured to receive alerts
|
// we expect the alert manager label to be present as there is more than one alert manager configured to receive alerts
|
||||||
await waitFor(() => {
|
expect(await ui.grafanaAlertManagerLabel.find()).toBeInTheDocument();
|
||||||
expect(ui.grafanaAlertManagerLabel.query()).toBeInTheDocument();
|
expect(await ui.otherAlertManagerLabel.find()).toBeInTheDocument();
|
||||||
});
|
|
||||||
expect(ui.otherAlertManagerLabel.query()).toBeInTheDocument();
|
const matchingPoliciesElements = await ui.route.findAll();
|
||||||
|
|
||||||
const matchingPoliciesElements = ui.route.queryAll();
|
|
||||||
expect(matchingPoliciesElements).toHaveLength(2);
|
expect(matchingPoliciesElements).toHaveLength(2);
|
||||||
expect(matchingPoliciesElements[0]).toHaveTextContent(/tomato = red/);
|
expect(matchingPoliciesElements[0]).toHaveTextContent(/tomato = red/);
|
||||||
expect(matchingPoliciesElements[1]).toHaveTextContent(/tomato = red/);
|
expect(matchingPoliciesElements[1]).toHaveTextContent(/tomato = red/);
|
||||||
@ -187,13 +173,15 @@ describe('NotificationPreview', () => {
|
|||||||
mockPreviewApiResponse(server, [{ labels: [{ tomato: 'red', avocate: 'green' }] }]);
|
mockPreviewApiResponse(server, [{ labels: [{ tomato: 'red', avocate: 'green' }] }]);
|
||||||
mockHasEditPermission(true);
|
mockHasEditPermission(true);
|
||||||
|
|
||||||
render(<NotificationPreview alertQueries={[alertQuery]} customLabels={[]} condition="A" folder={folder} />);
|
const { user } = render(
|
||||||
|
<NotificationPreview alertQueries={[alertQuery]} customLabels={[]} condition="A" folder={folder} />
|
||||||
|
);
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(ui.loadingIndicator.query()).not.toBeInTheDocument();
|
expect(ui.loadingIndicator.query()).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
await userEvent.click(ui.previewButton.get());
|
await user.click(ui.previewButton.get());
|
||||||
await userEvent.click(await ui.seeDetails.find());
|
await user.click(await ui.seeDetails.find());
|
||||||
expect(ui.details.title.query()).toBeInTheDocument();
|
expect(ui.details.title.query()).toBeInTheDocument();
|
||||||
//we expect seeing the default policy
|
//we expect seeing the default policy
|
||||||
expect(screen.getByText(/default policy/i)).toBeInTheDocument();
|
expect(screen.getByText(/default policy/i)).toBeInTheDocument();
|
||||||
@ -209,13 +197,15 @@ describe('NotificationPreview', () => {
|
|||||||
mockPreviewApiResponse(server, [{ labels: [{ tomato: 'red', avocate: 'green' }] }]);
|
mockPreviewApiResponse(server, [{ labels: [{ tomato: 'red', avocate: 'green' }] }]);
|
||||||
mockHasEditPermission(false);
|
mockHasEditPermission(false);
|
||||||
|
|
||||||
render(<NotificationPreview alertQueries={[alertQuery]} customLabels={[]} condition="A" folder={folder} />);
|
const { user } = render(
|
||||||
|
<NotificationPreview alertQueries={[alertQuery]} customLabels={[]} condition="A" folder={folder} />
|
||||||
|
);
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(ui.loadingIndicator.query()).not.toBeInTheDocument();
|
expect(ui.loadingIndicator.query()).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
await userEvent.click(ui.previewButton.get());
|
await user.click(ui.previewButton.get());
|
||||||
await userEvent.click(await ui.seeDetails.find());
|
await user.click(await ui.seeDetails.find());
|
||||||
expect(ui.details.title.query()).toBeInTheDocument();
|
expect(ui.details.title.query()).toBeInTheDocument();
|
||||||
//we expect seeing the default policy
|
//we expect seeing the default policy
|
||||||
expect(screen.getByText(/default policy/i)).toBeInTheDocument();
|
expect(screen.getByText(/default policy/i)).toBeInTheDocument();
|
||||||
@ -234,7 +224,7 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
{ job: 'prometheus', severity: 'warning' },
|
{ job: 'prometheus', severity: 'warning' },
|
||||||
];
|
];
|
||||||
|
|
||||||
mockApi(server).getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, (amConfigBuilder) =>
|
const mockConfig = getMockConfig((amConfigBuilder) =>
|
||||||
amConfigBuilder
|
amConfigBuilder
|
||||||
.withRoute((routeBuilder) =>
|
.withRoute((routeBuilder) =>
|
||||||
routeBuilder
|
routeBuilder
|
||||||
@ -246,10 +236,9 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
.addReceivers((b) => b.withName('slack'))
|
.addReceivers((b) => b.withName('slack'))
|
||||||
.addReceivers((b) => b.withName('opsgenie'))
|
.addReceivers((b) => b.withName('opsgenie'))
|
||||||
);
|
);
|
||||||
|
setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, mockConfig);
|
||||||
|
|
||||||
const user = userEvent.setup();
|
const { user } = render(
|
||||||
|
|
||||||
render(
|
|
||||||
<NotificationPreviewByAlertManager
|
<NotificationPreviewByAlertManager
|
||||||
alertManagerSource={grafanaAlertManagerDataSource}
|
alertManagerSource={grafanaAlertManagerDataSource}
|
||||||
potentialInstances={potentialInstances}
|
potentialInstances={potentialInstances}
|
||||||
@ -285,7 +274,7 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
{ job: 'prometheus', severity: 'warning' },
|
{ job: 'prometheus', severity: 'warning' },
|
||||||
];
|
];
|
||||||
|
|
||||||
mockApi(server).getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, (amConfigBuilder) =>
|
const mockConfig = getMockConfig((amConfigBuilder) =>
|
||||||
amConfigBuilder
|
amConfigBuilder
|
||||||
.withRoute((routeBuilder) =>
|
.withRoute((routeBuilder) =>
|
||||||
routeBuilder
|
routeBuilder
|
||||||
@ -300,10 +289,9 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
.addReceivers((b) => b.withName('slack'))
|
.addReceivers((b) => b.withName('slack'))
|
||||||
.addReceivers((b) => b.withName('opsgenie'))
|
.addReceivers((b) => b.withName('opsgenie'))
|
||||||
);
|
);
|
||||||
|
setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, mockConfig);
|
||||||
|
|
||||||
const user = userEvent.setup();
|
const { user } = render(
|
||||||
|
|
||||||
render(
|
|
||||||
<NotificationPreviewByAlertManager
|
<NotificationPreviewByAlertManager
|
||||||
alertManagerSource={grafanaAlertManagerDataSource}
|
alertManagerSource={grafanaAlertManagerDataSource}
|
||||||
potentialInstances={potentialInstances}
|
potentialInstances={potentialInstances}
|
||||||
@ -339,7 +327,7 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
{ job: 'prometheus', severity: 'warning' },
|
{ job: 'prometheus', severity: 'warning' },
|
||||||
];
|
];
|
||||||
|
|
||||||
mockApi(server).getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, (amConfigBuilder) =>
|
const mockConfig = getMockConfig((amConfigBuilder) =>
|
||||||
amConfigBuilder
|
amConfigBuilder
|
||||||
.withRoute((routeBuilder) =>
|
.withRoute((routeBuilder) =>
|
||||||
routeBuilder
|
routeBuilder
|
||||||
@ -355,9 +343,9 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
.addReceivers((b) => b.withName('opsgenie'))
|
.addReceivers((b) => b.withName('opsgenie'))
|
||||||
);
|
);
|
||||||
|
|
||||||
const user = userEvent.setup();
|
setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, mockConfig);
|
||||||
|
|
||||||
render(
|
const { user } = render(
|
||||||
<NotificationPreviewByAlertManager
|
<NotificationPreviewByAlertManager
|
||||||
alertManagerSource={grafanaAlertManagerDataSource}
|
alertManagerSource={grafanaAlertManagerDataSource}
|
||||||
potentialInstances={potentialInstances}
|
potentialInstances={potentialInstances}
|
||||||
@ -392,7 +380,7 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
it('does not match regex in middle of the word as alertmanager will anchor when queried via API', async () => {
|
it('does not match regex in middle of the word as alertmanager will anchor when queried via API', async () => {
|
||||||
const potentialInstances: Labels[] = [{ regexfield: 'foobarfoo' }];
|
const potentialInstances: Labels[] = [{ regexfield: 'foobarfoo' }];
|
||||||
|
|
||||||
mockApi(server).getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, (amConfigBuilder) =>
|
const mockConfig = getMockConfig((amConfigBuilder) =>
|
||||||
amConfigBuilder
|
amConfigBuilder
|
||||||
.addReceivers((b) => b.withName('email'))
|
.addReceivers((b) => b.withName('email'))
|
||||||
.withRoute((routeBuilder) =>
|
.withRoute((routeBuilder) =>
|
||||||
@ -402,6 +390,8 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, mockConfig);
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<NotificationPreviewByAlertManager
|
<NotificationPreviewByAlertManager
|
||||||
alertManagerSource={grafanaAlertManagerDataSource}
|
alertManagerSource={grafanaAlertManagerDataSource}
|
||||||
@ -417,7 +407,7 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
it('matches regex at the start of the word', async () => {
|
it('matches regex at the start of the word', async () => {
|
||||||
const potentialInstances: Labels[] = [{ regexfield: 'baaaaaaah' }];
|
const potentialInstances: Labels[] = [{ regexfield: 'baaaaaaah' }];
|
||||||
|
|
||||||
mockApi(server).getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, (amConfigBuilder) =>
|
const mockConfig = getMockConfig((amConfigBuilder) =>
|
||||||
amConfigBuilder
|
amConfigBuilder
|
||||||
.addReceivers((b) => b.withName('email'))
|
.addReceivers((b) => b.withName('email'))
|
||||||
.withRoute((routeBuilder) =>
|
.withRoute((routeBuilder) =>
|
||||||
@ -426,6 +416,7 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
.addRoute((rb) => rb.withReceiver('email').addMatcher('regexfield', MatcherOperator.regex, 'ba.*h'))
|
.addRoute((rb) => rb.withReceiver('email').addMatcher('regexfield', MatcherOperator.regex, 'ba.*h'))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, mockConfig);
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<NotificationPreviewByAlertManager
|
<NotificationPreviewByAlertManager
|
||||||
@ -441,7 +432,7 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
it('handles negated regex correctly', async () => {
|
it('handles negated regex correctly', async () => {
|
||||||
const potentialInstances: Labels[] = [{ regexfield: 'thing' }];
|
const potentialInstances: Labels[] = [{ regexfield: 'thing' }];
|
||||||
|
|
||||||
mockApi(server).getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, (amConfigBuilder) =>
|
const mockConfig = getMockConfig((amConfigBuilder) =>
|
||||||
amConfigBuilder
|
amConfigBuilder
|
||||||
.addReceivers((b) => b.withName('email'))
|
.addReceivers((b) => b.withName('email'))
|
||||||
.withRoute((routeBuilder) =>
|
.withRoute((routeBuilder) =>
|
||||||
@ -450,6 +441,7 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
.addRoute((rb) => rb.withReceiver('email').addMatcher('regexfield', MatcherOperator.notRegex, 'thing'))
|
.addRoute((rb) => rb.withReceiver('email').addMatcher('regexfield', MatcherOperator.notRegex, 'thing'))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, mockConfig);
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<NotificationPreviewByAlertManager
|
<NotificationPreviewByAlertManager
|
||||||
@ -466,7 +458,7 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
it('matches regex with flags', async () => {
|
it('matches regex with flags', async () => {
|
||||||
const potentialInstances: Labels[] = [{ regexfield: 'baaaaaaah' }];
|
const potentialInstances: Labels[] = [{ regexfield: 'baaaaaaah' }];
|
||||||
|
|
||||||
mockApi(server).getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, (amConfigBuilder) =>
|
const mockConfig = getMockConfig((amConfigBuilder) =>
|
||||||
amConfigBuilder
|
amConfigBuilder
|
||||||
.addReceivers((b) => b.withName('email'))
|
.addReceivers((b) => b.withName('email'))
|
||||||
.withRoute((routeBuilder) =>
|
.withRoute((routeBuilder) =>
|
||||||
@ -475,6 +467,7 @@ describe('NotificationPreviewByAlertmanager', () => {
|
|||||||
.addRoute((rb) => rb.withReceiver('email').addMatcher('regexfield', MatcherOperator.regex, '(?i)BA.*h'))
|
.addRoute((rb) => rb.withReceiver('email').addMatcher('regexfield', MatcherOperator.regex, '(?i)BA.*h'))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, mockConfig);
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<NotificationPreviewByAlertManager
|
<NotificationPreviewByAlertManager
|
||||||
|
@ -2,6 +2,7 @@ import { compact } from 'lodash';
|
|||||||
import { lazy, Suspense } from 'react';
|
import { lazy, Suspense } from 'react';
|
||||||
|
|
||||||
import { Button, LoadingPlaceholder, Stack, Text } from '@grafana/ui';
|
import { Button, LoadingPlaceholder, Stack, Text } from '@grafana/ui';
|
||||||
|
import { Trans } from 'app/core/internationalization';
|
||||||
import { alertRuleApi } from 'app/features/alerting/unified/api/alertRuleApi';
|
import { alertRuleApi } from 'app/features/alerting/unified/api/alertRuleApi';
|
||||||
import { AlertQuery } from 'app/types/unified-alerting-dto';
|
import { AlertQuery } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
@ -64,26 +65,32 @@ export const NotificationPreview = ({
|
|||||||
<Stack direction="column">
|
<Stack direction="column">
|
||||||
<Stack direction="row" alignItems="flex-start" justifyContent="space-between">
|
<Stack direction="row" alignItems="flex-start" justifyContent="space-between">
|
||||||
<Stack direction="column" gap={1}>
|
<Stack direction="column" gap={1}>
|
||||||
<Text element="h5">Alert instance routing preview</Text>
|
<Text element="h5">
|
||||||
|
<Trans i18nKey="alerting.notification-preview.title">Alert instance routing preview</Trans>
|
||||||
|
</Text>
|
||||||
{isLoading && previewUninitialized && (
|
{isLoading && previewUninitialized && (
|
||||||
<Text color="secondary" variant="bodySmall">
|
<Text color="secondary" variant="bodySmall">
|
||||||
Loading...
|
<Trans i18nKey="alerting.common.loading">Loading...</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
{previewUninitialized ? (
|
{previewUninitialized ? (
|
||||||
<Text color="secondary" variant="bodySmall">
|
<Text color="secondary" variant="bodySmall">
|
||||||
When you have your folder selected and your query and labels are configured, click "Preview
|
<Trans i18nKey="alerting.notification-preview.uninitialized">
|
||||||
routing" to see the results here.
|
When you have your folder selected and your query and labels are configured, click "Preview
|
||||||
|
routing" to see the results here.
|
||||||
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<Text color="secondary" variant="bodySmall">
|
<Text color="secondary" variant="bodySmall">
|
||||||
Based on the labels added, alert instances are routed to the following notification policies. Expand each
|
<Trans i18nKey="alerting.notification-preview.initialized">
|
||||||
notification policy below to view more details.
|
Based on the labels added, alert instances are routed to the following notification policies. Expand
|
||||||
|
each notification policy below to view more details.
|
||||||
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
<Button icon="sync" variant="secondary" type="button" onClick={onPreview} disabled={disabled}>
|
<Button icon="sync" variant="secondary" type="button" onClick={onPreview} disabled={disabled}>
|
||||||
Preview routing
|
<Trans i18nKey="alerting.notification-preview.preview-routing">Preview routing</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
{!isLoading && !previewUninitialized && potentialInstances.length > 0 && (
|
{!isLoading && !previewUninitialized && potentialInstances.length > 0 && (
|
||||||
|
@ -2,6 +2,8 @@ import { css } from '@emotion/css';
|
|||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { Alert, LoadingPlaceholder, useStyles2, withErrorBoundary } from '@grafana/ui';
|
import { Alert, LoadingPlaceholder, useStyles2, withErrorBoundary } from '@grafana/ui';
|
||||||
|
import { t, Trans } from 'app/core/internationalization';
|
||||||
|
import { stringifyErrorLike } from 'app/features/alerting/unified/utils/misc';
|
||||||
|
|
||||||
import { Stack } from '../../../../../../plugins/datasource/parca/QueryEditor/Stack';
|
import { Stack } from '../../../../../../plugins/datasource/parca/QueryEditor/Stack';
|
||||||
import { Labels } from '../../../../../../types/unified-alerting-dto';
|
import { Labels } from '../../../../../../types/unified-alerting-dto';
|
||||||
@ -27,9 +29,12 @@ function NotificationPreviewByAlertManager({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
const title = t('alerting.notification-preview.error', 'Could not load routing preview for {{alertmanager}}', {
|
||||||
|
alertmanager: alertManagerSource.name,
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<Alert title="Cannot load Alertmanager configuration" severity="error">
|
<Alert title={title} severity="error">
|
||||||
{error.message}
|
{stringifyErrorLike(error)}
|
||||||
</Alert>
|
</Alert>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -46,8 +51,7 @@ function NotificationPreviewByAlertManager({
|
|||||||
<Stack direction="row" alignItems="center">
|
<Stack direction="row" alignItems="center">
|
||||||
<div className={styles.firstAlertManagerLine}></div>
|
<div className={styles.firstAlertManagerLine}></div>
|
||||||
<div className={styles.alertManagerName}>
|
<div className={styles.alertManagerName}>
|
||||||
{' '}
|
<Trans i18nKey="alerting.notification-preview.alertmanager">Alertmanager:</Trans>
|
||||||
Alertmanager:
|
|
||||||
<img src={alertManagerSource.imgUrl} alt="" className={styles.img} />
|
<img src={alertManagerSource.imgUrl} alt="" className={styles.img} />
|
||||||
{alertManagerSource.name}
|
{alertManagerSource.name}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useAsync } from 'react-use';
|
import { useAsync } from 'react-use';
|
||||||
|
|
||||||
|
import { useContactPointsWithStatus } from 'app/features/alerting/unified/components/contact-points/useContactPoints';
|
||||||
|
import { useNotificationPolicyRoute } from 'app/features/alerting/unified/components/notification-policies/useNotificationPolicyRoute';
|
||||||
|
|
||||||
import { Receiver } from '../../../../../../plugins/datasource/alertmanager/types';
|
import { Receiver } from '../../../../../../plugins/datasource/alertmanager/types';
|
||||||
import { Labels } from '../../../../../../types/unified-alerting-dto';
|
import { Labels } from '../../../../../../types/unified-alerting-dto';
|
||||||
import { useAlertmanagerConfig } from '../../../hooks/useAlertmanagerConfig';
|
|
||||||
import { useRouteGroupsMatcher } from '../../../useRouteGroupsMatcher';
|
import { useRouteGroupsMatcher } from '../../../useRouteGroupsMatcher';
|
||||||
import { addUniqueIdentifierToRoute } from '../../../utils/amroutes';
|
import { addUniqueIdentifierToRoute } from '../../../utils/amroutes';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from '../../../utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from '../../../utils/datasource';
|
||||||
@ -11,41 +13,50 @@ import { AlertInstanceMatch, computeInheritedTree, normalizeRoute } from '../../
|
|||||||
|
|
||||||
import { getRoutesByIdMap, RouteWithPath } from './route';
|
import { getRoutesByIdMap, RouteWithPath } from './route';
|
||||||
|
|
||||||
export const useAlertmanagerNotificationRoutingPreview = (
|
export const useAlertmanagerNotificationRoutingPreview = (alertmanager: string, potentialInstances: Labels[]) => {
|
||||||
alertManagerSourceName: string,
|
const {
|
||||||
potentialInstances: Labels[]
|
data: currentData,
|
||||||
) => {
|
isLoading: isPoliciesLoading,
|
||||||
const { currentData, isLoading: configLoading, error: configError } = useAlertmanagerConfig(alertManagerSourceName);
|
error: policiesError,
|
||||||
const config = currentData?.alertmanager_config;
|
} = useNotificationPolicyRoute({ alertmanager });
|
||||||
|
|
||||||
|
const {
|
||||||
|
contactPoints,
|
||||||
|
isLoading: contactPointsLoading,
|
||||||
|
error: contactPointsError,
|
||||||
|
} = useContactPointsWithStatus({
|
||||||
|
alertmanager,
|
||||||
|
fetchPolicies: false,
|
||||||
|
fetchStatuses: false,
|
||||||
|
});
|
||||||
|
|
||||||
const { matchInstancesToRoute } = useRouteGroupsMatcher();
|
const { matchInstancesToRoute } = useRouteGroupsMatcher();
|
||||||
|
|
||||||
// to create the list of matching contact points we need to first get the rootRoute
|
const [defaultPolicy] = currentData ?? [];
|
||||||
const { rootRoute, receivers } = useMemo(() => {
|
const rootRoute = useMemo(() => {
|
||||||
if (!config) {
|
if (!defaultPolicy) {
|
||||||
return {
|
return;
|
||||||
receivers: [],
|
|
||||||
rootRoute: undefined,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
return normalizeRoute(addUniqueIdentifierToRoute(defaultPolicy));
|
||||||
return {
|
}, [defaultPolicy]);
|
||||||
rootRoute: config.route ? normalizeRoute(addUniqueIdentifierToRoute(config.route)) : undefined,
|
|
||||||
receivers: config.receivers ?? [],
|
|
||||||
};
|
|
||||||
}, [config]);
|
|
||||||
|
|
||||||
// create maps for routes to be get by id, this map also contains the path to the route
|
// create maps for routes to be get by id, this map also contains the path to the route
|
||||||
// ⚠️ don't forget to compute the inherited tree before using this map
|
// ⚠️ don't forget to compute the inherited tree before using this map
|
||||||
const routesByIdMap: Map<string, RouteWithPath> = rootRoute
|
const routesByIdMap = rootRoute
|
||||||
? getRoutesByIdMap(computeInheritedTree(rootRoute))
|
? getRoutesByIdMap(computeInheritedTree(rootRoute))
|
||||||
: new Map();
|
: new Map<string, RouteWithPath>();
|
||||||
|
|
||||||
// create map for receivers to be get by name
|
// to create the list of matching contact points we need to first get the rootRoute
|
||||||
const receiversByName =
|
const receiversByName = useMemo(() => {
|
||||||
receivers.reduce((map, receiver) => {
|
if (!contactPoints) {
|
||||||
|
return new Map<string, Receiver>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create map for receivers to be get by name
|
||||||
|
return contactPoints.reduce((map, receiver) => {
|
||||||
return map.set(receiver.name, receiver);
|
return map.set(receiver.name, receiver);
|
||||||
}, new Map<string, Receiver>()) ?? new Map<string, Receiver>();
|
}, new Map<string, Receiver>());
|
||||||
|
}, [contactPoints]);
|
||||||
|
|
||||||
// match labels in the tree => map of notification policies and the alert instances (list of labels) in each one
|
// match labels in the tree => map of notification policies and the alert instances (list of labels) in each one
|
||||||
const {
|
const {
|
||||||
@ -56,8 +67,9 @@ export const useAlertmanagerNotificationRoutingPreview = (
|
|||||||
if (!rootRoute) {
|
if (!rootRoute) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await matchInstancesToRoute(rootRoute, potentialInstances, {
|
return await matchInstancesToRoute(rootRoute, potentialInstances, {
|
||||||
unquoteMatchers: alertManagerSourceName !== GRAFANA_RULES_SOURCE_NAME,
|
unquoteMatchers: alertmanager !== GRAFANA_RULES_SOURCE_NAME,
|
||||||
});
|
});
|
||||||
}, [rootRoute, potentialInstances]);
|
}, [rootRoute, potentialInstances]);
|
||||||
|
|
||||||
@ -65,7 +77,7 @@ export const useAlertmanagerNotificationRoutingPreview = (
|
|||||||
routesByIdMap,
|
routesByIdMap,
|
||||||
receiversByName,
|
receiversByName,
|
||||||
matchingMap,
|
matchingMap,
|
||||||
loading: configLoading || matchingLoading,
|
loading: isPoliciesLoading || contactPointsLoading || matchingLoading,
|
||||||
error: configError ?? matchingError,
|
error: policiesError ?? contactPointsError ?? matchingError,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -159,23 +159,11 @@ export class AlertmanagerReceiverBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mockApi(server: SetupServer) {
|
export const getMockConfig = (configure: (builder: AlertmanagerConfigBuilder) => void): AlertManagerCortexConfig => {
|
||||||
return {
|
const builder = new AlertmanagerConfigBuilder();
|
||||||
getAlertmanagerConfig: (amName: string, configure: (builder: AlertmanagerConfigBuilder) => void) => {
|
configure(builder);
|
||||||
const builder = new AlertmanagerConfigBuilder();
|
return { alertmanager_config: builder.build(), template_files: {} };
|
||||||
configure(builder);
|
};
|
||||||
|
|
||||||
server.use(
|
|
||||||
http.get(`api/alertmanager/${amName}/config/api/v1/alerts`, () =>
|
|
||||||
HttpResponse.json<AlertManagerCortexConfig>({
|
|
||||||
alertmanager_config: builder.build(),
|
|
||||||
template_files: {},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mockAlertRuleApi(server: SetupServer) {
|
export function mockAlertRuleApi(server: SetupServer) {
|
||||||
return {
|
return {
|
||||||
|
@ -14,9 +14,8 @@ const normalizeMatchers = (route: Route) => {
|
|||||||
const routeMatchers: ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Matcher[] = [];
|
const routeMatchers: ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Matcher[] = [];
|
||||||
|
|
||||||
if (route.object_matchers) {
|
if (route.object_matchers) {
|
||||||
// todo foreach
|
route.object_matchers.forEach(([label, type, value]) => {
|
||||||
route.object_matchers.map(([label, type, value]) => {
|
routeMatchers.push({ label, type, value });
|
||||||
return { label, type, value };
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,41 +7,43 @@ import { ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Receiver } f
|
|||||||
import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
|
||||||
import { PROVENANCE_NONE, K8sAnnotations } from 'app/features/alerting/unified/utils/k8s/constants';
|
import { PROVENANCE_NONE, K8sAnnotations } from 'app/features/alerting/unified/utils/k8s/constants';
|
||||||
|
|
||||||
const config = getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME);
|
const getReceiversList = () => {
|
||||||
|
const config = getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME);
|
||||||
|
|
||||||
// Turn our mock alertmanager config into the format that we expect to be returned by the k8s API
|
// Turn our mock alertmanager config into the format that we expect to be returned by the k8s API
|
||||||
const mappedReceivers =
|
const mappedReceivers =
|
||||||
config.alertmanager_config?.receivers?.map((contactPoint) => {
|
config.alertmanager_config?.receivers?.map((contactPoint) => {
|
||||||
const provenance =
|
const provenance =
|
||||||
contactPoint.grafana_managed_receiver_configs?.find((integration) => {
|
contactPoint.grafana_managed_receiver_configs?.find((integration) => {
|
||||||
return integration.provenance;
|
return integration.provenance;
|
||||||
})?.provenance || PROVENANCE_NONE;
|
})?.provenance || PROVENANCE_NONE;
|
||||||
return {
|
return {
|
||||||
metadata: {
|
metadata: {
|
||||||
// This isn't exactly accurate, but its the cleanest way to use the same data for AM config and K8S responses
|
// This isn't exactly accurate, but its the cleanest way to use the same data for AM config and K8S responses
|
||||||
uid: camelCase(contactPoint.name),
|
uid: camelCase(contactPoint.name),
|
||||||
annotations: {
|
annotations: {
|
||||||
[K8sAnnotations.Provenance]: provenance,
|
[K8sAnnotations.Provenance]: provenance,
|
||||||
[K8sAnnotations.AccessAdmin]: 'true',
|
[K8sAnnotations.AccessAdmin]: 'true',
|
||||||
[K8sAnnotations.AccessDelete]: 'true',
|
[K8sAnnotations.AccessDelete]: 'true',
|
||||||
[K8sAnnotations.AccessWrite]: 'true',
|
[K8sAnnotations.AccessWrite]: 'true',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
spec: {
|
||||||
spec: {
|
title: contactPoint.name,
|
||||||
title: contactPoint.name,
|
integrations: contactPoint.grafana_managed_receiver_configs || [],
|
||||||
integrations: contactPoint.grafana_managed_receiver_configs || [],
|
},
|
||||||
},
|
};
|
||||||
};
|
}) || [];
|
||||||
}) || [];
|
|
||||||
|
|
||||||
const parsedReceivers = getK8sResponse<ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Receiver>(
|
return getK8sResponse<ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Receiver>(
|
||||||
'ReceiverList',
|
'ReceiverList',
|
||||||
mappedReceivers
|
mappedReceivers
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const listNamespacedReceiverHandler = () =>
|
const listNamespacedReceiverHandler = () =>
|
||||||
http.get<{ namespace: string }>(`${ALERTING_API_SERVER_BASE_URL}/namespaces/:namespace/receivers`, () => {
|
http.get<{ namespace: string }>(`${ALERTING_API_SERVER_BASE_URL}/namespaces/:namespace/receivers`, () => {
|
||||||
return HttpResponse.json(parsedReceivers);
|
return HttpResponse.json(getReceiversList());
|
||||||
});
|
});
|
||||||
|
|
||||||
const createNamespacedReceiverHandler = () =>
|
const createNamespacedReceiverHandler = () =>
|
||||||
@ -58,6 +60,7 @@ const deleteNamespacedReceiverHandler = () =>
|
|||||||
`${ALERTING_API_SERVER_BASE_URL}/namespaces/:namespace/receivers/:name`,
|
`${ALERTING_API_SERVER_BASE_URL}/namespaces/:namespace/receivers/:name`,
|
||||||
({ params }) => {
|
({ params }) => {
|
||||||
const { name } = params;
|
const { name } = params;
|
||||||
|
const parsedReceivers = getReceiversList();
|
||||||
const matchedReceiver = parsedReceivers.items.find((receiver) => receiver.metadata.uid === name);
|
const matchedReceiver = parsedReceivers.items.find((receiver) => receiver.metadata.uid === name);
|
||||||
if (matchedReceiver) {
|
if (matchedReceiver) {
|
||||||
return HttpResponse.json(parsedReceivers);
|
return HttpResponse.json(parsedReceivers);
|
||||||
|
@ -3,15 +3,19 @@ import { HttpResponse, http } from 'msw';
|
|||||||
import { getAlertmanagerConfig } from 'app/features/alerting/unified/mocks/server/entities/alertmanagers';
|
import { getAlertmanagerConfig } from 'app/features/alerting/unified/mocks/server/entities/alertmanagers';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
|
||||||
|
|
||||||
const alertmanagerConfig = getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME);
|
const getNotificationReceiversHandler = () =>
|
||||||
const defaultReceiversResponse = alertmanagerConfig.alertmanager_config.receivers;
|
http.get('/api/v1/notifications/receivers', () => {
|
||||||
const defaultTimeIntervalsResponse = alertmanagerConfig.alertmanager_config.time_intervals;
|
const receivers = getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME).alertmanager_config.receivers || [];
|
||||||
|
|
||||||
const getNotificationReceiversHandler = (response = defaultReceiversResponse) =>
|
return HttpResponse.json(receivers);
|
||||||
http.get('/api/v1/notifications/receivers', () => HttpResponse.json(response));
|
});
|
||||||
|
|
||||||
const getTimeIntervalsHandler = (response = defaultTimeIntervalsResponse) =>
|
const getTimeIntervalsHandler = () =>
|
||||||
http.get('/api/v1/notifications/time-intervals', () => HttpResponse.json(response));
|
http.get('/api/v1/notifications/time-intervals', () => {
|
||||||
|
const intervals = getAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME).alertmanager_config.time_intervals;
|
||||||
|
|
||||||
|
return HttpResponse.json(intervals);
|
||||||
|
});
|
||||||
|
|
||||||
const handlers = [getNotificationReceiversHandler(), getTimeIntervalsHandler()];
|
const handlers = [getNotificationReceiversHandler(), getTimeIntervalsHandler()];
|
||||||
|
|
||||||
|
@ -245,6 +245,7 @@
|
|||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
"export-all": "Export all",
|
"export-all": "Export all",
|
||||||
|
"loading": "Loading...",
|
||||||
"view": "View"
|
"view": "View"
|
||||||
},
|
},
|
||||||
"contact-points": {
|
"contact-points": {
|
||||||
@ -329,6 +330,14 @@
|
|||||||
"save": "Save mute timing",
|
"save": "Save mute timing",
|
||||||
"saving": "Saving mute timing"
|
"saving": "Saving mute timing"
|
||||||
},
|
},
|
||||||
|
"notification-preview": {
|
||||||
|
"alertmanager": "Alertmanager:",
|
||||||
|
"error": "Could not load routing preview for {{alertmanager}}",
|
||||||
|
"initialized": "Based on the labels added, alert instances are routed to the following notification policies. Expand each notification policy below to view more details.",
|
||||||
|
"preview-routing": "Preview routing",
|
||||||
|
"title": "Alert instance routing preview",
|
||||||
|
"uninitialized": "When you have your folder selected and your query and labels are configured, click \"Preview routing\" to see the results here."
|
||||||
|
},
|
||||||
"policies": {
|
"policies": {
|
||||||
"default-policy": {
|
"default-policy": {
|
||||||
"description": "All alert instances will be handled by the default policy if no other matching policies are found.",
|
"description": "All alert instances will be handled by the default policy if no other matching policies are found.",
|
||||||
|
@ -245,6 +245,7 @@
|
|||||||
"edit": "Ēđįŧ",
|
"edit": "Ēđįŧ",
|
||||||
"export": "Ēχpőřŧ",
|
"export": "Ēχpőřŧ",
|
||||||
"export-all": "Ēχpőřŧ äľľ",
|
"export-all": "Ēχpőřŧ äľľ",
|
||||||
|
"loading": "Ŀőäđįʼnģ...",
|
||||||
"view": "Vįęŵ"
|
"view": "Vįęŵ"
|
||||||
},
|
},
|
||||||
"contact-points": {
|
"contact-points": {
|
||||||
@ -329,6 +330,14 @@
|
|||||||
"save": "Ŝävę mūŧę ŧįmįʼnģ",
|
"save": "Ŝävę mūŧę ŧįmįʼnģ",
|
||||||
"saving": "Ŝävįʼnģ mūŧę ŧįmįʼnģ"
|
"saving": "Ŝävįʼnģ mūŧę ŧįmįʼnģ"
|
||||||
},
|
},
|
||||||
|
"notification-preview": {
|
||||||
|
"alertmanager": "Åľęřŧmäʼnäģęř:",
|
||||||
|
"error": "Cőūľđ ʼnőŧ ľőäđ řőūŧįʼnģ přęvįęŵ ƒőř {{alertmanager}}",
|
||||||
|
"initialized": "ßäşęđ őʼn ŧĥę ľäþęľş äđđęđ, äľęřŧ įʼnşŧäʼnčęş äřę řőūŧęđ ŧő ŧĥę ƒőľľőŵįʼnģ ʼnőŧįƒįčäŧįőʼn pőľįčįęş. Ēχpäʼnđ ęäčĥ ʼnőŧįƒįčäŧįőʼn pőľįčy þęľőŵ ŧő vįęŵ mőřę đęŧäįľş.",
|
||||||
|
"preview-routing": "Přęvįęŵ řőūŧįʼnģ",
|
||||||
|
"title": "Åľęřŧ įʼnşŧäʼnčę řőūŧįʼnģ přęvįęŵ",
|
||||||
|
"uninitialized": "Ŵĥęʼn yőū ĥävę yőūř ƒőľđęř şęľęčŧęđ äʼnđ yőūř qūęřy äʼnđ ľäþęľş äřę čőʼnƒįģūřęđ, čľįčĸ \"Přęvįęŵ řőūŧįʼnģ\" ŧő şęę ŧĥę řęşūľŧş ĥęřę."
|
||||||
|
},
|
||||||
"policies": {
|
"policies": {
|
||||||
"default-policy": {
|
"default-policy": {
|
||||||
"description": "Åľľ äľęřŧ įʼnşŧäʼnčęş ŵįľľ þę ĥäʼnđľęđ þy ŧĥę đęƒäūľŧ pőľįčy įƒ ʼnő őŧĥęř mäŧčĥįʼnģ pőľįčįęş äřę ƒőūʼnđ.",
|
"description": "Åľľ äľęřŧ įʼnşŧäʼnčęş ŵįľľ þę ĥäʼnđľęđ þy ŧĥę đęƒäūľŧ pőľįčy įƒ ʼnő őŧĥęř mäŧčĥįʼnģ pőľįčįęş äřę ƒőūʼnđ.",
|
||||||
|
Loading…
Reference in New Issue
Block a user