Adds formik hook functionality to wfjt form

This commit is contained in:
Alex Corey
2020-04-02 15:37:40 -04:00
parent dbe949a2c2
commit 8b69b08991
3 changed files with 330 additions and 381 deletions

View File

@@ -12,7 +12,19 @@ function WorkflowJobTemplateAdd() {
const [formSubmitError, setFormSubmitError] = useState(null); const [formSubmitError, setFormSubmitError] = useState(null);
const handleSubmit = async values => { const handleSubmit = async values => {
const { labels, organizationId, ...remainingValues } = values; const {
labels,
inventory,
organization,
webhook_credential,
webhookKey,
...remainingValues
} = values;
remainingValues.inventory = inventory?.id;
remainingValues.organization = organization?.id;
remainingValues.webhook_credential = webhook_credential?.id;
const organizationId =
organization?.id || inventory?.summary_fields?.organization.id || null;
try { try {
const { const {
data: { id }, data: { id },

View File

@@ -11,10 +11,23 @@ function WorkflowJobTemplateEdit({ template, webhook_key }) {
const [formSubmitError, setFormSubmitError] = useState(null); const [formSubmitError, setFormSubmitError] = useState(null);
const handleSubmit = async values => { const handleSubmit = async values => {
const { labels, ...remainingValues } = values; const {
labels,
inventory,
organization,
webhook_credential,
webhookKey,
...remainingValues
} = values;
remainingValues.inventory = inventory?.id;
remainingValues.organization = organization?.id;
remainingValues.webhook_credential = webhook_credential?.id || null;
const formOrgId =
organization?.id || inventory?.summary_fields?.organization.id || null;
try { try {
await Promise.all( await Promise.all(
await submitLabels(labels, values.organization, template.organization) await submitLabels(labels, formOrgId, template.organization)
); );
await WorkflowJobTemplatesAPI.update(template.id, remainingValues); await WorkflowJobTemplatesAPI.update(template.id, remainingValues);
history.push(`/templates/workflow_job_template/${template.id}/details`); history.push(`/templates/workflow_job_template/${template.id}/details`);
@@ -60,7 +73,7 @@ function WorkflowJobTemplateEdit({ template, webhook_key }) {
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
handleCancel={handleCancel} handleCancel={handleCancel}
template={template} template={template}
webhook_key={webhook_key} webhookKey={webhook_key}
submitError={formSubmitError} submitError={formSubmitError}
/> />
</CardBody> </CardBody>

View File

@@ -1,11 +1,11 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { useRouteMatch, useParams } from 'react-router-dom'; import { useRouteMatch, useParams, withRouter } from 'react-router-dom';
import { func, shape } from 'prop-types'; import PropTypes, { shape } from 'prop-types';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { Formik, Field } from 'formik'; import { useField, withFormik } from 'formik';
import { import {
Form, Form,
FormGroup, FormGroup,
@@ -40,38 +40,49 @@ import ContentError from '@components/ContentError';
import CheckboxField from '@components/FormField/CheckboxField'; import CheckboxField from '@components/FormField/CheckboxField';
import LabelSelect from './LabelSelect'; import LabelSelect from './LabelSelect';
const urlOrigin = window.location.origin;
function WorkflowJobTemplateForm({ function WorkflowJobTemplateForm({
handleSubmit, handleSubmit,
handleCancel, handleCancel,
i18n, i18n,
template = {},
webhook_key,
submitError, submitError,
}) { }) {
const urlOrigin = window.location.origin;
const { id } = useParams(); const { id } = useParams();
const wfjtAddMatch = useRouteMatch('/templates/workflow_job_template/add'); const wfjtAddMatch = useRouteMatch('/templates/workflow_job_template/add');
const [hasContentError, setContentError] = useState(null); const [hasContentError, setContentError] = useState(null);
const [webhook_url, setWebhookUrl] = useState(
template?.related?.webhook_receiver const [organizationField, organizationMeta, organizationHelpers] = useField(
? `${urlOrigin}${template.related.webhook_receiver}` 'organization'
: ''
); );
const [inventory, setInventory] = useState( const [inventoryField, inventoryMeta, inventoryHelpers] = useField(
template?.summary_fields?.inventory || null 'inventory'
); );
const [organization, setOrganization] = useState( const [labelsField, , labelsHelpers] = useField('labels');
template?.summary_fields?.organization || null
const [
webhookServiceField,
webhookServiceMeta,
webhookServiceHelpers,
] = useField('webhook_service');
const [webhookKeyField, webhookKeyMeta, webhookKeyHelpers] = useField(
'webhookKey'
); );
const [webhookCredential, setWebhookCredential] = useState(
template?.summary_fields?.webhook_credential || null const [hasWebhooks, setHasWebhooks] = useState(
Boolean(webhookServiceField.value)
); );
const [webhookKey, setWebHookKey] = useState(webhook_key);
const [webhookService, setWebHookService] = useState( const [
template.webhook_service || '' webhookCredentialField,
webhookCredentialMeta,
webhookCredentialHelpers,
] = useField('webhook_credential');
const [webhookUrlField, webhookUrlMeta, webhookUrlHelpers] = useField(
'webhook_url'
); );
const [hasWebhooks, setHasWebhooks] = useState(Boolean(webhookService));
const webhookServiceOptions = [ const webhookServiceOptions = [
{ {
@@ -93,6 +104,38 @@ function WorkflowJobTemplateForm({
isDisabled: false, isDisabled: false,
}, },
]; ];
const storeWebhookValues = webhookServiceValue => {
if (
webhookServiceValue === webhookServiceMeta.initialValue ||
webhookServiceValue === ''
) {
webhookCredentialHelpers.setValue(webhookCredentialMeta.initialValue);
webhookUrlHelpers.setValue(webhookUrlMeta.initialValue);
webhookServiceHelpers.setValue(webhookServiceMeta.initialValue);
webhookKeyHelpers.setValue(webhookKeyMeta.initialValue);
} else {
webhookCredentialHelpers.setValue(null);
webhookUrlHelpers.setValue(
`${urlOrigin}/api/v2/workflow_job_templates/${id}/${webhookServiceValue}/`
);
webhookKeyHelpers.setValue(
i18n._(t`a new webhook key will be generated on save.`).toUpperCase()
);
}
};
const handleWebhookEnablement = (enabledWebhooks, webhookServiceValue) => {
if (!enabledWebhooks) {
webhookCredentialHelpers.setValue(null);
webhookServiceHelpers.setValue('');
webhookUrlHelpers.setValue('');
webhookKeyHelpers.setValue('');
} else {
storeWebhookValues(webhookServiceValue);
}
};
const { const {
request: loadCredentialType, request: loadCredentialType,
error: contentError, error: contentError,
@@ -101,15 +144,15 @@ function WorkflowJobTemplateForm({
} = useRequest( } = useRequest(
useCallback(async () => { useCallback(async () => {
let results; let results;
if (webhookService) { if (webhookServiceField.value) {
results = await CredentialTypesAPI.read({ results = await CredentialTypesAPI.read({
namespace: `${webhookService}_token`, namespace: `${webhookServiceField.value}_token`,
}); });
// TODO: Consider how to handle the situation where the results returns // TODO: Consider how to handle the situation where the results returns
// and empty array, or any of the other values is undefined or null (data, results, id) // and empty array, or any of the other values is undefined or null (data, results, id)
} }
return results?.data?.results[0]?.id; return results?.data?.results[0]?.id;
}, [webhookService]) }, [webhookServiceField.value])
); );
useEffect(() => { useEffect(() => {
@@ -124,66 +167,12 @@ function WorkflowJobTemplateForm({
const { const {
data: { webhook_key: key }, data: { webhook_key: key },
} = await WorkflowJobTemplatesAPI.updateWebhookKey(id); } = await WorkflowJobTemplatesAPI.updateWebhookKey(id);
setWebHookKey(key); webhookKeyHelpers.setValue(key);
} catch (err) { } catch (err) {
setContentError(err); setContentError(err);
} }
}; };
let initialWebhookKey = webhook_key;
const initialWebhookCredential = template?.summary_fields?.webhook_credential;
const storeWebhookValues = (form, webhookServiceValue) => {
if (
webhookServiceValue === form.initialValues.webhook_service ||
webhookServiceValue === ''
) {
form.setFieldValue(
'webhook_credential',
form.initialValues.webhook_credential
);
setWebhookCredential(initialWebhookCredential);
setWebhookUrl(
template?.related?.webhook_receiver
? `${urlOrigin}${template.related.webhook_receiver}`
: ''
);
form.setFieldValue('webhook_service', form.initialValues.webhook_service);
setWebHookService(form.initialValues.webhook_service);
setWebHookKey(initialWebhookKey);
} else {
form.setFieldValue('webhook_credential', null);
setWebhookCredential(null);
setWebhookUrl(
`${urlOrigin}/api/v2/workflow_job_templates/${template.id}/${webhookServiceValue}/`
);
setWebHookKey(
i18n._(t`a new webhook key will be generated on save.`).toUpperCase()
);
}
};
const handleWebhookEnablement = (
form,
enabledWebhooks,
webhookServiceValue
) => {
if (!enabledWebhooks) {
initialWebhookKey = webhookKey;
form.setFieldValue('webhook_credential', null);
form.setFieldValue('webhook_service', '');
setWebhookUrl('');
setWebHookService('');
setWebHookKey('');
} else {
storeWebhookValues(form, webhookServiceValue);
}
};
if (hasContentError || contentError) { if (hasContentError || contentError) {
return <ContentError error={contentError || hasContentError} />; return <ContentError error={contentError || hasContentError} />;
} }
@@ -193,312 +182,213 @@ function WorkflowJobTemplateForm({
} }
return ( return (
<Formik <Form autoComplete="off" onSubmit={handleSubmit}>
onSubmit={values => { <FormColumnLayout>
if (values.webhook_service === '') { <FormField
values.webhook_credential = ''; id="wfjt-name"
} name="name"
return handleSubmit(values); type="text"
}} label={i18n._(t`Name`)}
initialValues={{ validate={required(null, i18n)}
name: template.name || '', isRequired
description: template.description || '', />
inventory: template?.summary_fields?.inventory?.id || null, <FormField
organization: template?.summary_fields?.organization?.id || null, id="wfjt-description"
labels: template.summary_fields?.labels?.results || [], name="description"
extra_vars: template.extra_vars || '---', type="text"
limit: template.limit || '', label={i18n._(t`Description`)}
scm_branch: template.scm_branch || '', />
allow_simultaneous: template.allow_simultaneous || false, <OrganizationLookup
webhook_credential: helperTextInvalid={organizationMeta.error}
template?.summary_fields?.webhook_credential?.id || null, onChange={value => {
webhook_service: template.webhook_service || '', organizationHelpers.setValue(value || null);
ask_limit_on_launch: template.ask_limit_on_launch || false, }}
ask_inventory_on_launch: template.ask_inventory_on_launch || false, value={organizationField.value}
ask_variables_on_launch: template.ask_variables_on_launch || false, isValid={!organizationMeta.error}
ask_scm_branch_on_launch: template.ask_scm_branch_on_launch || false, />
}} <FormGroup label={i18n._(t`Inventory`)} fieldId="wfjt-inventory">
> <FieldTooltip
{formik => ( content={i18n._(
<Form autoComplete="off" onSubmit={formik.handleSubmit}> t`Select an inventory for the workflow. This inventory is applied to all job template nodes that prompt for an inventory.`
<FormColumnLayout> )}
<FormField />
id="wfjt-name" <InventoryLookup
name="name" value={inventoryField.value}
type="text" isValid={!inventoryMeta.error}
label={i18n._(t`Name`)} helperTextInvalid={inventoryMeta.error}
validate={required(null, i18n)} onChange={value => {
isRequired inventoryHelpers.setValue(value || null);
/> }}
<FormField />
id="wfjt-description" </FormGroup>
name="description" <FormField
type="text" type="text"
label={i18n._(t`Description`)} name="limit"
/> id="wfjt-limit"
<Field label={i18n._(t`Limit`)}
id="wfjt-organization" tooltip={i18n._(
label={i18n._(t`Organization`)} t`Provide a host pattern to further constrain the list of hosts that will be managed or affected by the workflow. This limit is applied to all job template nodes that prompt for a limit. Refer to Ansible documentation for more information and examples on patterns.`
name="organization" )}
> />
{({ form }) => ( <FormField
<OrganizationLookup type="text"
helperTextInvalid={form.errors.organization} label={i18n._(t`SCM Branch`)}
onChange={value => { tooltip={i18n._(
form.setFieldValue('organization', value?.id || null); t`Select a branch for the workflow. This branch is applied to all job template nodes that prompt for a branch.`
setOrganization(value); )}
}} id="wfjt-scm_branch"
value={organization} name="scm_branch"
isValid={!form.errors.organization} />
/> </FormColumnLayout>
)} <FormFullWidthLayout>
</Field> <FormGroup label={i18n._(t`Labels`)} fieldId="template-labels">
<Field name="inventory"> <FieldTooltip
{({ form }) => ( content={i18n._(t`Optional labels that describe this job template,
<FormGroup
label={i18n._(t`Inventory`)}
fieldId="wfjt-inventory"
>
<FieldTooltip
content={i18n._(
t`Select an inventory for the workflow. This inventory is applied to all job template nodes that prompt for an inventory.`
)}
/>
<InventoryLookup
value={inventory}
isValid={!form.errors.inventory}
helperTextInvalid={form.errors.inventory}
onChange={value => {
form.setFieldValue('inventory', value?.id || null);
setInventory(value);
form.setFieldValue('organizationId', value?.organization);
}}
/>
</FormGroup>
)}
</Field>
<FormField
type="text"
name="limit"
id="wfjt-limit"
label={i18n._(t`Limit`)}
tooltip={i18n._(
t`Provide a host pattern to further constrain the list of hosts that will be managed or affected by the workflow. This limit is applied to all job template nodes that prompt for a limit. Refer to Ansible documentation for more information and examples on patterns.`
)}
/>
<FormField
type="text"
label={i18n._(t`Source Control Branch`)}
tooltip={i18n._(
t`Select a branch for the workflow. This branch is applied to all job template nodes that prompt for a branch.`
)}
id="wfjt-scm_branch"
name="scm_branch"
/>
</FormColumnLayout>
<FormFullWidthLayout>
<Field name="labels">
{({ form, field }) => (
<FormGroup
label={i18n._(t`Labels`)}
helperTextInvalid={form.errors.webhook_service}
isValid={!(form.touched.labels || form.errors.labels)}
name="wfjt-labels"
fieldId="wfjt-labels"
>
<FieldTooltip
content={i18n._(t`Optional labels that describe this job template,
such as 'dev' or 'test'. Labels can be used to group and filter such as 'dev' or 'test'. Labels can be used to group and filter
job templates and completed jobs.`)} job templates and completed jobs.`)}
/>
<LabelSelect
value={field.value}
onChange={labels => form.setFieldValue('labels', labels)}
onError={err => setContentError(err)}
/>
</FormGroup>
)}
</Field>
</FormFullWidthLayout>
<FormFullWidthLayout>
<VariablesField
id="wfjt-variables"
name="extra_vars"
label={i18n._(t`Variables`)}
tooltip={i18n._(
t`Pass extra command line variables to the playbook. This is the -e or --extra-vars command line parameter for ansible-playbook. Provide key/value pairs using either YAML or JSON. Refer to the Ansible Tower documentation for example syntax.`
)}
/>
</FormFullWidthLayout>
<FormCheckboxLayout
fieldId="options"
isInline
label={i18n._(t`Options`)}
>
<Field id="wfjt-webhooks" name="hasWebhooks">
{({ form }) => (
<Checkbox
aria-label={i18n._(t`Enable Webhooks`)}
label={
<span>
{i18n._(t`Enable Webhooks`)}
&nbsp;
<FieldTooltip
content={i18n._(
t`Enable webhooks for this workflow job template.`
)}
/>
</span>
}
id="wfjt-enabled-webhooks"
isChecked={
Boolean(form.values.webhook_service) || hasWebhooks
}
onChange={checked => {
setHasWebhooks(checked);
handleWebhookEnablement(form, checked, webhookService);
}}
/>
)}
</Field>
<CheckboxField
name="allow_simultaneous"
id="allow_simultaneous"
tooltip={i18n._(
t`If enabled, simultaneous runs of this workflow job template will be allowed.`
)}
label={i18n._(t`Enable Concurrent Jobs`)}
/>
</FormCheckboxLayout>
{hasWebhooks && (
<FormColumnLayout>
<Field name="webhook_service">
{({ form, field }) => (
<FormGroup
name="webhook_service"
fieldId="webhook_service"
helperTextInvalid={form.errors.webhook_service}
isValid={
!(
form.touched.webhook_service ||
form.errors.webhook_service
)
}
label={i18n._(t`Webhook Service`)}
>
<FieldTooltip
content={i18n._(t`Select a webhook service`)}
/>
<AnsibleSelect
id="webhook_service"
data={webhookServiceOptions}
value={field.value}
onChange={(event, val) => {
setWebHookService(val);
storeWebhookValues(form, val);
form.setFieldValue('webhook_service', val);
}}
/>
</FormGroup>
)}
</Field>
{!wfjtAddMatch && (
<>
<FormGroup
type="text"
fieldId="wfjt-webhookURL"
label={i18n._(t`Webhook URL`)}
id="wfjt-webhook-url"
name="webhook_url"
>
<FieldTooltip
content={i18n._(
t`Webhook services can launch jobs with this workflow job template by making a POST request to this URL.`
)}
/>
<TextInput
aria-label={i18n._(t`Webhook URL`)}
value={webhook_url}
isReadOnly
/>
</FormGroup>
<Field>
{({ form }) => (
<FormGroup
fieldId="wfjt-webhook-key"
type="text"
id="wfjt-webhook-key"
name="webhook_key"
isValid={
!(form.touched.webhook_key || form.errors.webhook_key)
}
helperTextInvalid={form.errors.webhook_service}
label={i18n._(t`Webhook Key`)}
>
<FieldTooltip
content={i18n._(
t`Webhook services can use this as a shared secret.`
)}
/>
<InputGroup>
<TextInput
isReadOnly
aria-label="wfjt-webhook-key"
value={webhookKey}
/>
<Button variant="tertiary" onClick={changeWebhookKey}>
<SyncAltIcon />
</Button>
</InputGroup>
</FormGroup>
)}
</Field>
</>
)}
{credTypeId && (
// TODO: Consider how to handle the situation where the results returns
// an empty array, or any of the other values is undefined or null
// (data, results, id)
<Field name="webhook_credential">
{({ form }) => (
<CredentialLookup
label={i18n._(t`Webhook Credential`)}
tooltip={i18n._(
t`Optionally select the credential to use to send status updates back to the webhook service.`
)}
credentialTypeId={credTypeId}
onChange={value => {
form.setFieldValue(
'webhook_credential',
value?.id || null
);
setWebhookCredential(value);
}}
isValid={!form.errors.webhook_credential}
helperTextInvalid={form.errors.webhook_credential}
value={webhookCredential}
/>
)}
</Field>
)}
</FormColumnLayout>
)}
{submitError && <FormSubmitError error={submitError} />}
<FormActionGroup
onCancel={handleCancel}
onSubmit={formik.handleSubmit}
/> />
</Form> <LabelSelect
value={labelsField.value}
onChange={labels => labelsHelpers.setValue(labels)}
onError={setContentError}
/>
</FormGroup>
</FormFullWidthLayout>
<FormFullWidthLayout>
<VariablesField
id="wfjt-variables"
name="extra_vars"
label={i18n._(t`Variables`)}
tooltip={i18n._(
t`Pass extra command line variables to the playbook. This is the -e or --extra-vars command line parameter for ansible-playbook. Provide key/value pairs using either YAML or JSON. Refer to the Ansible Tower documentation for example syntax.`
)}
/>
</FormFullWidthLayout>
<FormCheckboxLayout fieldId="options" isInline label={i18n._(t`Options`)}>
<Checkbox
aria-label={i18n._(t`Enable Webhook`)}
label={
<span>
{i18n._(t`Enable Webhook`)}
&nbsp;
<FieldTooltip
content={i18n._(
t`Enable webhook for this workflow job template.`
)}
/>
</span>
}
id="wfjt-enabled-webhooks"
isChecked={Boolean(webhookServiceField.value) || hasWebhooks}
onChange={checked => {
setHasWebhooks(checked);
handleWebhookEnablement(checked, webhookServiceField.value);
}}
/>
<CheckboxField
name="allow_simultaneous"
id="allow_simultaneous"
tooltip={i18n._(
t`If enabled, simultaneous runs of this workflow job template will be allowed.`
)}
label={i18n._(t`Enable Concurrent Jobs`)}
/>
</FormCheckboxLayout>
{hasWebhooks && (
<FormColumnLayout>
<FormGroup
name="webhook_service"
fieldId="webhook_service"
helperTextInvalid={webhookServiceMeta.error}
isValid={!(webhookServiceMeta.touched || webhookServiceMeta.error)}
label={i18n._(t`Webhook Service`)}
>
<FieldTooltip content={i18n._(t`Select a webhook service`)} />
<AnsibleSelect
id="webhook_service"
data={webhookServiceOptions}
value={webhookServiceField.value}
onChange={(event, val) => {
storeWebhookValues(val);
webhookServiceHelpers.setValue(val);
}}
/>
</FormGroup>
{!wfjtAddMatch && (
<>
<FormGroup
type="text"
fieldId="wfjt-webhookURL"
label={i18n._(t`Webhook URL`)}
id="wfjt-webhook-url"
name="webhook_url"
>
<FieldTooltip
content={i18n._(
t`Webhook services can launch jobs with this workflow job template by making a POST request to this URL.`
)}
/>
<TextInput
aria-label={i18n._(t`Webhook URL`)}
value={webhookUrlField.value}
isReadOnly
/>
</FormGroup>
<FormGroup
fieldId="wfjt-webhook-key"
type="text"
id="wfjt-webhook-key"
name="webhookKey"
label={i18n._(t`Webhook Key`)}
>
<FieldTooltip
content={i18n._(
t`Webhook services can use this as a shared secret.`
)}
/>
<InputGroup>
<TextInput
isReadOnly
aria-label="wfjt-webhook-key"
value={webhookKeyField.value}
/>
<Button variant="tertiary" onClick={changeWebhookKey}>
<SyncAltIcon />
</Button>
</InputGroup>
</FormGroup>
</>
)}
{credTypeId && (
// TODO: Consider how to handle the situation where the results returns
// an empty array, or any of the other values is undefined or null
// (data, results, id)
<CredentialLookup
label={i18n._(t`Webhook Credential`)}
tooltip={i18n._(
t`Optionally select the credential to use to send status updates back to the webhook service.`
)}
credentialTypeId={credTypeId}
onChange={value => {
webhookCredentialHelpers.setValue(value || null);
}}
isValid={!webhookCredentialMeta.error}
helperTextInvalid={webhookCredentialMeta.error}
value={webhookCredentialField.value}
/>
)}
</FormColumnLayout>
)} )}
</Formik> {submitError && <FormSubmitError error={submitError} />}
<FormActionGroup onCancel={handleCancel} onSubmit={handleSubmit} />
</Form>
); );
} }
WorkflowJobTemplateForm.propTypes = { WorkflowJobTemplateForm.propTypes = {
handleSubmit: func.isRequired, handleSubmit: PropTypes.func.isRequired,
handleCancel: func.isRequired, handleCancel: PropTypes.func.isRequired,
submitError: shape({}), submitError: shape({}),
}; };
@@ -506,4 +396,38 @@ WorkflowJobTemplateForm.defaultProps = {
submitError: null, submitError: null,
}; };
export default withI18n()(WorkflowJobTemplateForm); const FormikApp = withFormik({
mapPropsToValues({ template = {}, webhookKey }) {
return {
name: template.name || '',
description: template.description || '',
inventory: template?.summary_fields?.inventory || null,
organization: template?.summary_fields?.organization || null,
labels: template.summary_fields?.labels?.results || [],
extra_vars: template.extra_vars || '---',
limit: template.limit || '',
scm_branch: template.scm_branch || '',
allow_simultaneous: template.allow_simultaneous || false,
webhook_credential: template?.summary_fields?.webhook_credential || null,
webhook_service: template.webhook_service || '',
ask_limit_on_launch: template.ask_limit_on_launch || false,
ask_inventory_on_launch: template.ask_inventory_on_launch || false,
ask_variables_on_launch: template.ask_variables_on_launch || false,
ask_scm_branch_on_launch: template.ask_scm_branch_on_launch || false,
webhook_url: template?.related?.webhook_receiver
? `${urlOrigin}${template.related.webhook_receiver}`
: '',
webhookKey: webhookKey || null,
};
},
handleSubmit: async (values, { props, setErrors }) => {
try {
await props.handleSubmit(values);
} catch (errors) {
setErrors(errors);
}
},
})(WorkflowJobTemplateForm);
export { WorkflowJobTemplateForm as _WorkflowJobTemplateForm };
export default withI18n()(withRouter(FormikApp));