mirror of
https://github.com/ansible/awx.git
synced 2026-03-15 07:57:29 -02:30
fixes credential type delete warnings
This commit is contained in:
9
awx/ui_next/src/api/models/Metrics.js
Normal file
9
awx/ui_next/src/api/models/Metrics.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
class Metrics extends Base {
|
||||||
|
constructor(http) {
|
||||||
|
super(http);
|
||||||
|
this.baseUrl = '/api/v2/inventories/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default Metrics;
|
||||||
@@ -77,7 +77,7 @@ function AlertModal({
|
|||||||
aria-label={label || i18n._(t`Alert modal`)}
|
aria-label={label || i18n._(t`Alert modal`)}
|
||||||
aria-labelledby="alert-modal-header-label"
|
aria-labelledby="alert-modal-header-label"
|
||||||
isOpen={Boolean(isOpen)}
|
isOpen={Boolean(isOpen)}
|
||||||
variant="medium"
|
variant="small"
|
||||||
title={title}
|
title={title}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { Button, Badge, Alert } from '@patternfly/react-core';
|
import { Button, Badge, Alert, Tooltip } from '@patternfly/react-core';
|
||||||
import AlertModal from '../AlertModal';
|
import AlertModal from '../AlertModal';
|
||||||
import { getRelatedResourceDeleteCounts } from '../../util/getRelatedResourceDeleteDetails';
|
import { getRelatedResourceDeleteCounts } from '../../util/getRelatedResourceDeleteDetails';
|
||||||
import ErrorDetail from '../ErrorDetail';
|
import ErrorDetail from '../ErrorDetail';
|
||||||
@@ -11,9 +11,9 @@ import ErrorDetail from '../ErrorDetail';
|
|||||||
const WarningMessage = styled(Alert)`
|
const WarningMessage = styled(Alert)`
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
`;
|
`;
|
||||||
const DetailsWrapper = styled.span`
|
const Label = styled.span`
|
||||||
:not(:first-of-type) {
|
&& {
|
||||||
padding-left: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
function DeleteButton({
|
function DeleteButton({
|
||||||
@@ -27,11 +27,11 @@ function DeleteButton({
|
|||||||
ouiaId,
|
ouiaId,
|
||||||
deleteMessage,
|
deleteMessage,
|
||||||
deleteDetailsRequests,
|
deleteDetailsRequests,
|
||||||
|
disabledTooltip,
|
||||||
}) {
|
}) {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [deleteMessageError, setDeleteMessageError] = useState();
|
const [deleteMessageError, setDeleteMessageError] = useState();
|
||||||
const [deleteDetails, setDeleteDetails] = useState({});
|
const [deleteDetails, setDeleteDetails] = useState({});
|
||||||
|
|
||||||
const toggleModal = async isModalOpen => {
|
const toggleModal = async isModalOpen => {
|
||||||
if (deleteDetailsRequests?.length && isModalOpen) {
|
if (deleteDetailsRequests?.length && isModalOpen) {
|
||||||
const { results, error } = await getRelatedResourceDeleteCounts(
|
const { results, error } = await getRelatedResourceDeleteCounts(
|
||||||
@@ -62,15 +62,29 @@ function DeleteButton({
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button
|
{disabledTooltip ? (
|
||||||
variant={variant || 'secondary'}
|
<Tooltip content={disabledTooltip} position="top">
|
||||||
aria-label={i18n._(t`Delete`)}
|
<div>
|
||||||
isDisabled={isDisabled}
|
<Button
|
||||||
onClick={() => toggleModal(true)}
|
variant={variant || 'secondary'}
|
||||||
>
|
aria-label={i18n._(t`Delete`)}
|
||||||
{children || i18n._(t`Delete`)}
|
isDisabled={isDisabled}
|
||||||
</Button>
|
onClick={() => toggleModal(true)}
|
||||||
|
>
|
||||||
|
{children || i18n._(t`Delete`)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant={variant || 'secondary'}
|
||||||
|
aria-label={i18n._(t`Delete`)}
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
onClick={() => toggleModal(true)}
|
||||||
|
>
|
||||||
|
{children || i18n._(t`Delete`)}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<AlertModal
|
<AlertModal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
title={modalTitle}
|
title={modalTitle}
|
||||||
@@ -83,7 +97,10 @@ function DeleteButton({
|
|||||||
variant="danger"
|
variant="danger"
|
||||||
aria-label={i18n._(t`Confirm Delete`)}
|
aria-label={i18n._(t`Confirm Delete`)}
|
||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
onClick={onConfirm}
|
onClick={() => {
|
||||||
|
onConfirm();
|
||||||
|
toggleModal(false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{i18n._(t`Delete`)}
|
{i18n._(t`Delete`)}
|
||||||
</Button>,
|
</Button>,
|
||||||
@@ -110,9 +127,9 @@ function DeleteButton({
|
|||||||
<div aria-label={deleteMessage}>{deleteMessage}</div>
|
<div aria-label={deleteMessage}>{deleteMessage}</div>
|
||||||
<br />
|
<br />
|
||||||
{Object.entries(deleteDetails).map(([key, value]) => (
|
{Object.entries(deleteDetails).map(([key, value]) => (
|
||||||
<DetailsWrapper aria-label={`${key}: ${value}`} key={key}>
|
<div aria-label={`${key}: ${value}`} key={key}>
|
||||||
<span>{key}</span> <Badge>{value}</Badge>
|
<Label>{key}</Label> <Badge>{value}</Badge>
|
||||||
</DetailsWrapper>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,10 @@ import ErrorDetail from '../ErrorDetail';
|
|||||||
const WarningMessage = styled(Alert)`
|
const WarningMessage = styled(Alert)`
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
`;
|
`;
|
||||||
const DetailsWrapper = styled.span`
|
|
||||||
:not(:first-of-type) {
|
const Label = styled.span`
|
||||||
padding-left: 10px;
|
&& {
|
||||||
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -99,11 +100,7 @@ function ToolbarDeleteButton({
|
|||||||
}) {
|
}) {
|
||||||
const { isKebabified, onKebabModalChange } = useContext(KebabifiedContext);
|
const { isKebabified, onKebabModalChange } = useContext(KebabifiedContext);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [deleteDetails, setDeleteDetails] = useState({});
|
const [deleteDetails, setDeleteDetails] = useState(null);
|
||||||
|
|
||||||
const deleteMessages = [warningMessage, deleteMessage].filter(
|
|
||||||
message => message
|
|
||||||
);
|
|
||||||
|
|
||||||
const [deleteMessageError, setDeleteMessageError] = useState();
|
const [deleteMessageError, setDeleteMessageError] = useState();
|
||||||
const handleDelete = () => {
|
const handleDelete = () => {
|
||||||
@@ -112,6 +109,7 @@ function ToolbarDeleteButton({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const toggleModal = async isOpen => {
|
const toggleModal = async isOpen => {
|
||||||
|
setDeleteDetails(null);
|
||||||
if (
|
if (
|
||||||
isOpen &&
|
isOpen &&
|
||||||
itemsToDelete.length === 1 &&
|
itemsToDelete.length === 1 &&
|
||||||
@@ -164,6 +162,36 @@ function ToolbarDeleteButton({
|
|||||||
const isDisabled =
|
const isDisabled =
|
||||||
itemsToDelete.length === 0 || itemsToDelete.some(cannotDelete);
|
itemsToDelete.length === 0 || itemsToDelete.some(cannotDelete);
|
||||||
|
|
||||||
|
const buildDeleteWarning = () => {
|
||||||
|
const deleteMessages = [];
|
||||||
|
if (warningMessage) {
|
||||||
|
deleteMessages.push(warningMessage);
|
||||||
|
}
|
||||||
|
if (deleteMessage) {
|
||||||
|
if (itemsToDelete[0]?.type !== 'inventory') {
|
||||||
|
deleteMessages.push(deleteMessage);
|
||||||
|
} else if (deleteDetails || itemsToDelete.length > 1) {
|
||||||
|
deleteMessages.push(deleteMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{deleteMessages.map(message => (
|
||||||
|
<div aria-label={message} key={message}>
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{deleteDetails &&
|
||||||
|
Object.entries(deleteDetails).map(([key, value]) => (
|
||||||
|
<div key={key} aria-label={`${key}: ${value}`}>
|
||||||
|
<Label>{key}</Label>
|
||||||
|
<Badge>{value}</Badge>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
if (deleteMessageError) {
|
if (deleteMessageError) {
|
||||||
return (
|
return (
|
||||||
<AlertModal
|
<AlertModal
|
||||||
@@ -218,6 +246,9 @@ function ToolbarDeleteButton({
|
|||||||
key="delete"
|
key="delete"
|
||||||
variant="danger"
|
variant="danger"
|
||||||
aria-label={i18n._(t`confirm delete`)}
|
aria-label={i18n._(t`confirm delete`)}
|
||||||
|
isDisabled={Boolean(
|
||||||
|
deleteDetails && itemsToDelete[0]?.type === 'credential_type'
|
||||||
|
)}
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
>
|
>
|
||||||
{i18n._(t`Delete`)}
|
{i18n._(t`Delete`)}
|
||||||
@@ -239,42 +270,11 @@ function ToolbarDeleteButton({
|
|||||||
<br />
|
<br />
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
{itemsToDelete.length === 1 &&
|
{(deleteDetails || deleteMessage || warningMessage) && (
|
||||||
Object.values(deleteDetails).length > 0 && (
|
|
||||||
<WarningMessage
|
|
||||||
variant="warning"
|
|
||||||
isInline
|
|
||||||
title={
|
|
||||||
<div>
|
|
||||||
{deleteMessages.map(message => (
|
|
||||||
<div aria-label={message} key={message}>
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{itemsToDelete.length === 1 && (
|
|
||||||
<>
|
|
||||||
<br />
|
|
||||||
{Object.entries(deleteDetails).map(([key, value]) => (
|
|
||||||
<DetailsWrapper
|
|
||||||
key={key}
|
|
||||||
aria-label={`${key}: ${value}`}
|
|
||||||
>
|
|
||||||
<span>{key}</span> <Badge>{value}</Badge>
|
|
||||||
</DetailsWrapper>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{itemsToDelete.length > 1 && (
|
|
||||||
<WarningMessage
|
<WarningMessage
|
||||||
variant="warning"
|
variant="warning"
|
||||||
isInline
|
isInline
|
||||||
title={deleteMessages.map(message => (
|
title={buildDeleteWarning()}
|
||||||
<div>{message}</div>
|
|
||||||
))}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</AlertModal>
|
</AlertModal>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const itemC = {
|
|||||||
|
|
||||||
describe('<ToolbarDeleteButton />', () => {
|
describe('<ToolbarDeleteButton />', () => {
|
||||||
let deleteDetailsRequests;
|
let deleteDetailsRequests;
|
||||||
|
let wrapper;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
deleteDetailsRequests = [
|
deleteDetailsRequests = [
|
||||||
{
|
{
|
||||||
@@ -35,8 +36,13 @@ describe('<ToolbarDeleteButton />', () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
test('should render button', () => {
|
test('should render button', () => {
|
||||||
const wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<ToolbarDeleteButton onDelete={() => {}} itemsToDelete={[]} />
|
<ToolbarDeleteButton onDelete={() => {}} itemsToDelete={[]} />
|
||||||
);
|
);
|
||||||
expect(wrapper.find('button')).toHaveLength(1);
|
expect(wrapper.find('button')).toHaveLength(1);
|
||||||
@@ -44,7 +50,6 @@ describe('<ToolbarDeleteButton />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should open confirmation modal', async () => {
|
test('should open confirmation modal', async () => {
|
||||||
let wrapper;
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<ToolbarDeleteButton
|
<ToolbarDeleteButton
|
||||||
@@ -65,8 +70,89 @@ describe('<ToolbarDeleteButton />', () => {
|
|||||||
expect(CredentialsAPI.read).toBeCalled();
|
expect(CredentialsAPI.read).toBeCalled();
|
||||||
expect(wrapper.find('Modal')).toHaveLength(1);
|
expect(wrapper.find('Modal')).toHaveLength(1);
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('span[aria-label="Workflow Job Template Node: 1"]')
|
wrapper.find('div[aria-label="Workflow Job Template Node: 1"]')
|
||||||
).toHaveLength(1);
|
).toHaveLength(1);
|
||||||
|
expect(
|
||||||
|
wrapper.find('Button[aria-label="confirm delete"]').prop('isDisabled')
|
||||||
|
).toBe(false);
|
||||||
|
expect(wrapper.find('div[aria-label="Delete this?"]')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should open confirmation with enabled delete button modal', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<ToolbarDeleteButton
|
||||||
|
onDelete={() => {}}
|
||||||
|
itemsToDelete={[
|
||||||
|
{
|
||||||
|
name: 'foo',
|
||||||
|
id: 1,
|
||||||
|
type: 'credential_type',
|
||||||
|
summary_fields: { user_capabilities: { delete: true } },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'bar',
|
||||||
|
id: 2,
|
||||||
|
type: 'credential_type',
|
||||||
|
summary_fields: { user_capabilities: { delete: true } },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
deleteDetailsRequests={deleteDetailsRequests}
|
||||||
|
deleteMessage="Delete this?"
|
||||||
|
warningMessage="Are you sure to want to delete this"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('Modal')).toHaveLength(0);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('button').prop('onClick')();
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'Modal', el => el.length > 0);
|
||||||
|
expect(CredentialsAPI.read).not.toBeCalled();
|
||||||
|
expect(wrapper.find('Modal')).toHaveLength(1);
|
||||||
|
expect(
|
||||||
|
wrapper.find('Button[aria-label="confirm delete"]').prop('isDisabled')
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should disable confirm delete button', async () => {
|
||||||
|
const request = [
|
||||||
|
{
|
||||||
|
label: 'Workflow Job Template Node',
|
||||||
|
request: CredentialsAPI.read.mockResolvedValue({ data: { count: 3 } }),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<ToolbarDeleteButton
|
||||||
|
onDelete={() => {}}
|
||||||
|
itemsToDelete={[
|
||||||
|
{
|
||||||
|
name: 'foo',
|
||||||
|
id: 1,
|
||||||
|
type: 'credential_type',
|
||||||
|
summary_fields: { user_capabilities: { delete: true } },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
deleteDetailsRequests={request}
|
||||||
|
deleteMessage="Delete this?"
|
||||||
|
warningMessage="Are you sure to want to delete this"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find('Modal')).toHaveLength(0);
|
||||||
|
await act(async () => {
|
||||||
|
wrapper.find('button').prop('onClick')();
|
||||||
|
});
|
||||||
|
await waitForElement(wrapper, 'Modal', el => el.length > 0);
|
||||||
|
expect(CredentialsAPI.read).toBeCalled();
|
||||||
|
expect(wrapper.find('Modal')).toHaveLength(1);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
wrapper.find('Button[aria-label="confirm delete"]').prop('isDisabled')
|
||||||
|
).toBe(true);
|
||||||
expect(wrapper.find('div[aria-label="Delete this?"]')).toHaveLength(1);
|
expect(wrapper.find('div[aria-label="Delete this?"]')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -88,7 +174,7 @@ describe('<ToolbarDeleteButton />', () => {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
let wrapper;
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<ToolbarDeleteButton
|
<ToolbarDeleteButton
|
||||||
@@ -113,7 +199,7 @@ describe('<ToolbarDeleteButton />', () => {
|
|||||||
|
|
||||||
test('should invoke onDelete prop', () => {
|
test('should invoke onDelete prop', () => {
|
||||||
const onDelete = jest.fn();
|
const onDelete = jest.fn();
|
||||||
const wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<ToolbarDeleteButton onDelete={onDelete} itemsToDelete={[itemA]} />
|
<ToolbarDeleteButton onDelete={onDelete} itemsToDelete={[itemA]} />
|
||||||
);
|
);
|
||||||
wrapper.find('button').simulate('click');
|
wrapper.find('button').simulate('click');
|
||||||
@@ -127,14 +213,14 @@ describe('<ToolbarDeleteButton />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should disable button when no delete permissions', () => {
|
test('should disable button when no delete permissions', () => {
|
||||||
const wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<ToolbarDeleteButton onDelete={() => {}} itemsToDelete={[itemB]} />
|
<ToolbarDeleteButton onDelete={() => {}} itemsToDelete={[itemB]} />
|
||||||
);
|
);
|
||||||
expect(wrapper.find('button[disabled]')).toHaveLength(1);
|
expect(wrapper.find('button[disabled]')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should render tooltip', () => {
|
test('should render tooltip', () => {
|
||||||
const wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<ToolbarDeleteButton onDelete={() => {}} itemsToDelete={[itemA]} />
|
<ToolbarDeleteButton onDelete={() => {}} itemsToDelete={[itemA]} />
|
||||||
);
|
);
|
||||||
expect(wrapper.find('Tooltip')).toHaveLength(1);
|
expect(wrapper.find('Tooltip')).toHaveLength(1);
|
||||||
@@ -142,7 +228,7 @@ describe('<ToolbarDeleteButton />', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should render tooltip for username', () => {
|
test('should render tooltip for username', () => {
|
||||||
const wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
<ToolbarDeleteButton onDelete={() => {}} itemsToDelete={[itemC]} />
|
<ToolbarDeleteButton onDelete={() => {}} itemsToDelete={[itemC]} />
|
||||||
);
|
);
|
||||||
expect(wrapper.find('Tooltip')).toHaveLength(1);
|
expect(wrapper.find('Tooltip')).toHaveLength(1);
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
|
|||||||
title="Remove Team Access"
|
title="Remove Team Access"
|
||||||
titleIconVariant={null}
|
titleIconVariant={null}
|
||||||
titleLabel=""
|
titleLabel=""
|
||||||
variant="medium"
|
variant="small"
|
||||||
>
|
>
|
||||||
<Portal
|
<Portal
|
||||||
containerInfo={
|
containerInfo={
|
||||||
@@ -135,8 +135,8 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
|
|||||||
aria-label="Alert modal"
|
aria-label="Alert modal"
|
||||||
aria-labelledby="pf-modal-part-0 alert-modal-header-label pf-modal-part-1"
|
aria-labelledby="pf-modal-part-0 alert-modal-header-label pf-modal-part-1"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
class="pf-c-modal-box pf-m-md"
|
class="pf-c-modal-box pf-m-sm"
|
||||||
data-ouia-component-id="OUIA-Generated-Modal-medium-1"
|
data-ouia-component-id="OUIA-Generated-Modal-small-1"
|
||||||
data-ouia-component-type="PF4/ModalContent"
|
data-ouia-component-type="PF4/ModalContent"
|
||||||
data-ouia-safe="true"
|
data-ouia-safe="true"
|
||||||
id="pf-modal-part-0"
|
id="pf-modal-part-0"
|
||||||
@@ -277,13 +277,13 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
|
|||||||
isOpen={true}
|
isOpen={true}
|
||||||
labelId="pf-modal-part-1"
|
labelId="pf-modal-part-1"
|
||||||
onClose={[Function]}
|
onClose={[Function]}
|
||||||
ouiaId="OUIA-Generated-Modal-medium-1"
|
ouiaId="OUIA-Generated-Modal-small-1"
|
||||||
ouiaSafe={true}
|
ouiaSafe={true}
|
||||||
showClose={true}
|
showClose={true}
|
||||||
title="Remove Team Access"
|
title="Remove Team Access"
|
||||||
titleIconVariant={null}
|
titleIconVariant={null}
|
||||||
titleLabel=""
|
titleLabel=""
|
||||||
variant="medium"
|
variant="small"
|
||||||
>
|
>
|
||||||
<Backdrop>
|
<Backdrop>
|
||||||
<div
|
<div
|
||||||
@@ -308,20 +308,20 @@ exports[`<DeleteRoleConfirmationModal /> should render initially 1`] = `
|
|||||||
aria-label="Alert modal"
|
aria-label="Alert modal"
|
||||||
aria-labelledby="pf-modal-part-0 alert-modal-header-label pf-modal-part-1"
|
aria-labelledby="pf-modal-part-0 alert-modal-header-label pf-modal-part-1"
|
||||||
className=""
|
className=""
|
||||||
data-ouia-component-id="OUIA-Generated-Modal-medium-1"
|
data-ouia-component-id="OUIA-Generated-Modal-small-1"
|
||||||
data-ouia-component-type="PF4/ModalContent"
|
data-ouia-component-type="PF4/ModalContent"
|
||||||
data-ouia-safe={true}
|
data-ouia-safe={true}
|
||||||
id="pf-modal-part-0"
|
id="pf-modal-part-0"
|
||||||
style={Object {}}
|
style={Object {}}
|
||||||
variant="medium"
|
variant="small"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-describedby="pf-modal-part-2"
|
aria-describedby="pf-modal-part-2"
|
||||||
aria-label="Alert modal"
|
aria-label="Alert modal"
|
||||||
aria-labelledby="pf-modal-part-0 alert-modal-header-label pf-modal-part-1"
|
aria-labelledby="pf-modal-part-0 alert-modal-header-label pf-modal-part-1"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
className="pf-c-modal-box pf-m-md"
|
className="pf-c-modal-box pf-m-sm"
|
||||||
data-ouia-component-id="OUIA-Generated-Modal-medium-1"
|
data-ouia-component-id="OUIA-Generated-Modal-small-1"
|
||||||
data-ouia-component-type="PF4/ModalContent"
|
data-ouia-component-type="PF4/ModalContent"
|
||||||
data-ouia-safe={true}
|
data-ouia-safe={true}
|
||||||
id="pf-modal-part-0"
|
id="pf-modal-part-0"
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ describe('<CredentialDetail />', () => {
|
|||||||
test('should have proper number of delete detail requests', () => {
|
test('should have proper number of delete detail requests', () => {
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('DeleteButton').prop('deleteDetailsRequests')
|
wrapper.find('DeleteButton').prop('deleteDetailsRequests')
|
||||||
).toHaveLength(4);
|
).toHaveLength(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should render details', () => {
|
test('should render details', () => {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ describe('<CredentialList />', () => {
|
|||||||
test('should have proper number of delete detail requests', () => {
|
test('should have proper number of delete detail requests', () => {
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('ToolbarDeleteButton').prop('deleteDetailsRequests')
|
wrapper.find('ToolbarDeleteButton').prop('deleteDetailsRequests')
|
||||||
).toHaveLength(4);
|
).toHaveLength(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should fetch credentials from api and render the in the list', () => {
|
test('should fetch credentials from api and render the in the list', () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import { withI18n } from '@lingui/react';
|
import { withI18n } from '@lingui/react';
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Link, useHistory } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
@@ -16,7 +16,11 @@ import {
|
|||||||
import useRequest, { useDismissableError } from '../../../util/useRequest';
|
import useRequest, { useDismissableError } from '../../../util/useRequest';
|
||||||
import { CredentialTypesAPI } from '../../../api';
|
import { CredentialTypesAPI } from '../../../api';
|
||||||
import { jsonToYaml } from '../../../util/yaml';
|
import { jsonToYaml } from '../../../util/yaml';
|
||||||
import { relatedResourceDeleteRequests } from '../../../util/getRelatedResourceDeleteDetails';
|
import {
|
||||||
|
relatedResourceDeleteRequests,
|
||||||
|
getRelatedResourceDeleteCounts,
|
||||||
|
} from '../../../util/getRelatedResourceDeleteDetails';
|
||||||
|
import ErrorDetail from '../../../components/ErrorDetail';
|
||||||
|
|
||||||
function CredentialTypeDetails({ credentialType, i18n }) {
|
function CredentialTypeDetails({ credentialType, i18n }) {
|
||||||
const { id, name, description, injectors, inputs } = credentialType;
|
const { id, name, description, injectors, inputs } = credentialType;
|
||||||
@@ -33,12 +37,35 @@ function CredentialTypeDetails({ credentialType, i18n }) {
|
|||||||
}, [id, history])
|
}, [id, history])
|
||||||
);
|
);
|
||||||
|
|
||||||
const deleteDetailsRequests = relatedResourceDeleteRequests.credentialType(
|
const {
|
||||||
credentialType,
|
result: { isDeleteDisabled },
|
||||||
i18n
|
error: deleteDetailsError,
|
||||||
|
request: fetchDeleteDetails,
|
||||||
|
} = useRequest(
|
||||||
|
useCallback(async () => {
|
||||||
|
const {
|
||||||
|
results: deleteDetails,
|
||||||
|
error,
|
||||||
|
} = await getRelatedResourceDeleteCounts(
|
||||||
|
relatedResourceDeleteRequests.credentialType(credentialType, i18n)
|
||||||
|
);
|
||||||
|
if (error) {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
if (deleteDetails) {
|
||||||
|
return { isDeleteDisabled: true };
|
||||||
|
}
|
||||||
|
return { isDeleteDisabled: false };
|
||||||
|
}, [credentialType, i18n]),
|
||||||
|
{ isDeleteDisabled: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
const { error, dismissError } = useDismissableError(deleteError);
|
useEffect(() => {
|
||||||
|
fetchDeleteDetails();
|
||||||
|
}, [fetchDeleteDetails]);
|
||||||
|
const { error, dismissError } = useDismissableError(
|
||||||
|
deleteError || deleteDetailsError
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardBody>
|
<CardBody>
|
||||||
@@ -88,11 +115,13 @@ function CredentialTypeDetails({ credentialType, i18n }) {
|
|||||||
name={name}
|
name={name}
|
||||||
modalTitle={i18n._(t`Delete credential type`)}
|
modalTitle={i18n._(t`Delete credential type`)}
|
||||||
onConfirm={deleteCredentialType}
|
onConfirm={deleteCredentialType}
|
||||||
isDisabled={isLoading}
|
isDisabled={isLoading || isDeleteDisabled}
|
||||||
deleteDetailsRequests={deleteDetailsRequests}
|
disabledTooltip={
|
||||||
deleteMessage={i18n._(
|
isDeleteDisabled &&
|
||||||
t`This credential type is currently being used by some credentials. Are you sure you want to delete it?`
|
i18n._(
|
||||||
)}
|
t`This credential type is currently being used by some credentials and cannot be deleted`
|
||||||
|
)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{i18n._(t`Delete`)}
|
{i18n._(t`Delete`)}
|
||||||
</DeleteButton>
|
</DeleteButton>
|
||||||
@@ -105,7 +134,9 @@ function CredentialTypeDetails({ credentialType, i18n }) {
|
|||||||
onClose={dismissError}
|
onClose={dismissError}
|
||||||
title={i18n._(t`Error`)}
|
title={i18n._(t`Error`)}
|
||||||
variant="error"
|
variant="error"
|
||||||
/>
|
>
|
||||||
|
<ErrorDetail error={error} />
|
||||||
|
</AlertModal>
|
||||||
)}
|
)}
|
||||||
</CardBody>
|
</CardBody>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { act } from 'react-dom/test-utils';
|
|||||||
import { createMemoryHistory } from 'history';
|
import { createMemoryHistory } from 'history';
|
||||||
|
|
||||||
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||||
import { CredentialTypesAPI } from '../../../api';
|
import { CredentialTypesAPI, CredentialsAPI } from '../../../api';
|
||||||
import { jsonToYaml } from '../../../util/yaml';
|
import { jsonToYaml } from '../../../util/yaml';
|
||||||
|
|
||||||
import CredentialTypeDetails from './CredentialTypeDetails';
|
import CredentialTypeDetails from './CredentialTypeDetails';
|
||||||
@@ -66,6 +66,10 @@ function expectDetailToMatch(wrapper, label, value) {
|
|||||||
|
|
||||||
describe('<CredentialTypeDetails/>', () => {
|
describe('<CredentialTypeDetails/>', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.unmount();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
test('should render details properly', async () => {
|
test('should render details properly', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
wrapper = mountWithContexts(
|
wrapper = mountWithContexts(
|
||||||
@@ -92,10 +96,36 @@ describe('<CredentialTypeDetails/>', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should have proper number of delete detail requests', () => {
|
test('should disabled delete and show proper tooltip requests', async () => {
|
||||||
expect(
|
CredentialsAPI.read.mockResolvedValue({ data: { count: 15 } });
|
||||||
wrapper.find('DeleteButton').prop('deleteDetailsRequests')
|
await act(async () => {
|
||||||
).toHaveLength(1);
|
wrapper = mountWithContexts(
|
||||||
|
<CredentialTypeDetails credentialType={credentialTypeData} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
|
||||||
|
expect(wrapper.find('DeleteButton').prop('disabledTooltip')).toBe(
|
||||||
|
'This credential type is currently being used by some credentials and cannot be deleted'
|
||||||
|
);
|
||||||
|
expect(wrapper.find('Button[aria-label="Delete"]').prop('isDisabled')).toBe(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
expect(wrapper.find('Tooltip').length).toBe(1);
|
||||||
|
expect(wrapper.find('Tooltip').prop('content')).toBe(
|
||||||
|
'This credential type is currently being used by some credentials and cannot be deleted'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should throw error', async () => {
|
||||||
|
CredentialsAPI.read.mockRejectedValue(new Error('error'));
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<CredentialTypeDetails credentialType={credentialTypeData} />
|
||||||
|
);
|
||||||
|
});
|
||||||
|
wrapper.update();
|
||||||
|
expect(wrapper.find('ErrorDetail').length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('expected api call is made for delete', async () => {
|
test('expected api call is made for delete', async () => {
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ function CredentialTypeList({ i18n }) {
|
|||||||
pluralizedItemName={i18n._(t`Credential Types`)}
|
pluralizedItemName={i18n._(t`Credential Types`)}
|
||||||
deleteDetailsRequests={deleteDetailsRequests}
|
deleteDetailsRequests={deleteDetailsRequests}
|
||||||
deleteMessage={i18n._(
|
deleteMessage={i18n._(
|
||||||
'{numItemsToDelete, plural, one {This credential type is currently being used by some credentials. Are you sure you want to delete it?} other {Deleting these credential types could impact other credentials that rely on them. Are you sure you want to delete anyway?}}',
|
'{numItemsToDelete, plural, one {This credential type is currently being used by some credentials and cannot be deleted.} other {Credential types that are being used by credentials cannot be deleted. Are you sure you want to delete anyway?}}',
|
||||||
{ numItemsToDelete: selected.length }
|
{ numItemsToDelete: selected.length }
|
||||||
)}
|
)}
|
||||||
/>,
|
/>,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
import useRequest, { useDismissableError } from '../../../util/useRequest';
|
import useRequest, { useDismissableError } from '../../../util/useRequest';
|
||||||
import { toTitleCase } from '../../../util/strings';
|
import { toTitleCase } from '../../../util/strings';
|
||||||
import { ExecutionEnvironmentsAPI } from '../../../api';
|
import { ExecutionEnvironmentsAPI } from '../../../api';
|
||||||
|
import { relatedResourceDeleteRequests } from '../../../util/getRelatedResourceDeleteDetails';
|
||||||
|
|
||||||
function ExecutionEnvironmentDetails({ executionEnvironment, i18n }) {
|
function ExecutionEnvironmentDetails({ executionEnvironment, i18n }) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -41,7 +42,10 @@ function ExecutionEnvironmentDetails({ executionEnvironment, i18n }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { error, dismissError } = useDismissableError(deleteError);
|
const { error, dismissError } = useDismissableError(deleteError);
|
||||||
|
const deleteDetailsRequests = relatedResourceDeleteRequests.executionEnvironment(
|
||||||
|
executionEnvironment,
|
||||||
|
i18n
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<DetailList>
|
<DetailList>
|
||||||
@@ -120,6 +124,10 @@ function ExecutionEnvironmentDetails({ executionEnvironment, i18n }) {
|
|||||||
onConfirm={deleteExecutionEnvironment}
|
onConfirm={deleteExecutionEnvironment}
|
||||||
isDisabled={isLoading}
|
isDisabled={isLoading}
|
||||||
ouiaId="delete-button"
|
ouiaId="delete-button"
|
||||||
|
deleteDetailsRequests={deleteDetailsRequests}
|
||||||
|
deleteMessage={i18n._(
|
||||||
|
t`This execution environment is currently being used by other resources. Are you sure you want to delete it?`
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{i18n._(t`Delete`)}
|
{i18n._(t`Delete`)}
|
||||||
</DeleteButton>
|
</DeleteButton>
|
||||||
|
|||||||
@@ -175,4 +175,22 @@ describe('<ExecutionEnvironmentDetails/>', () => {
|
|||||||
|
|
||||||
expect(wrapper.find('Button[aria-label="Delete"]')).toHaveLength(0);
|
expect(wrapper.find('Button[aria-label="Delete"]')).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
test('should have proper number of delete detail requests', async () => {
|
||||||
|
const history = createMemoryHistory({
|
||||||
|
initialEntries: ['/execution_environments/42/details'],
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<ExecutionEnvironmentDetails
|
||||||
|
executionEnvironment={executionEnvironment}
|
||||||
|
/>,
|
||||||
|
{
|
||||||
|
context: { router: { history } },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
wrapper.find('DeleteButton').prop('deleteDetailsRequests')
|
||||||
|
).toHaveLength(2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
import { ExecutionEnvironmentsAPI } from '../../../api';
|
import { ExecutionEnvironmentsAPI } from '../../../api';
|
||||||
import ExecutionEnvironmentList from './ExecutionEnvironmentList';
|
import ExecutionEnvironmentList from './ExecutionEnvironmentList';
|
||||||
|
|
||||||
jest.mock('../../../api/models/ExecutionEnvironments');
|
jest.mock('../../../api');
|
||||||
|
|
||||||
const executionEnvironments = {
|
const executionEnvironments = {
|
||||||
data: {
|
data: {
|
||||||
@@ -143,7 +143,6 @@ describe('<ExecutionEnvironmentList/>', () => {
|
|||||||
wrapper.find('Button[aria-label="Delete"]').prop('onClick')()
|
wrapper.find('Button[aria-label="Delete"]').prop('onClick')()
|
||||||
);
|
);
|
||||||
wrapper.update();
|
wrapper.update();
|
||||||
|
|
||||||
await act(async () =>
|
await act(async () =>
|
||||||
wrapper.find('Button[aria-label="confirm delete"]').prop('onClick')()
|
wrapper.find('Button[aria-label="confirm delete"]').prop('onClick')()
|
||||||
);
|
);
|
||||||
@@ -185,4 +184,17 @@ describe('<ExecutionEnvironmentList/>', () => {
|
|||||||
waitForElement(wrapper, 'ExecutionEnvironmentList', el => el.length > 0);
|
waitForElement(wrapper, 'ExecutionEnvironmentList', el => el.length > 0);
|
||||||
expect(wrapper.find('ToolbarAddButton').length).toBe(0);
|
expect(wrapper.find('ToolbarAddButton').length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should have proper number of delete detail requests', async () => {
|
||||||
|
ExecutionEnvironmentsAPI.read.mockResolvedValue(executionEnvironments);
|
||||||
|
ExecutionEnvironmentsAPI.readOptions.mockResolvedValue({
|
||||||
|
data: { actions: { POST: false } },
|
||||||
|
});
|
||||||
|
await act(async () => {
|
||||||
|
wrapper = mountWithContexts(<ExecutionEnvironmentList />);
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
wrapper.find('ToolbarDeleteButton').prop('deleteDetailsRequests')
|
||||||
|
).toHaveLength(2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import PaginatedTable, {
|
|||||||
import ErrorDetail from '../../../components/ErrorDetail';
|
import ErrorDetail from '../../../components/ErrorDetail';
|
||||||
import AlertModal from '../../../components/AlertModal';
|
import AlertModal from '../../../components/AlertModal';
|
||||||
import DatalistToolbar from '../../../components/DataListToolbar';
|
import DatalistToolbar from '../../../components/DataListToolbar';
|
||||||
|
import { relatedResourceDeleteRequests } from '../../../util/getRelatedResourceDeleteDetails';
|
||||||
import ExecutionEnvironmentsListItem from './ExecutionEnvironmentListItem';
|
import ExecutionEnvironmentsListItem from './ExecutionEnvironmentListItem';
|
||||||
|
|
||||||
const QS_CONFIG = getQSConfig('execution_environments', {
|
const QS_CONFIG = getQSConfig('execution_environments', {
|
||||||
@@ -105,7 +105,10 @@ function ExecutionEnvironmentList({ i18n }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const canAdd = actions && actions.POST;
|
const canAdd = actions && actions.POST;
|
||||||
|
const deleteDetailsRequests = relatedResourceDeleteRequests.executionEnvironment(
|
||||||
|
selected[0],
|
||||||
|
i18n
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageSection>
|
<PageSection>
|
||||||
@@ -181,6 +184,11 @@ function ExecutionEnvironmentList({ i18n }) {
|
|||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
itemsToDelete={selected}
|
itemsToDelete={selected}
|
||||||
pluralizedItemName={i18n._(t`Execution Environments`)}
|
pluralizedItemName={i18n._(t`Execution Environments`)}
|
||||||
|
deleteDetailsRequests={deleteDetailsRequests}
|
||||||
|
deleteMessage={i18n._(
|
||||||
|
'{numItemsToDelete, plural, one {This execution environment is currently being used by other resources. Are you sure you want to delete it?} other {Deleting these execution environemnts could impact other resources that rely on them. Are you sure you want to delete anyway?}}',
|
||||||
|
{ numItemsToDelete: selected.length }
|
||||||
|
)}
|
||||||
/>,
|
/>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ function OrganizationDetail({ i18n, organization }) {
|
|||||||
isDisabled={isLoading}
|
isDisabled={isLoading}
|
||||||
deleteDetailsRequests={deleteDetailsRequests}
|
deleteDetailsRequests={deleteDetailsRequests}
|
||||||
deleteMessage={i18n._(
|
deleteMessage={i18n._(
|
||||||
t`This organization is currently being used some credentials. Are you sure you want to delete it?`
|
t`This organization is currently being by other resources. Are you sure you want to delete it?`
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{i18n._(t`Delete`)}
|
{i18n._(t`Delete`)}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ describe('<OrganizationDetail />', () => {
|
|||||||
|
|
||||||
expect(
|
expect(
|
||||||
component.find('DeleteButton').prop('deleteDetailsRequests')
|
component.find('DeleteButton').prop('deleteDetailsRequests')
|
||||||
).toHaveLength(1);
|
).toHaveLength(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should render the expected instance group', async () => {
|
test('should render the expected instance group', async () => {
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ function OrganizationsList({ i18n }) {
|
|||||||
pluralizedItemName={i18n._(t`Organizations`)}
|
pluralizedItemName={i18n._(t`Organizations`)}
|
||||||
deleteDetailsRequests={deleteDetailsRequests}
|
deleteDetailsRequests={deleteDetailsRequests}
|
||||||
deleteMessage={i18n._(
|
deleteMessage={i18n._(
|
||||||
'{numItemsToDelete, plural, one {This organization is currently being used some credentials. Are you sure you want to delete it?} other {Deleting these organizations could impact some credentials that rely on them. Are you sure you want to delete anyway?}}',
|
'{numItemsToDelete, plural, one {This organization is currently being by other resources. Are you sure you want to delete it?} other {Deleting these organizations could impact other resources that rely on them. Are you sure you want to delete anyway?}}',
|
||||||
{ numItemsToDelete: selected.length }
|
{ numItemsToDelete: selected.length }
|
||||||
)}
|
)}
|
||||||
/>,
|
/>,
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ describe('<OrganizationsList />', () => {
|
|||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
wrapper.find('ToolbarDeleteButton').prop('deleteDetailsRequests')
|
wrapper.find('ToolbarDeleteButton').prop('deleteDetailsRequests')
|
||||||
).toHaveLength(1);
|
).toHaveLength(4);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Items are rendered after loading', async () => {
|
test('Items are rendered after loading', async () => {
|
||||||
|
|||||||
@@ -54,13 +54,13 @@ describe('delete details', () => {
|
|||||||
getRelatedResourceDeleteCounts(
|
getRelatedResourceDeleteCounts(
|
||||||
relatedResourceDeleteRequests.project({ id: 1 }, i18n)
|
relatedResourceDeleteRequests.project({ id: 1 }, i18n)
|
||||||
);
|
);
|
||||||
expect(WorkflowJobTemplatesAPI.read).toBeCalledWith({
|
expect(WorkflowJobTemplateNodesAPI.read).toBeCalledWith({
|
||||||
credentials: 1,
|
unified_job_template: 1,
|
||||||
});
|
});
|
||||||
expect(InventorySourcesAPI.read).toBeCalledWith({
|
expect(InventorySourcesAPI.read).toBeCalledWith({
|
||||||
credentials__id: 1,
|
source_project: 1,
|
||||||
});
|
});
|
||||||
expect(JobTemplatesAPI.read).toBeCalledWith({ credentials: 1 });
|
expect(JobTemplatesAPI.read).toBeCalledWith({ project: 1 });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should call api for templates list', () => {
|
test('should call api for templates list', () => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
UnifiedJobTemplatesAPI,
|
||||||
CredentialsAPI,
|
CredentialsAPI,
|
||||||
InventoriesAPI,
|
InventoriesAPI,
|
||||||
InventorySourcesAPI,
|
InventorySourcesAPI,
|
||||||
@@ -8,6 +9,12 @@ import {
|
|||||||
ProjectsAPI,
|
ProjectsAPI,
|
||||||
WorkflowJobTemplateNodesAPI,
|
WorkflowJobTemplateNodesAPI,
|
||||||
WorkflowJobTemplatesAPI,
|
WorkflowJobTemplatesAPI,
|
||||||
|
CredentialInputSourcesAPI,
|
||||||
|
TeamsAPI,
|
||||||
|
NotificationTemplatesAPI,
|
||||||
|
ExecutionEnvironmentsAPI,
|
||||||
|
ApplicationsAPI,
|
||||||
|
OrganizationsAPI,
|
||||||
} from '../api';
|
} from '../api';
|
||||||
|
|
||||||
export async function getRelatedResourceDeleteCounts(requests) {
|
export async function getRelatedResourceDeleteCounts(requests) {
|
||||||
@@ -65,6 +72,20 @@ export const relatedResourceDeleteRequests = {
|
|||||||
}),
|
}),
|
||||||
label: i18n._(t`Inventory Sources`),
|
label: i18n._(t`Inventory Sources`),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
request: () =>
|
||||||
|
CredentialInputSourcesAPI.read({
|
||||||
|
source_credential: selected.id,
|
||||||
|
}),
|
||||||
|
label: i18n._(t`Credential`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
request: () =>
|
||||||
|
ExecutionEnvironmentsAPI.read({
|
||||||
|
credential: selected.id,
|
||||||
|
}),
|
||||||
|
label: i18n._(t`Execution Environments`),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
credentialType: (selected, i18n) => [
|
credentialType: (selected, i18n) => [
|
||||||
@@ -111,18 +132,21 @@ export const relatedResourceDeleteRequests = {
|
|||||||
{
|
{
|
||||||
request: () =>
|
request: () =>
|
||||||
JobTemplatesAPI.read({
|
JobTemplatesAPI.read({
|
||||||
credentials: selected.id,
|
project: selected.id,
|
||||||
}),
|
}),
|
||||||
label: i18n._(t`Job Templates`),
|
label: i18n._(t`Job Templates`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
request: () => WorkflowJobTemplatesAPI.read({ credentials: selected.id }),
|
request: () =>
|
||||||
|
WorkflowJobTemplateNodesAPI.read({
|
||||||
|
unified_job_template: selected.id,
|
||||||
|
}),
|
||||||
label: i18n._(t`Workflow Job Templates`),
|
label: i18n._(t`Workflow Job Templates`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
request: () =>
|
request: () =>
|
||||||
InventorySourcesAPI.read({
|
InventorySourcesAPI.read({
|
||||||
credentials__id: selected.id,
|
source_project: selected.id,
|
||||||
}),
|
}),
|
||||||
label: i18n._(t`Inventory Sources`),
|
label: i18n._(t`Inventory Sources`),
|
||||||
},
|
},
|
||||||
@@ -134,7 +158,7 @@ export const relatedResourceDeleteRequests = {
|
|||||||
WorkflowJobTemplateNodesAPI.read({
|
WorkflowJobTemplateNodesAPI.read({
|
||||||
unified_job_template: selected.id,
|
unified_job_template: selected.id,
|
||||||
}),
|
}),
|
||||||
label: [i18n._(t`Workflow Job Template Node`)],
|
label: [i18n._(t`Workflow Job Template Nodes`)],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -146,5 +170,87 @@ export const relatedResourceDeleteRequests = {
|
|||||||
}),
|
}),
|
||||||
label: i18n._(t`Credential`),
|
label: i18n._(t`Credential`),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
request: async () =>
|
||||||
|
TeamsAPI.read({
|
||||||
|
organization: selected.id,
|
||||||
|
}),
|
||||||
|
label: i18n._(t`Teams`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
request: async () =>
|
||||||
|
NotificationTemplatesAPI.read({
|
||||||
|
organization: selected.id,
|
||||||
|
}),
|
||||||
|
label: i18n._(t`Notification Templates`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
request: () =>
|
||||||
|
ExecutionEnvironmentsAPI.read({
|
||||||
|
organization: selected.id,
|
||||||
|
}),
|
||||||
|
label: i18n._(t`Execution Environments`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
request: async () =>
|
||||||
|
ProjectsAPI.read({
|
||||||
|
organization: selected.id,
|
||||||
|
}),
|
||||||
|
label: [i18n._(t`Projects`)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
request: () =>
|
||||||
|
InventoriesAPI.read({
|
||||||
|
organization: selected.id,
|
||||||
|
}),
|
||||||
|
label: i18n._(t`Inventories`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
request: () =>
|
||||||
|
ApplicationsAPI.read({
|
||||||
|
organization: selected.id,
|
||||||
|
}),
|
||||||
|
label: i18n._(t`Applications`),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
executionEnvironment: (selected, i18n) => [
|
||||||
|
{
|
||||||
|
request: async () =>
|
||||||
|
UnifiedJobTemplatesAPI.read({
|
||||||
|
execution_environment: selected.id,
|
||||||
|
}),
|
||||||
|
label: [i18n._(t`Templates`)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
request: async () =>
|
||||||
|
ProjectsAPI.read({
|
||||||
|
default_environment: selected.id,
|
||||||
|
}),
|
||||||
|
label: [i18n._(t`Projects`)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
request: async () =>
|
||||||
|
OrganizationsAPI.read({
|
||||||
|
execution_environment: selected.id,
|
||||||
|
}),
|
||||||
|
label: [i18n._(t`Organizations`)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
request: async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await WorkflowJobTemplateNodesAPI.read({
|
||||||
|
execution_environment: selected.id,
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
data.summary_fields.unified_job_template.unified_job_type ===
|
||||||
|
'inventory_update'
|
||||||
|
) {
|
||||||
|
await InventorySourcesAPI.read();
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
|
|
||||||
|
label: [i18n._(t`Organizations`)],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user