Alerting: Use monaco editor for admin page (#69514)

pull/69720/head
Gilles De Mey 2 years ago committed by GitHub
parent a91de30f99
commit 8a8b5da1ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 33
      public/app/features/alerting/unified/components/admin/AlertmanagerConfig.test.tsx
  2. 109
      public/app/features/alerting/unified/components/admin/ConfigEditor.tsx
  3. 20
      public/app/features/alerting/unified/components/admin/__snapshots__/AlertmanagerConfig.test.tsx.snap

@ -78,7 +78,7 @@ const ui = {
confirmButton: byRole('button', { name: /Yes, reset configuration/ }),
resetButton: byRole('button', { name: /Reset configuration/ }),
saveButton: byRole('button', { name: /Save/ }),
configInput: byLabelText<HTMLTextAreaElement>(/Configuration/),
configInput: byLabelText(/Code editor container/),
readOnlyConfig: byTestId('readonly-config'),
};
@ -107,37 +107,29 @@ describe('Admin config', () => {
expect(ui.confirmButton.query()).not.toBeInTheDocument();
});
it('Edit and save alertmanager config', async () => {
it('Editable alertmanager config', async () => {
let savedConfig: AlertManagerCortexConfig | undefined = undefined;
const defaultConfig = {
template_files: {
foo: 'bar',
template_files: {},
alertmanager_config: {
route: {
receiver: 'old one',
},
},
alertmanager_config: {},
};
const newConfig = {
template_files: {
bar: 'baz',
},
alertmanager_config: {},
};
mocks.api.fetchConfig.mockImplementation(() => Promise.resolve(savedConfig ?? defaultConfig));
mocks.api.updateAlertManagerConfig.mockResolvedValue();
renderAdminPage(dataSources.alertManager.name);
const input = await ui.configInput.find();
expect(input.value).toEqual(JSON.stringify(defaultConfig, null, 2));
await userEvent.clear(input);
// What is this regex replace doing? in userEvent v13, '{' and '[' are special characters.
// To get the literal character, you have to escape them by typing '{{' or '[['.
// See https://github.com/testing-library/user-event/issues/584.
await userEvent.type(input, JSON.stringify(newConfig, null, 2).replace(/[{[]/g, '$&$&'));
await ui.configInput.find();
await userEvent.click(ui.saveButton.get());
await waitFor(() => expect(mocks.api.updateAlertManagerConfig).toHaveBeenCalled());
expect(mocks.api.updateAlertManagerConfig.mock.lastCall).toMatchSnapshot();
await waitFor(() => expect(mocks.api.fetchConfig).toHaveBeenCalledTimes(3));
expect(input.value).toEqual(JSON.stringify(newConfig, null, 2));
});
it('Read-only when using Prometheus Alertmanager', async () => {
@ -148,7 +140,6 @@ describe('Admin config', () => {
renderAdminPage(dataSources.promAlertManager.name);
await ui.readOnlyConfig.find();
expect(ui.configInput.query()).not.toBeInTheDocument();
expect(ui.resetButton.query()).not.toBeInTheDocument();
expect(ui.saveButton.query()).not.toBeInTheDocument();

@ -1,6 +1,6 @@
import React from 'react';
import { Button, ConfirmModal, Field, Form, HorizontalGroup, TextArea } from '@grafana/ui';
import { Button, CodeEditor, ConfirmModal, Field, Form, HorizontalGroup } from '@grafana/ui';
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
@ -31,33 +31,46 @@ export const ConfigEditor = ({
}: ConfigEditorProps) => {
return (
<Form defaultValues={defaultValues} onSubmit={onSubmit} key={defaultValues.configJSON}>
{({ register, errors }) => (
<>
{!readOnly && (
<>
<Field
disabled={loading}
label="Configuration"
invalid={!!errors.configJSON}
error={errors.configJSON?.message}
>
<TextArea
{...register('configJSON', {
required: { value: true, message: 'Required.' },
validate: (v) => {
try {
JSON.parse(v);
return true;
} catch (e) {
return e instanceof Error ? e.message : 'Invalid JSON.';
}
},
})}
id="configuration"
rows={25}
/>
</Field>
{({ errors, setValue, register }) => {
register('configJSON', {
required: { value: true, message: 'Required' },
validate: (value: string) => {
try {
JSON.parse(value);
return true;
} catch (e) {
return e instanceof Error ? e.message : 'JSON is invalid';
}
},
});
return (
<>
<Field
disabled={loading}
label="Configuration"
invalid={!!errors.configJSON}
error={errors.configJSON?.message}
data-testid={readOnly ? 'readonly-config' : 'config'}
>
<CodeEditor
language="json"
width="100%"
height={500}
showLineNumbers={true}
value={defaultValues.configJSON}
showMiniMap={false}
onSave={(value) => {
setValue('configJSON', value);
}}
onBlur={(value) => {
setValue('configJSON', value);
}}
readOnly={readOnly}
/>
</Field>
{!readOnly && (
<HorizontalGroup>
<Button type="submit" variant="primary" disabled={loading}>
Save configuration
@ -68,29 +81,25 @@ export const ConfigEditor = ({
</Button>
)}
</HorizontalGroup>
</>
)}
{readOnly && (
<Field label="Configuration">
<pre data-testid="readonly-config">{defaultValues.configJSON}</pre>
</Field>
)}
{Boolean(showConfirmDeleteAMConfig) && onConfirmReset && onDismiss && (
<ConfirmModal
isOpen={true}
title="Reset Alertmanager configuration"
body={`Are you sure you want to reset configuration ${
alertManagerSourceName === GRAFANA_RULES_SOURCE_NAME
? 'for the Grafana Alertmanager'
: `for "${alertManagerSourceName}"`
}? Contact points and notification policies will be reset to their defaults.`}
confirmText="Yes, reset configuration"
onConfirm={onConfirmReset}
onDismiss={onDismiss}
/>
)}
</>
)}
)}
{Boolean(showConfirmDeleteAMConfig) && onConfirmReset && onDismiss && (
<ConfirmModal
isOpen={true}
title="Reset Alertmanager configuration"
body={`Are you sure you want to reset configuration ${
alertManagerSourceName === GRAFANA_RULES_SOURCE_NAME
? 'for the Grafana Alertmanager'
: `for "${alertManagerSourceName}"`
}? Contact points and notification policies will be reset to their defaults.`}
confirmText="Yes, reset configuration"
onConfirm={onConfirmReset}
onDismiss={onDismiss}
/>
)}
</>
);
}}
</Form>
);
};

@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Admin config Editable alertmanager config 1`] = `
[
"CloudManager",
{
"alertmanager_config": {
"receivers": [
{
"name": "default ",
},
],
"route": {
"receiver": "old one",
},
},
"template_files": {},
},
]
`;
Loading…
Cancel
Save