fix(storage): refresh operations after resource change
This commit is contained in:
parent
d4ab8f8fb1
commit
421ef23518
|
|
@ -6,7 +6,7 @@ import { DEFAULT_NODE_CAPABILITIES, RESOURCE_NODE_CAPABILITIES } from '@models/f
|
|||
import { HFNode } from '@models/nodes';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
import { isResourceNode, resolveNodeCapabilities, ReteRuntimeContext } from './rete-editor';
|
||||
import { isResourceNode, resolveNodeCapabilities, ReteRuntimeContext, syncResourceLinkPresentation } from './rete-editor';
|
||||
|
||||
function makeNode(typeName: string, nodeFamily: 'block' | 'container' | 'resource'): HFNode {
|
||||
return { data: { typeName, nodeFamily } } as unknown as HFNode;
|
||||
|
|
@ -88,6 +88,45 @@ describe('isResourceNode', () => {
|
|||
});
|
||||
|
||||
describe('the link between a resource and a step that uses it', () => {
|
||||
it('replaces an S3 operation choice with PostgreSQL choices when its resource changes', async () => {
|
||||
const { NodeEditor, ClassicPreset } = await import('rete');
|
||||
const { FLOW_RESOURCE_LINK_PORT_KEY, FLOW_RESOURCE_PORT_KEY } = await import('@models/flow');
|
||||
const editor = new NodeEditor<any>();
|
||||
const socket = new ClassicPreset.Socket('__FLOW_RESOURCE__');
|
||||
const s3 = new ClassicPreset.Node('S3') as any;
|
||||
s3.data = { id: 's3', typeName: 'S3Storage', nodeFamily: 'resource' };
|
||||
s3.addOutput(FLOW_RESOURCE_PORT_KEY, new ClassicPreset.Output(socket));
|
||||
const postgres = new ClassicPreset.Node('PostgreSQL') as any;
|
||||
postgres.data = { id: 'postgres', typeName: 'PostgresStorage', nodeFamily: 'resource' };
|
||||
postgres.addOutput(FLOW_RESOURCE_PORT_KEY, new ClassicPreset.Output(socket));
|
||||
const operation = new ClassicPreset.Node('Operation') as any;
|
||||
operation.data = {
|
||||
id: 'operation', typeName: 'StorageOperation', nodeFamily: 'block', __resourceRefField: 'storageNode',
|
||||
specificConfiguration: { storageNode: 's3', resourceType: 'S3Storage', operationId: 'GET_OBJECT', settings: { key: 'a' } }
|
||||
};
|
||||
operation.addInput(FLOW_RESOURCE_LINK_PORT_KEY, new ClassicPreset.Input(socket, undefined, true));
|
||||
await editor.addNode(s3);
|
||||
await editor.addNode(postgres);
|
||||
await editor.addNode(operation);
|
||||
const runtime = makeRuntime({
|
||||
blocksService: { peekBlockType: vi.fn().mockReturnValue(null) } as any
|
||||
});
|
||||
const area = { update: vi.fn().mockResolvedValue(undefined) } as any;
|
||||
|
||||
const oldLink = new ClassicPreset.Connection(s3, FLOW_RESOURCE_PORT_KEY, operation, FLOW_RESOURCE_LINK_PORT_KEY);
|
||||
await editor.addConnection(oldLink);
|
||||
await editor.removeConnection(oldLink.id);
|
||||
await syncResourceLinkPresentation(editor, area, runtime, oldLink as any);
|
||||
const newLink = new ClassicPreset.Connection(postgres, FLOW_RESOURCE_PORT_KEY, operation, FLOW_RESOURCE_LINK_PORT_KEY);
|
||||
await editor.addConnection(newLink);
|
||||
await syncResourceLinkPresentation(editor, area, runtime, newLink as any);
|
||||
|
||||
expect(operation.data.specificConfiguration).toMatchObject({
|
||||
storageNode: 'postgres', resourceType: 'PostgresStorage', operationId: null, settings: {}
|
||||
});
|
||||
expect(area.update).toHaveBeenCalledWith('node', operation.id);
|
||||
});
|
||||
|
||||
it('is what fills the step\'s resource field, and empties it when the link is gone', async () => {
|
||||
const { NodeEditor, ClassicPreset } = await import('rete');
|
||||
const { exportGraph } = await import('./rete-editor');
|
||||
|
|
|
|||
|
|
@ -846,7 +846,7 @@ async function replaceResourceLink(editor: NodeEditor<HFSchemes>, stepNodeId: st
|
|||
}
|
||||
|
||||
/** Keep a node's visible Resource state in step with the one allowed resource connection. */
|
||||
async function syncResourceLinkPresentation(
|
||||
export async function syncResourceLinkPresentation(
|
||||
editor: NodeEditor<HFSchemes>,
|
||||
area: AreaPlugin<HFSchemes, AreaExtra>,
|
||||
runtime: ReteRuntimeContext,
|
||||
|
|
@ -855,15 +855,23 @@ async function syncResourceLinkPresentation(
|
|||
if (getGraphConnectionKind(connection.sourceOutput, connection.targetInput) !== 'resource') return;
|
||||
|
||||
const target = editor.getNode(connection.target) as HFNode | undefined;
|
||||
const reference = resourceRefOf(runtime, target);
|
||||
if (!target?.data || !reference) return;
|
||||
const targetData = target?.data as Record<string, any> | undefined;
|
||||
// A block rebuilt after a structural setting update may no longer have its descriptor in the
|
||||
// client cache, but addBlockToEditor has already recorded the field that owns this link.
|
||||
const rememberedField = typeof targetData?.['__resourceRefField'] === 'string'
|
||||
? targetData['__resourceRefField']
|
||||
: null;
|
||||
const reference = resourceRefOf(runtime, target)
|
||||
?? (rememberedField ? { field: rememberedField, resourceKind: '' } : null);
|
||||
if (!target || !targetData || !reference) return;
|
||||
|
||||
const activeLink = editor.getConnections().find((candidate) =>
|
||||
candidate.target === target.id && candidate.targetInput === FLOW_RESOURCE_LINK_PORT_KEY);
|
||||
const source = activeLink ? editor.getNode(activeLink.source) as HFNode | undefined : undefined;
|
||||
const resourceId = typeof source?.data?.id === 'string' ? source.data.id : null;
|
||||
const resourceType = typeof source?.data?.typeName === 'string' ? source.data.typeName : null;
|
||||
const targetData = target.data as Record<string, any>;
|
||||
const resourceType = typeof source?.data?.typeName === 'string' && source.data.typeName.trim()
|
||||
? source.data.typeName
|
||||
: null;
|
||||
const configuration = targetData['specificConfiguration'] as Record<string, unknown> | undefined;
|
||||
const resourceChanged = configuration?.[reference.field] !== resourceId;
|
||||
if (!resourceChanged && configuration?.['resourceType'] === resourceType) return;
|
||||
|
|
@ -871,8 +879,8 @@ async function syncResourceLinkPresentation(
|
|||
targetData['specificConfiguration'] = {
|
||||
...(configuration ?? {}),
|
||||
[reference.field]: resourceId,
|
||||
...(targetData['typeName'] === 'StorageOperation' ? { resourceType } : {}),
|
||||
...(resourceChanged && targetData['typeName'] === 'StorageOperation' ? { operationId: null, settings: {} } : {})
|
||||
...(reference.field === 'storageNode' ? { resourceType } : {}),
|
||||
...(resourceChanged && reference.field === 'storageNode' ? { operationId: null, settings: {} } : {})
|
||||
};
|
||||
await area.update('node', target.id);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue