mirror of
https://github.com/ansible/awx.git
synced 2026-01-17 04:31:21 -03:30
Pre-fill project for job template from query params
Pre-fill project when creating JT from Project -> Job Templates List
This commit is contained in:
parent
39b8fd433b
commit
8095adb945
@ -8,6 +8,7 @@ import {
|
||||
oneOfType,
|
||||
shape,
|
||||
node,
|
||||
object,
|
||||
} from 'prop-types';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { useField } from 'formik';
|
||||
@ -222,7 +223,7 @@ Lookup.propTypes = {
|
||||
header: string,
|
||||
modalDescription: oneOfType([string, node]),
|
||||
onChange: func.isRequired,
|
||||
value: oneOfType([Item, arrayOf(Item)]),
|
||||
value: oneOfType([Item, arrayOf(Item), object]),
|
||||
multiple: bool,
|
||||
required: bool,
|
||||
onBlur: func,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { node, string, func, bool } from 'prop-types';
|
||||
import { node, string, func, bool, object, oneOfType } from 'prop-types';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { t } from '@lingui/macro';
|
||||
import { FormGroup } from '@patternfly/react-core';
|
||||
@ -184,7 +184,7 @@ ProjectLookup.propTypes = {
|
||||
onChange: func.isRequired,
|
||||
required: bool,
|
||||
tooltip: string,
|
||||
value: Project,
|
||||
value: oneOfType([Project, object]),
|
||||
isOverrideDisabled: bool,
|
||||
validate: func,
|
||||
fieldName: string,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useParams, useLocation } from 'react-router-dom';
|
||||
|
||||
import { t, Plural } from '@lingui/macro';
|
||||
import { Card } from '@patternfly/react-core';
|
||||
@ -14,7 +14,12 @@ import PaginatedTable, {
|
||||
ToolbarDeleteButton,
|
||||
getSearchableKeys,
|
||||
} from 'components/PaginatedTable';
|
||||
import { getQSConfig, parseQueryString, mergeParams } from 'util/qs';
|
||||
import {
|
||||
getQSConfig,
|
||||
parseQueryString,
|
||||
mergeParams,
|
||||
encodeQueryString,
|
||||
} from 'util/qs';
|
||||
import useWsTemplates from 'hooks/useWsTemplates';
|
||||
import useSelected from 'hooks/useSelected';
|
||||
import useExpanded from 'hooks/useExpanded';
|
||||
@ -29,7 +34,8 @@ const QS_CONFIG = getQSConfig('template', {
|
||||
order_by: 'name',
|
||||
});
|
||||
|
||||
function RelatedTemplateList({ searchParams }) {
|
||||
function RelatedTemplateList({ searchParams, projectName = null }) {
|
||||
const { id: projectId } = useParams();
|
||||
const location = useLocation();
|
||||
const { addToast, Toast, toastProps } = useToast();
|
||||
|
||||
@ -122,9 +128,18 @@ function RelatedTemplateList({ searchParams }) {
|
||||
const canAddJT =
|
||||
actions && Object.prototype.hasOwnProperty.call(actions, 'POST');
|
||||
|
||||
const addButton = (
|
||||
<ToolbarAddButton key="add" linkTo="/templates/job_template/add/" />
|
||||
);
|
||||
let linkTo = '';
|
||||
|
||||
if (projectName) {
|
||||
const qs = encodeQueryString({
|
||||
project_id: projectId,
|
||||
project_name: projectName,
|
||||
});
|
||||
linkTo = `/templates/job_template/add/?${qs}`;
|
||||
} else {
|
||||
linkTo = '/templates/job_template/add';
|
||||
}
|
||||
const addButton = <ToolbarAddButton key="add" linkTo={linkTo} />;
|
||||
|
||||
const deleteDetailsRequests = relatedResourceDeleteRequests.template(
|
||||
selected[0]
|
||||
|
||||
@ -174,7 +174,12 @@ function Project({ setBreadcrumb }) {
|
||||
</Route>
|
||||
)}
|
||||
<Route path="/projects/:id/job_templates">
|
||||
<RelatedTemplateList searchParams={{ project__id: project.id }} />
|
||||
<RelatedTemplateList
|
||||
searchParams={{
|
||||
project__id: project.id,
|
||||
}}
|
||||
projectName={project.name}
|
||||
/>
|
||||
</Route>
|
||||
{project?.scm_type && project.scm_type !== '' && (
|
||||
<Route path="/projects/:id/schedules">
|
||||
|
||||
@ -9,6 +9,32 @@ function JobTemplateAdd() {
|
||||
const [formSubmitError, setFormSubmitError] = useState(null);
|
||||
const history = useHistory();
|
||||
|
||||
const projectParams = {
|
||||
project_id: null,
|
||||
project_name: null,
|
||||
};
|
||||
history.location.search
|
||||
.replace(/^\?/, '')
|
||||
.split('&')
|
||||
.map((s) => s.split('='))
|
||||
.forEach(([key, val]) => {
|
||||
if (!(key in projectParams)) {
|
||||
return;
|
||||
}
|
||||
projectParams[key] = decodeURIComponent(val);
|
||||
});
|
||||
|
||||
let projectValues = null;
|
||||
|
||||
if (
|
||||
Object.values(projectParams).filter((item) => item !== null).length === 2
|
||||
) {
|
||||
projectValues = {
|
||||
id: projectParams.project_id,
|
||||
name: projectParams.project_name,
|
||||
};
|
||||
}
|
||||
|
||||
const handleSubmit = async (values) => {
|
||||
const {
|
||||
labels,
|
||||
@ -35,7 +61,11 @@ function JobTemplateAdd() {
|
||||
execution_environment: values.execution_environment?.id,
|
||||
});
|
||||
await Promise.all([
|
||||
submitLabels(id, values.project.summary_fields.organization.id, labels),
|
||||
submitLabels(
|
||||
id,
|
||||
values.project.summary_fields?.organization.id,
|
||||
labels
|
||||
),
|
||||
submitInstanceGroups(id, instanceGroups),
|
||||
submitCredentials(id, credentials),
|
||||
]);
|
||||
@ -92,6 +122,7 @@ function JobTemplateAdd() {
|
||||
handleCancel={handleCancel}
|
||||
handleSubmit={handleSubmit}
|
||||
submitError={formSubmitError}
|
||||
projectValues={projectValues}
|
||||
isOverrideDisabledLookup
|
||||
/>
|
||||
</CardBody>
|
||||
|
||||
@ -257,4 +257,33 @@ describe('<JobTemplateAdd />', () => {
|
||||
});
|
||||
expect(history.location.pathname).toEqual('/templates');
|
||||
});
|
||||
|
||||
test('should parse and pre-fill project field from query params', async () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: [
|
||||
'/templates/job_template/add/add?project_id=6&project_name=Demo%20Project',
|
||||
],
|
||||
});
|
||||
let wrapper;
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<JobTemplateAdd />, {
|
||||
context: { router: { history } },
|
||||
});
|
||||
});
|
||||
await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0);
|
||||
expect(wrapper.find('input#project').prop('value')).toEqual('Demo Project');
|
||||
expect(ProjectsAPI.readPlaybooks).toBeCalledWith('6');
|
||||
});
|
||||
|
||||
test('should not call ProjectsAPI.readPlaybooks if there is no project', async () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/templates/job_template/add'],
|
||||
});
|
||||
await act(async () =>
|
||||
mountWithContexts(<JobTemplateAdd />, {
|
||||
context: { router: history },
|
||||
})
|
||||
);
|
||||
expect(ProjectsAPI.readPlaybooks).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@ -392,60 +392,4 @@ describe('<JobTemplateEdit />', () => {
|
||||
'/templates/job_template/1/details'
|
||||
);
|
||||
});
|
||||
test('should not call ProjectsAPI.readPlaybooks if there is no project', async () => {
|
||||
const history = createMemoryHistory({});
|
||||
const noProjectTemplate = {
|
||||
id: 1,
|
||||
name: 'Foo',
|
||||
description: 'Bar',
|
||||
job_type: 'run',
|
||||
inventory: 2,
|
||||
playbook: 'Baz',
|
||||
type: 'job_template',
|
||||
forks: 0,
|
||||
limit: '',
|
||||
verbosity: '0',
|
||||
job_slice_count: 1,
|
||||
timeout: 0,
|
||||
job_tags: '',
|
||||
skip_tags: '',
|
||||
diff_mode: false,
|
||||
allow_callbacks: false,
|
||||
allow_simultaneous: false,
|
||||
use_fact_cache: false,
|
||||
host_config_key: '',
|
||||
summary_fields: {
|
||||
user_capabilities: {
|
||||
edit: true,
|
||||
},
|
||||
labels: {
|
||||
results: [
|
||||
{ name: 'Sushi', id: 1 },
|
||||
{ name: 'Major', id: 2 },
|
||||
],
|
||||
},
|
||||
inventory: {
|
||||
id: 2,
|
||||
name: 'Demo Inventory',
|
||||
organization_id: 1,
|
||||
},
|
||||
credentials: [
|
||||
{ id: 1, kind: 'cloud', name: 'Foo' },
|
||||
{ id: 2, kind: 'ssh', name: 'Bar' },
|
||||
],
|
||||
webhook_credential: {
|
||||
id: 7,
|
||||
name: 'webhook credential',
|
||||
kind: 'github_token',
|
||||
credential_type_id: 12,
|
||||
},
|
||||
},
|
||||
};
|
||||
await act(async () =>
|
||||
mountWithContexts(<JobTemplateEdit template={noProjectTemplate} />, {
|
||||
context: { router: { history } },
|
||||
})
|
||||
);
|
||||
expect(ProjectsAPI.readPlaybooks).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@ -64,7 +64,7 @@ function JobTemplateForm({
|
||||
Boolean(template?.host_config_key)
|
||||
);
|
||||
const [enableWebhooks, setEnableWebhooks] = useState(
|
||||
Boolean(template.webhook_service)
|
||||
Boolean(template?.webhook_service)
|
||||
);
|
||||
const isMounted = useIsMounted();
|
||||
const brandName = useBrandName();
|
||||
@ -646,7 +646,7 @@ JobTemplateForm.defaultProps = {
|
||||
};
|
||||
|
||||
const FormikApp = withFormik({
|
||||
mapPropsToValues({ template = {} }) {
|
||||
mapPropsToValues({ projectValues = {}, template = {} }) {
|
||||
const {
|
||||
summary_fields = {
|
||||
labels: { results: [] },
|
||||
@ -684,7 +684,7 @@ const FormikApp = withFormik({
|
||||
limit: template.limit || '',
|
||||
name: template.name || '',
|
||||
playbook: template.playbook || '',
|
||||
project: summary_fields?.project || null,
|
||||
project: summary_fields?.project || projectValues || null,
|
||||
scm_branch: template.scm_branch || '',
|
||||
skip_tags: template.skip_tags || '',
|
||||
timeout: template.timeout || 0,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user