mirror of
https://github.com/ansible/awx.git
synced 2026-01-23 15:38:06 -03:30
flush out notification form and type subform
This commit is contained in:
parent
9c90804300
commit
09178dd5f2
@ -6,3 +6,4 @@ export { default as MultiCredentialsLookup } from './MultiCredentialsLookup';
|
||||
export { default as CredentialLookup } from './CredentialLookup';
|
||||
export { default as ApplicationLookup } from './ApplicationLookup';
|
||||
export { default as HostFilterLookup } from './HostFilterLookup';
|
||||
export { default as OrganizationLookup } from './OrganizationLookup';
|
||||
|
||||
@ -104,7 +104,7 @@ function CredentialFormFields({
|
||||
error={orgMeta.error}
|
||||
/>
|
||||
<FormGroup
|
||||
fieldId="credential-credentialType"
|
||||
fieldId="credential-Type"
|
||||
helperTextInvalid={credTypeMeta.error}
|
||||
isRequired
|
||||
validated={
|
||||
@ -114,7 +114,7 @@ function CredentialFormFields({
|
||||
>
|
||||
<AnsibleSelect
|
||||
{...credTypeField}
|
||||
id="credential_type"
|
||||
id="credential-type"
|
||||
data={[
|
||||
{
|
||||
value: '',
|
||||
|
||||
@ -24,17 +24,23 @@ function NotificationTemplate({ setBreadcrumb, i18n }) {
|
||||
const match = useRouteMatch();
|
||||
const location = useLocation();
|
||||
const {
|
||||
result: template,
|
||||
result: { template, defaultMessages },
|
||||
isLoading,
|
||||
error,
|
||||
request: fetchTemplate,
|
||||
} = useRequest(
|
||||
useCallback(async () => {
|
||||
const { data } = await NotificationTemplatesAPI.readDetail(templateId);
|
||||
setBreadcrumb(data);
|
||||
return data;
|
||||
const [detail, options] = await Promise.all([
|
||||
NotificationTemplatesAPI.readDetail(templateId),
|
||||
NotificationTemplatesAPI.readOptions(),
|
||||
]);
|
||||
setBreadcrumb(detail.data);
|
||||
return {
|
||||
template: detail.data,
|
||||
defaultMessages: options.data.actions.POST.messages,
|
||||
};
|
||||
}, [templateId, setBreadcrumb]),
|
||||
null
|
||||
{ template: null, defaultMessages: null }
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -88,11 +94,18 @@ function NotificationTemplate({ setBreadcrumb, i18n }) {
|
||||
to="/notification_templates/:id/details"
|
||||
exact
|
||||
/>
|
||||
{/* <Route path="/notification_templates/add">
|
||||
<NotificationTemplateAdd
|
||||
defaultMessages={defaultMessages}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</Route> */}
|
||||
{template && (
|
||||
<>
|
||||
<Route path="/notification_templates/:id/edit">
|
||||
<NotificationTemplateEdit
|
||||
template={template}
|
||||
defaultMessages={defaultMessages}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
@ -234,7 +234,7 @@ function NotificationTemplateDetail({ i18n, template }) {
|
||||
<Detail
|
||||
label={i18n._(t`Username`)}
|
||||
value={configuration.rocketchat_username}
|
||||
dataCy="nt-detail-pagerduty-rocketchat-username"
|
||||
dataCy="nt-detail-rocketchat-username"
|
||||
/>
|
||||
<Detail
|
||||
label={i18n._(t`Icon URL`)}
|
||||
|
||||
@ -7,7 +7,7 @@ import { Config } from '../../../contexts/Config';
|
||||
|
||||
import NotificationTemplateForm from '../shared/NotificationTemplateForm';
|
||||
|
||||
function NotificationTemplateEdit({ template }) {
|
||||
function NotificationTemplateEdit({ template, defaultMessages }) {
|
||||
const detailsUrl = `/notification_templates/${template.id}/details`;
|
||||
const history = useHistory();
|
||||
const [formError, setFormError] = useState(null);
|
||||
@ -41,17 +41,13 @@ function NotificationTemplateEdit({ template }) {
|
||||
|
||||
return (
|
||||
<CardBody>
|
||||
<Config>
|
||||
{({ me }) => (
|
||||
<NotificationTemplateForm
|
||||
template={template}
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={handleCancel}
|
||||
me={me || {}}
|
||||
submitError={formError}
|
||||
/>
|
||||
)}
|
||||
</Config>
|
||||
<NotificationTemplateForm
|
||||
template={template}
|
||||
defaultMessages={defaultMessages}
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={handleCancel}
|
||||
submitError={formError}
|
||||
/>
|
||||
</CardBody>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,3 +1,146 @@
|
||||
export default function NotificationTemplateForm() {
|
||||
//
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { shape, func } from 'prop-types';
|
||||
import { Formik, useField } from 'formik';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Form, FormGroup } from '@patternfly/react-core';
|
||||
|
||||
import { OrganizationsAPI } from '../../../api';
|
||||
import { ConfigContext } from '../../../contexts/Config';
|
||||
import AnsibleSelect from '../../../components/AnsibleSelect';
|
||||
import ContentError from '../../../components/ContentError';
|
||||
import ContentLoading from '../../../components/ContentLoading';
|
||||
import FormField, { FormSubmitError } from '../../../components/FormField';
|
||||
import FormActionGroup from '../../../components/FormActionGroup/FormActionGroup';
|
||||
import { OrganizationLookup } from '../../../components/Lookup';
|
||||
import { getAddedAndRemoved } from '../../../util/lists';
|
||||
import { required, minMaxValue } from '../../../util/validators';
|
||||
import { FormColumnLayout } from '../../../components/FormLayout';
|
||||
import TypeInputsSubForm from './TypeInputsSubForm';
|
||||
import { NotificationTemplate } from '../../../types';
|
||||
|
||||
function NotificationTemplateFormFields({ i18n, defaultMessages }) {
|
||||
const [orgField, orgMeta, orgHelpers] = useField('organization');
|
||||
const [typeField, typeMeta, typeHelpers] = useField({
|
||||
name: 'notification_type',
|
||||
validate: required(i18n._(t`Select a value for this field`), i18n),
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
id="notification-name"
|
||||
name="name"
|
||||
type="text"
|
||||
label={i18n._(t`Name`)}
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="notification-description"
|
||||
name="description"
|
||||
type="text"
|
||||
label={i18n._(t`Description`)}
|
||||
/>
|
||||
<OrganizationLookup
|
||||
helperTextInvalid={orgMeta.error}
|
||||
isValid={!orgMeta.touched || !orgMeta.error}
|
||||
onBlur={() => orgHelpers.setTouched()}
|
||||
onChange={value => {
|
||||
orgHelpers.setValue(value);
|
||||
}}
|
||||
value={orgField.value}
|
||||
touched={orgMeta.touched}
|
||||
error={orgMeta.error}
|
||||
required
|
||||
/>
|
||||
<FormGroup
|
||||
fieldId="notification-type"
|
||||
helperTextInvalid={typeMeta.error}
|
||||
isRequired
|
||||
validated={!typeMeta.touched || typeMeta.error ? 'default' : 'error'}
|
||||
label={i18n._(t`Type`)}
|
||||
>
|
||||
<AnsibleSelect
|
||||
{...typeField}
|
||||
id="notification-type"
|
||||
data={[
|
||||
{
|
||||
value: '',
|
||||
key: 'none',
|
||||
label: i18n._(t`Choose a Notification Type`),
|
||||
isDisabled: true,
|
||||
},
|
||||
{ value: 'email', key: 'email', label: i18n._(t`E-mail`) },
|
||||
{ value: 'grafana', key: 'grafana', label: 'Grafana' },
|
||||
{ value: 'irc', key: 'irc', label: 'IRC' },
|
||||
{ value: 'mattermost', key: 'mattermost', label: 'Mattermost' },
|
||||
{ value: 'pagerduty', key: 'pagerduty', label: 'Pagerduty' },
|
||||
{ value: 'rocketchat', key: 'rocketchat', label: 'Rocket.Chat' },
|
||||
{ value: 'slack', key: 'slack', label: 'Slack' },
|
||||
{ value: 'twilio', key: 'twilio', label: 'Twilio' },
|
||||
{ value: 'webhook', key: 'webhook', label: 'Webhook' },
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
{typeField.value && <TypeInputsSubForm type={typeField.value} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function NotificationTemplateForm({
|
||||
template,
|
||||
defaultMessages,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
submitError,
|
||||
i18n,
|
||||
}) {
|
||||
const handleSubmit = values => {
|
||||
console.log(values);
|
||||
// onSubmit(values);
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={{
|
||||
name: template.name,
|
||||
description: template.description,
|
||||
notification_type: template.notification_type,
|
||||
}}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{formik => (
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<NotificationTemplateFormFields i18n={i18n} />
|
||||
<FormSubmitError error={submitError} />
|
||||
<FormActionGroup
|
||||
onCancel={onCancel}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
</FormColumnLayout>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
}
|
||||
|
||||
NotificationTemplateForm.propTypes = {
|
||||
template: NotificationTemplate,
|
||||
defaultMessages: shape().isRequired,
|
||||
onSubmit: func.isRequired,
|
||||
onCancel: func.isRequired,
|
||||
submitError: shape(),
|
||||
};
|
||||
|
||||
NotificationTemplateForm.defaultProps = {
|
||||
template: {
|
||||
name: '',
|
||||
description: '',
|
||||
notification_type: '',
|
||||
},
|
||||
submitError: null,
|
||||
};
|
||||
|
||||
export default withI18n()(NotificationTemplateForm);
|
||||
|
||||
@ -0,0 +1,528 @@
|
||||
import React from 'react';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { useField } from 'formik';
|
||||
import { FormGroup, Title } from '@patternfly/react-core';
|
||||
import {
|
||||
FormCheckboxLayout,
|
||||
FormColumnLayout,
|
||||
FormFullWidthLayout,
|
||||
SubFormLayout,
|
||||
} from '../../../components/FormLayout';
|
||||
import FormField, {
|
||||
PasswordField,
|
||||
CheckboxField,
|
||||
FieldTooltip,
|
||||
} from '../../../components/FormField';
|
||||
import AnsibleSelect from '../../../components/AnsibleSelect';
|
||||
import CodeMirrorInput from '../../../components/CodeMirrorInput';
|
||||
import {
|
||||
combine,
|
||||
required,
|
||||
requiredEmail,
|
||||
url,
|
||||
} from '../../../util/validators';
|
||||
import { NotificationType } from '../../../types';
|
||||
|
||||
const TypeFields = {
|
||||
email: EmailFields,
|
||||
grafana: GrafanaFields,
|
||||
irc: IRCFields,
|
||||
mattermost: MattermostFields,
|
||||
pagerduty: PagerdutyFields,
|
||||
rocketchat: RocketChatFields,
|
||||
slack: SlackFields,
|
||||
twilio: TwilioFields,
|
||||
webhook: WebhookFields,
|
||||
};
|
||||
|
||||
function TypeInputsSubForm({ type, i18n }) {
|
||||
const Fields = TypeFields[type];
|
||||
return (
|
||||
<SubFormLayout>
|
||||
<Title size="md" headingLevel="h4">
|
||||
{i18n._(t`Type Details`)}
|
||||
</Title>
|
||||
<FormColumnLayout>
|
||||
<Fields i18n={i18n} />
|
||||
</FormColumnLayout>
|
||||
</SubFormLayout>
|
||||
);
|
||||
}
|
||||
TypeInputsSubForm.propTypes = {
|
||||
type: NotificationType.isRequired,
|
||||
};
|
||||
|
||||
export default withI18n()(TypeInputsSubForm);
|
||||
|
||||
function EmailFields({ i18n }) {
|
||||
const [optionsField, optionsMeta] = useField({
|
||||
name: 'emailOptions',
|
||||
validate: required(i18n._(t`Select a value for this field`), i18n),
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
id="email-username"
|
||||
label={i18n._(t`Username`)}
|
||||
name="notification_configuration.username"
|
||||
type="text"
|
||||
/>
|
||||
<PasswordField
|
||||
id="email-password"
|
||||
label={i18n._(t`Password`)}
|
||||
name="notification_configuration.password"
|
||||
/>
|
||||
<FormField
|
||||
id="email-host"
|
||||
label={i18n._(t`Host`)}
|
||||
name="notification_configuration.host"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="email-recipients"
|
||||
label={i18n._(t`Recipient list`)}
|
||||
name="notification_configuration.recipients"
|
||||
type="textarea"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
rows="3"
|
||||
tooltip={i18n._(t`Enter one email address per line to create a recipient
|
||||
list for this type of notification.`)}
|
||||
/>
|
||||
<FormField
|
||||
id="email-sender"
|
||||
label={i18n._(t`Sender e-mail`)}
|
||||
name="notification_configuration.sender"
|
||||
type="text"
|
||||
validate={requiredEmail(i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="email-port"
|
||||
label={i18n._(t`Port`)}
|
||||
name="notification_configuration.port"
|
||||
type="number"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
min="0"
|
||||
/>
|
||||
<FormField
|
||||
id="email-timeout"
|
||||
label={i18n._(t`Timeout`)}
|
||||
name="notification_configuration.timeout"
|
||||
type="number"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
min="1"
|
||||
max="120"
|
||||
tooltip={i18n._(t`The amount of time (in seconds) before the email
|
||||
notification stops trying to reach the host and times out. Ranges
|
||||
from 1 to 120 seconds.`)}
|
||||
/>
|
||||
<FormGroup
|
||||
fieldId="email-options"
|
||||
helperTextInvalid={optionsMeta.error}
|
||||
isRequired
|
||||
validated={
|
||||
!optionsMeta.touched || !optionsMeta.error ? 'default' : 'error'
|
||||
}
|
||||
label={i18n._(t`E-mail options`)}
|
||||
>
|
||||
<AnsibleSelect
|
||||
{...optionsField}
|
||||
id="email-options"
|
||||
data={[
|
||||
{
|
||||
value: '',
|
||||
key: '',
|
||||
label: i18n._(t`Choose an email option`),
|
||||
isDisabled: true,
|
||||
},
|
||||
{ value: 'tls', key: 'tls', label: i18n._(t`Use TLS`) },
|
||||
{ value: 'ssl', key: 'ssl', label: i18n._(t`Use SSL`) },
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function GrafanaFields({ i18n }) {
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
id="grafana-url"
|
||||
label={i18n._(t`Grafana URL`)}
|
||||
name="notification_configuration.grafana_url"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
tooltip={i18n._(t`The base URL of the Grafana server - the
|
||||
/api/annotations endpoint will be added automatically to the base
|
||||
Grafana URL.`)}
|
||||
/>
|
||||
<PasswordField
|
||||
id="grafana-key"
|
||||
label={i18n._(t`Grafana API key`)}
|
||||
name="notification_configuration.grafana_key"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="grafana-dashboard-id"
|
||||
label={i18n._(t`ID of the dashboard (optional)`)}
|
||||
name="notification_configuration.dashboardId"
|
||||
type="text"
|
||||
/>
|
||||
<FormField
|
||||
id="grafana-panel-id"
|
||||
label={i18n._(t`ID of the panel (optional)`)}
|
||||
name="notification_configuration.panelId"
|
||||
type="text"
|
||||
/>
|
||||
<FormField
|
||||
id="grafana-tags"
|
||||
label={i18n._(t`Tags for the annotation (optional)`)}
|
||||
name="notification_configuration.annotation_tags"
|
||||
type="textarea"
|
||||
rows="3"
|
||||
tooltip={i18n._(t`Enter one Annotation Tag per line, without commas.`)}
|
||||
/>
|
||||
<CheckboxField
|
||||
id="grafana-ssl"
|
||||
label={i18n._(t`Disable SSL verification`)}
|
||||
name="notification_configuration.grafana_no_verify_ssl"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function IRCFields({ i18n }) {
|
||||
return (
|
||||
<>
|
||||
<PasswordField
|
||||
id="irc-password"
|
||||
label={i18n._(t`IRC server password`)}
|
||||
name="notification_configuration.password"
|
||||
/>
|
||||
<FormField
|
||||
id="irc-port"
|
||||
label={i18n._(t`IRC server port`)}
|
||||
name="notification_configuration.port"
|
||||
type="number"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
min="0"
|
||||
/>
|
||||
<FormField
|
||||
id="irc-server"
|
||||
label={i18n._(t`IRC server address`)}
|
||||
name="notification_configuration.server"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="irc-nickname"
|
||||
label={i18n._(t`IRC nick`)}
|
||||
name="notification_configuration.nickname"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="irc-targets"
|
||||
label={i18n._(t`Destination channels or users`)}
|
||||
name="notification_configuration.targets"
|
||||
type="textarea"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
tooltip={i18n._(t`Enter one IRC channel or username per line. The pound
|
||||
symbol (#) for channels, and the at (@) symbol for users, are not
|
||||
required.`)}
|
||||
/>
|
||||
<CheckboxField
|
||||
id="grafana-ssl"
|
||||
label={i18n._(t`Disable SSL verification`)}
|
||||
name="notification_configuration.use_ssl"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function MattermostFields({ i18n }) {
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
id="mattermost-url"
|
||||
label={i18n._(t`Target URL`)}
|
||||
name="notification_configuration.mattermost_url"
|
||||
type="text"
|
||||
validate={combine([required(null, i18n), url(i18n)])}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="mattermost-username"
|
||||
label={i18n._(t`Username`)}
|
||||
name="notification_configuration.mattermost_username"
|
||||
type="text"
|
||||
/>
|
||||
<FormField
|
||||
id="mattermost-channel"
|
||||
label={i18n._(t`Channel`)}
|
||||
name="notification_configuration.mattermost_channel"
|
||||
type="text"
|
||||
/>
|
||||
<FormField
|
||||
id="mattermost-icon"
|
||||
label={i18n._(t`Icon URL`)}
|
||||
name="notification_configuration.mattermost_icon_url"
|
||||
type="text"
|
||||
validate={url(i18n)}
|
||||
/>
|
||||
<CheckboxField
|
||||
id="mattermost-ssl"
|
||||
label={i18n._(t`Disable SSL verification`)}
|
||||
name="notification_configuration.mattermost_no_verify_ssl"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function PagerdutyFields({ i18n }) {
|
||||
return (
|
||||
<>
|
||||
<PasswordField
|
||||
id="pagerduty-token"
|
||||
label={i18n._(t`API Token`)}
|
||||
name="notification_configuration.token"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="pagerduty-subdomain"
|
||||
label={i18n._(t`Pagerduty subdomain`)}
|
||||
name="notification_configuration.subdomain"
|
||||
type="text"
|
||||
validate={required(i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="pagerduty-service-key"
|
||||
label={i18n._(t`API service/integration key`)}
|
||||
name="notification_configuration.service_key"
|
||||
type="text"
|
||||
validate={required(i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="pagerduty-identifier"
|
||||
label={i18n._(t`Client identifier`)}
|
||||
name="notification_configuration.client_name"
|
||||
type="text"
|
||||
validate={required(i18n)}
|
||||
isRequired
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function RocketChatFields({ i18n }) {
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
id="rocketchat-url"
|
||||
label={i18n._(t`Target URL`)}
|
||||
name="notification_configuration.rocketchat_url"
|
||||
type="text"
|
||||
validate={combine([required(null, i18n), url(i18n)])}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="rocketchat-username"
|
||||
label={i18n._(t`Username`)}
|
||||
name="notification_configuration.rocketchat_username"
|
||||
type="text"
|
||||
/>
|
||||
<FormField
|
||||
id="rocketchat-icon-url"
|
||||
label={i18n._(t`Icon URL`)}
|
||||
name="notification_configuration.rocketchat_icon_url"
|
||||
type="text"
|
||||
validate={url(i18n)}
|
||||
/>
|
||||
<CheckboxField
|
||||
id="rocketchat-ssl"
|
||||
label={i18n._(t`Disable SSL verification`)}
|
||||
name="notification_configuration.rocketchat_no_verify_ssl"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function SlackFields({ i18n }) {
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
id="slack-channels"
|
||||
label={i18n._(t`Destination channels`)}
|
||||
name="notification_configuration.channels"
|
||||
type="textarea"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
tooltip={i18n._(t`Enter one Slack channel per line. The pound symbol (#)
|
||||
is required for channels.`)}
|
||||
/>
|
||||
<PasswordField
|
||||
id="slack-token"
|
||||
label={i18n._(t`Token`)}
|
||||
name="notification_configuration.token"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="slack-color"
|
||||
label={i18n._(t`Notification color`)}
|
||||
name="notification_configuration.hex_color"
|
||||
type="text"
|
||||
tooltip={i18n._(t`Specify a notification color. Acceptable colors are hex
|
||||
color code (example: #3af or #789abc).`)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function TwilioFields({ i18n }) {
|
||||
return (
|
||||
<>
|
||||
<PasswordField
|
||||
id="twilio-token"
|
||||
label={i18n._(t`Account token`)}
|
||||
name="notification_configuration.account_token"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
<FormField
|
||||
id="twilio-from-phone"
|
||||
label={i18n._(t`Source phone number`)}
|
||||
name="notification_configuration.from_number"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
tooltip={i18n._(t`Enter the number associated with the "Messaging
|
||||
Service" in Twilio in the format +18005550199.`)}
|
||||
/>
|
||||
<FormField
|
||||
id="twilio-destination-numbers"
|
||||
label={i18n._(t`Destination SMS number(s)`)}
|
||||
name="notification_configuration.account_token"
|
||||
type="textarea"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
tooltip={i18n._(t`Enter one phone number per line to specify where to
|
||||
route SMS messages.`)}
|
||||
/>
|
||||
<FormField
|
||||
id="twilio-account-sid"
|
||||
label={i18n._(t`Account SID`)}
|
||||
name="notification_configuration.account_sid"
|
||||
type="text"
|
||||
validate={required(null, i18n)}
|
||||
isRequired
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function WebhookFields({ i18n }) {
|
||||
const [methodField, methodMeta] = useField({
|
||||
name: 'notification_configuration.http_method',
|
||||
validate: required(i18n._(t`Select a value for this field`), i18n),
|
||||
});
|
||||
const [headersField, headersMeta, headersHelpers] = useField({
|
||||
name: 'notification_configuration.headers',
|
||||
validate: required(i18n._(t`Select enter a value for this field`), i18n),
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
id="webhook-username"
|
||||
label={i18n._(t`Username`)}
|
||||
name="notification_configuration.username"
|
||||
type="text"
|
||||
/>
|
||||
<PasswordField
|
||||
id="webhook-password"
|
||||
label={i18n._(t`Basic auth password`)}
|
||||
name="notification_configuration.password"
|
||||
/>
|
||||
<FormField
|
||||
id="webhook-url"
|
||||
label={i18n._(t`Target URL`)}
|
||||
name="notification_configuration.url"
|
||||
type="text"
|
||||
validate={combine([required(null, i18n), url(i18n)])}
|
||||
isRequired
|
||||
/>
|
||||
<CheckboxField
|
||||
id="webhook-ssl"
|
||||
label={i18n._(t`Disable SSL verification`)}
|
||||
name="notification_configuration.disable_ssl_verification"
|
||||
/>
|
||||
<FormFullWidthLayout>
|
||||
<FormGroup
|
||||
fieldId="webhook-headers"
|
||||
helperTextInvalid={headersMeta.error}
|
||||
isRequired
|
||||
validated={
|
||||
!headersMeta.touched || !headersMeta.error ? 'default' : 'error'
|
||||
}
|
||||
label={i18n._(t`HTTP headers`)}
|
||||
labelIcon={
|
||||
<FieldTooltip
|
||||
content={i18n._(t`Specify HTTP Headers in JSON format. Refer to
|
||||
the Ansible Tower documentation for example syntax.`)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<CodeMirrorInput
|
||||
{...headersField}
|
||||
id="webhook-headers"
|
||||
onChange={value => {
|
||||
headersHelpers.setValue(value);
|
||||
}}
|
||||
mode="javascript"
|
||||
rows="5"
|
||||
/>
|
||||
</FormGroup>
|
||||
</FormFullWidthLayout>
|
||||
<FormGroup
|
||||
fieldId="webhook-http-method"
|
||||
helperTextInvalid={methodMeta.error}
|
||||
isRequired
|
||||
validated={
|
||||
!methodMeta.touched || !methodMeta.error ? 'default' : 'error'
|
||||
}
|
||||
label={i18n._(t`E-mail options`)}
|
||||
>
|
||||
<AnsibleSelect
|
||||
{...methodField}
|
||||
id="webhook-http-method"
|
||||
data={[
|
||||
{
|
||||
value: '',
|
||||
key: '',
|
||||
label: i18n._(t`Choose an HTTP method`),
|
||||
isDisabled: true,
|
||||
},
|
||||
{ value: 'POST', key: 'post', label: i18n._(t`POST`) },
|
||||
{ value: 'PUT', key: 'put', label: i18n._(t`PUT`) },
|
||||
]}
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -367,3 +367,27 @@ export const CredentialType = shape({
|
||||
namespace: string,
|
||||
inputs: shape({}).isRequired,
|
||||
});
|
||||
|
||||
export const NotificationType = oneOf([
|
||||
'email',
|
||||
'grafana',
|
||||
'irc',
|
||||
'mattermost',
|
||||
'pagerduty',
|
||||
'rocketchat',
|
||||
'slack',
|
||||
'twilio',
|
||||
'webhook',
|
||||
]);
|
||||
|
||||
export const NotificationTemplate = shape({
|
||||
id: number.isRequired,
|
||||
name: string.isRequired,
|
||||
description: string,
|
||||
url: string.isRequired,
|
||||
organization: number.isRequired,
|
||||
notification_type: NotificationType,
|
||||
summary_fields: shape({
|
||||
organization: Organization,
|
||||
}),
|
||||
});
|
||||
|
||||
@ -76,6 +76,21 @@ export function integer(i18n) {
|
||||
};
|
||||
}
|
||||
|
||||
export function url(i18n) {
|
||||
return value => {
|
||||
// URL regex from https://urlregex.com/
|
||||
if (
|
||||
// eslint-disable-next-line max-len
|
||||
!/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www\.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w\-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/\\\w]*))?)/.test(
|
||||
value
|
||||
)
|
||||
) {
|
||||
return i18n._(t`Please enter a valid URL`);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export function combine(validators) {
|
||||
return value => {
|
||||
for (let i = 0; i < validators.length; i++) {
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
maxLength,
|
||||
noWhiteSpace,
|
||||
integer,
|
||||
url,
|
||||
combine,
|
||||
regExp,
|
||||
} from './validators';
|
||||
@ -111,6 +112,26 @@ describe('validators', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('url should reject incomplete url', () => {
|
||||
expect(url(i18n)('abcd')).toEqual({
|
||||
id: 'Please enter a valid URL',
|
||||
});
|
||||
});
|
||||
|
||||
test('url should accept fully qualified url', () => {
|
||||
expect(url(i18n)('http://example.com/foo')).toBeUndefined();
|
||||
});
|
||||
|
||||
test('url should accept url with query params', () => {
|
||||
expect(url(i18n)('https://example.com/foo?bar=baz')).toBeUndefined();
|
||||
});
|
||||
|
||||
test('url should reject short protocol', () => {
|
||||
expect(url(i18n)('h://example.com/foo')).toEqual({
|
||||
id: 'Please enter a valid URL',
|
||||
});
|
||||
});
|
||||
|
||||
test('combine should run all validators', () => {
|
||||
const validators = [required(null, i18n), noWhiteSpace(i18n)];
|
||||
expect(combine(validators)('')).toEqual({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user