-
Notifications
You must be signed in to change notification settings - Fork 0
feat: implement execution registry and execution routes for node exec… #66
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,61 @@ | ||||||||||||||||||||
| import { Response, Request, Router } from "express"; | ||||||||||||||||||||
| import { AuthRequest, userMiddleware } from "./userMiddleware.js"; | ||||||||||||||||||||
| import { ExecuteNode, statusCodes } from "@repo/common/zod"; | ||||||||||||||||||||
| import { prismaClient } from "@repo/db"; | ||||||||||||||||||||
| import { ExecutionRegister } from '@repo/nodes' | ||||||||||||||||||||
| export const execRouter: Router = Router() | ||||||||||||||||||||
|
|
||||||||||||||||||||
| execRouter.post('/node', userMiddleware, async(req: AuthRequest, res: Response)=>{ | ||||||||||||||||||||
| try{ | ||||||||||||||||||||
| if(!req.user){ | ||||||||||||||||||||
| return res.status(statusCodes.BAD_REQUEST).json({ | ||||||||||||||||||||
| message: "User is not logged in ", | ||||||||||||||||||||
| }); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| const data = req.body; | ||||||||||||||||||||
| const dataSafe = ExecuteNode.safeParse(data) | ||||||||||||||||||||
| if(!dataSafe.success){ | ||||||||||||||||||||
| return res.status(statusCodes.BAD_REQUEST).json({ | ||||||||||||||||||||
| message: "Invalid input" | ||||||||||||||||||||
| }) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| const nodeData = await prismaClient.node.findFirst({ | ||||||||||||||||||||
| where: {id: dataSafe.data.NodeId}, | ||||||||||||||||||||
| include: { | ||||||||||||||||||||
| AvailableNode: true | ||||||||||||||||||||
| } | ||||||||||||||||||||
| }) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if(nodeData){ | ||||||||||||||||||||
| const type = nodeData.AvailableNode.type | ||||||||||||||||||||
| const config = dataSafe.data.Config ? dataSafe.data.Config : nodeData.config // for test api data prefered fist then config in db | ||||||||||||||||||||
| console.log(`config and type: ${JSON.stringify(config)} & ${type}`) | ||||||||||||||||||||
| const context = { | ||||||||||||||||||||
| userId: req.user.sub || "", | ||||||||||||||||||||
| config: config | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
Comment on lines
+33
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing The context object doesn't include 🐛 Proposed fix - include credentials const context = {
userId: req.user.sub || "",
- config: config
+ config: config,
+ credentialsId: nodeData.CredentialsID
}Note: Verify that 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| const executionResult = await ExecutionRegister.execute(type, context) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| console.log(`Execution result: ${executionResult}`) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if(executionResult) | ||||||||||||||||||||
| return res.status(statusCodes.ACCEPTED).json({ | ||||||||||||||||||||
| message: `${nodeData.name} node execution done` , | ||||||||||||||||||||
| data: executionResult | ||||||||||||||||||||
| }) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| return res.status(statusCodes.FORBIDDEN).json({ | ||||||||||||||||||||
| message: `${nodeData.name} node execution failed` | ||||||||||||||||||||
| }) | ||||||||||||||||||||
|
Comment on lines
+41
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Logic error: Response always returns 202 regardless of execution success. The condition 🐛 Proposed fix- if(executionResult)
+ if(executionResult.success)
return res.status(statusCodes.ACCEPTED).json({
message: `${nodeData.name} node execution done` ,
data: executionResult
})
return res.status(statusCodes.FORBIDDEN).json({
- message: `${nodeData.name} node execution failed`
+ message: `${nodeData.name} node execution failed`,
+ error: executionResult.error
})🤖 Prompt for AI Agents |
||||||||||||||||||||
| } | ||||||||||||||||||||
| return res.status(statusCodes.NOT_FOUND).json({ | ||||||||||||||||||||
| message: `${dataSafe.data.NodeId} not found` | ||||||||||||||||||||
| }) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| }catch(e){ | ||||||||||||||||||||
| console.log("This is the error from executing node", e); | ||||||||||||||||||||
| return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({ | ||||||||||||||||||||
| message: "Internal server Error from node execution ", | ||||||||||||||||||||
| }); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| }) | ||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| export interface ExecutionContext { | ||
| nodeId?: string; | ||
| userId: string; | ||
| credentialsId?: string; | ||
| config: Record<string, any>; | ||
| inputData?: any; | ||
| } | ||
|
Comment on lines
+1
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Search for credId and credentialsId usage patterns in executor files
echo "=== Checking credId usage ==="
rg -n "credId" --type ts
echo ""
echo "=== Checking credentialsId usage ==="
rg -n "credentialsId" --type tsRepository: Dev-Pross/BuildFlow Length of output: 3481 Fix field naming mismatch between
🤖 Prompt for AI Agents |
||
| export interface ExecutionResult { | ||
| success: boolean; | ||
| output?: any; | ||
| error?: string; | ||
| metadata?: Record<any, any>; | ||
| } | ||
| export interface NodeExecutor { | ||
| execute(context: ExecutionContext): Promise<ExecutionResult>; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,47 @@ | ||||||||||||||||||||||||||
| import { ExecutionContext, ExecutionResult, NodeExecutor } from "../registry/Execution.config.types.js"; | ||||||||||||||||||||||||||
| import { GmailExecutor } from "@repo/nodes/nodeClient"; | ||||||||||||||||||||||||||
| import {GoogleSheetsNodeExecutor} from "@repo/nodes/nodeClient"; | ||||||||||||||||||||||||||
| class ExecutionRegistry { | ||||||||||||||||||||||||||
| private executors = new Map<string, NodeExecutor>(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| register(nodeType: string, executor: NodeExecutor) { | ||||||||||||||||||||||||||
| this.executors.set(nodeType, executor); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| async execute( | ||||||||||||||||||||||||||
| nodeType: string, | ||||||||||||||||||||||||||
| context: ExecutionContext | ||||||||||||||||||||||||||
| ): Promise<ExecutionResult> { | ||||||||||||||||||||||||||
| const executor = this.executors.get(nodeType); | ||||||||||||||||||||||||||
| if (!executor) { | ||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||
| success: false, | ||||||||||||||||||||||||||
| error: `No Executor found for ${nodeType}`, | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||
| const result = await executor.execute(context) | ||||||||||||||||||||||||||
| console.log("Execute result:", result); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return result; | ||||||||||||||||||||||||||
| } catch (error: any) { | ||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||
| success: false, | ||||||||||||||||||||||||||
| error: error, | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+27
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error object assigned to string field.
🐛 Proposed fix } catch (error: any) {
return {
success: false,
- error: error,
+ error: error instanceof Error ? error.message : String(error),
};
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| initialize() { | ||||||||||||||||||||||||||
| // TODO: Ensure GmailExecutor implements NodeExecutor and is compatible with ExecutionContext | ||||||||||||||||||||||||||
| // If needed, adapt/extract a compatible Executor for registration. | ||||||||||||||||||||||||||
| // For now, this cast suppresses the type error. Refactor as soon as possible! | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| //wehen visits this next time make sure chang gmail executor implements NodeExecutor | ||||||||||||||||||||||||||
| this.register("gmail", new GmailExecutor() as unknown as NodeExecutor); | ||||||||||||||||||||||||||
| this.register("google_sheet", new GoogleSheetsNodeExecutor() as unknown as NodeExecutor) | ||||||||||||||||||||||||||
| console.log(`The current Executors are ${this.executors.size}`); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+34
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Address the type incompatibility TODOs before merging. The Would you like me to help design an adapter pattern to properly bridge the executor implementations with the 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| export const ExecutionRegister = new ExecutionRegistry(); | ||||||||||||||||||||||||||
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.
Empty string fallback for
userIdmay cause issues.If
req.user.subis undefined, using an empty string asuserIdcould cause downstream failures (e.g., credential lookups). Consider returning an error instead.🛡️ Suggested defensive handling
🤖 Prompt for AI Agents