Merge pull request #9710 from nixocio/ui_issue_6663

Make Org a required field for org admins on WFJT

Make Org a required field for org admins on WFJT.
See: #6663

Reviewed-by: Alex Corey <Alex.swansboro@gmail.com>
Reviewed-by: Tiago Góes <tiago.goes2009@gmail.com>
This commit is contained in:
softwarefactory-project-zuul[bot]
2021-04-06 20:32:51 +00:00
committed by GitHub
6 changed files with 150 additions and 8 deletions

View File

@@ -1,13 +1,22 @@
import React, { useState } from 'react'; import React, { useState, useCallback, useEffect } from 'react';
import { useHistory } from 'react-router-dom'; 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 { WorkflowJobTemplatesAPI, OrganizationsAPI } from '../../../api'; import {
WorkflowJobTemplatesAPI,
OrganizationsAPI,
UsersAPI,
} from '../../../api';
import WorkflowJobTemplateForm from '../shared/WorkflowJobTemplateForm'; 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() { function WorkflowJobTemplateAdd() {
const { me = {} } = useConfig();
const history = useHistory(); const history = useHistory();
const [formSubmitError, setFormSubmitError] = useState(null); const [formSubmitError, setFormSubmitError] = useState(null);
@@ -60,6 +69,33 @@ function WorkflowJobTemplateAdd() {
history.push(`/templates`); 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 <ContentError error={contentError} />;
}
if (isLoading || !orgAdminResults) {
return <ContentLoading />;
}
return ( return (
<PageSection> <PageSection>
<Card> <Card>
@@ -68,6 +104,7 @@ function WorkflowJobTemplateAdd() {
handleCancel={handleCancel} handleCancel={handleCancel}
handleSubmit={handleSubmit} handleSubmit={handleSubmit}
submitError={formSubmitError} submitError={formSubmitError}
isOrgAdmin={isOrgAdmin}
/> />
</CardBody> </CardBody>
</Card> </Card>

View File

@@ -7,8 +7,12 @@ import {
OrganizationsAPI, OrganizationsAPI,
LabelsAPI, LabelsAPI,
ExecutionEnvironmentsAPI, ExecutionEnvironmentsAPI,
UsersAPI,
} from '../../../api'; } from '../../../api';
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; import {
mountWithContexts,
waitForElement,
} from '../../../../testUtils/enzymeHelpers';
import WorkflowJobTemplateAdd from './WorkflowJobTemplateAdd'; import WorkflowJobTemplateAdd from './WorkflowJobTemplateAdd';
@@ -17,6 +21,7 @@ jest.mock('../../../api/models/Organizations');
jest.mock('../../../api/models/Labels'); jest.mock('../../../api/models/Labels');
jest.mock('../../../api/models/Inventories'); jest.mock('../../../api/models/Inventories');
jest.mock('../../../api/models/ExecutionEnvironments'); jest.mock('../../../api/models/ExecutionEnvironments');
jest.mock('../../../api/models/Users');
describe('<WorkflowJobTemplateAdd/>', () => { describe('<WorkflowJobTemplateAdd/>', () => {
let wrapper; let wrapper;
@@ -40,6 +45,10 @@ describe('<WorkflowJobTemplateAdd/>', () => {
data: { results: [{ id: 1, name: 'Foo', image: 'localhost.com' }] }, data: { results: [{ id: 1, name: 'Foo', image: 'localhost.com' }] },
}); });
UsersAPI.readAdminOfOrganizations.mockResolvedValue({
data: { count: 0, results: [] },
});
await act(async () => { await act(async () => {
history = createMemoryHistory({ history = createMemoryHistory({
initialEntries: ['/templates/workflow_job_template/add'], initialEntries: ['/templates/workflow_job_template/add'],
@@ -67,6 +76,7 @@ describe('<WorkflowJobTemplateAdd/>', () => {
} }
); );
}); });
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
}); });
}); });
afterEach(async () => { afterEach(async () => {

View File

@@ -1,12 +1,21 @@
import React, { useState } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { CardBody } from '../../../components/Card'; import { CardBody } from '../../../components/Card';
import { getAddedAndRemoved } from '../../../util/lists'; import { getAddedAndRemoved } from '../../../util/lists';
import { WorkflowJobTemplatesAPI, OrganizationsAPI } from '../../../api'; import {
WorkflowJobTemplatesAPI,
OrganizationsAPI,
UsersAPI,
} from '../../../api';
import { WorkflowJobTemplateForm } from '../shared'; 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 }) { function WorkflowJobTemplateEdit({ template }) {
const { me = {} } = useConfig();
const history = useHistory(); const history = useHistory();
const [formSubmitError, setFormSubmitError] = useState(null); const [formSubmitError, setFormSubmitError] = useState(null);
@@ -69,6 +78,33 @@ function WorkflowJobTemplateEdit({ template }) {
history.push(`/templates/workflow_job_template/${template.id}/details`); 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 <ContentError error={contentError} />;
}
if (isLoading || !orgAdminResults) {
return <ContentLoading />;
}
return ( return (
<CardBody> <CardBody>
<WorkflowJobTemplateForm <WorkflowJobTemplateForm
@@ -76,6 +112,7 @@ function WorkflowJobTemplateEdit({ template }) {
handleCancel={handleCancel} handleCancel={handleCancel}
template={template} template={template}
submitError={formSubmitError} submitError={formSubmitError}
isOrgAdmin={isOrgAdmin}
/> />
</CardBody> </CardBody>
); );

View File

@@ -7,8 +7,12 @@ import {
OrganizationsAPI, OrganizationsAPI,
LabelsAPI, LabelsAPI,
ExecutionEnvironmentsAPI, ExecutionEnvironmentsAPI,
UsersAPI,
} from '../../../api'; } from '../../../api';
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; import {
mountWithContexts,
waitForElement,
} from '../../../../testUtils/enzymeHelpers';
import WorkflowJobTemplateEdit from './WorkflowJobTemplateEdit'; import WorkflowJobTemplateEdit from './WorkflowJobTemplateEdit';
jest.mock('../../../api/models/WorkflowJobTemplates'); jest.mock('../../../api/models/WorkflowJobTemplates');
@@ -16,6 +20,7 @@ jest.mock('../../../api/models/Labels');
jest.mock('../../../api/models/Organizations'); jest.mock('../../../api/models/Organizations');
jest.mock('../../../api/models/Inventories'); jest.mock('../../../api/models/Inventories');
jest.mock('../../../api/models/ExecutionEnvironments'); jest.mock('../../../api/models/ExecutionEnvironments');
jest.mock('../../../api/models/Users');
const mockTemplate = { const mockTemplate = {
id: 6, id: 6,
@@ -74,6 +79,10 @@ describe('<WorkflowJobTemplateEdit/>', () => {
}, },
}); });
UsersAPI.readAdminOfOrganizations.mockResolvedValue({
data: { count: 1, results: [{ id: 1 }] },
});
await act(async () => { await act(async () => {
history = createMemoryHistory({ history = createMemoryHistory({
initialEntries: ['/templates/workflow_job_template/6/edit'], initialEntries: ['/templates/workflow_job_template/6/edit'],
@@ -95,6 +104,7 @@ describe('<WorkflowJobTemplateEdit/>', () => {
}, },
} }
); );
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
}); });
}); });
@@ -241,6 +251,7 @@ describe('<WorkflowJobTemplateEdit/>', () => {
} }
); );
}); });
await waitForElement(newWrapper, 'ContentLoading', el => el.length === 0);
OrganizationsAPI.read.mockRejectedValue({ OrganizationsAPI.read.mockRejectedValue({
response: { response: {
config: { config: {

View File

@@ -43,6 +43,7 @@ function WorkflowJobTemplateForm({
handleCancel, handleCancel,
i18n, i18n,
submitError, submitError,
isOrgAdmin,
}) { }) {
const { setFieldValue } = useFormikContext(); const { setFieldValue } = useFormikContext();
const [enableWebhooks, setEnableWebhooks] = useState( const [enableWebhooks, setEnableWebhooks] = useState(
@@ -55,7 +56,9 @@ function WorkflowJobTemplateForm({
); );
const [labelsField, , labelsHelpers] = useField('labels'); const [labelsField, , labelsHelpers] = useField('labels');
const [limitField, limitMeta, limitHelpers] = useField('limit'); const [limitField, limitMeta, limitHelpers] = useField('limit');
const [organizationField, organizationMeta] = useField('organization'); const [organizationField, organizationMeta, organizationHelpers] = useField(
'organization'
);
const [scmField, , scmHelpers] = useField('scm_branch'); const [scmField, , scmHelpers] = useField('scm_branch');
const [, webhookServiceMeta, webhookServiceHelpers] = useField( const [, webhookServiceMeta, webhookServiceHelpers] = useField(
'webhook_service' 'webhook_service'
@@ -119,9 +122,14 @@ function WorkflowJobTemplateForm({
/> />
<OrganizationLookup <OrganizationLookup
helperTextInvalid={organizationMeta.error} helperTextInvalid={organizationMeta.error}
isValid={!organizationMeta.touched || !organizationMeta.error}
onBlur={() => organizationHelpers.setTouched()}
onChange={onOrganizationChange} onChange={onOrganizationChange}
value={organizationField.value} value={organizationField.value}
isValid={!organizationMeta.error} touched={organizationMeta.touched}
error={organizationMeta.error}
required={isOrgAdmin}
autoPopulate={isOrgAdmin}
/> />
<> <>
<InventoryLookup <InventoryLookup
@@ -287,6 +295,7 @@ WorkflowJobTemplateForm.propTypes = {
handleSubmit: PropTypes.func.isRequired, handleSubmit: PropTypes.func.isRequired,
handleCancel: PropTypes.func.isRequired, handleCancel: PropTypes.func.isRequired,
submitError: shape({}), submitError: shape({}),
isOrgAdmin: PropTypes.bool,
}; };
WorkflowJobTemplateForm.defaultProps = { WorkflowJobTemplateForm.defaultProps = {
@@ -297,6 +306,7 @@ WorkflowJobTemplateForm.defaultProps = {
inventory: undefined, inventory: undefined,
project: undefined, project: undefined,
}, },
isOrgAdmin: false,
}; };
const FormikApp = withFormik({ const FormikApp = withFormik({

View File

@@ -124,6 +124,40 @@ describe('<WorkflowJobTemplateForm/>', () => {
expect(wrapper.length).toBe(1); expect(wrapper.length).toBe(1);
}); });
test('organization is a required field for organization admins', async () => {
await act(async () => {
wrapper = mountWithContexts(
<Route
path="/templates/workflow_job_template/:id/edit"
component={() => (
<WorkflowJobTemplateForm
template={mockTemplate}
handleCancel={handleCancel}
handleSubmit={handleSubmit}
isOrgAdmin
/>
)}
/>,
{
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', () => { test('all the fields render successfully', () => {
const fields = [ const fields = [
'FormField[name="name"]', 'FormField[name="name"]',
@@ -140,6 +174,9 @@ describe('<WorkflowJobTemplateForm/>', () => {
expect(wrapper.find(`${field}`).length).toBe(1); expect(wrapper.find(`${field}`).length).toBe(1);
}; };
fields.map((field, index) => assertField(field, index)); fields.map((field, index) => assertField(field, index));
expect(
wrapper.find('FormGroup[label="Organization"]').prop('isRequired')
).toBeFalsy();
}); });
test('changing inputs should update values', async () => { test('changing inputs should update values', async () => {