fix(v5): preserve firewall directions when editing ports

This commit is contained in:
Andras Bacsai
2026-07-06 17:52:04 +02:00
parent 6ae45684f9
commit 9db103cc77
2 changed files with 38 additions and 12 deletions
+21 -12
View File
@@ -28,6 +28,23 @@ export function pruneConnectionPortsByDirection(
};
}
function connectionPortsPayload(connection: CanvasConnection): Record<string, number[]> {
return Object.fromEntries(
Object.entries(connection.portsByDirection).map(([direction, ports]) => [
direction,
ports.map((port) => Number(port)).filter((port) => Number.isInteger(port)),
]),
);
}
function preserveActiveDirection(connection: CanvasConnection, activeConnection: CanvasConnection): CanvasConnection {
return {
...connection,
fromApplicationId: activeConnection.fromApplicationId,
toApplicationId: activeConnection.toApplicationId,
};
}
function normalizeConnection(connection: V5ResourceConnection): CanvasConnection {
return {
...connection,
@@ -124,27 +141,20 @@ export function useCanvasConnections(initialConnections: V5ResourceConnection[],
const persistConnectionPorts = useCallback(
async (updatedConnection: CanvasConnection, previousConnection: CanvasConnection): Promise<void> => {
const portsByDirection = Object.fromEntries(
Object.entries(pruneConnectionPortsByDirection(updatedConnection)).map(([direction, ports]) => [
direction,
ports.map((port) => Number(port)).filter((port) => Number.isInteger(port)),
]),
);
await runOptimisticUpdate<CanvasConnection>({
apply: () => replaceConnection(updatedConnection),
rollback: () => replaceConnection(previousConnection),
request: async () => {
const response = await canvasRequest(`/v5/resource-connections/${updatedConnection.id}`, {
method: 'PATCH',
body: { ports_by_direction: portsByDirection },
body: { ports_by_direction: connectionPortsPayload(updatedConnection) },
});
return parseConnectionResponse(response, 'Could not save allowed ports.');
},
fallbackErrorMessage: 'Could not save allowed ports.',
notify,
onSuccess: replaceConnection,
onSuccess: (nextConnection) => replaceConnection(preserveActiveDirection(nextConnection, updatedConnection)),
});
},
[notify, replaceConnection],
@@ -198,12 +208,11 @@ export function useCanvasConnections(initialConnections: V5ResourceConnection[],
...connection,
fromApplicationId,
toApplicationId,
portsByDirection: pruneConnectionPortsByDirection(connection, fromApplicationId, toApplicationId),
};
void persistConnectionPorts(updatedConnection, connection);
replaceConnection(updatedConnection);
},
[persistConnectionPorts],
[replaceConnection],
);
const setConnectionPortDraft = useCallback((connectionId: string, value: string): void => {
@@ -930,3 +930,20 @@ it('hardens v5 dashboard fetches and websocket merges against failures', functio
expect(strpos($addNginxSource, '!response.ok'))->not->toBeFalse();
});
it('preserves all v5 resource connection firewall directions when editing ports', function () {
$connectionsHook = file_get_contents(resource_path('js/v5/lib/use-canvas-connections.ts'));
$updateDirectionSource = substr(
$connectionsHook,
strpos($connectionsHook, 'const updateConnectionDirection'),
strpos($connectionsHook, 'const setConnectionPortDraft') - strpos($connectionsHook, 'const updateConnectionDirection'),
);
expect($connectionsHook)
->toContain('function connectionPortsPayload(connection: CanvasConnection)')
->toContain('body: { ports_by_direction: connectionPortsPayload(updatedConnection) }')
->toContain('preserveActiveDirection(nextConnection, updatedConnection)')
->and($updateDirectionSource)
->toContain('replaceConnection(updatedConnection)')
->not->toContain('persistConnectionPorts(updatedConnection, connection)');
});