mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Fix issue with Slack contact point validation (#47559)
* secureFields and secureSettings * revert channelIndex * readd lost code * use specific return * register secure fields and use not hard coded index * fix for determineReadOnly * fix lint error * fix test suite Co-authored-by: gillesdemey <gilles.de.mey@gmail.com>
This commit is contained in:
parent
e58a015baf
commit
39d3c8afd7
@ -383,7 +383,7 @@ func GetAvailableNotifiers() []*alerting.NotifierPlugin {
|
||||
Description: "Specify channel, private group, or IM channel (can be an encoded ID or a name) - required unless you provide a webhook",
|
||||
PropertyName: "recipient",
|
||||
Required: true,
|
||||
DependsOn: "secureSettings.url",
|
||||
DependsOn: "url",
|
||||
},
|
||||
// Logically, this field should be required when not using a webhook, since the Slack API needs a token.
|
||||
// However, since the UI doesn't allow to say that a field is required or not depending on another field,
|
||||
@ -397,7 +397,7 @@ func GetAvailableNotifiers() []*alerting.NotifierPlugin {
|
||||
PropertyName: "token",
|
||||
Secure: true,
|
||||
Required: true,
|
||||
DependsOn: "secureSettings.url",
|
||||
DependsOn: "url",
|
||||
},
|
||||
{
|
||||
Label: "Username",
|
||||
@ -463,7 +463,7 @@ func GetAvailableNotifiers() []*alerting.NotifierPlugin {
|
||||
PropertyName: "url",
|
||||
Secure: true,
|
||||
Required: true,
|
||||
DependsOn: "secureSettings.token",
|
||||
DependsOn: "token",
|
||||
},
|
||||
{ // New in 8.4.
|
||||
Label: "Endpoint URL",
|
||||
|
@ -47,12 +47,7 @@ export function ChannelOptions<R extends ChannelValues>({
|
||||
value="Configured"
|
||||
suffix={
|
||||
readOnly ? null : (
|
||||
<Button
|
||||
onClick={() => onResetSecureField(option.propertyName)}
|
||||
variant="link"
|
||||
type="button"
|
||||
size="sm"
|
||||
>
|
||||
<Button onClick={() => onResetSecureField(option.propertyName)} fill="text" type="button" size="sm">
|
||||
Clear
|
||||
</Button>
|
||||
)
|
||||
@ -74,7 +69,8 @@ export function ChannelOptions<R extends ChannelValues>({
|
||||
readOnly={readOnly}
|
||||
key={key}
|
||||
error={error}
|
||||
pathPrefix={option.secure ? `${pathPrefix}secureSettings.` : `${pathPrefix}settings.`}
|
||||
pathPrefix={pathPrefix}
|
||||
pathSuffix={option.secure ? 'secureSettings.' : 'settings.'}
|
||||
option={option}
|
||||
/>
|
||||
);
|
||||
|
@ -37,12 +37,15 @@ export function ChannelSubForm<R extends ChannelValues>({
|
||||
}: Props<R>): JSX.Element {
|
||||
const styles = useStyles2(getStyles);
|
||||
const name = (fieldName: string) => `${pathPrefix}${fieldName}`;
|
||||
const { control, watch, register, trigger, formState } = useFormContext();
|
||||
const { control, watch, register, trigger, formState, setValue } = useFormContext();
|
||||
const selectedType = watch(name('type')) ?? defaultValues.type; // nope, setting "default" does not work at all.
|
||||
const { loading: testingReceiver } = useUnifiedAlertingSelector((state) => state.testReceivers);
|
||||
|
||||
useEffect(() => {
|
||||
register(`${pathPrefix}.__id`);
|
||||
/* Need to manually register secureFields or else they'll
|
||||
be lost when testing a contact point */
|
||||
register(`${pathPrefix}.secureFields`);
|
||||
}, [register, pathPrefix]);
|
||||
|
||||
const [_secureFields, setSecureFields] = useState(secureFields ?? {});
|
||||
@ -52,6 +55,7 @@ export function ChannelSubForm<R extends ChannelValues>({
|
||||
const updatedSecureFields = { ...secureFields };
|
||||
delete updatedSecureFields[key];
|
||||
setSecureFields(updatedSecureFields);
|
||||
setValue(`${pathPrefix}.secureFields`, updatedSecureFields);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { FC, useEffect } from 'react';
|
||||
import { Checkbox, Field, Input, InputControl, Select, TextArea } from '@grafana/ui';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { NotificationChannelOption } from 'app/types';
|
||||
import { useFormContext, FieldError, DeepMap } from 'react-hook-form';
|
||||
import { SubformField } from './SubformField';
|
||||
@ -13,11 +14,22 @@ interface Props {
|
||||
option: NotificationChannelOption;
|
||||
invalid?: boolean;
|
||||
pathPrefix: string;
|
||||
pathSuffix?: string;
|
||||
error?: FieldError | DeepMap<any, FieldError>;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export const OptionField: FC<Props> = ({ option, invalid, pathPrefix, error, defaultValue, readOnly = false }) => {
|
||||
export const OptionField: FC<Props> = ({
|
||||
option,
|
||||
invalid,
|
||||
pathPrefix,
|
||||
pathSuffix = '',
|
||||
error,
|
||||
defaultValue,
|
||||
readOnly = false,
|
||||
}) => {
|
||||
const optionPath = `${pathPrefix}${pathSuffix}`;
|
||||
|
||||
if (option.element === 'subform') {
|
||||
return (
|
||||
<SubformField
|
||||
@ -25,7 +37,7 @@ export const OptionField: FC<Props> = ({ option, invalid, pathPrefix, error, def
|
||||
defaultValue={defaultValue}
|
||||
option={option}
|
||||
errors={error as DeepMap<any, FieldError> | undefined}
|
||||
pathPrefix={pathPrefix}
|
||||
pathPrefix={optionPath}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -35,7 +47,7 @@ export const OptionField: FC<Props> = ({ option, invalid, pathPrefix, error, def
|
||||
readOnly={readOnly}
|
||||
defaultValues={defaultValue}
|
||||
option={option}
|
||||
pathPrefix={pathPrefix}
|
||||
pathPrefix={optionPath}
|
||||
errors={error as Array<DeepMap<any, FieldError>> | undefined}
|
||||
/>
|
||||
);
|
||||
@ -48,18 +60,26 @@ export const OptionField: FC<Props> = ({ option, invalid, pathPrefix, error, def
|
||||
error={error?.message}
|
||||
>
|
||||
<OptionInput
|
||||
id={`${pathPrefix}${option.propertyName}`}
|
||||
id={`${optionPath}${option.propertyName}`}
|
||||
defaultValue={defaultValue}
|
||||
option={option}
|
||||
invalid={invalid}
|
||||
pathPrefix={pathPrefix}
|
||||
pathPrefix={optionPath}
|
||||
readOnly={readOnly}
|
||||
pathIndex={pathPrefix}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
||||
const OptionInput: FC<Props & { id: string }> = ({ option, invalid, id, pathPrefix = '', readOnly = false }) => {
|
||||
const OptionInput: FC<Props & { id: string; pathIndex?: string }> = ({
|
||||
option,
|
||||
invalid,
|
||||
id,
|
||||
pathPrefix = '',
|
||||
pathIndex = '',
|
||||
readOnly = false,
|
||||
}) => {
|
||||
const { control, register, unregister, getValues } = useFormContext();
|
||||
const name = `${pathPrefix}${option.propertyName}`;
|
||||
|
||||
@ -87,11 +107,11 @@ const OptionInput: FC<Props & { id: string }> = ({ option, invalid, id, pathPref
|
||||
return (
|
||||
<Input
|
||||
id={id}
|
||||
readOnly={readOnly || determineReadOnly(option, getValues)}
|
||||
readOnly={readOnly || determineReadOnly(option, getValues, pathIndex)}
|
||||
invalid={invalid}
|
||||
type={option.inputType}
|
||||
{...register(name, {
|
||||
required: determineRequired(option, getValues),
|
||||
required: determineRequired(option, getValues, pathIndex),
|
||||
validate: (v) => (option.validationRule !== '' ? validateOption(v, option.validationRule) : true),
|
||||
})}
|
||||
placeholder={option.placeholder}
|
||||
@ -165,19 +185,26 @@ const validateOption = (value: string, validationRule: string) => {
|
||||
return RegExp(validationRule).test(value) ? true : 'Invalid format';
|
||||
};
|
||||
|
||||
const determineRequired = (option: NotificationChannelOption, getValues: any) => {
|
||||
const determineRequired = (option: NotificationChannelOption, getValues: any, pathIndex: string) => {
|
||||
if (!option.dependsOn) {
|
||||
return option.required ? 'Required' : false;
|
||||
}
|
||||
|
||||
const dependentOn = getValues(`items[0].${option.dependsOn}`);
|
||||
return !dependentOn && option.required ? 'Required' : false;
|
||||
if (isEmpty(getValues(`${pathIndex}secureFields`))) {
|
||||
const dependentOn = getValues(`${pathIndex}secureSettings.${option.dependsOn}`);
|
||||
return !Boolean(dependentOn) && option.required ? 'Required' : false;
|
||||
} else {
|
||||
const dependentOn: boolean = getValues(`${pathIndex}secureFields.${option.dependsOn}`);
|
||||
return !dependentOn && option.required ? 'Required' : false;
|
||||
}
|
||||
};
|
||||
|
||||
const determineReadOnly = (option: NotificationChannelOption, getValues: any) => {
|
||||
const determineReadOnly = (option: NotificationChannelOption, getValues: any, pathIndex: string) => {
|
||||
if (!option.dependsOn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getValues(`items[0].${option.dependsOn}`);
|
||||
if (isEmpty(getValues(`${pathIndex}secureFields`))) {
|
||||
return getValues(`${pathIndex}secureSettings.${option.dependsOn}`);
|
||||
} else {
|
||||
return getValues(`${pathIndex}secureFields.${option.dependsOn}`);
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user