( data: UpdateWorkflowGroupData, requestId: string )
| 219 | * every row's `data`. |
| 220 | */ |
| 221 | export async function updateWorkflowGroup( |
| 222 | data: UpdateWorkflowGroupData, |
| 223 | requestId: string |
| 224 | ): Promise<TableDefinition> { |
| 225 | const mappingUpdates = data.mappingUpdates ?? [] |
| 226 | |
| 227 | // Phase 1 (no lock): when there are mapping updates, load the workflow once to |
| 228 | // resolve each remap's new leaf type. Kept OFF the advisory-lock critical |
| 229 | // section so concurrent group edits on the same table don't time out waiting |
| 230 | // on this DB load. Best-effort — a resolution failure leaves column types |
| 231 | // unchanged (workflow deleted, block removed). The result is applied against |
| 232 | // the fresh schema under the lock in phase 2. |
| 233 | const remapLeafTypeByColumn = new Map<string, ColumnDefinition['type']>() |
| 234 | // The workflow id the leaf types above were resolved against. Phase 2 only |
| 235 | // applies the resolved types if the group still points at this workflow under |
| 236 | // the lock — a concurrent `workflowId` change would make them stale. |
| 237 | let resolvedForWorkflowId: string | undefined |
| 238 | if (mappingUpdates.length > 0) { |
| 239 | try { |
| 240 | const preTable = await getTableById(data.tableId) |
| 241 | const preGroup = preTable?.schema.workflowGroups?.find((g) => g.id === data.groupId) |
| 242 | const targetWorkflowId = data.workflowId ?? preGroup?.workflowId |
| 243 | if (targetWorkflowId) { |
| 244 | resolvedForWorkflowId = targetWorkflowId |
| 245 | const [ |
| 246 | { loadWorkflowFromNormalizedTables }, |
| 247 | { flattenWorkflowOutputs }, |
| 248 | { columnTypeForLeaf }, |
| 249 | ] = await Promise.all([ |
| 250 | import('@/lib/workflows/persistence/utils'), |
| 251 | import('@/lib/workflows/blocks/flatten-outputs'), |
| 252 | import('@/lib/table/column-naming'), |
| 253 | ]) |
| 254 | const normalized = await loadWorkflowFromNormalizedTables(targetWorkflowId) |
| 255 | if (normalized) { |
| 256 | const blocks = Object.values(normalized.blocks ?? {}).map((b) => ({ |
| 257 | id: b.id, |
| 258 | type: b.type, |
| 259 | name: b.name, |
| 260 | triggerMode: (b as { triggerMode?: boolean }).triggerMode, |
| 261 | subBlocks: b.subBlocks as Record<string, unknown> | undefined, |
| 262 | })) |
| 263 | const flattened = flattenWorkflowOutputs(blocks, normalized.edges ?? []) |
| 264 | const flatByKey = new Map(flattened.map((f) => [`${f.blockId}::${f.path}`, f])) |
| 265 | for (const u of mappingUpdates) { |
| 266 | const match = flatByKey.get(`${u.blockId}::${u.path}`) |
| 267 | if (!match) continue |
| 268 | const newType = columnTypeForLeaf(match.leafType) |
| 269 | if (newType) remapLeafTypeByColumn.set(u.columnName, newType) |
| 270 | } |
| 271 | } |
| 272 | } |
| 273 | } catch (err) { |
| 274 | logger.warn( |
| 275 | `[${requestId}] Could not resolve new leaf types for remap on group ${data.groupId}; leaving column types unchanged:`, |
| 276 | err |
| 277 | ) |
| 278 | } |
no test coverage detected