mirror of
https://github.com/ansible/awx.git
synced 2026-01-14 19:30:39 -03:30
add custom notification messages subform
This commit is contained in:
parent
bb12e0a3a9
commit
ba95775ded
@ -0,0 +1,73 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
string,
|
||||
oneOfType,
|
||||
object,
|
||||
func,
|
||||
bool,
|
||||
node,
|
||||
oneOf,
|
||||
number,
|
||||
} from 'prop-types';
|
||||
import { useField } from 'formik';
|
||||
import { FormGroup } from '@patternfly/react-core';
|
||||
import CodeMirrorInput from './CodeMirrorInput';
|
||||
import { FieldTooltip } from '../FormField';
|
||||
|
||||
function CodeMirrorField({
|
||||
id,
|
||||
name,
|
||||
label,
|
||||
tooltip,
|
||||
helperText,
|
||||
validate,
|
||||
isRequired,
|
||||
mode,
|
||||
...rest
|
||||
}) {
|
||||
const [field, meta, helpers] = useField({ name, validate });
|
||||
const isValid = !(meta.touched && meta.error);
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
fieldId={id}
|
||||
helperText={helperText}
|
||||
helperTextInvalid={meta.error}
|
||||
isRequired={isRequired}
|
||||
validated={isValid ? 'default' : 'error'}
|
||||
label={label}
|
||||
labelIcon={<FieldTooltip content={tooltip} />}
|
||||
>
|
||||
<CodeMirrorInput
|
||||
id="webhook-headers"
|
||||
{...rest}
|
||||
{...field}
|
||||
onChange={value => {
|
||||
helpers.setValue(value);
|
||||
}}
|
||||
mode={mode}
|
||||
/>
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
CodeMirrorField.propTypes = {
|
||||
helperText: string,
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
label: oneOfType([object, string]).isRequired,
|
||||
validate: func,
|
||||
isRequired: bool,
|
||||
tooltip: node,
|
||||
mode: oneOf(['javascript', 'yaml', 'jinja2']).isRequired,
|
||||
rows: number,
|
||||
};
|
||||
|
||||
CodeMirrorField.defaultProps = {
|
||||
helperText: '',
|
||||
validate: () => {},
|
||||
isRequired: false,
|
||||
tooltip: null,
|
||||
rows: 5,
|
||||
};
|
||||
|
||||
export default CodeMirrorField;
|
||||
@ -1,6 +1,7 @@
|
||||
import CodeMirrorInput from './CodeMirrorInput';
|
||||
|
||||
export default CodeMirrorInput;
|
||||
export { default as CodeMirrorField } from './CodeMirrorField';
|
||||
export { default as VariablesDetail } from './VariablesDetail';
|
||||
export { default as VariablesInput } from './VariablesInput';
|
||||
export { default as VariablesField } from './VariablesField';
|
||||
|
||||
@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { CardBody } from '../../../components/Card';
|
||||
import { OrganizationsAPI } from '../../../api';
|
||||
import { Config } from '../../../contexts/Config';
|
||||
|
||||
import NotificationTemplateForm from '../shared/NotificationTemplateForm';
|
||||
|
||||
|
||||
@ -0,0 +1,213 @@
|
||||
import 'styled-components/macro';
|
||||
import React from 'react';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { useField } from 'formik';
|
||||
import {
|
||||
FormGroup,
|
||||
Title,
|
||||
Switch,
|
||||
Text,
|
||||
TextVariants,
|
||||
} from '@patternfly/react-core';
|
||||
import {
|
||||
FormColumnLayout,
|
||||
FormFullWidthLayout,
|
||||
SubFormLayout,
|
||||
} from '../../../components/FormLayout';
|
||||
import FormField, {
|
||||
PasswordField,
|
||||
CheckboxField,
|
||||
FieldTooltip,
|
||||
} from '../../../components/FormField';
|
||||
import AnsibleSelect from '../../../components/AnsibleSelect';
|
||||
import { CodeMirrorField } from '../../../components/CodeMirrorInput';
|
||||
import {
|
||||
combine,
|
||||
required,
|
||||
requiredEmail,
|
||||
url,
|
||||
} from '../../../util/validators';
|
||||
import { NotificationType } from '../../../types';
|
||||
|
||||
function CustomMessagesSubForm({ defaultMessages, type, i18n }) {
|
||||
const [useCustomField, , useCustomHelpers] = useField('useCustomMessages');
|
||||
const showMessages = type !== 'webhook';
|
||||
const showBodies = ['email', 'pagerduty', 'webhook'].includes(type);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Switch
|
||||
id="toggle-custom-messages"
|
||||
label={i18n._(t`Customize messages…`)}
|
||||
isChecked={!!useCustomField.value}
|
||||
onChange={() => useCustomHelpers.setValue(!useCustomField.value)}
|
||||
/>
|
||||
{useCustomField.value && (
|
||||
<SubFormLayout>
|
||||
<Text
|
||||
className="pf-c-content"
|
||||
css="margin-bottom: var(--pf-c-content--MarginBottom)"
|
||||
>
|
||||
<small>
|
||||
Use custom messages to change the content of notifications sent
|
||||
when a job starts, succeeds, or fails. Use curly braces to access
|
||||
information about the job:{' '}
|
||||
<code>
|
||||
{'{{'} job_friendly_name {'}}'}
|
||||
</code>
|
||||
,{' '}
|
||||
<code>
|
||||
{'{{'} url {'}}'}
|
||||
</code>
|
||||
, or attributes of the job such as{' '}
|
||||
<code>
|
||||
{'{{'} job.status {'}}'}
|
||||
</code>
|
||||
. You may apply a number of possible variables in the message.
|
||||
Refer to the{' '}
|
||||
<a
|
||||
href="https://docs.ansible.com/ansible-tower/latest/html/userguide/notifications.html#create-custom-notifications"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Ansible Tower documentation
|
||||
</a>{' '}
|
||||
for more details.
|
||||
</small>
|
||||
</Text>
|
||||
<FormFullWidthLayout>
|
||||
{showMessages && (
|
||||
<CodeMirrorField
|
||||
id="start-message"
|
||||
name="messages.started.message"
|
||||
label={i18n._(t`Start message`)}
|
||||
mode="jinja2"
|
||||
rows={2}
|
||||
/>
|
||||
)}
|
||||
{showBodies && (
|
||||
<CodeMirrorField
|
||||
id="start-body"
|
||||
name="messages.started.body"
|
||||
label={i18n._(t`Start message body`)}
|
||||
mode="jinja2"
|
||||
rows={6}
|
||||
/>
|
||||
)}
|
||||
{showMessages && (
|
||||
<CodeMirrorField
|
||||
id="success-message"
|
||||
name="messages.success.message"
|
||||
label={i18n._(t`Success message`)}
|
||||
mode="jinja2"
|
||||
rows={2}
|
||||
/>
|
||||
)}
|
||||
{showBodies && (
|
||||
<CodeMirrorField
|
||||
id="success-body"
|
||||
name="messages.success.body"
|
||||
label={i18n._(t`Success message body`)}
|
||||
mode="jinja2"
|
||||
rows={6}
|
||||
/>
|
||||
)}
|
||||
{showMessages && (
|
||||
<CodeMirrorField
|
||||
id="error-message"
|
||||
name="messages.error.message"
|
||||
label={i18n._(t`Error message`)}
|
||||
mode="jinja2"
|
||||
rows={2}
|
||||
/>
|
||||
)}
|
||||
{showBodies && (
|
||||
<CodeMirrorField
|
||||
id="error-body"
|
||||
name="messages.error.body"
|
||||
label={i18n._(t`Error message body`)}
|
||||
mode="jinja2"
|
||||
rows={6}
|
||||
/>
|
||||
)}
|
||||
{showMessages && (
|
||||
<CodeMirrorField
|
||||
id="wf-approved-message"
|
||||
name="messages.workflow_approval.approved.message"
|
||||
label={i18n._(t`Workflow approved message`)}
|
||||
mode="jinja2"
|
||||
rows={2}
|
||||
/>
|
||||
)}
|
||||
{showBodies && (
|
||||
<CodeMirrorField
|
||||
id="wf-approved-body"
|
||||
name="messages.workflow_approval.approved.body"
|
||||
label={i18n._(t`Workflow approved message body`)}
|
||||
mode="jinja2"
|
||||
rows={6}
|
||||
/>
|
||||
)}
|
||||
{showMessages && (
|
||||
<CodeMirrorField
|
||||
id="wf-denied-message"
|
||||
name="messages.workflow_approval.denied.message"
|
||||
label={i18n._(t`Workflow denied message`)}
|
||||
mode="jinja2"
|
||||
rows={2}
|
||||
/>
|
||||
)}
|
||||
{showBodies && (
|
||||
<CodeMirrorField
|
||||
id="wf-denied-body"
|
||||
name="messages.workflow_approval.denied.body"
|
||||
label={i18n._(t`Workflow denied message body`)}
|
||||
mode="jinja2"
|
||||
rows={6}
|
||||
/>
|
||||
)}
|
||||
{showMessages && (
|
||||
<CodeMirrorField
|
||||
id="wf-running-message"
|
||||
name="messages.workflow_approval.running.message"
|
||||
label={i18n._(t`Workflow pending message`)}
|
||||
mode="jinja2"
|
||||
rows={2}
|
||||
/>
|
||||
)}
|
||||
{showBodies && (
|
||||
<CodeMirrorField
|
||||
id="wf-running-body"
|
||||
name="messages.workflow_approval.running.body"
|
||||
label={i18n._(t`Workflow pending message body`)}
|
||||
mode="jinja2"
|
||||
rows={6}
|
||||
/>
|
||||
)}
|
||||
{showMessages && (
|
||||
<CodeMirrorField
|
||||
id="wf-timed-out-message"
|
||||
name="messages.workflow_approval.timed_out.message"
|
||||
label={i18n._(t`Workflow timed out message`)}
|
||||
mode="jinja2"
|
||||
rows={2}
|
||||
/>
|
||||
)}
|
||||
{showBodies && (
|
||||
<CodeMirrorField
|
||||
id="wf-timed-out-body"
|
||||
name="messages.workflow_approval.timed_out.body"
|
||||
label={i18n._(t`Workflow timed out message body`)}
|
||||
mode="jinja2"
|
||||
rows={6}
|
||||
/>
|
||||
)}
|
||||
</FormFullWidthLayout>
|
||||
</SubFormLayout>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default withI18n()(CustomMessagesSubForm);
|
||||
@ -1,22 +1,18 @@
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import React 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 { required } from '../../../util/validators';
|
||||
import { FormColumnLayout } from '../../../components/FormLayout';
|
||||
import TypeInputsSubForm from './TypeInputsSubForm';
|
||||
import CustomMessagesSubForm from './CustomMessagesSubForm';
|
||||
import typeFieldNames, { initialConfigValues } from './typeFieldNames';
|
||||
import { NotificationTemplate } from '../../../types';
|
||||
|
||||
@ -85,6 +81,10 @@ function NotificationTemplateFormFields({ i18n, defaultMessages }) {
|
||||
/>
|
||||
</FormGroup>
|
||||
{typeField.value && <TypeInputsSubForm type={typeField.value} />}
|
||||
<CustomMessagesSubForm
|
||||
defaultMessages={defaultMessages}
|
||||
type={typeField.value}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -105,6 +105,14 @@ function NotificationTemplateForm({
|
||||
if (template.notification_type === 'email') {
|
||||
emailOptions = template.notification_configuration.use_ssl ? 'ssl' : 'tls';
|
||||
}
|
||||
const messages = template.messages || { workflow_approval: {} };
|
||||
const defs = defaultMessages[template.notification_type || 'email'];
|
||||
const mergeDefaultMessages = (templ = {}, def) => {
|
||||
return {
|
||||
message: templ.message || def.message || '',
|
||||
body: templ.body || def.body || '',
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik
|
||||
@ -117,6 +125,38 @@ function NotificationTemplateForm({
|
||||
...template.notification_configuration,
|
||||
},
|
||||
emailOptions,
|
||||
messages: {
|
||||
started: { ...mergeDefaultMessages(messages.started, defs.started) },
|
||||
success: { ...mergeDefaultMessages(messages.success, defs.success) },
|
||||
error: { ...mergeDefaultMessages(messages.error, defs.error) },
|
||||
workflow_approval: {
|
||||
approved: {
|
||||
...mergeDefaultMessages(
|
||||
messages.workflow_approval.approved,
|
||||
defs.workflow_approval.approved
|
||||
),
|
||||
},
|
||||
denied: {
|
||||
...mergeDefaultMessages(
|
||||
messages.workflow_approval.denied,
|
||||
defs.workflow_approval.denied
|
||||
),
|
||||
},
|
||||
running: {
|
||||
...mergeDefaultMessages(
|
||||
messages.workflow_approval.running,
|
||||
defs.workflow_approval.running
|
||||
),
|
||||
},
|
||||
timed_out: {
|
||||
...mergeDefaultMessages(
|
||||
messages.workflow_approval.timed_out,
|
||||
defs.workflow_approval.timed_out
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
useCustomMessages: hasCustomMessages(messages, defs),
|
||||
}}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
@ -155,6 +195,42 @@ NotificationTemplateForm.defaultProps = {
|
||||
|
||||
export default withI18n()(NotificationTemplateForm);
|
||||
|
||||
function hasCustomMessages(messages, defaults) {
|
||||
return (
|
||||
isCustomized(messages.started, defaults.started) ||
|
||||
isCustomized(messages.success, defaults.success) ||
|
||||
isCustomized(messages.error, defaults.error) ||
|
||||
isCustomized(
|
||||
messages.workflow_approval.approved,
|
||||
defaults.workflow_approval.approved
|
||||
) ||
|
||||
isCustomized(
|
||||
messages.workflow_approval.denied,
|
||||
defaults.workflow_approval.denied
|
||||
) ||
|
||||
isCustomized(
|
||||
messages.workflow_approval.running,
|
||||
defaults.workflow_approval.running
|
||||
) ||
|
||||
isCustomized(
|
||||
messages.workflow_approval.timed_out,
|
||||
defaults.workflow_approval.timed_out
|
||||
)
|
||||
);
|
||||
}
|
||||
function isCustomized(message, defaultMessage) {
|
||||
if (!message) {
|
||||
return false;
|
||||
}
|
||||
if (!message.message || message.message !== defaultMessage.message) {
|
||||
return true;
|
||||
}
|
||||
if (!message.body || message.body !== defaultMessage.body) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If the user filled in some of the Type Details fields, then switched
|
||||
* to a different notification type, unecessary fields may be set in the
|
||||
* notification_configuration — this function strips them off */
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { useField, useFormikContext } from 'formik';
|
||||
import { useField } from 'formik';
|
||||
import { FormGroup, Title } from '@patternfly/react-core';
|
||||
import {
|
||||
FormColumnLayout,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user