mirror of
https://github.com/ansible/awx.git
synced 2026-01-12 10:30:03 -03:30
adds counts for inventory groups and hosts that are related to an inventory source
This commit is contained in:
parent
be148b5fd4
commit
652e7a500b
@ -22,6 +22,14 @@ class InventorySources extends LaunchUpdateMixin(
|
||||
});
|
||||
}
|
||||
|
||||
readGroups(id) {
|
||||
return this.http.get(`${this.baseUrl}${id}/groups/`);
|
||||
}
|
||||
|
||||
readHosts(id) {
|
||||
return this.http.get(`${this.baseUrl}${id}/hosts/`);
|
||||
}
|
||||
|
||||
destroyGroups(id) {
|
||||
return this.http.delete(`${this.baseUrl}${id}/groups/`);
|
||||
}
|
||||
|
||||
@ -32,7 +32,10 @@ function DeleteButton({
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [deleteMessageError, setDeleteMessageError] = useState();
|
||||
const [deleteDetails, setDeleteDetails] = useState({});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const toggleModal = async isModalOpen => {
|
||||
setIsLoading(true);
|
||||
if (deleteDetailsRequests?.length && isModalOpen) {
|
||||
const { results, error } = await getRelatedResourceDeleteCounts(
|
||||
deleteDetailsRequests
|
||||
@ -43,6 +46,7 @@ function DeleteButton({
|
||||
setDeleteDetails(results);
|
||||
}
|
||||
}
|
||||
setIsLoading(false);
|
||||
setIsOpen(isModalOpen);
|
||||
};
|
||||
|
||||
@ -66,10 +70,13 @@ function DeleteButton({
|
||||
<Tooltip content={disabledTooltip} position="top">
|
||||
<div>
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
spinnerAriaValueText={isLoading ? 'Loading' : undefined}
|
||||
variant={variant || 'secondary'}
|
||||
aria-label={i18n._(t`Delete`)}
|
||||
isDisabled={isDisabled}
|
||||
onClick={() => toggleModal(true)}
|
||||
ouiaId={ouiaId}
|
||||
>
|
||||
{children || i18n._(t`Delete`)}
|
||||
</Button>
|
||||
@ -77,6 +84,8 @@ function DeleteButton({
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
spinnerAriaValueText={isLoading ? 'Loading' : undefined}
|
||||
variant={variant || 'secondary'}
|
||||
aria-label={i18n._(t`Delete`)}
|
||||
isDisabled={isDisabled}
|
||||
|
||||
@ -101,6 +101,7 @@ function ToolbarDeleteButton({
|
||||
const { isKebabified, onKebabModalChange } = useContext(KebabifiedContext);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [deleteDetails, setDeleteDetails] = useState(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [deleteMessageError, setDeleteMessageError] = useState();
|
||||
const handleDelete = () => {
|
||||
@ -109,6 +110,7 @@ function ToolbarDeleteButton({
|
||||
};
|
||||
|
||||
const toggleModal = async isOpen => {
|
||||
setIsLoading(true);
|
||||
setDeleteDetails(null);
|
||||
if (
|
||||
isOpen &&
|
||||
@ -125,7 +127,7 @@ function ToolbarDeleteButton({
|
||||
setDeleteDetails(results);
|
||||
}
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
setIsModalOpen(isOpen);
|
||||
};
|
||||
|
||||
@ -221,8 +223,12 @@ function ToolbarDeleteButton({
|
||||
<DropdownItem
|
||||
key="add"
|
||||
isDisabled={isDisabled}
|
||||
isLoading={isLoading}
|
||||
spinnerAriaValueText={isLoading ? 'Loading' : undefined}
|
||||
component="button"
|
||||
onClick={() => toggleModal(true)}
|
||||
onClick={() => {
|
||||
toggleModal(true);
|
||||
}}
|
||||
>
|
||||
{i18n._(t`Delete`)}
|
||||
</DropdownItem>
|
||||
@ -232,6 +238,8 @@ function ToolbarDeleteButton({
|
||||
<div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
isLoading={isLoading}
|
||||
spinnerAriaValueText={isLoading ? 'Loading' : undefined}
|
||||
aria-label={i18n._(t`Delete`)}
|
||||
onClick={() => toggleModal(true)}
|
||||
isDisabled={isDisabled}
|
||||
|
||||
@ -75,6 +75,7 @@ exports[`<ToolbarDeleteButton /> should render button 1`] = `
|
||||
<Button
|
||||
aria-label="Delete"
|
||||
isDisabled={true}
|
||||
isLoading={false}
|
||||
onClick={[Function]}
|
||||
variant="secondary"
|
||||
>
|
||||
@ -93,13 +94,14 @@ exports[`<ToolbarDeleteButton /> should render button 1`] = `
|
||||
<Button
|
||||
aria-label="Delete"
|
||||
isDisabled={true}
|
||||
isLoading={false}
|
||||
onClick={[Function]}
|
||||
variant="secondary"
|
||||
>
|
||||
<button
|
||||
aria-disabled={true}
|
||||
aria-label="Delete"
|
||||
className="pf-c-button pf-m-secondary pf-m-disabled"
|
||||
className="pf-c-button pf-m-secondary pf-m-disabled pf-m-progress"
|
||||
data-ouia-component-id="OUIA-Generated-Button-secondary-1"
|
||||
data-ouia-component-type="PF4/Button"
|
||||
data-ouia-safe={true}
|
||||
|
||||
@ -6,10 +6,22 @@ import {
|
||||
waitForElement,
|
||||
} from '../../../../testUtils/enzymeHelpers';
|
||||
|
||||
import { ExecutionEnvironmentsAPI } from '../../../api';
|
||||
import {
|
||||
ExecutionEnvironmentsAPI,
|
||||
InventorySourcesAPI,
|
||||
WorkflowJobTemplateNodesAPI,
|
||||
OrganizationsAPI,
|
||||
ProjectsAPI,
|
||||
UnifiedJobTemplatesAPI,
|
||||
} from '../../../api';
|
||||
import ExecutionEnvironmentList from './ExecutionEnvironmentList';
|
||||
|
||||
jest.mock('../../../api');
|
||||
jest.mock('../../../api/models/ExecutionEnvironments');
|
||||
jest.mock('../../../api/models/UnifiedJobTemplates');
|
||||
jest.mock('../../../api/models/Projects');
|
||||
jest.mock('../../../api/models/Organizations');
|
||||
jest.mock('../../../api/models/InventorySources');
|
||||
jest.mock('../../../api/models/WorkflowJobTemplateNodes');
|
||||
|
||||
const executionEnvironments = {
|
||||
data: {
|
||||
@ -43,6 +55,16 @@ describe('<ExecutionEnvironmentList/>', () => {
|
||||
beforeEach(() => {
|
||||
ExecutionEnvironmentsAPI.read.mockResolvedValue(executionEnvironments);
|
||||
ExecutionEnvironmentsAPI.readOptions.mockResolvedValue(options);
|
||||
InventorySourcesAPI.read.mockResolvedValue({
|
||||
data: { results: [{ id: 10000000 }] },
|
||||
});
|
||||
WorkflowJobTemplateNodesAPI.read.mockResolvedValue({ data: { count: 0 } });
|
||||
|
||||
OrganizationsAPI.read.mockResolvedValue({ data: { count: 0 } });
|
||||
|
||||
UnifiedJobTemplatesAPI.read.mockResolvedValue({ data: { count: 0 } });
|
||||
|
||||
ProjectsAPI.read.mockResolvedValue({ data: { count: 0 } });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@ -143,6 +165,12 @@ describe('<ExecutionEnvironmentList/>', () => {
|
||||
wrapper.find('Button[aria-label="Delete"]').prop('onClick')()
|
||||
);
|
||||
wrapper.update();
|
||||
|
||||
await waitForElement(
|
||||
wrapper,
|
||||
'Button[aria-label="confirm delete"]',
|
||||
el => el.length > 0
|
||||
);
|
||||
await act(async () =>
|
||||
wrapper.find('Button[aria-label="confirm delete"]').prop('onClick')()
|
||||
);
|
||||
|
||||
@ -99,7 +99,8 @@ function InventorySourceDetail({ inventorySource, i18n }) {
|
||||
|
||||
const deleteDetailsRequests = relatedResourceDeleteRequests.inventorySource(
|
||||
inventorySource.inventory,
|
||||
i18n
|
||||
i18n,
|
||||
inventorySource
|
||||
);
|
||||
|
||||
const VERBOSITY = {
|
||||
@ -289,7 +290,7 @@ function InventorySourceDetail({ inventorySource, i18n }) {
|
||||
onConfirm={handleDelete}
|
||||
deleteDetailsRequests={deleteDetailsRequests}
|
||||
deleteMessage={i18n._(
|
||||
t`This inventory source is currently being used some workflow job template nodes. Are you sure you want to delete it?`
|
||||
t`This inventory source is currently being used by other resources that rely on it. Are you sure you want to delete it?`
|
||||
)}
|
||||
>
|
||||
{i18n._(t`Delete`)}
|
||||
|
||||
@ -120,7 +120,7 @@ describe('InventorySourceDetail', () => {
|
||||
});
|
||||
expect(
|
||||
wrapper.find('DeleteButton').prop('deleteDetailsRequests')
|
||||
).toHaveLength(1);
|
||||
).toHaveLength(3);
|
||||
});
|
||||
|
||||
test('should hide expected action buttons for users without permissions', async () => {
|
||||
|
||||
@ -146,7 +146,8 @@ function InventorySourceList({ i18n }) {
|
||||
|
||||
const deleteDetailsRequests = relatedResourceDeleteRequests.inventorySource(
|
||||
id,
|
||||
i18n
|
||||
i18n,
|
||||
selected[0]
|
||||
);
|
||||
return (
|
||||
<>
|
||||
@ -182,7 +183,7 @@ function InventorySourceList({ i18n }) {
|
||||
pluralizedItemName={i18n._(t`Inventory Sources`)}
|
||||
deleteDetailsRequests={deleteDetailsRequests}
|
||||
deleteMessage={i18n._(
|
||||
'{numItemsToDelete, plural, one {This inventory source is currently being used by workflow job template nodes. Are you sure you want to delete it?} other {Deleting these inventory sources could impact workflow job template nodes that rely on them. Are you sure you want to delete anyway?}}',
|
||||
'{numItemsToDelete, plural, one {This inventory source is currently being used by other resources that rely on it. Are you sure you want to delete it?} other {Deleting these inventory sources could impact other resources that rely on them. Are you sure you want to delete anyway?}}',
|
||||
{ numItemsToDelete: selected.length }
|
||||
)}
|
||||
/>,
|
||||
|
||||
@ -65,11 +65,13 @@ describe('<InventorySourceList />', () => {
|
||||
beforeEach(async () => {
|
||||
debug = global.console.debug; // eslint-disable-line prefer-destructuring
|
||||
global.console.debug = () => {};
|
||||
InventoriesAPI.readSources.mockResolvedValue(sources);
|
||||
InventoriesAPI.updateSources.mockResolvedValue({
|
||||
data: [{ inventory_source: 1 }],
|
||||
});
|
||||
InventorySourcesAPI.readGroups.mockResolvedValue({ data: { count: 0 } });
|
||||
InventorySourcesAPI.readHosts.mockResolvedValue({ data: { count: 0 } });
|
||||
WorkflowJobTemplateNodesAPI.read.mockResolvedValue({ data: { count: 0 } });
|
||||
InventoriesAPI.readSources.mockResolvedValue(sources);
|
||||
InventorySourcesAPI.readOptions.mockResolvedValue({
|
||||
data: {
|
||||
actions: {
|
||||
@ -131,7 +133,7 @@ describe('<InventorySourceList />', () => {
|
||||
test('should have proper number of delete detail requests', async () => {
|
||||
expect(
|
||||
wrapper.find('ToolbarDeleteButton').prop('deleteDetailsRequests')
|
||||
).toHaveLength(1);
|
||||
).toHaveLength(3);
|
||||
});
|
||||
|
||||
test('source data should render properly', async () => {
|
||||
|
||||
@ -112,25 +112,24 @@ export const relatedResourceDeleteRequests = {
|
||||
},
|
||||
],
|
||||
|
||||
inventorySource: (inventoryId, i18n) => [
|
||||
inventorySource: (inventoryId, i18n, inventorySource) => [
|
||||
{
|
||||
request: async () => {
|
||||
try {
|
||||
const { data } = await InventoriesAPI.updateSources(inventoryId);
|
||||
let total = 0;
|
||||
await Promise.all(
|
||||
data.map(async datum => {
|
||||
const {
|
||||
data: { count },
|
||||
} = await WorkflowJobTemplateNodesAPI.read({
|
||||
|
||||
const results = await Promise.all(
|
||||
data.map(async datum =>
|
||||
WorkflowJobTemplateNodesAPI.read({
|
||||
unified_job_template: datum.inventory_source,
|
||||
});
|
||||
if (count > 0) {
|
||||
total += count;
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
);
|
||||
console.log(total, 'total');
|
||||
const total = results.reduce(
|
||||
({ data: { count: acc } }, { data: { count: cur } }) => acc + cur,
|
||||
{ data: { count: 0 } }
|
||||
);
|
||||
|
||||
return { data: { count: total } };
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
@ -138,6 +137,14 @@ export const relatedResourceDeleteRequests = {
|
||||
},
|
||||
label: i18n._(t`Workflow Job Template Nodes`),
|
||||
},
|
||||
{
|
||||
request: async () => InventorySourcesAPI.readGroups(inventorySource.id),
|
||||
label: i18n._(t`Groups`),
|
||||
},
|
||||
{
|
||||
request: async () => InventorySourcesAPI.readHosts(inventorySource.id),
|
||||
label: i18n._(t`Hosts`),
|
||||
},
|
||||
],
|
||||
|
||||
project: (selected, i18n) => [
|
||||
@ -255,18 +262,18 @@ export const relatedResourceDeleteRequests = {
|
||||
} = await InventorySourcesAPI.read({
|
||||
execution_environment: selected.id,
|
||||
});
|
||||
let total = 0;
|
||||
await Promise.all(
|
||||
results.map(async result => {
|
||||
const {
|
||||
data: { count },
|
||||
} = await WorkflowJobTemplateNodesAPI.read({
|
||||
|
||||
const responses = await Promise.all(
|
||||
results.map(result =>
|
||||
WorkflowJobTemplateNodesAPI.read({
|
||||
unified_job_template: result.id,
|
||||
});
|
||||
if (count > 0) {
|
||||
total += count;
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const total = responses.reduce(
|
||||
({ data: { count: acc } }, { data: { count: cur } }) => acc + cur,
|
||||
{ data: { count: 0 } }
|
||||
);
|
||||
return { data: { count: total } };
|
||||
} catch (err) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user