mirror of
https://github.com/ansible/awx.git
synced 2026-03-29 06:45:09 -02:30
Disallows disassociate of hubrid type instances from controlplane instance group
Introduce new pattern for is_valid_removal Makes disassociate error message a bit more dynamic
This commit is contained in:
@@ -18,6 +18,7 @@ function DisassociateButton({
|
||||
modalTitle = t`Disassociate?`,
|
||||
onDisassociate,
|
||||
verifyCannotDisassociate = true,
|
||||
isProtectedInstanceGroup = false,
|
||||
}) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { isKebabified, onKebabModalChange } = useContext(KebabifiedContext);
|
||||
@@ -37,7 +38,10 @@ function DisassociateButton({
|
||||
return !item.summary_fields?.user_capabilities?.delete;
|
||||
}
|
||||
function cannotDisassociateInstances(item) {
|
||||
return item.node_type === 'control';
|
||||
return (
|
||||
item.node_type === 'control' ||
|
||||
(isProtectedInstanceGroup && item.node_type === 'hybrid')
|
||||
);
|
||||
}
|
||||
|
||||
const cannotDisassociate = itemsToDisassociate.some(
|
||||
@@ -73,11 +77,7 @@ function DisassociateButton({
|
||||
|
||||
let isDisabled = false;
|
||||
if (verifyCannotDisassociate) {
|
||||
isDisabled =
|
||||
itemsToDisassociate.length === 0 ||
|
||||
itemsToDisassociate.some(cannotDisassociate);
|
||||
} else {
|
||||
isDisabled = itemsToDisassociate.length === 0;
|
||||
isDisabled = itemsToDisassociate.some(cannotDisassociate);
|
||||
}
|
||||
|
||||
// NOTE: Once PF supports tooltips on disabled elements,
|
||||
@@ -89,7 +89,7 @@ function DisassociateButton({
|
||||
<DropdownItem
|
||||
key="add"
|
||||
aria-label={t`disassociate`}
|
||||
isDisabled={isDisabled}
|
||||
isDisabled={isDisabled || !itemsToDisassociate.length}
|
||||
component="button"
|
||||
ouiaId="disassociate-tooltip"
|
||||
onClick={() => setIsOpen(true)}
|
||||
@@ -108,7 +108,7 @@ function DisassociateButton({
|
||||
variant="secondary"
|
||||
aria-label={t`Disassociate`}
|
||||
onClick={() => setIsOpen(true)}
|
||||
isDisabled={isDisabled}
|
||||
isDisabled={isDisabled || !itemsToDisassociate.length}
|
||||
>
|
||||
{t`Disassociate`}
|
||||
</Button>
|
||||
|
||||
@@ -124,5 +124,21 @@ describe('<DisassociateButton />', () => {
|
||||
);
|
||||
expect(wrapper.find('button[disabled]')).toHaveLength(1);
|
||||
});
|
||||
test('should disable button when selected items contain instances thaat are hybrid and are inside a protected instances', () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<DisassociateButton
|
||||
onDisassociate={() => {}}
|
||||
isProectedInstanceGroup
|
||||
itemsToDelete={[
|
||||
{
|
||||
id: 1,
|
||||
hostname: 'awx',
|
||||
node_type: 'control',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.find('button[disabled]')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -275,10 +275,11 @@ function InstanceDetails({ setBreadcrumb, instanceGroup }) {
|
||||
</Tooltip>
|
||||
{me.is_superuser && instance.node_type !== 'control' && (
|
||||
<DisassociateButton
|
||||
verifyCannotDisassociate={!me.is_superuser}
|
||||
verifyCannotDisassociate={instanceGroup.name === 'controlplane'}
|
||||
key="disassociate"
|
||||
onDisassociate={disassociateInstance}
|
||||
itemsToDisassociate={[instance]}
|
||||
isProtectedInstanceGroup={instanceGroup.name === 'controlplane'}
|
||||
modalTitle={t`Disassociate instance from instance group?`}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -31,7 +31,7 @@ const QS_CONFIG = getQSConfig('instance', {
|
||||
order_by: 'hostname',
|
||||
});
|
||||
|
||||
function InstanceList() {
|
||||
function InstanceList({ instanceGroup }) {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const location = useLocation();
|
||||
const { id: instanceGroupId } = useParams();
|
||||
@@ -224,13 +224,15 @@ function InstanceList() {
|
||||
]
|
||||
: []),
|
||||
<DisassociateButton
|
||||
verifyCannotDisassociate={selected.some(
|
||||
(s) => s.node_type === 'control'
|
||||
)}
|
||||
verifyCannotDisassociate={
|
||||
selected.some((s) => s.node_type === 'control') ||
|
||||
instanceGroup.name === 'controlplane'
|
||||
}
|
||||
key="disassociate"
|
||||
onDisassociate={handleDisassociate}
|
||||
itemsToDisassociate={selected}
|
||||
modalTitle={t`Disassociate instance from instance group?`}
|
||||
isProtectedInstanceGroup={instanceGroup.name === 'controlplane'}
|
||||
/>,
|
||||
<HealthCheckButton
|
||||
isDisabled={!canAdd}
|
||||
|
||||
@@ -123,7 +123,7 @@ describe('<InstanceList/>', () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<Route path="/instance_groups/:id/instances">
|
||||
<InstanceList />
|
||||
<InstanceList instanceGroup={{ name: 'Alex' }} />
|
||||
</Route>,
|
||||
{
|
||||
context: {
|
||||
|
||||
@@ -21,7 +21,7 @@ function Instances({ setBreadcrumb, instanceGroup }) {
|
||||
/>
|
||||
</Route>
|
||||
<Route key="instanceList" path="/instance_groups/:id/instances">
|
||||
<InstanceList />
|
||||
<InstanceList instanceGroup={instanceGroup} />
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
|
||||
@@ -186,8 +186,3 @@ export function regExp() {
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export function protectedResourceName(message, names = []) {
|
||||
return (value) =>
|
||||
names.some((name) => value.trim() === `${name}`) ? message : undefined;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
regExp,
|
||||
requiredEmail,
|
||||
validateTime,
|
||||
protectedResourceName,
|
||||
} from './validators';
|
||||
|
||||
describe('validators', () => {
|
||||
@@ -188,21 +187,4 @@ describe('validators', () => {
|
||||
expect(validateTime()('12.15 PM')).toEqual('Invalid time format');
|
||||
expect(validateTime()('12;15 PM')).toEqual('Invalid time format');
|
||||
});
|
||||
test('protectedResourceName should validate properly', () => {
|
||||
expect(
|
||||
protectedResourceName('failed validation', ['Alex'])('Apollo')
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
protectedResourceName('failed validation', ['Alex', 'Athena'])('alex')
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
protectedResourceName('failed validation', ['Alex', 'Athena'])('Alex')
|
||||
).toEqual('failed validation');
|
||||
expect(
|
||||
protectedResourceName('failed validation', ['Alex'])('Alex')
|
||||
).toEqual('failed validation');
|
||||
expect(
|
||||
protectedResourceName('failed validation', ['Alex'])('Alex ')
|
||||
).toEqual('failed validation');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user