mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Add View YAML button for Grafana/provisioned rules (#71983)
* Add View YAML button for grafana/provisioned rules * Address PR comments
This commit is contained in:
parent
eee8e52605
commit
45b5b81db6
@ -1,12 +1,16 @@
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { Disable, Enable } from 'react-enable';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { withErrorBoundary } from '@grafana/ui';
|
||||
import { Button, HorizontalGroup, withErrorBoundary } from '@grafana/ui';
|
||||
import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate';
|
||||
import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport';
|
||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||
|
||||
import { AlertingPageWrapper } from './components/AlertingPageWrapper';
|
||||
import { GrafanaRuleInspector } from './components/rule-editor/GrafanaRuleInspector';
|
||||
import { AlertingFeature } from './features';
|
||||
import { GRAFANA_RULES_SOURCE_NAME } from './utils/datasource';
|
||||
|
||||
const DetailViewV1 = SafeDynamicImport(() => import('./components/rule-viewer/RuleViewer.v1'));
|
||||
const DetailViewV2 = SafeDynamicImport(() => import('./components/rule-viewer/v2/RuleViewer.v2'));
|
||||
@ -16,15 +20,34 @@ type RuleViewerProps = GrafanaRouteComponentProps<{
|
||||
sourceName: string;
|
||||
}>;
|
||||
|
||||
const RuleViewer = (props: RuleViewerProps): JSX.Element => (
|
||||
<AlertingPageWrapper>
|
||||
<Enable feature={AlertingFeature.DetailsViewV2}>
|
||||
<DetailViewV2 {...props} />
|
||||
</Enable>
|
||||
<Disable feature={AlertingFeature.DetailsViewV2}>
|
||||
<DetailViewV1 {...props} />
|
||||
</Disable>
|
||||
</AlertingPageWrapper>
|
||||
);
|
||||
const RuleViewer = (props: RuleViewerProps): JSX.Element => {
|
||||
const routeParams = useParams<{ type: string; id: string }>();
|
||||
const uidFromParams = routeParams.id;
|
||||
|
||||
const sourceName = props.match.params.sourceName;
|
||||
|
||||
const [showYaml, setShowYaml] = useState(false);
|
||||
const actionButtons =
|
||||
sourceName === GRAFANA_RULES_SOURCE_NAME ? (
|
||||
<HorizontalGroup height="auto" justify="flex-end">
|
||||
<Button variant="secondary" type="button" onClick={() => setShowYaml(true)} size="sm">
|
||||
View YAML
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<AlertingPageWrapper>
|
||||
<AppChromeUpdate actions={actionButtons} />
|
||||
{showYaml && <GrafanaRuleInspector alertUid={uidFromParams} onClose={() => setShowYaml(false)} />}
|
||||
<Enable feature={AlertingFeature.DetailsViewV2}>
|
||||
<DetailViewV2 {...props} />
|
||||
</Enable>
|
||||
<Disable feature={AlertingFeature.DetailsViewV2}>
|
||||
<DetailViewV1 {...props} />
|
||||
</Disable>
|
||||
</AlertingPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default withErrorBoundary(RuleViewer, { style: 'page' });
|
||||
|
@ -35,6 +35,10 @@ export interface Datasource {
|
||||
export const PREVIEW_URL = '/api/v1/rule/test/grafana';
|
||||
export const PROM_RULES_URL = 'api/prometheus/grafana/api/v1/rules';
|
||||
|
||||
function getProvisioningUrl(ruleUid: string, format: 'yaml' | 'json' = 'yaml') {
|
||||
return `/api/v1/provisioning/alert-rules/${ruleUid}/export?format=${format}`;
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
refId: string;
|
||||
relativeTimeRange: RelativeTimeRange;
|
||||
@ -127,5 +131,9 @@ export const alertRuleApi = alertingApi.injectEndpoints({
|
||||
return groupRulesByFileName(response.data.groups, GRAFANA_RULES_SOURCE_NAME);
|
||||
},
|
||||
}),
|
||||
|
||||
exportRule: build.query<string, { uid: string; format: 'yaml' | 'json' }>({
|
||||
query: ({ uid, format }) => ({ url: getProvisioningUrl(uid, format) }),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
@ -27,6 +27,7 @@ import * as ruleId from '../../utils/rule-id';
|
||||
import { CloudEvaluationBehavior } from './CloudEvaluationBehavior';
|
||||
import { DetailsStep } from './DetailsStep';
|
||||
import { GrafanaEvaluationBehavior } from './GrafanaEvaluationBehavior';
|
||||
import { GrafanaRuleInspector } from './GrafanaRuleInspector';
|
||||
import { NotificationsStep } from './NotificationsStep';
|
||||
import { RuleEditorSection } from './RuleEditorSection';
|
||||
import { RuleInspector } from './RuleInspector';
|
||||
@ -225,17 +226,16 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => {
|
||||
Delete
|
||||
</Button>
|
||||
) : null}
|
||||
{isCortexLokiOrRecordingRule(watch) && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
type="button"
|
||||
onClick={() => setShowEditYaml(true)}
|
||||
disabled={submitState.loading}
|
||||
size="sm"
|
||||
>
|
||||
Edit YAML
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="secondary"
|
||||
type="button"
|
||||
onClick={() => setShowEditYaml(true)}
|
||||
disabled={submitState.loading}
|
||||
size="sm"
|
||||
>
|
||||
{isCortexLokiOrRecordingRule(watch) ? 'Edit YAML' : 'View YAML'}
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
);
|
||||
|
||||
@ -278,7 +278,13 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => {
|
||||
onDismiss={() => setShowDeleteModal(false)}
|
||||
/>
|
||||
) : null}
|
||||
{showEditYaml ? <RuleInspector onClose={() => setShowEditYaml(false)} /> : null}
|
||||
{showEditYaml ? (
|
||||
type === RuleFormType.grafana ? (
|
||||
<GrafanaRuleInspector alertUid={uidFromParams} onClose={() => setShowEditYaml(false)} />
|
||||
) : (
|
||||
<RuleInspector onClose={() => setShowEditYaml(false)} />
|
||||
)
|
||||
) : null}
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,74 @@
|
||||
import { dump } from 'js-yaml';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
|
||||
import { CodeEditor, Drawer, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { alertRuleApi } from '../../api/alertRuleApi';
|
||||
|
||||
import { drawerStyles, RuleInspectorSubtitle, yamlTabStyle } from './RuleInspector';
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
alertUid: string;
|
||||
}
|
||||
|
||||
export const GrafanaRuleInspector = ({ onClose, alertUid }: Props) => {
|
||||
const [activeTab, setActiveTab] = useState('yaml');
|
||||
|
||||
const styles = useStyles2(drawerStyles);
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
title="Inspect Alert rule"
|
||||
subtitle={
|
||||
<div className={styles.subtitle}>
|
||||
<RuleInspectorSubtitle setActiveTab={setActiveTab} activeTab={activeTab} />
|
||||
</div>
|
||||
}
|
||||
onClose={onClose}
|
||||
>
|
||||
{activeTab === 'yaml' && <GrafanaInspectorYamlTab alertUid={alertUid} />}
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
|
||||
const { useExportRuleQuery } = alertRuleApi;
|
||||
|
||||
interface YamlTabProps {
|
||||
alertUid: string;
|
||||
}
|
||||
|
||||
const GrafanaInspectorYamlTab = ({ alertUid }: YamlTabProps) => {
|
||||
const styles = useStyles2(yamlTabStyle);
|
||||
|
||||
const { currentData: ruleYamlConfig, isLoading } = useExportRuleQuery({ uid: alertUid, format: 'yaml' });
|
||||
|
||||
const yamlRule = useMemo(() => dump(ruleYamlConfig), [ruleYamlConfig]);
|
||||
|
||||
if (isLoading) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.content}>
|
||||
<AutoSizer disableWidth>
|
||||
{({ height }) => (
|
||||
<CodeEditor
|
||||
width="100%"
|
||||
height={height}
|
||||
language="yaml"
|
||||
value={yamlRule}
|
||||
monacoOptions={{
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
@ -57,7 +57,7 @@ interface SubtitleProps {
|
||||
setActiveTab: (tab: string) => void;
|
||||
}
|
||||
|
||||
const RuleInspectorSubtitle = ({ activeTab, setActiveTab }: SubtitleProps) => {
|
||||
export const RuleInspectorSubtitle = ({ activeTab, setActiveTab }: SubtitleProps) => {
|
||||
return (
|
||||
<TabsBar>
|
||||
{tabs.map((tab, index) => {
|
||||
@ -153,7 +153,7 @@ function rulerRuleToRuleFormValues(rulerRule: RulerRuleDTO): Partial<RuleFormVal
|
||||
return {};
|
||||
}
|
||||
|
||||
const yamlTabStyle = (theme: GrafanaTheme2) => ({
|
||||
export const yamlTabStyle = (theme: GrafanaTheme2) => ({
|
||||
content: css`
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
@ -170,7 +170,7 @@ const yamlTabStyle = (theme: GrafanaTheme2) => ({
|
||||
`,
|
||||
});
|
||||
|
||||
const drawerStyles = () => ({
|
||||
export const drawerStyles = () => ({
|
||||
subtitle: css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
Loading…
Reference in New Issue
Block a user