mirror of
https://github.com/ansible/awx.git
synced 2026-05-09 02:17:37 -02:30
Merge pull request #6385 from AlexSCorey/6317-ConvertJTFormstoFormikHooks
Uses formik hooks for JT Form Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -3,7 +3,7 @@ import { useHistory } from 'react-router-dom';
|
|||||||
import { Card, PageSection } from '@patternfly/react-core';
|
import { Card, PageSection } from '@patternfly/react-core';
|
||||||
import { CardBody } from '@components/Card';
|
import { CardBody } from '@components/Card';
|
||||||
import JobTemplateForm from '../shared/JobTemplateForm';
|
import JobTemplateForm from '../shared/JobTemplateForm';
|
||||||
import { JobTemplatesAPI, OrganizationsAPI } from '@api';
|
import { JobTemplatesAPI } from '@api';
|
||||||
|
|
||||||
function JobTemplateAdd() {
|
function JobTemplateAdd() {
|
||||||
const [formSubmitError, setFormSubmitError] = useState(null);
|
const [formSubmitError, setFormSubmitError] = useState(null);
|
||||||
@@ -12,7 +12,6 @@ function JobTemplateAdd() {
|
|||||||
async function handleSubmit(values) {
|
async function handleSubmit(values) {
|
||||||
const {
|
const {
|
||||||
labels,
|
labels,
|
||||||
organizationId,
|
|
||||||
instanceGroups,
|
instanceGroups,
|
||||||
initialInstanceGroups,
|
initialInstanceGroups,
|
||||||
credentials,
|
credentials,
|
||||||
@@ -20,12 +19,13 @@ function JobTemplateAdd() {
|
|||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
setFormSubmitError(null);
|
setFormSubmitError(null);
|
||||||
|
remainingValues.project = remainingValues.project.id;
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
data: { id, type },
|
data: { id, type },
|
||||||
} = await JobTemplatesAPI.create(remainingValues);
|
} = await JobTemplatesAPI.create(remainingValues);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
submitLabels(id, labels, organizationId),
|
submitLabels(id, labels, values.project.summary_fields.organization.id),
|
||||||
submitInstanceGroups(id, instanceGroups),
|
submitInstanceGroups(id, instanceGroups),
|
||||||
submitCredentials(id, credentials),
|
submitCredentials(id, credentials),
|
||||||
]);
|
]);
|
||||||
@@ -35,16 +35,7 @@ function JobTemplateAdd() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitLabels(templateId, labels = [], formOrg) {
|
async function submitLabels(templateId, labels = [], orgId) {
|
||||||
let orgId = formOrg;
|
|
||||||
|
|
||||||
if (!orgId && labels.length > 0) {
|
|
||||||
const {
|
|
||||||
data: { results },
|
|
||||||
} = await OrganizationsAPI.read();
|
|
||||||
orgId = results[0].id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const associationPromises = labels.map(label =>
|
const associationPromises = labels.map(label =>
|
||||||
JobTemplatesAPI.associateLabel(templateId, label, orgId)
|
JobTemplatesAPI.associateLabel(templateId, label, orgId)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const jobTemplateData = {
|
|||||||
limit: '',
|
limit: '',
|
||||||
name: '',
|
name: '',
|
||||||
playbook: '',
|
playbook: '',
|
||||||
project: 1,
|
project: { id: 1, summary_fields: { organization: { id: 1 } } },
|
||||||
scm_branch: '',
|
scm_branch: '',
|
||||||
skip_tags: '',
|
skip_tags: '',
|
||||||
timeout: 0,
|
timeout: 0,
|
||||||
@@ -123,6 +123,7 @@ describe('<JobTemplateAdd />', () => {
|
|||||||
wrapper.find('ProjectLookup').invoke('onChange')({
|
wrapper.find('ProjectLookup').invoke('onChange')({
|
||||||
id: 2,
|
id: 2,
|
||||||
name: 'project',
|
name: 'project',
|
||||||
|
summary_fields: { organization: { id: 1, name: 'Org Foo' } },
|
||||||
});
|
});
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
wrapper
|
wrapper
|
||||||
@@ -161,6 +162,7 @@ describe('<JobTemplateAdd />', () => {
|
|||||||
id: 1,
|
id: 1,
|
||||||
type: 'job_template',
|
type: 'job_template',
|
||||||
...jobTemplateData,
|
...jobTemplateData,
|
||||||
|
project: jobTemplateData.project.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
let wrapper;
|
let wrapper;
|
||||||
@@ -181,6 +183,7 @@ describe('<JobTemplateAdd />', () => {
|
|||||||
wrapper.find('ProjectLookup').invoke('onChange')({
|
wrapper.find('ProjectLookup').invoke('onChange')({
|
||||||
id: 2,
|
id: 2,
|
||||||
name: 'project',
|
name: 'project',
|
||||||
|
summary_fields: { organization: { id: 1, name: 'Org Foo' } },
|
||||||
});
|
});
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
wrapper
|
wrapper
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { withRouter, Redirect } from 'react-router-dom';
|
|||||||
import { CardBody } from '@components/Card';
|
import { CardBody } from '@components/Card';
|
||||||
import ContentError from '@components/ContentError';
|
import ContentError from '@components/ContentError';
|
||||||
import ContentLoading from '@components/ContentLoading';
|
import ContentLoading from '@components/ContentLoading';
|
||||||
import { JobTemplatesAPI, OrganizationsAPI, ProjectsAPI } from '@api';
|
import { JobTemplatesAPI, ProjectsAPI } from '@api';
|
||||||
import { JobTemplate } from '@types';
|
import { JobTemplate } from '@types';
|
||||||
import { getAddedAndRemoved } from '@util/lists';
|
import { getAddedAndRemoved } from '@util/lists';
|
||||||
import JobTemplateForm from '../shared/JobTemplateForm';
|
import JobTemplateForm from '../shared/JobTemplateForm';
|
||||||
@@ -97,7 +97,6 @@ class JobTemplateEdit extends Component {
|
|||||||
const { template, history } = this.props;
|
const { template, history } = this.props;
|
||||||
const {
|
const {
|
||||||
labels,
|
labels,
|
||||||
organizationId,
|
|
||||||
instanceGroups,
|
instanceGroups,
|
||||||
initialInstanceGroups,
|
initialInstanceGroups,
|
||||||
credentials,
|
credentials,
|
||||||
@@ -105,10 +104,14 @@ class JobTemplateEdit extends Component {
|
|||||||
} = values;
|
} = values;
|
||||||
|
|
||||||
this.setState({ formSubmitError: null });
|
this.setState({ formSubmitError: null });
|
||||||
|
remainingValues.project = values.project.id;
|
||||||
try {
|
try {
|
||||||
await JobTemplatesAPI.update(template.id, remainingValues);
|
await JobTemplatesAPI.update(template.id, remainingValues);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.submitLabels(labels, organizationId),
|
this.submitLabels(
|
||||||
|
labels,
|
||||||
|
values.project.summary_fields.organization.id
|
||||||
|
),
|
||||||
this.submitInstanceGroups(instanceGroups, initialInstanceGroups),
|
this.submitInstanceGroups(instanceGroups, initialInstanceGroups),
|
||||||
this.submitCredentials(credentials),
|
this.submitCredentials(credentials),
|
||||||
]);
|
]);
|
||||||
@@ -118,22 +121,14 @@ class JobTemplateEdit extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async submitLabels(labels = [], formOrgId) {
|
async submitLabels(labels = [], orgId) {
|
||||||
const { template } = this.props;
|
const { template } = this.props;
|
||||||
let orgId = formOrgId;
|
|
||||||
|
|
||||||
const { added, removed } = getAddedAndRemoved(
|
const { added, removed } = getAddedAndRemoved(
|
||||||
template.summary_fields.labels.results,
|
template.summary_fields.labels.results,
|
||||||
labels
|
labels
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!orgId && added.length > 0) {
|
|
||||||
const {
|
|
||||||
data: { results },
|
|
||||||
} = await OrganizationsAPI.read();
|
|
||||||
orgId = results[0].id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const disassociationPromises = removed.map(label =>
|
const disassociationPromises = removed.map(label =>
|
||||||
JobTemplatesAPI.disassociateLabel(template.id, label)
|
JobTemplatesAPI.disassociateLabel(template.id, label)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const mockJobTemplate = {
|
|||||||
limit: '',
|
limit: '',
|
||||||
name: 'Foo',
|
name: 'Foo',
|
||||||
playbook: 'Baz',
|
playbook: 'Baz',
|
||||||
project: 3,
|
project: { id: 3, summary_fields: { organization: { id: 1 } } },
|
||||||
scm_branch: '',
|
scm_branch: '',
|
||||||
skip_tags: '',
|
skip_tags: '',
|
||||||
summary_fields: {
|
summary_fields: {
|
||||||
@@ -239,6 +239,7 @@ describe('<JobTemplateEdit />', () => {
|
|||||||
|
|
||||||
const expected = {
|
const expected = {
|
||||||
...mockJobTemplate,
|
...mockJobTemplate,
|
||||||
|
project: mockJobTemplate.project.id,
|
||||||
...updatedTemplateData,
|
...updatedTemplateData,
|
||||||
};
|
};
|
||||||
delete expected.summary_fields;
|
delete expected.summary_fields;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { withFormik, Field } from 'formik';
|
import { withFormik, useField, useFormikContext } from 'formik';
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
@@ -47,11 +47,9 @@ function JobTemplateForm({
|
|||||||
validateField,
|
validateField,
|
||||||
handleCancel,
|
handleCancel,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
handleBlur,
|
|
||||||
setFieldValue,
|
setFieldValue,
|
||||||
submitError,
|
submitError,
|
||||||
i18n,
|
i18n,
|
||||||
touched,
|
|
||||||
}) {
|
}) {
|
||||||
const [contentError, setContentError] = useState(false);
|
const [contentError, setContentError] = useState(false);
|
||||||
const [project, setProject] = useState(null);
|
const [project, setProject] = useState(null);
|
||||||
@@ -62,6 +60,35 @@ function JobTemplateForm({
|
|||||||
Boolean(template?.host_config_key)
|
Boolean(template?.host_config_key)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { values: formikValues } = useFormikContext();
|
||||||
|
const [jobTypeField, jobTypeMeta, jobTypeHelpers] = useField({
|
||||||
|
name: 'job_type',
|
||||||
|
validate: required(null, i18n),
|
||||||
|
});
|
||||||
|
const [, inventoryMeta, inventoryHelpers] = useField('inventory');
|
||||||
|
const [projectField, projectMeta, projectHelpers] = useField({
|
||||||
|
name: 'project',
|
||||||
|
validate: () => handleProjectValidation(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const [scmField, , scmHelpers] = useField('scm_branch');
|
||||||
|
|
||||||
|
const [playbookField, playbookMeta, playbookHelpers] = useField({
|
||||||
|
name: 'playbook',
|
||||||
|
validate: required(i18n._(t`Select a value for this field`), i18n),
|
||||||
|
});
|
||||||
|
|
||||||
|
const [credentialField, , credentialHelpers] = useField('credentials');
|
||||||
|
const [labelsField, , labelsHelpers] = useField('labels');
|
||||||
|
const [limitField, limitMeta] = useField('limit');
|
||||||
|
const [verbosityField] = useField('verbosity');
|
||||||
|
const [diffModeField, , diffModeHelpers] = useField('diff_mode');
|
||||||
|
const [instanceGroupsField, , instanceGroupsHelpers] = useField(
|
||||||
|
'instanceGroups'
|
||||||
|
);
|
||||||
|
const [jobTagsField, , jobTagsHelpers] = useField('job_tags');
|
||||||
|
const [skipTagsField, , skipTagsHelpers] = useField('skip_tags');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
request: fetchProject,
|
request: fetchProject,
|
||||||
error: projectContentError,
|
error: projectContentError,
|
||||||
@@ -100,7 +127,7 @@ function JobTemplateForm({
|
|||||||
}, [loadRelatedInstanceGroups]);
|
}, [loadRelatedInstanceGroups]);
|
||||||
|
|
||||||
const handleProjectValidation = () => {
|
const handleProjectValidation = () => {
|
||||||
if (!project && touched.project) {
|
if (!project && projectMeta.touched) {
|
||||||
return i18n._(t`Select a value for this field`);
|
return i18n._(t`Select a value for this field`);
|
||||||
}
|
}
|
||||||
if (project && project.status === 'never updated') {
|
if (project && project.status === 'never updated') {
|
||||||
@@ -112,11 +139,11 @@ function JobTemplateForm({
|
|||||||
const handleProjectUpdate = useCallback(
|
const handleProjectUpdate = useCallback(
|
||||||
newProject => {
|
newProject => {
|
||||||
setProject(newProject);
|
setProject(newProject);
|
||||||
setFieldValue('project', newProject.id);
|
projectHelpers.setValue(newProject);
|
||||||
setFieldValue('playbook', 0);
|
playbookHelpers.setValue(0);
|
||||||
setFieldValue('scm_branch', '');
|
scmHelpers.setValue('');
|
||||||
},
|
},
|
||||||
[setFieldValue, setProject]
|
[setProject, projectHelpers, playbookHelpers, scmHelpers]
|
||||||
);
|
);
|
||||||
|
|
||||||
const jobTypeOptions = [
|
const jobTypeOptions = [
|
||||||
@@ -184,26 +211,15 @@ function JobTemplateForm({
|
|||||||
test environment setup, and report problems without
|
test environment setup, and report problems without
|
||||||
executing the playbook.`)}
|
executing the playbook.`)}
|
||||||
>
|
>
|
||||||
<Field
|
<AnsibleSelect
|
||||||
name="job_type"
|
{...jobTypeField}
|
||||||
validate={required(null, i18n)}
|
isValid={!jobTypeMeta.touched || !jobTypeMeta.error}
|
||||||
onBlur={handleBlur}
|
id="template-job-type"
|
||||||
>
|
data={jobTypeOptions}
|
||||||
{({ form, field }) => {
|
onChange={(event, value) => {
|
||||||
const isValid = !form.touched.job_type || !form.errors.job_type;
|
jobTypeHelpers.setValue(value);
|
||||||
return (
|
|
||||||
<AnsibleSelect
|
|
||||||
isValid={isValid}
|
|
||||||
id="template-job-type"
|
|
||||||
data={jobTypeOptions}
|
|
||||||
{...field}
|
|
||||||
onChange={(event, value) => {
|
|
||||||
form.setFieldValue('job_type', value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
</Field>
|
/>
|
||||||
</FieldWithPrompt>
|
</FieldWithPrompt>
|
||||||
<FieldWithPrompt
|
<FieldWithPrompt
|
||||||
fieldId="template-inventory"
|
fieldId="template-inventory"
|
||||||
@@ -214,108 +230,71 @@ function JobTemplateForm({
|
|||||||
tooltip={i18n._(t`Select the inventory containing the hosts
|
tooltip={i18n._(t`Select the inventory containing the hosts
|
||||||
you want this job to manage.`)}
|
you want this job to manage.`)}
|
||||||
>
|
>
|
||||||
<Field name="inventory">
|
<InventoryLookup
|
||||||
{({ form }) => (
|
value={inventory}
|
||||||
<>
|
onBlur={() => inventoryHelpers.setTouched()}
|
||||||
<InventoryLookup
|
onChange={value => {
|
||||||
value={inventory}
|
inventoryHelpers.setValue(value.id);
|
||||||
onBlur={() => {
|
setInventory(value);
|
||||||
form.setFieldTouched('inventory');
|
}}
|
||||||
}}
|
required
|
||||||
onChange={value => {
|
touched={inventoryMeta.touched}
|
||||||
form.setValues({
|
error={inventoryMeta.error}
|
||||||
...form.values,
|
/>
|
||||||
inventory: value.id,
|
{(inventoryMeta.touched || formikValues.ask_inventory_on_launch) &&
|
||||||
organizationId: value.organization,
|
inventoryMeta.error && (
|
||||||
});
|
<div
|
||||||
setInventory(value);
|
className="pf-c-form__helper-text pf-m-error"
|
||||||
}}
|
aria-live="polite"
|
||||||
required
|
>
|
||||||
touched={form.touched.inventory}
|
{inventoryMeta.error}
|
||||||
error={form.errors.inventory}
|
</div>
|
||||||
/>
|
|
||||||
{(form.touched.inventory ||
|
|
||||||
form.touched.ask_inventory_on_launch) &&
|
|
||||||
form.errors.inventory && (
|
|
||||||
<div
|
|
||||||
className="pf-c-form__helper-text pf-m-error"
|
|
||||||
aria-live="polite"
|
|
||||||
>
|
|
||||||
{form.errors.inventory}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</Field>
|
|
||||||
</FieldWithPrompt>
|
</FieldWithPrompt>
|
||||||
<Field name="project" validate={handleProjectValidation}>
|
|
||||||
{({ form }) => (
|
<ProjectLookup
|
||||||
<ProjectLookup
|
value={project}
|
||||||
value={project}
|
onBlur={() => projectHelpers.setTouched()}
|
||||||
onBlur={() => form.setFieldTouched('project')}
|
tooltip={i18n._(t`Select the project containing the playbook
|
||||||
tooltip={i18n._(t`Select the project containing the playbook
|
|
||||||
you want this job to execute.`)}
|
you want this job to execute.`)}
|
||||||
isValid={!form.touched.project || !form.errors.project}
|
isValid={!projectMeta.touched || !projectMeta.error}
|
||||||
helperTextInvalid={form.errors.project}
|
helperTextInvalid={projectMeta.error}
|
||||||
onChange={handleProjectUpdate}
|
onChange={handleProjectUpdate}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
)}
|
{project?.allow_override && (
|
||||||
</Field>
|
|
||||||
{project && project.allow_override && (
|
|
||||||
<FieldWithPrompt
|
<FieldWithPrompt
|
||||||
fieldId="template-scm-branch"
|
fieldId="template-scm-branch"
|
||||||
label={i18n._(t`SCM Branch`)}
|
label={i18n._(t`SCM Branch`)}
|
||||||
promptId="template-ask-scm-branch-on-launch"
|
promptId="template-ask-scm-branch-on-launch"
|
||||||
promptName="ask_scm_branch_on_launch"
|
promptName="ask_scm_branch_on_launch"
|
||||||
>
|
>
|
||||||
<Field name="scm_branch">
|
<TextInput
|
||||||
{({ field }) => {
|
id="template-scm-branch"
|
||||||
return (
|
{...scmField}
|
||||||
<TextInput
|
onChange={(value, event) => {
|
||||||
id="template-scm-branch"
|
scmField.onChange(event);
|
||||||
{...field}
|
|
||||||
onChange={(value, event) => {
|
|
||||||
field.onChange(event);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
</Field>
|
/>
|
||||||
</FieldWithPrompt>
|
</FieldWithPrompt>
|
||||||
)}
|
)}
|
||||||
<Field
|
<FormGroup
|
||||||
name="playbook"
|
fieldId="template-playbook"
|
||||||
validate={required(i18n._(t`Select a value for this field`), i18n)}
|
helperTextInvalid={playbookMeta.error}
|
||||||
onBlur={handleBlur}
|
isRequired
|
||||||
|
label={i18n._(t`Playbook`)}
|
||||||
>
|
>
|
||||||
{({ field, form }) => {
|
<FieldTooltip
|
||||||
const isValid = !form.touched.playbook || !form.errors.playbook;
|
content={i18n._(t`Select the playbook to be executed by this job.`)}
|
||||||
return (
|
/>
|
||||||
<FormGroup
|
<PlaybookSelect
|
||||||
fieldId="template-playbook"
|
projectId={project?.id || projectField.value?.id}
|
||||||
helperTextInvalid={form.errors.playbook}
|
isValid={!(playbookMeta.touched || playbookMeta.error)}
|
||||||
isRequired
|
field={playbookField}
|
||||||
isValid={isValid}
|
onBlur={() => playbookHelpers.setTouched()}
|
||||||
label={i18n._(t`Playbook`)}
|
onError={setContentError}
|
||||||
>
|
/>
|
||||||
<FieldTooltip
|
</FormGroup>
|
||||||
content={i18n._(
|
|
||||||
t`Select the playbook to be executed by this job.`
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<PlaybookSelect
|
|
||||||
projectId={form.values.project}
|
|
||||||
isValid={isValid}
|
|
||||||
form={form}
|
|
||||||
field={field}
|
|
||||||
onBlur={() => form.setFieldTouched('playbook')}
|
|
||||||
onError={setContentError}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Field>
|
|
||||||
<FormFullWidthLayout>
|
<FormFullWidthLayout>
|
||||||
<FieldWithPrompt
|
<FieldWithPrompt
|
||||||
fieldId="template-credentials"
|
fieldId="template-credentials"
|
||||||
@@ -328,36 +307,26 @@ function JobTemplateForm({
|
|||||||
credential at run time. If you select credentials and check "Prompt on launch", the selected
|
credential at run time. If you select credentials and check "Prompt on launch", the selected
|
||||||
credential(s) become the defaults that can be updated at run time.`)}
|
credential(s) become the defaults that can be updated at run time.`)}
|
||||||
>
|
>
|
||||||
<Field name="credentials" fieldId="template-credentials">
|
<MultiCredentialsLookup
|
||||||
{({ field }) => {
|
value={credentialField.value}
|
||||||
return (
|
onChange={newCredentials =>
|
||||||
<MultiCredentialsLookup
|
credentialHelpers.setValue(newCredentials)
|
||||||
value={field.value}
|
}
|
||||||
onChange={newCredentials =>
|
onError={setContentError}
|
||||||
setFieldValue('credentials', newCredentials)
|
/>
|
||||||
}
|
|
||||||
onError={setContentError}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Field>
|
|
||||||
</FieldWithPrompt>
|
</FieldWithPrompt>
|
||||||
<Field name="labels">
|
<FormGroup label={i18n._(t`Labels`)} fieldId="template-labels">
|
||||||
{({ field }) => (
|
<FieldTooltip
|
||||||
<FormGroup label={i18n._(t`Labels`)} fieldId="template-labels">
|
content={i18n._(t`Optional labels that describe this job template,
|
||||||
<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
|
<LabelSelect
|
||||||
value={field.value}
|
value={labelsField.value}
|
||||||
onChange={labels => setFieldValue('labels', labels)}
|
onChange={labels => labelsHelpers.setValue(labels)}
|
||||||
onError={setContentError}
|
onError={setContentError}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
<VariablesField
|
<VariablesField
|
||||||
id="template-variables"
|
id="template-variables"
|
||||||
name="extra_vars"
|
name="extra_vars"
|
||||||
@@ -394,20 +363,14 @@ function JobTemplateForm({
|
|||||||
playbook. Multiple patterns are allowed. Refer to Ansible
|
playbook. Multiple patterns are allowed. Refer to Ansible
|
||||||
documentation for more information and examples on patterns.`)}
|
documentation for more information and examples on patterns.`)}
|
||||||
>
|
>
|
||||||
<Field name="limit">
|
<TextInput
|
||||||
{({ form, field }) => {
|
id="template-limit"
|
||||||
return (
|
{...limitField}
|
||||||
<TextInput
|
isValid={!limitMeta.touched || !limitMeta.error}
|
||||||
id="template-limit"
|
onChange={(value, event) => {
|
||||||
{...field}
|
limitField.onChange(event);
|
||||||
isValid={!form.touched.job_type || !form.errors.job_type}
|
|
||||||
onChange={(value, event) => {
|
|
||||||
field.onChange(event);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
</Field>
|
/>
|
||||||
</FieldWithPrompt>
|
</FieldWithPrompt>
|
||||||
<FieldWithPrompt
|
<FieldWithPrompt
|
||||||
fieldId="template-verbosity"
|
fieldId="template-verbosity"
|
||||||
@@ -417,15 +380,11 @@ function JobTemplateForm({
|
|||||||
tooltip={i18n._(t`Control the level of output ansible will
|
tooltip={i18n._(t`Control the level of output ansible will
|
||||||
produce as the playbook executes.`)}
|
produce as the playbook executes.`)}
|
||||||
>
|
>
|
||||||
<Field name="verbosity">
|
<AnsibleSelect
|
||||||
{({ field }) => (
|
id="template-verbosity"
|
||||||
<AnsibleSelect
|
data={verbosityOptions}
|
||||||
id="template-verbosity"
|
{...verbosityField}
|
||||||
data={verbosityOptions}
|
/>
|
||||||
{...field}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</FieldWithPrompt>
|
</FieldWithPrompt>
|
||||||
<FormField
|
<FormField
|
||||||
id="template-job-slicing"
|
id="template-job-slicing"
|
||||||
@@ -456,32 +415,20 @@ function JobTemplateForm({
|
|||||||
Ansible tasks, where supported. This is equivalent
|
Ansible tasks, where supported. This is equivalent
|
||||||
to Ansible’s --diff mode.`)}
|
to Ansible’s --diff mode.`)}
|
||||||
>
|
>
|
||||||
<Field name="diff_mode">
|
<Switch
|
||||||
{({ form, field }) => {
|
id="template-show-changes"
|
||||||
return (
|
label={diffModeField.value ? i18n._(t`On`) : i18n._(t`Off`)}
|
||||||
<Switch
|
isChecked={diffModeField.value}
|
||||||
id="template-show-changes"
|
onChange={checked => diffModeHelpers.setValue(checked)}
|
||||||
label={field.value ? i18n._(t`On`) : i18n._(t`Off`)}
|
/>
|
||||||
isChecked={field.value}
|
|
||||||
onChange={checked =>
|
|
||||||
form.setFieldValue(field.name, checked)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Field>
|
|
||||||
</FieldWithPrompt>
|
</FieldWithPrompt>
|
||||||
<FormFullWidthLayout>
|
<FormFullWidthLayout>
|
||||||
<Field name="instanceGroups">
|
<InstanceGroupsLookup
|
||||||
{({ field, form }) => (
|
value={instanceGroupsField.value}
|
||||||
<InstanceGroupsLookup
|
onChange={value => instanceGroupsHelpers.setValue(value)}
|
||||||
value={field.value}
|
tooltip={i18n._(t`Select the Instance Groups for this Organization
|
||||||
onChange={value => form.setFieldValue(field.name, value)}
|
|
||||||
tooltip={i18n._(t`Select the Instance Groups for this Organization
|
|
||||||
to run on.`)}
|
to run on.`)}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
<FieldWithPrompt
|
<FieldWithPrompt
|
||||||
fieldId="template-tags"
|
fieldId="template-tags"
|
||||||
label={i18n._(t`Job Tags`)}
|
label={i18n._(t`Job Tags`)}
|
||||||
@@ -493,14 +440,10 @@ function JobTemplateForm({
|
|||||||
Refer to Ansible Tower documentation for details on
|
Refer to Ansible Tower documentation for details on
|
||||||
the usage of tags.`)}
|
the usage of tags.`)}
|
||||||
>
|
>
|
||||||
<Field name="job_tags">
|
<TagMultiSelect
|
||||||
{({ field, form }) => (
|
value={jobTagsField.value}
|
||||||
<TagMultiSelect
|
onChange={value => jobTagsHelpers.setValue(value)}
|
||||||
value={field.value}
|
/>
|
||||||
onChange={value => form.setFieldValue(field.name, value)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</FieldWithPrompt>
|
</FieldWithPrompt>
|
||||||
<FieldWithPrompt
|
<FieldWithPrompt
|
||||||
fieldId="template-skip-tags"
|
fieldId="template-skip-tags"
|
||||||
@@ -513,14 +456,10 @@ function JobTemplateForm({
|
|||||||
to Ansible Tower documentation for details on the usage
|
to Ansible Tower documentation for details on the usage
|
||||||
of tags.`)}
|
of tags.`)}
|
||||||
>
|
>
|
||||||
<Field name="skip_tags">
|
<TagMultiSelect
|
||||||
{({ field, form }) => (
|
value={skipTagsField.value}
|
||||||
<TagMultiSelect
|
onChange={value => skipTagsHelpers.setValue(value)}
|
||||||
value={field.value}
|
/>
|
||||||
onChange={value => form.setFieldValue(field.name, value)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</FieldWithPrompt>
|
</FieldWithPrompt>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
fieldId="template-option-checkboxes"
|
fieldId="template-option-checkboxes"
|
||||||
@@ -636,10 +575,6 @@ const FormikApp = withFormik({
|
|||||||
},
|
},
|
||||||
} = template;
|
} = template;
|
||||||
|
|
||||||
const hasInventory = summary_fields.inventory
|
|
||||||
? summary_fields.inventory.organization_id
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ask_credential_on_launch: template.ask_credential_on_launch || false,
|
ask_credential_on_launch: template.ask_credential_on_launch || false,
|
||||||
ask_diff_mode_on_launch: template.ask_diff_mode_on_launch || false,
|
ask_diff_mode_on_launch: template.ask_diff_mode_on_launch || false,
|
||||||
@@ -655,7 +590,7 @@ const FormikApp = withFormik({
|
|||||||
description: template.description || '',
|
description: template.description || '',
|
||||||
job_type: template.job_type || 'run',
|
job_type: template.job_type || 'run',
|
||||||
inventory: template.inventory || null,
|
inventory: template.inventory || null,
|
||||||
project: template.project || '',
|
project: template.project || null,
|
||||||
scm_branch: template.scm_branch || '',
|
scm_branch: template.scm_branch || '',
|
||||||
playbook: template.playbook || '',
|
playbook: template.playbook || '',
|
||||||
labels: summary_fields.labels.results || [],
|
labels: summary_fields.labels.results || [],
|
||||||
@@ -672,7 +607,6 @@ const FormikApp = withFormik({
|
|||||||
allow_simultaneous: template.allow_simultaneous || false,
|
allow_simultaneous: template.allow_simultaneous || false,
|
||||||
use_fact_cache: template.use_fact_cache || false,
|
use_fact_cache: template.use_fact_cache || false,
|
||||||
host_config_key: template.host_config_key || '',
|
host_config_key: template.host_config_key || '',
|
||||||
organizationId: hasInventory,
|
|
||||||
initialInstanceGroups: [],
|
initialInstanceGroups: [],
|
||||||
instanceGroups: [],
|
instanceGroups: [],
|
||||||
credentials: summary_fields.credentials || [],
|
credentials: summary_fields.credentials || [],
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ describe('<JobTemplateForm />', () => {
|
|||||||
description: 'Bar',
|
description: 'Bar',
|
||||||
job_type: 'run',
|
job_type: 'run',
|
||||||
inventory: 2,
|
inventory: 2,
|
||||||
project: 3,
|
project: { id: 3, summary_fields: { organization: { id: 1 } } },
|
||||||
playbook: 'Baz',
|
playbook: 'Baz',
|
||||||
type: 'job_template',
|
type: 'job_template',
|
||||||
scm_branch: 'Foo',
|
scm_branch: 'Foo',
|
||||||
|
|||||||
@@ -5,15 +5,7 @@ import { t } from '@lingui/macro';
|
|||||||
import AnsibleSelect from '@components/AnsibleSelect';
|
import AnsibleSelect from '@components/AnsibleSelect';
|
||||||
import { ProjectsAPI } from '@api';
|
import { ProjectsAPI } from '@api';
|
||||||
|
|
||||||
function PlaybookSelect({
|
function PlaybookSelect({ projectId, isValid, field, onBlur, onError, i18n }) {
|
||||||
projectId,
|
|
||||||
isValid,
|
|
||||||
form,
|
|
||||||
field,
|
|
||||||
onBlur,
|
|
||||||
onError,
|
|
||||||
i18n,
|
|
||||||
}) {
|
|
||||||
const [options, setOptions] = useState([]);
|
const [options, setOptions] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -47,7 +39,6 @@ function PlaybookSelect({
|
|||||||
id="template-playbook"
|
id="template-playbook"
|
||||||
data={options}
|
data={options}
|
||||||
isValid={isValid}
|
isValid={isValid}
|
||||||
form={form}
|
|
||||||
{...field}
|
{...field}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export const JobTemplate = shape({
|
|||||||
inventory: number,
|
inventory: number,
|
||||||
job_type: oneOf(['run', 'check']),
|
job_type: oneOf(['run', 'check']),
|
||||||
playbook: string,
|
playbook: string,
|
||||||
project: number,
|
project: shape({}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Inventory = shape({
|
export const Inventory = shape({
|
||||||
|
|||||||
Reference in New Issue
Block a user