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
commit 84ab88b073
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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 { 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 <ContentError error={contentError} />;
}
if (isLoading || !orgAdminResults) {
return <ContentLoading />;
}
return (
<PageSection>
<Card>
@ -68,6 +104,7 @@ function WorkflowJobTemplateAdd() {
handleCancel={handleCancel}
handleSubmit={handleSubmit}
submitError={formSubmitError}
isOrgAdmin={isOrgAdmin}
/>
</CardBody>
</Card>

View File

@ -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('<WorkflowJobTemplateAdd/>', () => {
let wrapper;
@ -40,6 +45,10 @@ describe('<WorkflowJobTemplateAdd/>', () => {
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('<WorkflowJobTemplateAdd/>', () => {
}
);
});
await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
});
});
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 { 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 <ContentError error={contentError} />;
}
if (isLoading || !orgAdminResults) {
return <ContentLoading />;
}
return (
<CardBody>
<WorkflowJobTemplateForm
@ -76,6 +112,7 @@ function WorkflowJobTemplateEdit({ template }) {
handleCancel={handleCancel}
template={template}
submitError={formSubmitError}
isOrgAdmin={isOrgAdmin}
/>
</CardBody>
);

View File

@ -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('<WorkflowJobTemplateEdit/>', () => {
},
});
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('<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({
response: {
config: {

View File

@ -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({
/>
<OrganizationLookup
helperTextInvalid={organizationMeta.error}
isValid={!organizationMeta.touched || !organizationMeta.error}
onBlur={() => organizationHelpers.setTouched()}
onChange={onOrganizationChange}
value={organizationField.value}
isValid={!organizationMeta.error}
touched={organizationMeta.touched}
error={organizationMeta.error}
required={isOrgAdmin}
autoPopulate={isOrgAdmin}
/>
<>
<InventoryLookup
@ -287,6 +295,7 @@ WorkflowJobTemplateForm.propTypes = {
handleSubmit: PropTypes.func.isRequired,
handleCancel: PropTypes.func.isRequired,
submitError: shape({}),
isOrgAdmin: PropTypes.bool,
};
WorkflowJobTemplateForm.defaultProps = {
@ -297,6 +306,7 @@ WorkflowJobTemplateForm.defaultProps = {
inventory: undefined,
project: undefined,
},
isOrgAdmin: false,
};
const FormikApp = withFormik({

View File

@ -124,6 +124,40 @@ describe('<WorkflowJobTemplateForm/>', () => {
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', () => {
const fields = [
'FormField[name="name"]',
@ -140,6 +174,9 @@ describe('<WorkflowJobTemplateForm/>', () => {
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 () => {