resolves crashing details view

This commit is contained in:
Alex Corey 2021-06-11 10:41:07 -04:00 committed by Shane McDonald
parent 04839a037a
commit 9992bf03b0
No known key found for this signature in database
GPG Key ID: 6F374AF6E9EB9374
16 changed files with 1339 additions and 1265 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ import { CaretLeftIcon } from '@patternfly/react-icons';
import { Card, PageSection } from '@patternfly/react-core';
import useRequest from '../../util/useRequest';
import { InstanceGroupsAPI } from '../../api';
import { InstanceGroupsAPI, SettingsAPI } from '../../api';
import RoutedTabs from '../../components/RoutedTabs';
import ContentError from '../../components/ContentError';
import ContentLoading from '../../components/ContentLoading';
@ -30,12 +30,24 @@ function ContainerGroup({ setBreadcrumb }) {
isLoading,
error: contentError,
request: fetchInstanceGroups,
result: instanceGroup,
result: { instanceGroup, defaultExecution },
} = useRequest(
useCallback(async () => {
const { data } = await InstanceGroupsAPI.readDetail(id);
return data;
}, [id])
const [
{ data },
{
data: { DEFAULT_EXECUTION_QUEUE_NAME },
},
] = await Promise.all([
InstanceGroupsAPI.readDetail(id),
SettingsAPI.readAll(),
]);
return {
instanceGroup: data,
defaultExecution: DEFAULT_EXECUTION_QUEUE_NAME,
};
}, [id]),
{ instanceGroup: null, defaultExecution: '' }
);
useEffect(() => {
@ -109,10 +121,16 @@ function ContainerGroup({ setBreadcrumb }) {
{instanceGroup && (
<>
<Route path="/instance_groups/container_group/:id/edit">
<ContainerGroupEdit instanceGroup={instanceGroup} />
<ContainerGroupEdit
instanceGroup={instanceGroup}
defaultExecution={defaultExecution}
/>
</Route>
<Route path="/instance_groups/container_group/:id/details">
<ContainerGroupDetails instanceGroup={instanceGroup} />
<ContainerGroupDetails
instanceGroup={instanceGroup}
defaultExecution={defaultExecution}
/>
</Route>
<Route path="/instance_groups/container_group/:id/jobs">
<JobList

View File

@ -6,6 +6,7 @@ import { Button, Label } from '@patternfly/react-core';
import { VariablesDetail } from '../../../components/CodeEditor';
import AlertModal from '../../../components/AlertModal';
import ErrorDetail from '../../../components/ErrorDetail';
import { CardBody, CardActionsRow } from '../../../components/Card';
import DeleteButton from '../../../components/DeleteButton';
import {
@ -18,7 +19,7 @@ import { jsonToYaml, isJsonString } from '../../../util/yaml';
import { InstanceGroupsAPI } from '../../../api';
import { relatedResourceDeleteRequests } from '../../../util/getRelatedResourceDeleteDetails';
function ContainerGroupDetails({ instanceGroup }) {
function ContainerGroupDetails({ instanceGroup, defaultExecution }) {
const { id, name } = instanceGroup;
const history = useHistory();
@ -102,7 +103,8 @@ function ContainerGroupDetails({ instanceGroup }) {
{t`Edit`}
</Button>
)}
{instanceGroup.summary_fields.user_capabilities &&
{name !== defaultExecution &&
instanceGroup.summary_fields.user_capabilities &&
instanceGroup.summary_fields.user_capabilities.delete && (
<DeleteButton
ouiaId="container-group-detail-delete-button"
@ -123,7 +125,9 @@ function ContainerGroupDetails({ instanceGroup }) {
onClose={dismissError}
title={t`Error`}
variant="error"
/>
>
<ErrorDetail error={error} />
</AlertModal>
)}
</CardBody>
);

View File

@ -31,16 +31,13 @@ function InstanceGroup({ setBreadcrumb }) {
isLoading,
error: contentError,
request: fetchInstanceGroups,
result: { instanceGroup, defaultControlPlane, defaultExecution },
result: { instanceGroup, defaultControlPlane },
} = useRequest(
useCallback(async () => {
const [
{ data },
{
data: {
DEFAULT_CONTROL_PLANE_QUEUE_NAME,
DEFAULT_EXECUTION_QUEUE_NAME,
},
data: { DEFAULT_CONTROL_PLANE_QUEUE_NAME },
},
] = await Promise.all([
InstanceGroupsAPI.readDetail(id),
@ -49,10 +46,9 @@ function InstanceGroup({ setBreadcrumb }) {
return {
instanceGroup: data,
defaultControlPlane: DEFAULT_CONTROL_PLANE_QUEUE_NAME,
defaultExecution: DEFAULT_EXECUTION_QUEUE_NAME,
};
}, [id]),
{ instanceGroup: {}, defaultControlPlane: '', defaultExecution: '' }
{ instanceGroup: null, defaultControlPlane: '' }
);
useEffect(() => {
@ -133,12 +129,14 @@ function InstanceGroup({ setBreadcrumb }) {
<Route path="/instance_groups/:id/edit">
<InstanceGroupEdit
instanceGroup={instanceGroup}
defaultExecution={defaultExecution}
defaultControlPlane={defaultControlPlane}
/>
</Route>
<Route path="/instance_groups/:id/details">
<InstanceGroupDetails instanceGroup={instanceGroup} />
<InstanceGroupDetails
defaultControlPlane={defaultControlPlane}
instanceGroup={instanceGroup}
/>
</Route>
<Route path="/instance_groups/:id/instances">
<InstanceList />

View File

@ -7,6 +7,7 @@ import { Button } from '@patternfly/react-core';
import AlertModal from '../../../components/AlertModal';
import { CardBody, CardActionsRow } from '../../../components/Card';
import ErrorDetail from '../../../components/ErrorDetail';
import DeleteButton from '../../../components/DeleteButton';
import {
Detail,
@ -22,7 +23,7 @@ const Unavailable = styled.span`
color: var(--pf-global--danger-color--200);
`;
function InstanceGroupDetails({ instanceGroup }) {
function InstanceGroupDetails({ instanceGroup, defaultControlPlane }) {
const { id, name } = instanceGroup;
const history = useHistory();
@ -110,7 +111,7 @@ function InstanceGroupDetails({ instanceGroup }) {
{t`Edit`}
</Button>
)}
{name !== 'tower' &&
{name !== defaultControlPlane &&
instanceGroup.summary_fields.user_capabilities &&
instanceGroup.summary_fields.user_capabilities.delete && (
<DeleteButton
@ -132,7 +133,9 @@ function InstanceGroupDetails({ instanceGroup }) {
onClose={dismissError}
title={t`Error`}
variant="error"
/>
>
<ErrorDetail error={error} />
</AlertModal>
)}
</CardBody>
);

View File

@ -5,11 +5,7 @@ import { CardBody } from '../../../components/Card';
import { InstanceGroupsAPI } from '../../../api';
import InstanceGroupForm from '../shared/InstanceGroupForm';
function InstanceGroupEdit({
instanceGroup,
defaultExecution,
defaultControlPlane,
}) {
function InstanceGroupEdit({ instanceGroup, defaultControlPlane }) {
const history = useHistory();
const [submitError, setSubmitError] = useState(null);
const detailsUrl = `/instance_groups/${instanceGroup.id}/details`;
@ -31,7 +27,6 @@ function InstanceGroupEdit({
<CardBody>
<InstanceGroupForm
instanceGroup={instanceGroup}
defaultExecution={defaultExecution}
defaultControlPlane={defaultControlPlane}
onSubmit={handleSubmit}
submitError={submitError}

View File

@ -56,7 +56,6 @@ describe('<InstanceGroupEdit>', () => {
await act(async () => {
wrapper = mountWithContexts(
<InstanceGroupEdit
defaultExecution="default"
defaultControlPlane="controlplane"
instanceGroup={instanceGroupData}
/>,
@ -77,7 +76,6 @@ describe('<InstanceGroupEdit>', () => {
await act(async () => {
towerWrapper = mountWithContexts(
<InstanceGroupEdit
defaultExecution="default"
defaultControlPlane="controlplane"
instanceGroup={{ ...instanceGroupData, name: 'controlplane' }}
/>,
@ -94,28 +92,6 @@ describe('<InstanceGroupEdit>', () => {
).toEqual('controlplane');
});
test('default instance group name can not be updated', async () => {
let towerWrapper;
await act(async () => {
towerWrapper = mountWithContexts(
<InstanceGroupEdit
defaultExecution="default"
defaultControlPlane="controlplane"
instanceGroup={{ ...instanceGroupData, name: 'default' }}
/>,
{
context: { router: { history } },
}
);
});
expect(
towerWrapper.find('input#instance-group-name').prop('disabled')
).toBeTruthy();
expect(
towerWrapper.find('input#instance-group-name').prop('value')
).toEqual('default');
});
test('handleSubmit should call the api and redirect to details page', async () => {
await act(async () => {
wrapper.find('InstanceGroupForm').invoke('onSubmit')(

View File

@ -55,6 +55,30 @@ function InstanceGroupList({
}) {
const location = useLocation();
const match = useRouteMatch();
const {
error: protectedItemsError,
isloading: isLoadingProtectedItems,
request: fetchProtectedItems,
result: { defaultControlPlane, defaultExecution },
} = useRequest(
useCallback(async () => {
const {
data: {
DEFAULT_CONTROL_PLANE_QUEUE_NAME,
DEFAULT_EXECUTION_QUEUE_NAME,
},
} = await SettingsAPI.readAll();
return {
defaultControlPlane: DEFAULT_CONTROL_PLANE_QUEUE_NAME,
defaultExecution: DEFAULT_EXECUTION_QUEUE_NAME,
};
}, []),
{ defaultControlPlane: '', defaultExecution: '' }
);
useEffect(() => {
fetchProtectedItems();
}, [fetchProtectedItems]);
const {
error: contentError,
@ -66,32 +90,18 @@ function InstanceGroupList({
actions,
relatedSearchableKeys,
searchableKeys,
defaultControlPlane,
defaultExecution,
},
} = useRequest(
useCallback(async () => {
const params = parseQueryString(QS_CONFIG, location.search);
const [
response,
responseActions,
{
data: {
DEFAULT_CONTROL_PLANE_QUEUE_NAME,
DEFAULT_EXECUTION_QUEUE_NAME,
},
},
] = await Promise.all([
const [response, responseActions] = await Promise.all([
InstanceGroupsAPI.read(params),
InstanceGroupsAPI.readOptions(),
SettingsAPI.readAll(),
]);
return {
instanceGroups: response.data.results,
defaultControlPlane: DEFAULT_CONTROL_PLANE_QUEUE_NAME,
defaultExecution: DEFAULT_EXECUTION_QUEUE_NAME,
instanceGroupsCount: response.data.count,
actions: responseActions.data.actions,
relatedSearchableKeys: (
@ -165,15 +175,17 @@ function InstanceGroupList({
const pluralizedItemName = t`Instance Groups`;
let errorMessageDelete = '';
const notdeletedable = selected.filter(
i => i.name === defaultControlPlane || i.name === defaultExecution
);
if (
modifiedSelected.some(
item =>
item.name === defaultControlPlane || item.name === defaultExecution
)
) {
errorMessageDelete = errorMessageDelete.concat(
t`The following Instance Group cannot be deleted`
if (notdeletedable.length) {
errorMessageDelete = (
<Plural
value={notdeletedable.length}
one="The following Instance Group cannot be deleted"
other="The following Instance Groups cannot be deleted"
/>
);
}
@ -227,9 +239,14 @@ function InstanceGroupList({
<PageSection>
<Card>
<PaginatedTable
contentError={contentError || settingsRequestError}
contentError={
contentError || settingsRequestError || protectedItemsError
}
hasContentLoading={
isLoading || deleteLoading || isSettingsRequestLoading
isLoading ||
deleteLoading ||
isSettingsRequestLoading ||
isLoadingProtectedItems
}
items={instanceGroups}
itemCount={instanceGroupsCount}

View File

@ -27,6 +27,8 @@ function ContainerGroupFormFields({ instanceGroup }) {
'credential'
);
const [nameField] = useField('name');
const [overrideField] = useField('override');
const handleCredentialUpdate = useCallback(
@ -45,6 +47,7 @@ function ContainerGroupFormFields({ instanceGroup }) {
label={t`Name`}
type="text"
validate={required(null)}
isDisabled={nameField.value === 'default'}
isRequired
/>
<CredentialLookup

View File

@ -10,7 +10,7 @@ import FormActionGroup from '../../../components/FormActionGroup';
import { required, minMaxValue } from '../../../util/validators';
import { FormColumnLayout } from '../../../components/FormLayout';
function InstanceGroupFormFields({ defaultExecution, defaultControlPlane }) {
function InstanceGroupFormFields({ defaultControlPlane }) {
const [instanceGroupNameField, ,] = useField('name');
return (
@ -22,10 +22,7 @@ function InstanceGroupFormFields({ defaultExecution, defaultControlPlane }) {
type="text"
validate={required(null)}
isRequired
isDisabled={
instanceGroupNameField.value === defaultExecution ||
instanceGroupNameField.value === defaultControlPlane
}
isDisabled={instanceGroupNameField.value === defaultControlPlane}
/>
<FormField
id="instance-group-policy-instance-minimum"