diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.jsx
index 3656c73c02..e47e1fcf95 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.jsx
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.jsx
@@ -1,13 +1,22 @@
-import React, { useState } from 'react';
+import React, { useState, useCallback, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { Card, PageSection } from '@patternfly/react-core';
import { CardBody } from '../../../components/Card';
-import { WorkflowJobTemplatesAPI, OrganizationsAPI } from '../../../api';
+import {
+ WorkflowJobTemplatesAPI,
+ OrganizationsAPI,
+ UsersAPI,
+} from '../../../api';
import WorkflowJobTemplateForm from '../shared/WorkflowJobTemplateForm';
+import { useConfig } from '../../../contexts/Config';
+import useRequest from '../../../util/useRequest';
+import ContentError from '../../../components/ContentError';
+import ContentLoading from '../../../components/ContentLoading';
function WorkflowJobTemplateAdd() {
+ const { me = {} } = useConfig();
const history = useHistory();
const [formSubmitError, setFormSubmitError] = useState(null);
@@ -60,6 +69,33 @@ function WorkflowJobTemplateAdd() {
history.push(`/templates`);
};
+ const {
+ isLoading,
+ request: fetchUserRole,
+ result: { orgAdminResults, isOrgAdmin },
+ error: contentError,
+ } = useRequest(
+ useCallback(async () => {
+ const {
+ data: { results, count },
+ } = await UsersAPI.readAdminOfOrganizations(me?.id);
+ return { isOrgAdmin: count > 0, orgAdminResults: results };
+ }, [me.id]),
+ { isOrgAdmin: false, orgAdminResults: null }
+ );
+
+ useEffect(() => {
+ fetchUserRole();
+ }, [fetchUserRole]);
+
+ if (contentError) {
+ return ;
+ }
+
+ if (isLoading || !orgAdminResults) {
+ return ;
+ }
+
return (
@@ -68,6 +104,7 @@ function WorkflowJobTemplateAdd() {
handleCancel={handleCancel}
handleSubmit={handleSubmit}
submitError={formSubmitError}
+ isOrgAdmin={isOrgAdmin}
/>
diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.test.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.test.jsx
index 2cfd5e0093..6b09435fab 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.test.jsx
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateAdd/WorkflowJobTemplateAdd.test.jsx
@@ -7,8 +7,12 @@ import {
OrganizationsAPI,
LabelsAPI,
ExecutionEnvironmentsAPI,
+ UsersAPI,
} from '../../../api';
-import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
+import {
+ mountWithContexts,
+ waitForElement,
+} from '../../../../testUtils/enzymeHelpers';
import WorkflowJobTemplateAdd from './WorkflowJobTemplateAdd';
@@ -17,6 +21,7 @@ jest.mock('../../../api/models/Organizations');
jest.mock('../../../api/models/Labels');
jest.mock('../../../api/models/Inventories');
jest.mock('../../../api/models/ExecutionEnvironments');
+jest.mock('../../../api/models/Users');
describe('', () => {
let wrapper;
@@ -40,6 +45,10 @@ describe('', () => {
data: { results: [{ id: 1, name: 'Foo', image: 'localhost.com' }] },
});
+ UsersAPI.readAdminOfOrganizations.mockResolvedValue({
+ data: { count: 0, results: [] },
+ });
+
await act(async () => {
history = createMemoryHistory({
initialEntries: ['/templates/workflow_job_template/add'],
@@ -67,6 +76,7 @@ describe('', () => {
}
);
});
+ await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
});
});
afterEach(async () => {
diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.jsx
index dd47118316..96a0be82df 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.jsx
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.jsx
@@ -1,12 +1,21 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect, useCallback } from 'react';
import { useHistory } from 'react-router-dom';
import { CardBody } from '../../../components/Card';
import { getAddedAndRemoved } from '../../../util/lists';
-import { WorkflowJobTemplatesAPI, OrganizationsAPI } from '../../../api';
+import {
+ WorkflowJobTemplatesAPI,
+ OrganizationsAPI,
+ UsersAPI,
+} from '../../../api';
import { WorkflowJobTemplateForm } from '../shared';
+import { useConfig } from '../../../contexts/Config';
+import useRequest from '../../../util/useRequest';
+import ContentError from '../../../components/ContentError';
+import ContentLoading from '../../../components/ContentLoading';
function WorkflowJobTemplateEdit({ template }) {
+ const { me = {} } = useConfig();
const history = useHistory();
const [formSubmitError, setFormSubmitError] = useState(null);
@@ -69,6 +78,33 @@ function WorkflowJobTemplateEdit({ template }) {
history.push(`/templates/workflow_job_template/${template.id}/details`);
};
+ const {
+ isLoading,
+ request: fetchUserRole,
+ result: { orgAdminResults, isOrgAdmin },
+ error: contentError,
+ } = useRequest(
+ useCallback(async () => {
+ const {
+ data: { results, count },
+ } = await UsersAPI.readAdminOfOrganizations(me?.id);
+ return { isOrgAdmin: count > 0, orgAdminResults: results };
+ }, [me.id]),
+ { isOrgAdmin: false, orgAdminResults: null }
+ );
+
+ useEffect(() => {
+ fetchUserRole();
+ }, [fetchUserRole]);
+
+ if (contentError) {
+ return ;
+ }
+
+ if (isLoading || !orgAdminResults) {
+ return ;
+ }
+
return (
);
diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.test.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.test.jsx
index 1f760bb302..f85e2f213f 100644
--- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.test.jsx
+++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateEdit/WorkflowJobTemplateEdit.test.jsx
@@ -7,8 +7,12 @@ import {
OrganizationsAPI,
LabelsAPI,
ExecutionEnvironmentsAPI,
+ UsersAPI,
} from '../../../api';
-import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
+import {
+ mountWithContexts,
+ waitForElement,
+} from '../../../../testUtils/enzymeHelpers';
import WorkflowJobTemplateEdit from './WorkflowJobTemplateEdit';
jest.mock('../../../api/models/WorkflowJobTemplates');
@@ -16,6 +20,7 @@ jest.mock('../../../api/models/Labels');
jest.mock('../../../api/models/Organizations');
jest.mock('../../../api/models/Inventories');
jest.mock('../../../api/models/ExecutionEnvironments');
+jest.mock('../../../api/models/Users');
const mockTemplate = {
id: 6,
@@ -74,6 +79,10 @@ describe('', () => {
},
});
+ UsersAPI.readAdminOfOrganizations.mockResolvedValue({
+ data: { count: 1, results: [{ id: 1 }] },
+ });
+
await act(async () => {
history = createMemoryHistory({
initialEntries: ['/templates/workflow_job_template/6/edit'],
@@ -95,6 +104,7 @@ describe('', () => {
},
}
);
+ await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
});
});
@@ -241,6 +251,7 @@ describe('', () => {
}
);
});
+ await waitForElement(newWrapper, 'ContentLoading', el => el.length === 0);
OrganizationsAPI.read.mockRejectedValue({
response: {
config: {
diff --git a/awx/ui_next/src/screens/Template/shared/WorkflowJobTemplateForm.jsx b/awx/ui_next/src/screens/Template/shared/WorkflowJobTemplateForm.jsx
index 01fd9378c0..a1891d26f0 100644
--- a/awx/ui_next/src/screens/Template/shared/WorkflowJobTemplateForm.jsx
+++ b/awx/ui_next/src/screens/Template/shared/WorkflowJobTemplateForm.jsx
@@ -43,6 +43,7 @@ function WorkflowJobTemplateForm({
handleCancel,
i18n,
submitError,
+ isOrgAdmin,
}) {
const { setFieldValue } = useFormikContext();
const [enableWebhooks, setEnableWebhooks] = useState(
@@ -55,7 +56,9 @@ function WorkflowJobTemplateForm({
);
const [labelsField, , labelsHelpers] = useField('labels');
const [limitField, limitMeta, limitHelpers] = useField('limit');
- const [organizationField, organizationMeta] = useField('organization');
+ const [organizationField, organizationMeta, organizationHelpers] = useField(
+ 'organization'
+ );
const [scmField, , scmHelpers] = useField('scm_branch');
const [, webhookServiceMeta, webhookServiceHelpers] = useField(
'webhook_service'
@@ -119,9 +122,14 @@ function WorkflowJobTemplateForm({
/>
organizationHelpers.setTouched()}
onChange={onOrganizationChange}
value={organizationField.value}
- isValid={!organizationMeta.error}
+ touched={organizationMeta.touched}
+ error={organizationMeta.error}
+ required={isOrgAdmin}
+ autoPopulate={isOrgAdmin}
/>
<>
', () => {
expect(wrapper.length).toBe(1);
});
+ test('organization is a required field for organization admins', async () => {
+ await act(async () => {
+ wrapper = mountWithContexts(
+ (
+
+ )}
+ />,
+ {
+ context: {
+ router: {
+ history,
+ route: {
+ location: history.location,
+ match: { params: { id: 6 } },
+ },
+ },
+ },
+ }
+ );
+ });
+
+ wrapper.update();
+ expect(
+ wrapper.find('FormGroup[label="Organization"]').prop('isRequired')
+ ).toBeTruthy();
+ });
+
test('all the fields render successfully', () => {
const fields = [
'FormField[name="name"]',
@@ -140,6 +174,9 @@ describe('', () => {
expect(wrapper.find(`${field}`).length).toBe(1);
};
fields.map((field, index) => assertField(field, index));
+ expect(
+ wrapper.find('FormGroup[label="Organization"]').prop('isRequired')
+ ).toBeFalsy();
});
test('changing inputs should update values', async () => {