B: Fix cyclic block hierarchy
This commit is contained in:
parent
854d32aea1
commit
2cb7c44f3a
|
@ -123,7 +123,7 @@ class UpdateBlockSerializer(StrictSerializer):
|
||||||
raise serializers.ValidationError({
|
raise serializers.ValidationError({
|
||||||
'parent': msg.parentNotInOSS()
|
'parent': msg.parentNotInOSS()
|
||||||
})
|
})
|
||||||
if parent == attrs['target']:
|
if attrs['target'].pk in _collect_ancestors(parent):
|
||||||
raise serializers.ValidationError({
|
raise serializers.ValidationError({
|
||||||
'parent': msg.blockCyclicHierarchy()
|
'parent': msg.blockCyclicHierarchy()
|
||||||
})
|
})
|
||||||
|
|
|
@ -266,3 +266,36 @@ class TestOssBlocks(EndpointTester):
|
||||||
|
|
||||||
data['layout'] = self.layout_data
|
data['layout'] = self.layout_data
|
||||||
self.executeOK(data=data)
|
self.executeOK(data=data)
|
||||||
|
|
||||||
|
|
||||||
|
@decl_endpoint('/api/oss/{item}/update-block', method='patch')
|
||||||
|
def test_update_block_cyclic_parent(self):
|
||||||
|
self.populateData()
|
||||||
|
# block1 -> block2
|
||||||
|
# Try to set block1's parent to block2 (should fail, direct cycle)
|
||||||
|
data = {
|
||||||
|
'target': self.block1.pk,
|
||||||
|
'item_data': {
|
||||||
|
'title': self.block1.title,
|
||||||
|
'description': self.block1.description,
|
||||||
|
'parent': self.block2.pk
|
||||||
|
},
|
||||||
|
}
|
||||||
|
self.executeBadData(data=data, item=self.owned_id)
|
||||||
|
|
||||||
|
# Create a deeper hierarchy: block1 -> block2 -> block3
|
||||||
|
self.block3 = self.owned.create_block(title='3', parent=self.block2)
|
||||||
|
# Try to set block1's parent to block3 (should fail, indirect cycle)
|
||||||
|
data['item_data']['parent'] = self.block3.pk
|
||||||
|
self.executeBadData(data=data, item=self.owned_id)
|
||||||
|
|
||||||
|
# Setting block2's parent to block1 (valid, as block1 is not a descendant)
|
||||||
|
data = {
|
||||||
|
'target': self.block2.pk,
|
||||||
|
'item_data': {
|
||||||
|
'title': self.block2.title,
|
||||||
|
'description': self.block2.description,
|
||||||
|
'parent': self.block1.pk
|
||||||
|
},
|
||||||
|
}
|
||||||
|
self.executeOK(data=data, item=self.owned_id)
|
||||||
|
|
|
@ -66,14 +66,18 @@ export function DlgEditBlock() {
|
||||||
<Controller
|
<Controller
|
||||||
name='item_data.parent'
|
name='item_data.parent'
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field }) => (
|
render={({ field }) => {
|
||||||
<SelectParent
|
const descendantNodeIDs = manager.oss.hierarchy.expandAllOutputs([target.nodeID]);
|
||||||
items={manager.oss.blocks.filter(block => block.id !== target.id)}
|
descendantNodeIDs.push(target.nodeID);
|
||||||
value={field.value ? manager.oss.blockByID.get(field.value) ?? null : null}
|
return (
|
||||||
placeholder='Родительский блок'
|
<SelectParent
|
||||||
onChange={value => field.onChange(value ? value.id : null)}
|
items={manager.oss.blocks.filter(block => !descendantNodeIDs.includes(block.nodeID))}
|
||||||
/>
|
value={field.value ? manager.oss.blockByID.get(field.value) ?? null : null}
|
||||||
)}
|
placeholder='Родительский блок'
|
||||||
|
onChange={value => field.onChange(value ? value.id : null)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextArea
|
<TextArea
|
||||||
|
|
Loading…
Reference in New Issue
Block a user