mirror of
https://github.com/ansible/awx.git
synced 2026-01-10 15:32:07 -03:30
Prepopulates job template form with related resource
This commit is contained in:
parent
11fbfc2063
commit
51ef1e808d
@ -34,8 +34,14 @@ const QS_CONFIG = getQSConfig('template', {
|
||||
order_by: 'name',
|
||||
});
|
||||
|
||||
function RelatedTemplateList({ searchParams, projectName = null }) {
|
||||
const { id: projectId } = useParams();
|
||||
const resources = {
|
||||
projects: 'project',
|
||||
inventories: 'inventory',
|
||||
credentials: 'credentials',
|
||||
};
|
||||
|
||||
function RelatedTemplateList({ searchParams, resourceName = null }) {
|
||||
const { id } = useParams();
|
||||
const location = useLocation();
|
||||
const { addToast, Toast, toastProps } = useToast();
|
||||
|
||||
@ -129,12 +135,19 @@ function RelatedTemplateList({ searchParams, projectName = null }) {
|
||||
actions && Object.prototype.hasOwnProperty.call(actions, 'POST');
|
||||
|
||||
let linkTo = '';
|
||||
|
||||
if (projectName) {
|
||||
const qs = encodeQueryString({
|
||||
project_id: projectId,
|
||||
project_name: projectName,
|
||||
});
|
||||
if (resourceName) {
|
||||
const queryString = {
|
||||
resource_id: id,
|
||||
resource_name: resourceName,
|
||||
resource_type: resources[location.pathname.split('/')[1]],
|
||||
resource_kind: null,
|
||||
};
|
||||
if (Array.isArray(resourceName)) {
|
||||
const [name, kind] = resourceName;
|
||||
queryString.resource_name = name;
|
||||
queryString.resource_kind = kind;
|
||||
}
|
||||
const qs = encodeQueryString(queryString);
|
||||
linkTo = `/templates/job_template/add/?${qs}`;
|
||||
} else {
|
||||
linkTo = '/templates/job_template/add';
|
||||
|
||||
@ -0,0 +1 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
@ -22,6 +22,16 @@ import { CredentialsAPI } from 'api';
|
||||
import CredentialDetail from './CredentialDetail';
|
||||
import CredentialEdit from './CredentialEdit';
|
||||
|
||||
const jobTemplateCredentialTypes = [
|
||||
'machine',
|
||||
'cloud',
|
||||
'net',
|
||||
'ssh',
|
||||
'vault',
|
||||
'kubernetes',
|
||||
'cryptography',
|
||||
];
|
||||
|
||||
function Credential({ setBreadcrumb }) {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
@ -75,13 +85,14 @@ function Credential({ setBreadcrumb }) {
|
||||
link: `/credentials/${id}/access`,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
];
|
||||
if (jobTemplateCredentialTypes.includes(credential?.kind)) {
|
||||
tabsArray.push({
|
||||
name: t`Job Templates`,
|
||||
link: `/credentials/${id}/job_templates`,
|
||||
id: 2,
|
||||
},
|
||||
];
|
||||
|
||||
});
|
||||
}
|
||||
let showCardHeader = true;
|
||||
|
||||
if (pathname.endsWith('edit') || pathname.endsWith('add')) {
|
||||
@ -133,6 +144,7 @@ function Credential({ setBreadcrumb }) {
|
||||
<Route key="job_templates" path="/credentials/:id/job_templates">
|
||||
<RelatedTemplateList
|
||||
searchParams={{ credentials__id: credential.id }}
|
||||
resourceName={[credential.name, credential.kind]}
|
||||
/>
|
||||
</Route>,
|
||||
<Route key="not-found" path="*">
|
||||
|
||||
@ -6,7 +6,8 @@ import {
|
||||
mountWithContexts,
|
||||
waitForElement,
|
||||
} from '../../../testUtils/enzymeHelpers';
|
||||
import mockCredential from './shared/data.scmCredential.json';
|
||||
import mockMachineCredential from './shared/data.machineCredential.json';
|
||||
import mockSCMCredential from './shared/data.scmCredential.json';
|
||||
import Credential from './Credential';
|
||||
|
||||
jest.mock('../../api');
|
||||
@ -21,13 +22,10 @@ jest.mock('react-router-dom', () => ({
|
||||
describe('<Credential />', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
test('initially renders user-based machine credential successfully', async () => {
|
||||
CredentialsAPI.readDetail.mockResolvedValueOnce({
|
||||
data: mockCredential,
|
||||
data: mockMachineCredential,
|
||||
});
|
||||
});
|
||||
|
||||
test('initially renders user-based credential successfully', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />);
|
||||
});
|
||||
@ -36,6 +34,18 @@ describe('<Credential />', () => {
|
||||
expect(wrapper.find('RoutedTabs li').length).toBe(4);
|
||||
});
|
||||
|
||||
test('initially renders user-based SCM credential successfully', async () => {
|
||||
CredentialsAPI.readDetail.mockResolvedValueOnce({
|
||||
data: mockSCMCredential,
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<Credential setBreadcrumb={() => {}} />);
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('Credential').length).toBe(1);
|
||||
expect(wrapper.find('RoutedTabs li').length).toBe(3);
|
||||
});
|
||||
|
||||
test('should render expected tabs', async () => {
|
||||
const expectedTabs = [
|
||||
'Back to Credentials',
|
||||
|
||||
@ -181,6 +181,7 @@ function Inventory({ setBreadcrumb }) {
|
||||
>
|
||||
<RelatedTemplateList
|
||||
searchParams={{ inventory__id: inventory.id }}
|
||||
resourceName={inventory.name}
|
||||
/>
|
||||
</Route>,
|
||||
<Route path="*" key="not-found">
|
||||
|
||||
@ -179,7 +179,7 @@ function Project({ setBreadcrumb }) {
|
||||
searchParams={{
|
||||
project__id: project.id,
|
||||
}}
|
||||
projectName={project.name}
|
||||
resourceName={project.name}
|
||||
/>
|
||||
</Route>
|
||||
{project?.scm_type && project.scm_type !== '' && (
|
||||
|
||||
@ -9,29 +9,31 @@ function JobTemplateAdd() {
|
||||
const [formSubmitError, setFormSubmitError] = useState(null);
|
||||
const history = useHistory();
|
||||
|
||||
const projectParams = {
|
||||
project_id: null,
|
||||
project_name: null,
|
||||
const resourceParams = {
|
||||
resource_id: null,
|
||||
resource_name: null,
|
||||
resource_type: null,
|
||||
resource_kind: null,
|
||||
};
|
||||
history.location.search
|
||||
.replace(/^\?/, '')
|
||||
.split('&')
|
||||
.map((s) => s.split('='))
|
||||
.forEach(([key, val]) => {
|
||||
if (!(key in projectParams)) {
|
||||
if (!(key in resourceParams)) {
|
||||
return;
|
||||
}
|
||||
projectParams[key] = decodeURIComponent(val);
|
||||
resourceParams[key] = decodeURIComponent(val);
|
||||
});
|
||||
|
||||
let projectValues = null;
|
||||
let resourceValues = null;
|
||||
|
||||
if (
|
||||
Object.values(projectParams).filter((item) => item !== null).length === 2
|
||||
) {
|
||||
projectValues = {
|
||||
id: projectParams.project_id,
|
||||
name: projectParams.project_name,
|
||||
if (history.location.search.includes('resource_id' && 'resource_name')) {
|
||||
resourceValues = {
|
||||
id: resourceParams.resource_id,
|
||||
name: resourceParams.resource_name,
|
||||
type: resourceParams.resource_type,
|
||||
kind: resourceParams.resource_kind, // refers to credential kind
|
||||
};
|
||||
}
|
||||
|
||||
@ -122,7 +124,7 @@ function JobTemplateAdd() {
|
||||
handleCancel={handleCancel}
|
||||
handleSubmit={handleSubmit}
|
||||
submitError={formSubmitError}
|
||||
projectValues={projectValues}
|
||||
resourceValues={resourceValues}
|
||||
isOverrideDisabledLookup
|
||||
/>
|
||||
</CardBody>
|
||||
|
||||
@ -274,9 +274,14 @@ describe('<JobTemplateAdd />', () => {
|
||||
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',
|
||||
'/templates/job_template/add?resource_id=6&resource_name=Demo%20Project&resource_type=project',
|
||||
],
|
||||
});
|
||||
ProjectsAPI.read.mockResolvedValueOnce({
|
||||
count: 1,
|
||||
results: [{ name: 'foo', id: 1, allow_override: true, organization: 1 }],
|
||||
});
|
||||
ProjectsAPI.readOptions.mockResolvedValueOnce({});
|
||||
let wrapper;
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<JobTemplateAdd />, {
|
||||
@ -284,8 +289,9 @@ describe('<JobTemplateAdd />', () => {
|
||||
});
|
||||
});
|
||||
await waitForElement(wrapper, 'EmptyStateBody', (el) => el.length === 0);
|
||||
|
||||
expect(wrapper.find('input#project').prop('value')).toEqual('Demo Project');
|
||||
expect(ProjectsAPI.readPlaybooks).toBeCalledWith('6');
|
||||
expect(ProjectsAPI.readPlaybooks).toBeCalledWith(6);
|
||||
});
|
||||
|
||||
test('should not call ProjectsAPI.readPlaybooks if there is no project', async () => {
|
||||
|
||||
@ -690,7 +690,7 @@ JobTemplateForm.defaultProps = {
|
||||
};
|
||||
|
||||
const FormikApp = withFormik({
|
||||
mapPropsToValues({ projectValues = {}, template = {} }) {
|
||||
mapPropsToValues({ resourceValues = null, template = {} }) {
|
||||
const {
|
||||
summary_fields = {
|
||||
labels: { results: [] },
|
||||
@ -698,7 +698,7 @@ const FormikApp = withFormik({
|
||||
},
|
||||
} = template;
|
||||
|
||||
return {
|
||||
const initialValues = {
|
||||
allow_callbacks: template.allow_callbacks || false,
|
||||
allow_simultaneous: template.allow_simultaneous || false,
|
||||
ask_credential_on_launch: template.ask_credential_on_launch || false,
|
||||
@ -739,7 +739,7 @@ const FormikApp = withFormik({
|
||||
playbook: template.playbook || '',
|
||||
prevent_instance_group_fallback:
|
||||
template.prevent_instance_group_fallback || false,
|
||||
project: summary_fields?.project || projectValues || null,
|
||||
project: summary_fields?.project || null,
|
||||
scm_branch: template.scm_branch || '',
|
||||
skip_tags: template.skip_tags || '',
|
||||
timeout: template.timeout || 0,
|
||||
@ -756,6 +756,24 @@ const FormikApp = withFormik({
|
||||
execution_environment:
|
||||
template.summary_fields?.execution_environment || null,
|
||||
};
|
||||
if (resourceValues !== null) {
|
||||
if (resourceValues.type === 'credentials') {
|
||||
initialValues[resourceValues.type] = [
|
||||
{
|
||||
id: parseInt(resourceValues.id, 10),
|
||||
name: resourceValues.name,
|
||||
kind: resourceValues.kind,
|
||||
},
|
||||
];
|
||||
} else {
|
||||
initialValues[resourceValues.type] = {
|
||||
id: parseInt(resourceValues.id, 10),
|
||||
name: resourceValues.name,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return initialValues;
|
||||
},
|
||||
handleSubmit: async (values, { props, setErrors }) => {
|
||||
try {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user