mirror of
https://github.com/grafana/grafana.git
synced 2025-12-22 04:34:27 +08:00
Compare commits
1 Commits
zoltan/pos
...
alerting/l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a6a8f661d |
@@ -1,5 +1,5 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { FormProvider, SubmitErrorHandler, UseFormWatch, useForm } from 'react-hook-form';
|
import { FormProvider, SubmitErrorHandler, UseFormWatch, useForm } from 'react-hook-form';
|
||||||
import { useParams } from 'react-router-dom-v5-compat';
|
import { useParams } from 'react-router-dom-v5-compat';
|
||||||
|
|
||||||
@@ -92,11 +92,12 @@ export const AlertRuleForm = ({ existing, prefill, isManualRestore }: Props) =>
|
|||||||
|
|
||||||
const ruleType = translateRouteParamToRuleType(routeParams.type);
|
const ruleType = translateRouteParamToRuleType(routeParams.type);
|
||||||
|
|
||||||
const defaultValues: RuleFormValues = useMemo(() => {
|
const defaultValues = useCallback(async () => {
|
||||||
// If we have an existing AND a prefill, then we're coming from the restore dialog
|
// If we have an existing AND a prefill, then we're coming from the restore dialog
|
||||||
// and we want to merge the two
|
// and we want to merge the two
|
||||||
if (existing && prefill) {
|
if (existing && prefill) {
|
||||||
return { ...formValuesFromExistingRule(existing), ...formValuesFromPrefill(prefill) };
|
const prefillValues = await formValuesFromPrefill(prefill);
|
||||||
|
return { ...formValuesFromExistingRule(existing), ...prefillValues };
|
||||||
}
|
}
|
||||||
if (existing) {
|
if (existing) {
|
||||||
return formValuesFromExistingRule(existing);
|
return formValuesFromExistingRule(existing);
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { clamp } from 'lodash';
|
import { clamp } from 'lodash';
|
||||||
import z from 'zod';
|
|
||||||
|
|
||||||
import { config, getDataSourceSrv } from '@grafana/runtime';
|
import { config, getDataSourceSrv } from '@grafana/runtime';
|
||||||
import { alertingAlertRuleFormSchema } from 'app/features/plugins/components/restrictedGrafanaApis/alerting/alertRuleFormSchema';
|
|
||||||
import { RuleWithLocation } from 'app/types/unified-alerting';
|
import { RuleWithLocation } from 'app/types/unified-alerting';
|
||||||
import { GrafanaAlertStateDecision, RulerRuleDTO } from 'app/types/unified-alerting-dto';
|
import { GrafanaAlertStateDecision, RulerRuleDTO } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
@@ -157,90 +155,15 @@ export function formValuesFromQueryParams(ruleDefinition: string, type: RuleForm
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// schema for cloud rule form values. This is necessary because the cloud rule form values are not the same as the grafana rule form values.
|
// ⚠️ Lazy-loaded function to avoid bundling zod in the main bundle
|
||||||
// schema for grafana rule values is navigateToAlertFormSchema , shared in the restrictedGrafanaApis.
|
// This function is async to allow dynamic imports of the zod schemas
|
||||||
// TODO: add this to the DMA new plugin.
|
export async function formValuesFromPrefill(rule: Partial<RuleFormValues>): Promise<RuleFormValues> {
|
||||||
|
const [{ cloudRuleFormValuesSchema }, { alertingAlertRuleFormSchema }] = await Promise.all([
|
||||||
|
import('./formDefaultsSchemas'),
|
||||||
|
import('app/features/plugins/components/restrictedGrafanaApis/alerting/alertRuleFormSchema'),
|
||||||
|
]);
|
||||||
|
|
||||||
const cloudRuleFormValuesSchema = z.looseObject({
|
let parsedRule: Partial<RuleFormValues>;
|
||||||
name: z.string().optional(),
|
|
||||||
type: z.enum(RuleFormType).catch(RuleFormType.grafana),
|
|
||||||
dataSourceName: z.string().optional().default(''),
|
|
||||||
group: z.string().optional(),
|
|
||||||
labels: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
key: z.string(),
|
|
||||||
value: z.string(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.optional()
|
|
||||||
.default([]),
|
|
||||||
annotations: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
key: z.string(),
|
|
||||||
value: z.string(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.optional()
|
|
||||||
.default([]),
|
|
||||||
queries: z.array(z.any()).optional(),
|
|
||||||
condition: z.string().optional(),
|
|
||||||
noDataState: z
|
|
||||||
.enum(GrafanaAlertStateDecision)
|
|
||||||
.optional()
|
|
||||||
.default(GrafanaAlertStateDecision.NoData)
|
|
||||||
.catch(GrafanaAlertStateDecision.NoData),
|
|
||||||
execErrState: z
|
|
||||||
.enum(GrafanaAlertStateDecision)
|
|
||||||
.optional()
|
|
||||||
.default(GrafanaAlertStateDecision.Error)
|
|
||||||
.catch(GrafanaAlertStateDecision.Error),
|
|
||||||
folder: z
|
|
||||||
.union([
|
|
||||||
z.object({
|
|
||||||
title: z.string(),
|
|
||||||
uid: z.string(),
|
|
||||||
}),
|
|
||||||
z.undefined(),
|
|
||||||
])
|
|
||||||
.optional(),
|
|
||||||
evaluateEvery: z.string().optional(),
|
|
||||||
evaluateFor: z.string().optional().default('0s'),
|
|
||||||
keepFiringFor: z.string().optional(),
|
|
||||||
isPaused: z.boolean().optional().default(false),
|
|
||||||
manualRouting: z.boolean().optional(),
|
|
||||||
contactPoints: z
|
|
||||||
.record(
|
|
||||||
z.string(),
|
|
||||||
z.object({
|
|
||||||
selectedContactPoint: z.string(),
|
|
||||||
overrideGrouping: z.boolean(),
|
|
||||||
groupBy: z.array(z.string()),
|
|
||||||
overrideTimings: z.boolean(),
|
|
||||||
groupWaitValue: z.string(),
|
|
||||||
groupIntervalValue: z.string(),
|
|
||||||
repeatIntervalValue: z.string(),
|
|
||||||
muteTimeIntervals: z.array(z.string()),
|
|
||||||
activeTimeIntervals: z.array(z.string()),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
editorSettings: z
|
|
||||||
.object({
|
|
||||||
simplifiedQueryEditor: z.boolean(),
|
|
||||||
simplifiedNotificationEditor: z.boolean(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
metric: z.string().optional(),
|
|
||||||
targetDatasourceUid: z.string().optional(),
|
|
||||||
namespace: z.string().optional(),
|
|
||||||
expression: z.string().optional(),
|
|
||||||
missingSeriesEvalsToResolve: z.number().optional(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export function formValuesFromPrefill(rule: Partial<RuleFormValues>): RuleFormValues {
|
|
||||||
let parsedRule: z.infer<typeof alertingAlertRuleFormSchema> | z.infer<typeof cloudRuleFormValuesSchema>;
|
|
||||||
// differencitate between cloud and grafana prefill
|
// differencitate between cloud and grafana prefill
|
||||||
if (rule.type === RuleFormType.cloudAlerting) {
|
if (rule.type === RuleFormType.cloudAlerting) {
|
||||||
// we use this schema to coerce prefilled query params into a valid "FormValues" interface
|
// we use this schema to coerce prefilled query params into a valid "FormValues" interface
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import z from 'zod';
|
||||||
|
|
||||||
|
import { GrafanaAlertStateDecision } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
|
import { RuleFormType } from '../types/rule-form';
|
||||||
|
|
||||||
|
// Schema for cloud rule form values. This is necessary because the cloud rule form values are not the same as the grafana rule form values.
|
||||||
|
// schema for grafana rule values is navigateToAlertFormSchema , shared in the restrictedGrafanaApis.
|
||||||
|
// TODO: add this to the DMA new plugin.
|
||||||
|
|
||||||
|
export const cloudRuleFormValuesSchema = z.looseObject({
|
||||||
|
name: z.string().optional(),
|
||||||
|
type: z.enum(RuleFormType).catch(RuleFormType.grafana),
|
||||||
|
dataSourceName: z.string().optional().default(''),
|
||||||
|
group: z.string().optional(),
|
||||||
|
labels: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
key: z.string(),
|
||||||
|
value: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
|
.default([]),
|
||||||
|
annotations: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
key: z.string(),
|
||||||
|
value: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
|
.default([]),
|
||||||
|
queries: z.array(z.any()).optional(),
|
||||||
|
condition: z.string().optional(),
|
||||||
|
noDataState: z
|
||||||
|
.enum(GrafanaAlertStateDecision)
|
||||||
|
.optional()
|
||||||
|
.default(GrafanaAlertStateDecision.NoData)
|
||||||
|
.catch(GrafanaAlertStateDecision.NoData),
|
||||||
|
execErrState: z
|
||||||
|
.enum(GrafanaAlertStateDecision)
|
||||||
|
.optional()
|
||||||
|
.default(GrafanaAlertStateDecision.Error)
|
||||||
|
.catch(GrafanaAlertStateDecision.Error),
|
||||||
|
folder: z
|
||||||
|
.union([
|
||||||
|
z.object({
|
||||||
|
title: z.string(),
|
||||||
|
uid: z.string(),
|
||||||
|
}),
|
||||||
|
z.undefined(),
|
||||||
|
])
|
||||||
|
.optional(),
|
||||||
|
evaluateEvery: z.string().optional(),
|
||||||
|
evaluateFor: z.string().optional().default('0s'),
|
||||||
|
keepFiringFor: z.string().optional(),
|
||||||
|
isPaused: z.boolean().optional().default(false),
|
||||||
|
manualRouting: z.boolean().optional(),
|
||||||
|
contactPoints: z
|
||||||
|
.record(
|
||||||
|
z.string(),
|
||||||
|
z.object({
|
||||||
|
selectedContactPoint: z.string(),
|
||||||
|
overrideGrouping: z.boolean(),
|
||||||
|
groupBy: z.array(z.string()),
|
||||||
|
overrideTimings: z.boolean(),
|
||||||
|
groupWaitValue: z.string(),
|
||||||
|
groupIntervalValue: z.string(),
|
||||||
|
repeatIntervalValue: z.string(),
|
||||||
|
muteTimeIntervals: z.array(z.string()),
|
||||||
|
activeTimeIntervals: z.array(z.string()),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
editorSettings: z
|
||||||
|
.object({
|
||||||
|
simplifiedQueryEditor: z.boolean(),
|
||||||
|
simplifiedNotificationEditor: z.boolean(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
metric: z.string().optional(),
|
||||||
|
targetDatasourceUid: z.string().optional(),
|
||||||
|
namespace: z.string().optional(),
|
||||||
|
expression: z.string().optional(),
|
||||||
|
missingSeriesEvalsToResolve: z.number().optional(),
|
||||||
|
});
|
||||||
@@ -21,7 +21,6 @@ import { notifyApp } from 'app/core/reducers/appNotification';
|
|||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
import { getMessageFromError } from 'app/core/utils/errors';
|
import { getMessageFromError } from 'app/core/utils/errors';
|
||||||
import { getCreateAlertInMenuAvailability } from 'app/features/alerting/unified/utils/access-control';
|
import { getCreateAlertInMenuAvailability } from 'app/features/alerting/unified/utils/access-control';
|
||||||
import { scenesPanelToRuleFormValues } from 'app/features/alerting/unified/utils/rule-form';
|
|
||||||
import { getTrackingSource, shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
|
import { getTrackingSource, shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
|
||||||
import { InspectTab } from 'app/features/inspector/types';
|
import { InspectTab } from 'app/features/inspector/types';
|
||||||
import { getScenePanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
|
import { getScenePanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
|
||||||
@@ -527,6 +526,8 @@ export function onRemovePanel(dashboard: DashboardScene, panel: VizPanel) {
|
|||||||
|
|
||||||
const onCreateAlert = async (panel: VizPanel) => {
|
const onCreateAlert = async (panel: VizPanel) => {
|
||||||
try {
|
try {
|
||||||
|
// ⚠️ Dynamically importing this to prevent Zod from being bundled into the dashboard bundle
|
||||||
|
const { scenesPanelToRuleFormValues } = await import('app/features/alerting/unified/utils/rule-form');
|
||||||
const formValues = await scenesPanelToRuleFormValues(panel);
|
const formValues = await scenesPanelToRuleFormValues(panel);
|
||||||
const ruleFormUrl = urlUtil.renderUrl('/alerting/new', {
|
const ruleFormUrl = urlUtil.renderUrl('/alerting/new', {
|
||||||
defaults: JSON.stringify(formValues),
|
defaults: JSON.stringify(formValues),
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { contextSrv } from 'app/core/services/context_srv';
|
|||||||
import { getMessageFromError } from 'app/core/utils/errors';
|
import { getMessageFromError } from 'app/core/utils/errors';
|
||||||
import { getExploreUrl } from 'app/core/utils/explore';
|
import { getExploreUrl } from 'app/core/utils/explore';
|
||||||
import { RuleFormValues } from 'app/features/alerting/unified/types/rule-form';
|
import { RuleFormValues } from 'app/features/alerting/unified/types/rule-form';
|
||||||
import { panelToRuleFormValues } from 'app/features/alerting/unified/utils/rule-form';
|
|
||||||
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
|
||||||
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
|
import { PanelModel } from 'app/features/dashboard/state/PanelModel';
|
||||||
import {
|
import {
|
||||||
@@ -175,6 +174,8 @@ export function getPanelMenu(
|
|||||||
const createAlert = async () => {
|
const createAlert = async () => {
|
||||||
let formValues: Partial<RuleFormValues> | undefined;
|
let formValues: Partial<RuleFormValues> | undefined;
|
||||||
try {
|
try {
|
||||||
|
// ⚠️ Dynamically importing this to prevent Zod from being bundled into the dashboard bundle
|
||||||
|
const { panelToRuleFormValues } = await import('app/features/alerting/unified/utils/rule-form');
|
||||||
formValues = await panelToRuleFormValues(panel, dashboard);
|
formValues = await panelToRuleFormValues(panel, dashboard);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const message = `Error getting rule values from the panel: ${getMessageFromError(err)}`;
|
const message = `Error getting rule values from the panel: ${getMessageFromError(err)}`;
|
||||||
|
|||||||
@@ -1,16 +1,7 @@
|
|||||||
import { PropsWithChildren, ReactElement } from 'react';
|
import { PropsWithChildren, ReactElement, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { RestrictedGrafanaApisContextProvider, RestrictedGrafanaApisContextType } from '@grafana/data';
|
import { RestrictedGrafanaApisContextProvider, RestrictedGrafanaApisContextType } from '@grafana/data';
|
||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
import { alertingAlertRuleFormSchemaApi } from 'app/features/plugins/components/restrictedGrafanaApis/alerting/alertRuleFormSchema';
|
|
||||||
|
|
||||||
const restrictedGrafanaApis: RestrictedGrafanaApisContextType = config.featureToggles.restrictedPluginApis
|
|
||||||
? {
|
|
||||||
// Add your restricted APIs here
|
|
||||||
// (APIs that should be availble to ALL plugins should be shared via our packages, e.g. @grafana/data.)
|
|
||||||
alertingAlertRuleFormSchema: alertingAlertRuleFormSchemaApi.alertingAlertRuleFormSchema,
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
|
|
||||||
// This Provider is a wrapper around `RestrictedGrafanaApisContextProvider` from `@grafana/data`.
|
// This Provider is a wrapper around `RestrictedGrafanaApisContextProvider` from `@grafana/data`.
|
||||||
// The reason for this is that like this we only need to define the configuration once (here) and can use it in multiple places (app root page, extensions).
|
// The reason for this is that like this we only need to define the configuration once (here) and can use it in multiple places (app root page, extensions).
|
||||||
@@ -18,6 +9,23 @@ export function RestrictedGrafanaApisProvider({
|
|||||||
children,
|
children,
|
||||||
pluginId,
|
pluginId,
|
||||||
}: PropsWithChildren<{ pluginId: string }>): ReactElement {
|
}: PropsWithChildren<{ pluginId: string }>): ReactElement {
|
||||||
|
const [restrictedGrafanaApis, setRestrictedGrafanaApis] = useState<RestrictedGrafanaApisContextType>({});
|
||||||
|
|
||||||
|
// Add your restricted APIs here
|
||||||
|
// (APIs that should be availble to ALL plugins should be shared via our packages, e.g. @grafana/data.)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!config.featureToggles.restrictedPluginApis) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ⚠️ Lazy-load the alerting schema to avoid bundling zod in the main app bundle
|
||||||
|
import('app/features/plugins/components/restrictedGrafanaApis/alerting/alertRuleFormSchema').then((module) => {
|
||||||
|
setRestrictedGrafanaApis({
|
||||||
|
alertingAlertRuleFormSchema: module.alertingAlertRuleFormSchemaApi.alertingAlertRuleFormSchema,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RestrictedGrafanaApisContextProvider
|
<RestrictedGrafanaApisContextProvider
|
||||||
pluginId={pluginId}
|
pluginId={pluginId}
|
||||||
|
|||||||
Reference in New Issue
Block a user