Add Container Group details

Add Container Group details.

See: https://github.com/ansible/awx/issues/8073
This commit is contained in:
nixocio 2020-09-03 13:18:20 -04:00
parent 22bff7adec
commit d3d3fe8892
3 changed files with 243 additions and 10 deletions

View File

@ -114,7 +114,7 @@ function ContainerGroup({ i18n, setBreadcrumb }) {
<ContainerGroupEdit instanceGroup={instanceGroup} />
</Route>
<Route path="/instance_groups/container_group/:id/details">
<ContainerGroupDetails />
<ContainerGroupDetails instanceGroup={instanceGroup} />
</Route>
<Route path="/instance_groups/container_group/:id/jobs">
<JobList

View File

@ -1,14 +1,118 @@
import React from 'react';
import { Card, PageSection } from '@patternfly/react-core';
import React, { useCallback } from 'react';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import { Link, useHistory } from 'react-router-dom';
import { Button, Label } from '@patternfly/react-core';
import { VariablesDetail } from '../../../components/CodeMirrorInput';
import AlertModal from '../../../components/AlertModal';
import { CardBody, CardActionsRow } from '../../../components/Card';
import DeleteButton from '../../../components/DeleteButton';
import {
Detail,
DetailList,
UserDateDetail,
} from '../../../components/DetailList';
import useRequest, { useDismissableError } from '../../../util/useRequest';
import { jsonToYaml, isJsonString } from '../../../util/yaml';
import { InstanceGroupsAPI } from '../../../api';
function ContainerGroupDetails({ instanceGroup, i18n }) {
const { id, name } = instanceGroup;
const history = useHistory();
const {
request: deleteInstanceGroup,
isLoading,
error: deleteError,
} = useRequest(
useCallback(async () => {
await InstanceGroupsAPI.destroy(id);
history.push(`/instance_groups`);
}, [id, history])
);
const { error, dismissError } = useDismissableError(deleteError);
function ContainerGroupDetails() {
return (
<PageSection>
<Card>
<div>Container group details</div>
</Card>
</PageSection>
<CardBody>
<DetailList>
<Detail
label={i18n._(t`Name`)}
value={instanceGroup.name}
dataCy="container-group-detail-name"
/>
<Detail
label={i18n._(t`Type`)}
value={i18n._(t`Container group`)}
dataCy="container-group-type"
/>
<Detail
label={i18n._(t`Credential`)}
value={
<Label variant="outline" color="blue">
{instanceGroup.summary_fields.credential.name}
</Label>
}
dataCy="container-group-credential"
/>
<UserDateDetail
label={i18n._(t`Created`)}
date={instanceGroup.created}
user={instanceGroup.summary_fields.created_by}
/>
<UserDateDetail
label={i18n._(t`Last Modified`)}
date={instanceGroup.modified}
user={instanceGroup.summary_fields.modified_by}
/>
{instanceGroup.pod_spec_override && (
<VariablesDetail
label={i18n._(t`Pod spec override`)}
value={
isJsonString(instanceGroup.pod_spec_override)
? jsonToYaml(instanceGroup.pod_spec_override)
: instanceGroup.pod_spec_override
}
rows={6}
/>
)}
</DetailList>
<CardActionsRow>
{instanceGroup.summary_fields.user_capabilities &&
instanceGroup.summary_fields.user_capabilities.edit && (
<Button
aria-label={i18n._(t`edit`)}
component={Link}
to={`/instance_groups/container_group/${id}/edit`}
>
{i18n._(t`Edit`)}
</Button>
)}
{instanceGroup.summary_fields.user_capabilities &&
instanceGroup.summary_fields.user_capabilities.delete && (
<DeleteButton
name={name}
modalTitle={i18n._(t`Delete instance group`)}
onConfirm={deleteInstanceGroup}
isDisabled={isLoading}
>
{i18n._(t`Delete`)}
</DeleteButton>
)}
</CardActionsRow>
{error && (
<AlertModal
isOpen={error}
onClose={dismissError}
title={i18n._(t`Error`)}
variant="error"
/>
)}
</CardBody>
);
}
export default ContainerGroupDetails;
export default withI18n()(ContainerGroupDetails);

View File

@ -0,0 +1,129 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { createMemoryHistory } from 'history';
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
import { InstanceGroupsAPI } from '../../../api';
import ContainerGroupDetails from './ContainerGroupDetails';
jest.mock('../../../api');
const instanceGroup = {
id: 42,
type: 'instance_group',
url: '/api/v2/instance_groups/128/',
related: {
named_url: '/api/v2/instance_groups/A1/',
jobs: '/api/v2/instance_groups/128/jobs/',
instances: '/api/v2/instance_groups/128/instances/',
credential: '/api/v2/credentials/71/',
},
name: 'Foo',
created: '2020-09-03T18:26:47.113934Z',
modified: '2020-09-03T19:34:23.244694Z',
capacity: 0,
committed_capacity: 0,
consumed_capacity: 0,
percent_capacity_remaining: 0.0,
jobs_running: 0,
jobs_total: 0,
instances: 0,
controller: null,
is_controller: false,
is_isolated: false,
is_containerized: true,
credential: 71,
policy_instance_percentage: 0,
policy_instance_minimum: 0,
policy_instance_list: [],
pod_spec_override:
'apiVersion: v1\nkind: Pod\nmetadata:\n namespace: default\nspec:\n containers:\n - image: ansible/ansible-runner\n tty: true\n stdin: true\n imagePullPolicy: Always\n args:\n - sleep\n - infinity\n - test',
summary_fields: {
credential: {
id: 71,
name: 'CG',
description: 'Container Group',
kind: 'kubernetes_bearer_token',
cloud: false,
kubernetes: true,
credential_type_id: 17,
},
user_capabilities: {
edit: true,
delete: true,
},
},
};
describe('<ContainerGroupDetails/>', () => {
let wrapper;
test('should render details properly', async () => {
await act(async () => {
wrapper = mountWithContexts(
<ContainerGroupDetails instanceGroup={instanceGroup} />
);
});
wrapper.update();
expect(wrapper.find('Detail[label="Name"]').prop('value')).toEqual(
instanceGroup.name
);
expect(wrapper.find('Detail[label="Type"]').prop('value')).toEqual(
'Container group'
);
expect(
wrapper.find('Detail[label="Credential"]').prop('value').props.children
).toEqual(instanceGroup.summary_fields.credential.name);
expect(wrapper.find('VariablesDetail').prop('value')).toEqual(
instanceGroup.pod_spec_override
);
const dates = wrapper.find('UserDateDetail');
expect(dates).toHaveLength(2);
expect(dates.at(0).prop('date')).toEqual(instanceGroup.created);
expect(dates.at(1).prop('date')).toEqual(instanceGroup.modified);
});
test('expected api call is made for delete', async () => {
const history = createMemoryHistory({
initialEntries: ['/credential_types/42/details'],
});
await act(async () => {
wrapper = mountWithContexts(
<ContainerGroupDetails instanceGroup={instanceGroup} />,
{
context: { router: { history } },
}
);
});
await act(async () => {
wrapper.find('DeleteButton').invoke('onConfirm')();
});
expect(InstanceGroupsAPI.destroy).toHaveBeenCalledTimes(1);
expect(history.location.pathname).toBe('/instance_groups');
});
test('should not render delete button', async () => {
instanceGroup.summary_fields.user_capabilities.delete = false;
await act(async () => {
wrapper = mountWithContexts(
<ContainerGroupDetails instanceGroup={instanceGroup} />
);
});
wrapper.update();
expect(wrapper.find('Button[aria-label="Delete"]').length).toBe(0);
});
test('should not render edit button', async () => {
instanceGroup.summary_fields.user_capabilities.edit = false;
await act(async () => {
wrapper = mountWithContexts(
<ContainerGroupDetails instanceGroup={instanceGroup} />
);
});
wrapper.update();
expect(wrapper.find('Button[aria-label="Edit"]').length).toBe(0);
});
});