-
Notifications
You must be signed in to change notification settings - Fork 0
Fix/workflow #58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix/workflow #58
Conversation
- Introduced workflowUpdateSchema for validating workflow updates. - Updated userRoutes to handle workflow updates, including error handling and user authentication checks. - Refactored API calls in the frontend to support PUT requests for updating workflows. - Improved workflow loading logic in WorkflowCanvas to manage nodes and edges effectively. - Added a save button in WorkflowCanvas for persisting workflow changes.
- Removed commented-out code for the getCredentials route in userRoutes for clarity. - Improved error handling in userRoutes to provide more informative messages. - Updated WorkflowCanvas to handle trigger nodes and action placeholders more effectively. - Enhanced workflow loading logic to safeguard against missing data and ensure proper node and edge management.
📝 WalkthroughWalkthroughThis PR implements full-stack workflow update functionality by adding a Changes
Sequence DiagramsequenceDiagram
participant User
participant Browser as Browser (UI)
participant API as Frontend API
participant Backend as Backend Handler
participant DB as Database
User->>Browser: Click Save Button
activate Browser
Browser->>API: put({nodes, edges, workflowId})
deactivate Browser
activate API
API->>Backend: PUT /user/workflow/update
deactivate API
activate Backend
Backend->>Backend: Validate with workflowUpdateSchema
Backend->>Backend: Check user auth & workflow exists
Backend->>DB: Update Workflow (nodes, edges)
deactivate Backend
activate DB
DB->>DB: Update Edges field
DB-->>Backend: Confirmation
deactivate DB
Backend-->>API: {status: 200, workflow: {...}}
activate API
API-->>Browser: Response data
deactivate API
activate Browser
Browser->>Browser: Log success / update state
deactivate Browser
Additionally, on page load: sequenceDiagram
participant Page as Page Mount
participant API as Frontend API
participant Backend as Backend Handler
participant DB as Database
Page->>API: GET /user/workflow/:id
activate API
API->>Backend: GET request
deactivate API
activate Backend
Backend->>DB: Fetch Workflow
deactivate Backend
activate DB
DB-->>Backend: {nodes, edges, Trigger, ...}
deactivate DB
Backend-->>API: Workflow data
activate API
API-->>Page: Data
deactivate API
activate Page
Page->>Page: Transform nodes (Trigger + Actions + Placeholder)
Page->>Page: Auto-wire edges from Trigger → First Action
Page->>Page: Render nodes and edges
deactivate Page
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds functionality to save and load workflow edges (connections between nodes) in the workflow canvas. The changes enable persisting the visual graph structure of workflows to the database.
Changes:
- Added
EdgesJSON field to the Workflow database schema - Created validation schema for workflow updates with nodes and edges
- Implemented workflow loading logic that reconstructs trigger nodes, action nodes, and edges from database
- Added save button and handler to persist workflow changes
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 19 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/db/prisma/schema.prisma | Added Edges field to Workflow model to store edge data |
| packages/common/src/index.ts | Added workflowUpdateSchema for validating workflow update requests |
| apps/web/app/workflows/[id]/page.tsx | Implemented workflow loading on mount, save functionality, and UI save button |
| apps/web/app/lib/api.ts | Added PUT method for workflow updates |
| apps/http-backend/src/routes/userRoutes/userRoutes.ts | Implemented workflow update endpoint with validation and authorization |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export const workflowUpdateSchema = z.object({ | ||
| nodes : z.any().optional(), | ||
| edges : z.any().optional(), | ||
| workflowId : z.string() | ||
| }) |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using z.any() for validation defeats the purpose of schema validation and allows any data type to pass through. This should be replaced with a proper schema that validates the expected structure of nodes and edges, such as z.array() or a specific object schema that matches the React Flow node/edge structure.
| export const workflowUpdateSchema = z.object({ | |
| nodes : z.any().optional(), | |
| edges : z.any().optional(), | |
| workflowId : z.string() | |
| }) | |
| const ReactFlowNodeSchema = z | |
| .object({ | |
| id: z.string(), | |
| type: z.string().optional(), | |
| data: z.unknown().optional(), | |
| position: z | |
| .object({ | |
| x: z.number(), | |
| y: z.number(), | |
| }) | |
| .optional(), | |
| selected: z.boolean().optional(), | |
| draggable: z.boolean().optional(), | |
| }) | |
| .passthrough(); | |
| const ReactFlowEdgeSchema = z | |
| .object({ | |
| id: z.string(), | |
| source: z.string(), | |
| target: z.string(), | |
| type: z.string().optional(), | |
| label: z.string().optional(), | |
| data: z.unknown().optional(), | |
| }) | |
| .passthrough(); | |
| export const workflowUpdateSchema = z.object({ | |
| nodes: z.array(ReactFlowNodeSchema).optional(), | |
| edges: z.array(ReactFlowEdgeSchema).optional(), | |
| workflowId: z.string(), | |
| }); |
| console.log("THe Nodes are ", payload.nodes) | ||
| console.log("The payload in handleSave is ", payload); |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Multiple debug console.log statements have been added throughout the code (lines 57, 63, 65, 621, 622). These should be removed before merging to production as they clutter the console output and are not useful for end users.
|
|
||
| if (!req.user) { | ||
| return res.status(statusCodes.BAD_REQUEST).json({ | ||
| message: "User is not logged in ", | ||
| }); | ||
| } | ||
| const Data = req.body; | ||
| const ParsedData = WorkflowSchema.safeParse(Data); | ||
| const UserID = req.user.id; | ||
| // const UserID = "343c9a0a-9c3f-40d0-81de-9a5969e03f92"; | ||
| // Ensure that the required fields are present in the parsed data and create the workflow properly. | ||
| if (!ParsedData.success) { | ||
| return res.status(statusCodes.BAD_REQUEST).json({ | ||
| message: "Incorrect Workflow Inputs", | ||
| }); | ||
| } try { |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The try block wraps only a portion of the code, leaving the validation logic (lines 244-259) outside error handling. This creates inconsistent error handling behavior. Either move the entire function logic inside the try block or handle errors for the validation section separately.
|
|
||
| // 1. SAFEGUARD: Default to empty arrays to prevent crashes | ||
| const dbNodes = workflows.data.Data.nodes || []; | ||
| const dbEdges = workflows.data.Data.Edges || []; |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a case inconsistency issue: the database schema uses "Edges" (PascalCase), but the frontend code references both "Edges" (line 61) and "edges" (lowercase in the API call). This inconsistency, combined with the schema naming issue, could lead to data not being properly saved or retrieved. The casing should be standardized across the entire stack.
| const dbEdges = workflows.data.Data.Edges || []; | |
| const dbEdges = workflows.data.Data.edges || []; |
| const dbNodes = workflows.data.Data.nodes || []; | ||
| const dbEdges = workflows.data.Data.Edges || []; | ||
| const Trigger = workflows.data.Data.Trigger; | ||
| console.log("This is the trigger Data is",workflows.data.Data); |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The console.log statement has a grammatical error with improper spacing in "the trigger Data is" (double space). Consider revising to: "Trigger Data:".
| console.log("This is the trigger Data is",workflows.data.Data); | |
| console.log("Trigger Data:", workflows.data.Data); |
| status WorkflowStatus? | ||
| isEmpty Boolean? @default(true) | ||
| nodes Node[] | ||
| Edges Json? |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The field name "Edges" uses PascalCase, which is inconsistent with the other fields in the model that use camelCase (e.g., "isEmpty", "nodes", "user"). For consistency with Prisma conventions and the existing schema, this should be renamed to "edges".
| Edges Json? | |
| edges Json? @map("Edges") |
| const loadWorkflows = async () => { | ||
| try { | ||
| const workflows = await api.workflows.get(workflowId); | ||
| console.log("This is the New Triggering trigger Data is",workflows.data.Data.Trigger.AvailableTriggerID); |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The console.log statement contains a grammatical error with improper spacing in "the New Triggering" (double space) and awkward phrasing. Consider revising to: "New Trigger Data - AvailableTriggerID:".
| console.log("This is the New Triggering trigger Data is",workflows.data.Data.Trigger.AvailableTriggerID); | |
| console.log("New Trigger Data - AvailableTriggerID:", workflows.data.Data.Trigger.AvailableTriggerID); |
| } catch (e: any) { | ||
| console.log("Internal server error from creating aworkflow", e); | ||
| return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({ | ||
| message: "Internal Server Error from CCreating Workflow", |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message contains a spelling error: "CCreating" should be "Creating" (duplicate C).
| message: "Internal Server Error from CCreating Workflow", | |
| message: "Internal Server Error from Creating Workflow", |
| error: parsedData.error | ||
| }) | ||
| } | ||
| const workflowId = parsedData.data.workflowId; | ||
| if (!req.user) { | ||
| return res.status(statusCodes.UNAUTHORIZED).json({ | ||
| message: "User Not Authenticated" | ||
| }) | ||
| } | ||
| const userId = req.user.id | ||
| try { | ||
| const workflowValid = await prismaClient.workflow.findFirst({ | ||
| where: { id: workflowId, userId: userId } | ||
| }) | ||
| if (!workflowValid) { | ||
| return res.status(statusCodes.BAD_REQUEST).json({ | ||
| message: "Didn't find the workflow" | ||
| }) | ||
| } | ||
| const update = await prismaClient.workflow.update({ | ||
| where: { id: workflowId, userId: userId }, | ||
| data: { | ||
| // Nodes: parsedData.data.nodes, | ||
| Edges: parsedData.data.edges | ||
| } | ||
| }) | ||
| return res.status(statusCodes.ACCEPTED).json({ | ||
| message: "Workflow Updated Succesfuly", | ||
| Data: update | ||
| }) | ||
| } catch (error: any) { | ||
| console.log("Error updating workflow:", error); | ||
| return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({ | ||
| message: "Internal Server Error from Updating workflow", | ||
| error: error instanceof Error ? error.message : "Unknown error" | ||
| }) | ||
| } | ||
| }) |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid automated semicolon insertion (94% of all statements in the enclosing script have an explicit semicolon).
| error: parsedData.error | |
| }) | |
| } | |
| const workflowId = parsedData.data.workflowId; | |
| if (!req.user) { | |
| return res.status(statusCodes.UNAUTHORIZED).json({ | |
| message: "User Not Authenticated" | |
| }) | |
| } | |
| const userId = req.user.id | |
| try { | |
| const workflowValid = await prismaClient.workflow.findFirst({ | |
| where: { id: workflowId, userId: userId } | |
| }) | |
| if (!workflowValid) { | |
| return res.status(statusCodes.BAD_REQUEST).json({ | |
| message: "Didn't find the workflow" | |
| }) | |
| } | |
| const update = await prismaClient.workflow.update({ | |
| where: { id: workflowId, userId: userId }, | |
| data: { | |
| // Nodes: parsedData.data.nodes, | |
| Edges: parsedData.data.edges | |
| } | |
| }) | |
| return res.status(statusCodes.ACCEPTED).json({ | |
| message: "Workflow Updated Succesfuly", | |
| Data: update | |
| }) | |
| } catch (error: any) { | |
| console.log("Error updating workflow:", error); | |
| return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({ | |
| message: "Internal Server Error from Updating workflow", | |
| error: error instanceof Error ? error.message : "Unknown error" | |
| }) | |
| } | |
| }) | |
| error: parsedData.error, | |
| }); | |
| } | |
| const workflowId = parsedData.data.workflowId; | |
| if (!req.user) { | |
| return res.status(statusCodes.UNAUTHORIZED).json({ | |
| message: "User Not Authenticated", | |
| }); | |
| } | |
| const userId = req.user.id; | |
| try { | |
| const workflowValid = await prismaClient.workflow.findFirst({ | |
| where: { id: workflowId, userId: userId }, | |
| }); | |
| if (!workflowValid) { | |
| return res.status(statusCodes.BAD_REQUEST).json({ | |
| message: "Didn't find the workflow", | |
| }); | |
| } | |
| const update = await prismaClient.workflow.update({ | |
| where: { id: workflowId, userId: userId }, | |
| data: { | |
| // Nodes: parsedData.data.nodes, | |
| Edges: parsedData.data.edges, | |
| }, | |
| }); | |
| return res.status(statusCodes.ACCEPTED).json({ | |
| message: "Workflow Updated Succesfuly", | |
| Data: update, | |
| }); | |
| } catch (error: any) { | |
| console.log("Error updating workflow:", error); | |
| return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({ | |
| message: "Internal Server Error from Updating workflow", | |
| error: error instanceof Error ? error.message : "Unknown error", | |
| }); | |
| } | |
| }); |
| nodes : z.any().optional(), | ||
| edges : z.any().optional(), | ||
| workflowId : z.string() | ||
| }) |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid automated semicolon insertion (90% of all statements in the enclosing script have an explicit semicolon).
| }) | |
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@apps/http-backend/src/routes/userRoutes/userRoutes.ts`:
- Around line 420-426: The update handler currently calls
prismaClient.workflow.update and only saves Edges while Nodes are ignored;
either persist incoming nodes or remove them from the payload. To fix, inspect
parsedData.data.nodes in the same route handling the workflow update (or
validate via workflowUpdateSchema) and choose one: (A) Persist nodes by using
the Node model with the workflowId—e.g., delete or upsert existing Node records
for the given workflowId and bulk create/update from parsedData.data.nodes after
prismaClient.workflow.update, referencing prismaClient.node and
workflowId/Nodes; or (B) strip nodes from the update payload and route node
changes to the existing /update/node endpoint instead, returning a clear
response indicating nodes must be updated via that endpoint. Ensure you
reference prismaClient.workflow.update, parsedData.data.nodes,
prismaClient.node, and the /update/node route when implementing the chosen
approach.
In `@apps/web/app/workflows/`[id]/page.tsx:
- Around line 53-87: The code creates and uses Trigger and builds triggerNode
before verifying Trigger exists, causing potential runtime crashes; move the
null/undefined check for Trigger (from workflows.data.Data.Trigger) immediately
after retrieving workflows (before any access to Trigger.id, Trigger.position,
Trigger.name, or Trigger.data), return early and call console.error if missing,
then proceed to construct triggerNode and other logic; also remove the stray '+'
character before the console.error and ensure functions referenced
(handleNodeConfigure, api.workflows.get, triggerNode) are only invoked after the
null check.
- Around line 170-173: The effect uses handleNodeConfigure inside its closures
but doesn't list it as a dependency; refactor handleNodeConfigure into a stable
memoized function using React's useCallback (e.g., wrap the existing
handleNodeConfigure that calls setSelectedNode and setConfigOpen with
useCallback and an appropriate dependency array) and then add
handleNodeConfigure to the effect's dependency list (so the effect becomes
dependent on [workflowId, handleNodeConfigure]); this ensures the effect and
loadWorkflows closures reference a stable function and avoids stale-closure
bugs.
🧹 Nitpick comments (11)
packages/db/prisma/schema.prisma (1)
91-91: Consider using camelCase for consistency with other data fields.The
Edgesfield uses PascalCase, but similar data fields likeconfig(line 86) andstatus(line 88) use camelCase. PascalCase in this model is typically reserved for relation fields (e.g.,Trigger,Node).Suggested change
- Edges Json? + edges Json?Note: If you adopt this change, remember to update the backend code that references
Edgesto useedgesinstead.packages/common/src/index.ts (2)
53-57: Consider adding type validation fornodesandedgesinstead ofz.any().Using
z.any()bypasses Zod's type-safety benefits. If malformed data is passed, errors will only surface at runtime during database operations or UI rendering rather than at validation time.Suggested improvement
export const workflowUpdateSchema = z.object({ - nodes : z.any().optional(), - edges : z.any().optional(), - workflowId : z.string() -}) + nodes: z.array(z.object({ + id: z.string(), + type: z.string().optional(), + position: z.object({ x: z.number(), y: z.number() }).optional(), + data: z.any().optional(), + })).optional(), + edges: z.array(z.object({ + id: z.string(), + source: z.string(), + target: z.string(), + type: z.string().optional(), + })).optional(), + workflowId: z.string(), +});
2-2: Unused import.The
numberimport from"zod/v4"is not used anywhere in this file.Remove unused import
-import { number } from "zod/v4";apps/http-backend/src/routes/userRoutes/userRoutes.ts (2)
393-409: Authentication check should precede validation.The auth check (lines 405-409) happens after body validation (lines 396-403). This inconsistency with other routes (e.g.,
/create/workflowat line 245) means unauthenticated requests waste server resources on validation before rejection.Reorder checks
router.put("/workflow/update", userMiddleware, async (req: AuthRequest, res: Response) => { + if (!req.user) { + return res.status(statusCodes.UNAUTHORIZED).json({ + message: "User Not Authenticated" + }) + } + const userId = req.user.id const data = req.body; const parsedData = workflowUpdateSchema.safeParse(data); if (!parsedData.success) { return res.status(statusCodes.BAD_REQUEST).json({ message: "Incorrect Input", error: parsedData.error }) } const workflowId = parsedData.data.workflowId; - if (!req.user) { - return res.status(statusCodes.UNAUTHORIZED).json({ - message: "User Not Authenticated" - }) - } - const userId = req.user.id try {
255-281: Formatting issues affect readability.The closing brace for the validation check and
tryblock start are on the same line (line 259), and there are extra blank lines at the end (lines 281-283).Improved formatting
if (!ParsedData.success) { return res.status(statusCodes.BAD_REQUEST).json({ message: "Incorrect Workflow Inputs", }); - } try { + } + try { const createWorkflow = await prismaClient.workflow.create({ // ... }); return res.status(statusCodes.CREATED).json({ message: "Workflow Created Successfully", Data: createWorkflow, }); } catch (e: any) { console.log("Internal server error from creating aworkflow", e); return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({ message: "Internal Server Error from CCreating Workflow", error: e instanceof Error ? e.message : "Unknown error" }); } - } - - + } );apps/web/app/lib/api.ts (1)
25-32: Consider typing thedataparameter.Using
anyloses type safety. SinceworkflowUpdateSchemais already defined, you can leverage its inferred type.Add type safety
+import { workflowUpdateSchema } from "@repo/common/zod"; +import z from "zod"; + // In the workflows object: - put: async (data: any) => { + put: async (data: z.infer<typeof workflowUpdateSchema>) => { return await axios.put(`${BACKEND_URL}/user/workflow/update`, data, { withCredentials: true, headers: { "Content-Type": "application/json" }, }) }apps/web/app/workflows/[id]/page.tsx (5)
176-380: Remove commented-out code.This ~200 line block of commented-out code clutters the file and reduces maintainability. Git history preserves previous implementations if ever needed.
615-634: Unusedpayloadvariable and missing user feedback.The
payloadobject is created (lines 616-620) and logged but not used in the API call (lines 624-628) which reconstructs the same object. Also, users receive no visual feedback on save success/failure.Use payload variable and add feedback
const handleSave = async () => { const payload = { workflowId: workflowId, nodes: nodes, edges: edges }; - console.log("THe Nodes are ", payload.nodes) - console.log("The payload in handleSave is ", payload); try { - const response = await api.workflows.put({ - workflowId: workflowId, - nodes : nodes, - edges: edges - }); - // Optionally, you can show a message or update UI on success + const response = await api.workflows.put(payload); console.log("Workflow updated successfully:", response.data); + // TODO: Add success toast/notification } catch (error: any) { setError(error); + // TODO: Add error toast/notification } }
648-654: Mixed styling approach and potential z-index issues.The Save button mixes Tailwind classes with inline styles. Placing it inside
<ReactFlow>may cause z-index conflicts with the Controls component.Move button outside ReactFlow and use consistent styling
</ReactFlow> + <button + onClick={handleSave} + className="fixed bottom-4 right-40 border bg-white text-black font-bold p-4 z-50 shadow-lg px-12 rounded-2xl" + > + Save + </button> {/* Inside ReactFlow, remove: */} - <button - onClick={handleSave} - className=" bottom-4 border bg-white text-black font-bold p-4 z-50 shadow-lg px-12 rounded-2xl" - style={{ position: 'fixed', bottom: '1rem', right: '10rem' }} - > - Save - </button>
679-687: Consider removing debug console.log statements.Multiple debug log statements exist throughout this file. Consider removing or replacing with a proper logging utility before merging to production.
3-3: Unused importact.The
actimport from React is not used in this file.Remove unused import
-import { useState, useEffect, act } from "react"; +import { useState, useEffect } from "react";
| const update = await prismaClient.workflow.update({ | ||
| where: { id: workflowId, userId: userId }, | ||
| data: { | ||
| // Nodes: parsedData.data.nodes, | ||
| Edges: parsedData.data.edges | ||
| } | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Find workflowUpdateSchema definition
fd -t f -e ts -e js | xargs grep -l "workflowUpdateSchema" | head -20Repository: Dev-Pross/BuildFlow
Length of output: 145
🏁 Script executed:
# Check the imports and full context of userRoutes.ts around line 420
cat -n apps/http-backend/src/routes/userRoutes/userRoutes.ts | sed -n '1,30p'Repository: Dev-Pross/BuildFlow
Length of output: 1245
🏁 Script executed:
# Look for Prisma schema to understand Workflow model structure
fd schema.prismaRepository: Dev-Pross/BuildFlow
Length of output: 95
🏁 Script executed:
# Check frontend code reference
test -f apps/web/app/workflows/[id]/page.tsx && cat -n apps/web/app/workflows/[id]/page.tsx | sed -n '610,630p' || echo "File not found at expected path"Repository: Dev-Pross/BuildFlow
Length of output: 767
🏁 Script executed:
# Find workflowUpdateSchema definition in common package
fd -t f -e ts -e js packages/common | xargs grep -A 10 "workflowUpdateSchema" | head -50Repository: Dev-Pross/BuildFlow
Length of output: 428
🏁 Script executed:
# Read the Prisma schema to understand Workflow model
cat -n packages/db/prisma/schema.prisma | grep -A 30 "model Workflow"Repository: Dev-Pross/BuildFlow
Length of output: 2532
🏁 Script executed:
# Get more context on workflowUpdateSchema - check full definition
rg "workflowUpdateSchema.*=" packages/common -A 15Repository: Dev-Pross/BuildFlow
Length of output: 869
🏁 Script executed:
# Get the full context of the workflow update route (lines 393-438)
cat -n apps/http-backend/src/routes/userRoutes/userRoutes.ts | sed -n '393,440p'Repository: Dev-Pross/BuildFlow
Length of output: 1844
🏁 Script executed:
# Check if there's a separate node update endpoint or how nodes are meant to be updated
rg "prismaClient.node" apps/http-backend/src/routes/userRoutes/userRoutes.ts -A 5 -B 2Repository: Dev-Pross/BuildFlow
Length of output: 647
🏁 Script executed:
# Look at the Node model in Prisma schema
cat -n packages/db/prisma/schema.prisma | grep -A 20 "^model Node"Repository: Dev-Pross/BuildFlow
Length of output: 45
🏁 Script executed:
# Find the Node model definition in the Prisma schema
rg "^model Node" packages/db/prisma/schema.prisma -A 20Repository: Dev-Pross/BuildFlow
Length of output: 2174
🏁 Script executed:
# Check if there's a separate endpoint for updating nodes
rg "router.put.*node\|router.patch.*node" apps/http-backend/src/routes/userRoutes/userRoutes.ts -iRepository: Dev-Pross/BuildFlow
Length of output: 45
🏁 Script executed:
# Look for how nodes are persisted - check for any node update logic in the workflow update or elsewhere
rg "updateMany.*node\|createMany.*node" apps/http-backend/src/routes/userRoutes/ -iRepository: Dev-Pross/BuildFlow
Length of output: 45
🏁 Script executed:
# Look at the workflow create endpoint to see how nodes are handled there
cat -n apps/http-backend/src/routes/userRoutes/userRoutes.ts | sed -n '244,290p'Repository: Dev-Pross/BuildFlow
Length of output: 1892
🏁 Script executed:
# Check if there's a workflow creation logic that handles nodes
rg "prismaClient.node.create\|prismaClient.node.updateMany" apps/http-backend/src/routes/userRoutes/userRoutes.ts -B 5 -A 10Repository: Dev-Pross/BuildFlow
Length of output: 45
🏁 Script executed:
# Look for any workflow-related route that might handle node updates
rg "router\.(post|put|patch)" apps/http-backend/src/routes/userRoutes/userRoutes.ts | grep -i "node\|workflow"Repository: Dev-Pross/BuildFlow
Length of output: 235
🏁 Script executed:
# Check how nodes are actually created - look for the node creation logic when a workflow is created
rg "prismaClient.node.create" apps/http-backend/src/routes/userRoutes/userRoutes.ts -B 10 -A 5Repository: Dev-Pross/BuildFlow
Length of output: 796
🏁 Script executed:
# Look for any endpoint that handles node creation in bulk
rg "createMany\|updateMany" apps/http-backend/src/routes/userRoutes/userRoutes.ts -B 5 -A 5Repository: Dev-Pross/BuildFlow
Length of output: 45
🏁 Script executed:
# Check all endpoints in userRoutes to see if there's a separate node update mechanism
rg "^router\.(post|put|patch|delete)" apps/http-backend/src/routes/userRoutes/userRoutes.ts | head -30Repository: Dev-Pross/BuildFlow
Length of output: 311
🏁 Script executed:
# Let me check what the complete workflow update route does and if there's any node handling elsewhere
cat -n apps/http-backend/src/routes/userRoutes/userRoutes.ts | sed -n '393,450p'Repository: Dev-Pross/BuildFlow
Length of output: 2185
🏁 Script executed:
# Check if there's any documentation or comments about the intended behavior
rg "workflow.*update\|update.*workflow" apps/http-backend/src/routes/userRoutes/userRoutes.ts -i -B 3 -A 3Repository: Dev-Pross/BuildFlow
Length of output: 45
🏁 Script executed:
# Look at the workspace - see if there's another file handling node updates
fd -t f -e ts "routes" apps/http-backend/src | head -20Repository: Dev-Pross/BuildFlow
Length of output: 161
🏁 Script executed:
# Check the nodes.routes.ts file to understand node update logic
cat -n apps/http-backend/src/routes/nodes.routes.tsRepository: Dev-Pross/BuildFlow
Length of output: 4017
🏁 Script executed:
# Also check how the frontend handles node updates - are they sent separately?
rg "api.nodes\|api.workflows" apps/web/app/workflows/[id]/page.tsx -B 2 -A 5Repository: Dev-Pross/BuildFlow
Length of output: 45
🏁 Script executed:
# Search for API endpoints in the web app - check the api client implementation
fd -t f -e ts -e js "api" apps/web | grep -E "(client|api)" | head -20Repository: Dev-Pross/BuildFlow
Length of output: 86
🏁 Script executed:
# Look for workflow update API calls in the web app
rg "workflows.put\|api.put.*workflow" apps/web -B 3 -A 5Repository: Dev-Pross/BuildFlow
Length of output: 45
🏁 Script executed:
# Check if nodes are being updated separately through a different endpoint
rg "nodes.put\|nodes.update\|api.put.*node" apps/web -B 3 -A 5Repository: Dev-Pross/BuildFlow
Length of output: 45
🏁 Script executed:
# Check the API client implementation
cat -n apps/web/app/lib/api.tsRepository: Dev-Pross/BuildFlow
Length of output: 3532
🏁 Script executed:
# Look at the entire handleSave function to understand the full flow
cat -n apps/web/app/workflows/[id]/page.tsx | sed -n '615,650p'Repository: Dev-Pross/BuildFlow
Length of output: 1356
Nodes in the update payload are being ignored and not persisted.
The workflowUpdateSchema accepts nodes, and the frontend sends them (lines 618-627 in the page component), but the workflow update route only saves Edges while the Nodes field remains commented out (line 423).
The current Prisma schema stores nodes as separate Node records with a workflowId relation, not as a JSON field on Workflow. If nodes are sent in this update request, they should either be:
- Persisted by updating individual
Noderecords in the database, or - Removed from the payload if nodes are managed through a separate endpoint (
/update/node)
Without handling the nodes data, any node updates from the UI are lost.
🤖 Prompt for AI Agents
In `@apps/http-backend/src/routes/userRoutes/userRoutes.ts` around lines 420 -
426, The update handler currently calls prismaClient.workflow.update and only
saves Edges while Nodes are ignored; either persist incoming nodes or remove
them from the payload. To fix, inspect parsedData.data.nodes in the same route
handling the workflow update (or validate via workflowUpdateSchema) and choose
one: (A) Persist nodes by using the Node model with the workflowId—e.g., delete
or upsert existing Node records for the given workflowId and bulk create/update
from parsedData.data.nodes after prismaClient.workflow.update, referencing
prismaClient.node and workflowId/Nodes; or (B) strip nodes from the update
payload and route node changes to the existing /update/node endpoint instead,
returning a clear response indicating nodes must be updated via that endpoint.
Ensure you reference prismaClient.workflow.update, parsedData.data.nodes,
prismaClient.node, and the /update/node route when implementing the chosen
approach.
| useEffect(() => { | ||
| const loadWorkflows = async () => { | ||
| try { | ||
| const workflows = await api.workflows.get(workflowId); | ||
| console.log("This is the New Triggering trigger Data is",workflows.data.Data.Trigger.AvailableTriggerID); | ||
|
|
||
| // 1. SAFEGUARD: Default to empty arrays to prevent crashes | ||
| const dbNodes = workflows.data.Data.nodes || []; | ||
| const dbEdges = workflows.data.Data.Edges || []; | ||
| const Trigger = workflows.data.Data.Trigger; | ||
| console.log("This is the trigger Data is",workflows.data.Data); | ||
|
|
||
| console.log("The Data of Trigger is",Trigger) | ||
| // 2. CREATE TRIGGER NODE | ||
| // We assume the trigger is always the starting point | ||
| const triggerNode = { | ||
| id: Trigger.id, | ||
| type: "customNode", | ||
| // Default to top center if no position exists | ||
| position: Trigger.position || { x: 250, y: 50 }, | ||
| data: { | ||
| label: Trigger.name || Trigger.data?.label || "Trigger", | ||
| icon: Trigger.data?.icon || "⚡", // Lightning icon for trigger | ||
| nodeType: "trigger", | ||
| isConfigured: true, | ||
| onConfigure: () => handleNodeConfigure({ | ||
| id: Trigger.id, | ||
| // ... pass your trigger config props here | ||
| }) | ||
| }, | ||
| }; | ||
| if (!Trigger) { | ||
| + console.error("No trigger found in workflow data"); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Null check for Trigger happens after it's already used.
The code accesses Trigger properties (lines 57, 62, 65, 68-83) before the null check at line 84. If Trigger is null/undefined, this will cause a runtime crash.
Additionally, line 85 has a stray + character: + console.error.
🐛 Proposed fix - move null check before usage
useEffect(() => {
const loadWorkflows = async () => {
try {
const workflows = await api.workflows.get(workflowId);
- console.log("This is the New Triggering trigger Data is",workflows.data.Data.Trigger.AvailableTriggerID);
-
// 1. SAFEGUARD: Default to empty arrays to prevent crashes
const dbNodes = workflows.data.Data.nodes || [];
const dbEdges = workflows.data.Data.Edges || [];
const Trigger = workflows.data.Data.Trigger;
- console.log("This is the trigger Data is",workflows.data.Data);
- console.log("The Data of Trigger is",Trigger)
+ // 2. CHECK TRIGGER EXISTS FIRST
+ if (!Trigger) {
+ console.error("No trigger found in workflow data");
+ return;
+ }
+
+ console.log("Trigger Data:", Trigger);
+
- // 2. CREATE TRIGGER NODE
+ // 3. CREATE TRIGGER NODE
// We assume the trigger is always the starting point
const triggerNode = {
id: Trigger.id,
// ... rest of triggerNode creation
};
- if (!Trigger) {
- + console.error("No trigger found in workflow data");
- return;
- }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect(() => { | |
| const loadWorkflows = async () => { | |
| try { | |
| const workflows = await api.workflows.get(workflowId); | |
| console.log("This is the New Triggering trigger Data is",workflows.data.Data.Trigger.AvailableTriggerID); | |
| // 1. SAFEGUARD: Default to empty arrays to prevent crashes | |
| const dbNodes = workflows.data.Data.nodes || []; | |
| const dbEdges = workflows.data.Data.Edges || []; | |
| const Trigger = workflows.data.Data.Trigger; | |
| console.log("This is the trigger Data is",workflows.data.Data); | |
| console.log("The Data of Trigger is",Trigger) | |
| // 2. CREATE TRIGGER NODE | |
| // We assume the trigger is always the starting point | |
| const triggerNode = { | |
| id: Trigger.id, | |
| type: "customNode", | |
| // Default to top center if no position exists | |
| position: Trigger.position || { x: 250, y: 50 }, | |
| data: { | |
| label: Trigger.name || Trigger.data?.label || "Trigger", | |
| icon: Trigger.data?.icon || "⚡", // Lightning icon for trigger | |
| nodeType: "trigger", | |
| isConfigured: true, | |
| onConfigure: () => handleNodeConfigure({ | |
| id: Trigger.id, | |
| // ... pass your trigger config props here | |
| }) | |
| }, | |
| }; | |
| if (!Trigger) { | |
| + console.error("No trigger found in workflow data"); | |
| return; | |
| } | |
| useEffect(() => { | |
| const loadWorkflows = async () => { | |
| try { | |
| const workflows = await api.workflows.get(workflowId); | |
| // 1. SAFEGUARD: Default to empty arrays to prevent crashes | |
| const dbNodes = workflows.data.Data.nodes || []; | |
| const dbEdges = workflows.data.Data.Edges || []; | |
| const Trigger = workflows.data.Data.Trigger; | |
| // 2. CHECK TRIGGER EXISTS FIRST | |
| if (!Trigger) { | |
| console.error("No trigger found in workflow data"); | |
| return; | |
| } | |
| console.log("Trigger Data:", Trigger); | |
| // 3. CREATE TRIGGER NODE | |
| // We assume the trigger is always the starting point | |
| const triggerNode = { | |
| id: Trigger.id, | |
| type: "customNode", | |
| // Default to top center if no position exists | |
| position: Trigger.position || { x: 250, y: 50 }, | |
| data: { | |
| label: Trigger.name || Trigger.data?.label || "Trigger", | |
| icon: Trigger.data?.icon || "⚡", // Lightning icon for trigger | |
| nodeType: "trigger", | |
| isConfigured: true, | |
| onConfigure: () => handleNodeConfigure({ | |
| id: Trigger.id, | |
| // ... pass your trigger config props here | |
| }) | |
| }, | |
| }; |
🤖 Prompt for AI Agents
In `@apps/web/app/workflows/`[id]/page.tsx around lines 53 - 87, The code creates
and uses Trigger and builds triggerNode before verifying Trigger exists, causing
potential runtime crashes; move the null/undefined check for Trigger (from
workflows.data.Data.Trigger) immediately after retrieving workflows (before any
access to Trigger.id, Trigger.position, Trigger.name, or Trigger.data), return
early and call console.error if missing, then proceed to construct triggerNode
and other logic; also remove the stray '+' character before the console.error
and ensure functions referenced (handleNodeConfigure, api.workflows.get,
triggerNode) are only invoked after the null check.
| }; | ||
|
|
||
| loadWorkflows(); | ||
| }, [workflowId]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '150,190p'Repository: Dev-Pross/BuildFlow
Length of output: 1803
🏁 Script executed:
cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '1,50p'Repository: Dev-Pross/BuildFlow
Length of output: 1859
🏁 Script executed:
cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '80,175p'Repository: Dev-Pross/BuildFlow
Length of output: 3889
🏁 Script executed:
rg -n "handleNodeConfigure" apps/web/app/workflows/\[id\]/page.tsx -A 5Repository: Dev-Pross/BuildFlow
Length of output: 1998
🏁 Script executed:
cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '60,175p'Repository: Dev-Pross/BuildFlow
Length of output: 4934
🏁 Script executed:
cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '54,75p'Repository: Dev-Pross/BuildFlow
Length of output: 1279
🏁 Script executed:
cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '50,60p'Repository: Dev-Pross/BuildFlow
Length of output: 576
🏁 Script executed:
cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '380,390p'Repository: Dev-Pross/BuildFlow
Length of output: 361
🏁 Script executed:
cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '23,48p'Repository: Dev-Pross/BuildFlow
Length of output: 1046
🏁 Script executed:
rg -n "eslint" apps/web/app/workflows/\[id\]/page.tsxRepository: Dev-Pross/BuildFlow
Length of output: 45
🏁 Script executed:
# Check if there's a .eslintrc or eslint config in the repo
find apps/web -name ".eslintrc*" -o -name "eslint.config.*" | head -5Repository: Dev-Pross/BuildFlow
Length of output: 88
🏁 Script executed:
cat apps/web/eslint.config.jsRepository: Dev-Pross/BuildFlow
Length of output: 201
🏁 Script executed:
# Check the exhaustive-deps rule configuration
rg -n "react-hooks" apps/web/Repository: Dev-Pross/BuildFlow
Length of output: 45
🏁 Script executed:
# Check if there are any existing ESLint rule files or configs at the workspace level
find . -path ./node_modules -prune -o -name ".eslintrc*" -print | grep -v node_modules | head -10Repository: Dev-Pross/BuildFlow
Length of output: 77
🏁 Script executed:
cat .eslintrc.jsRepository: Dev-Pross/BuildFlow
Length of output: 373
🏁 Script executed:
# Let's verify the actual behavior by checking how the effect works
# Check what happens with the callbacks in the nodes
cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '53,110p'Repository: Dev-Pross/BuildFlow
Length of output: 2764
handleNodeConfigure should be added to the dependency array or wrapped in useCallback.
The function is captured in closures within the effect (lines 78 and 99) and should be listed as a dependency. While the current code works because handleNodeConfigure only calls stable setters, wrapping it with useCallback would follow best practices and prevent potential issues if the function logic changes.
const handleNodeConfigure = useCallback((node: any) => {
setSelectedNode(node);
setConfigOpen(true);
}, []);
// Then add handleNodeConfigure to the dependency array:
}, [workflowId, handleNodeConfigure]);
🤖 Prompt for AI Agents
In `@apps/web/app/workflows/`[id]/page.tsx around lines 170 - 173, The effect uses
handleNodeConfigure inside its closures but doesn't list it as a dependency;
refactor handleNodeConfigure into a stable memoized function using React's
useCallback (e.g., wrap the existing handleNodeConfigure that calls
setSelectedNode and setConfigOpen with useCallback and an appropriate dependency
array) and then add handleNodeConfigure to the effect's dependency list (so the
effect becomes dependent on [workflowId, handleNodeConfigure]); this ensures the
effect and loadWorkflows closures reference a stable function and avoids
stale-closure bugs.
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.