feat(workflow): slack trigger node, trigger workflow from slack#1204
feat(workflow): slack trigger node, trigger workflow from slack#1204Sithaarth24 wants to merge 8 commits intomainfrom
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds Slack trigger and message support across frontend and backend: UI components and icons, API endpoints and client helpers, DB query for Slack-triggered workflows, server workflow execution helpers, and request/route wiring for Slack metadata and message posting. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Slack as Slack
participant SlackClient as Slack Client
participant DB as Database
participant WorkflowSvc as Workflow Utils
participant Engine as Workflow Engine
User->>Slack: Trigger (slash/mention)
Slack->>SlackClient: Event/command
SlackClient->>DB: getSlackTriggersInWorkflows(...)
DB-->>SlackClient: matching workflow(s)
SlackClient->>WorkflowSvc: executeWorkflowWithSlackTrigger(context)
WorkflowSvc->>DB: create workflowExecution & stepExecutions
WorkflowSvc->>Engine: executeWorkflowChain(root step)
Engine-->>WorkflowSvc: step results
WorkflowSvc->>DB: finalize statuses
WorkflowSvc-->>SlackClient: execution summary
SlackClient->>Slack: post result message
sequenceDiagram
actor User
participant Builder as Frontend Builder
participant SlackUI as Slack Config UI
participant API as Backend API
participant DB as Database
User->>Builder: Add Slack node
Builder->>SlackUI: open SlackTriggerConfigUI / SlackMessageConfigUI
SlackUI->>API: fetchSlackMetadata()
API->>DB: listSlackChannels()
DB-->>API: channels
API-->>SlackUI: channels
User->>SlackUI: Save config
SlackUI->>API: save node config (create/update)
API->>DB: create/update node & edges
DB-->>API: success
API-->>Builder: node created/updated
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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 |
Summary of ChangesHello @Sithaarth24, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant new capability: triggering workflows directly from Slack. It provides a comprehensive solution, encompassing a new frontend UI for configuring Slack-based triggers within the workflow builder, visual representation of these triggers, and robust backend logic to listen for and respond to specific Slack commands. This allows users to seamlessly initiate automated processes by interacting with a bot in Slack, enhancing workflow accessibility and integration with communication channels. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
Summary of ChangesHello @Sithaarth24, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant new feature enabling workflows to be triggered directly from Slack. It encompasses a full-stack implementation, from a new frontend UI for configuring Slack triggers within the workflow builder, to backend database queries for managing these triggers, and finally, updates to the Slack integration to listen for and respond to specific commands to list and execute these workflows. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a significant new feature: a Slack trigger for workflows. The implementation is comprehensive, covering the frontend UI for configuration, the workflow builder canvas rendering, and backend logic to handle Slack commands for listing and triggering workflows. The code is generally well-structured. I've identified one high-severity bug in the UI where a trigger option is missing, along with a few medium-severity suggestions to remove debug code, improve type safety, and enhance performance. Overall, this is a solid contribution that greatly expands the platform's integration capabilities.
There was a problem hiding this comment.
Code Review
This pull request introduces a new Slack Trigger feature, allowing workflows to be initiated directly from Slack. The changes include a new UI component for configuring Slack triggers, updates to the workflow builder to integrate this new trigger type, and backend logic to handle Slack commands and retrieve relevant workflows. The implementation looks solid, with good separation of concerns between frontend and backend components. The new getSlackTriggersInWorkflows function is a valuable addition for dynamic workflow discovery in Slack. The UI for configuring the trigger is intuitive, and the integration into the workflow builder is seamless. The command handling in Slack client is well-structured to support both listing workflows and triggering them. Overall, this is a well-executed feature.
There was a problem hiding this comment.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
frontend/src/components/workflow/SlackTriggerConfigUI.tsx(1 hunks)frontend/src/components/workflow/WorkflowBuilder.tsx(14 hunks)frontend/src/components/workflow/WorkflowIcons.tsx(1 hunks)server/db/workflowTool.ts(3 hunks)server/integrations/slack/client.ts(5 hunks)server/integrations/slack/types.ts(1 hunks)server/types/workflowTypes.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-28T07:51:03.144Z
Learnt from: Sithaarth24
Repo: xynehq/xyne PR: 1127
File: server/db/schema/workflows.ts:320-320
Timestamp: 2025-10-28T07:51:03.144Z
Learning: In the workflow template architecture, the API layer handles conversion between external UUIDs (used by frontend) and internal integer IDs (used in database). Frontend types expect UUID strings for workflow template IDs, while internal schemas like createWorkflowStepTemplateSchema expect integers. The publicWorkflowTemplateSchema maps external_id → id for API responses.
Applied to files:
server/db/workflowTool.ts
📚 Learning: 2025-09-02T16:41:31.729Z
Learnt from: devesh-juspay
Repo: xynehq/xyne PR: 758
File: server/db/schema/workflowTools.ts:9-14
Timestamp: 2025-09-02T16:41:31.729Z
Learning: In the workflowTools.ts schema, the user prefers to add notNull() constraint to the type field but does not want onDelete cascade behavior for the workflowTemplateId foreign key reference.
Applied to files:
server/db/workflowTool.ts
📚 Learning: 2025-11-06T09:49:42.236Z
Learnt from: Asrani-Aman
Repo: xynehq/xyne PR: 1163
File: server/api/chat/agents.ts:3941-3991
Timestamp: 2025-11-06T09:49:42.236Z
Learning: In server/api/chat/agents.ts dual RAG path and similar agent message flows, when `agentForDb.appIntegrations` is parsed, the outer function (agents.ts) only needs to extract high-level routing info like `agentAppEnums`. The detailed per-app selections (`selectedItems`) are intentionally extracted inside the inner RAG functions (`generateAnswerFromDualRag`, `UnderstandMessageAndAnswer` in chat.ts) by parsing the `agentPrompt` parameter. This is the established architectural pattern: outer function handles routing, inner function handles detailed selection parsing via `parseAppSelections(agentPromptData.appIntegrations)` and passes `selectedItem` to `searchVespaAgent` for per-app item scoping.
Applied to files:
server/integrations/slack/client.ts
🧬 Code graph analysis (4)
server/db/workflowTool.ts (2)
server/types.ts (1)
TxnOrClient(320-320)server/db/schema/workflows.ts (3)
workflowTemplate(63-87)workflowTool(115-133)workflowStepTemplate(90-112)
frontend/src/components/workflow/SlackTriggerConfigUI.tsx (1)
frontend/src/components/workflow/WorkflowIcons.tsx (1)
SlackIcon(494-526)
server/integrations/slack/client.ts (2)
server/integrations/slack/types.ts (2)
DbUser(23-31)SlackTriggerTool(70-77)server/db/workflowTool.ts (1)
getSlackTriggersInWorkflows(82-129)
frontend/src/components/workflow/WorkflowBuilder.tsx (2)
frontend/src/components/workflow/WorkflowIcons.tsx (1)
SlackIcon(494-526)frontend/src/components/workflow/SlackTriggerConfigUI.tsx (2)
SlackTriggerConfig(4-9)SlackTriggerConfigUI(39-355)
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
server/db/workflowTool.ts (1)
155-159: Remove the per-user filter on workspace triggers
eq(workflowTemplate.userId, userId)means a Slack user will only ever see workflows they personally authored. That breaks the core use case of listing workspace-wide Slack triggers (and it’s the same issue we called out earlier). Please drop the userId predicate—or replace it with the real permission check—so members can run team workflows from Slack.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/src/components/workflow/WorkflowBuilder.tsx(14 hunks)server/db/workflowTool.ts(3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-28T07:51:03.144Z
Learnt from: Sithaarth24
Repo: xynehq/xyne PR: 1127
File: server/db/schema/workflows.ts:320-320
Timestamp: 2025-10-28T07:51:03.144Z
Learning: In the workflow template architecture, the API layer handles conversion between external UUIDs (used by frontend) and internal integer IDs (used in database). Frontend types expect UUID strings for workflow template IDs, while internal schemas like createWorkflowStepTemplateSchema expect integers. The publicWorkflowTemplateSchema maps external_id → id for API responses.
Applied to files:
server/db/workflowTool.ts
📚 Learning: 2025-09-02T16:41:31.729Z
Learnt from: devesh-juspay
Repo: xynehq/xyne PR: 758
File: server/db/schema/workflowTools.ts:9-14
Timestamp: 2025-09-02T16:41:31.729Z
Learning: In the workflowTools.ts schema, the user prefers to add notNull() constraint to the type field but does not want onDelete cascade behavior for the workflowTemplateId foreign key reference.
Applied to files:
server/db/workflowTool.ts
🧬 Code graph analysis (2)
server/db/workflowTool.ts (2)
server/types.ts (1)
TxnOrClient(340-340)server/db/schema/workflows.ts (3)
workflowTemplate(65-89)workflowTool(117-135)workflowStepTemplate(92-114)
frontend/src/components/workflow/WorkflowBuilder.tsx (2)
frontend/src/components/workflow/WorkflowIcons.tsx (1)
SlackIcon(494-526)frontend/src/components/workflow/SlackTriggerConfigUI.tsx (2)
SlackTriggerConfig(4-9)SlackTriggerConfigUI(39-355)
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (3)
frontend/src/components/workflow/SlackTriggerConfigUI.tsx (2)
105-132: Validate and render channels for both “app_mention” and “message_in_channel”If you plan to support “message_in_channel”, require channels in validation and show the channel selector for both types.
- export interface SlackTriggerConfig { - triggerType: "app_mention" | "direct_message" + export interface SlackTriggerConfig { + triggerType: "app_mention" | "direct_message" | "message_in_channel" @@ - if (config.triggerType === "app_mention") { + if (config.triggerType === "app_mention" || config.triggerType === "message_in_channel") { if (!config.channelIds || config.channelIds.length === 0) { validationErrors.push( "At least one channel is required for 'App Mention' trigger", ) } } @@ -{config.triggerType === "app_mention" && ( +{(config.triggerType === "app_mention" || config.triggerType === "message_in_channel") && (Also applies to: 341-346
28-39: Expose “Message in Channel” in the dropdownAdd the missing option to keep UI, types, and validator consistent.
const triggerTypeOptions = [ { value: "app_mention" as const, label: "App Mention", description: "When bot/app is mentioned in channel with @botname", }, { value: "direct_message" as const, label: "Direct Message (DM)", description: "When a user sends a direct message to the bot", }, + { + value: "message_in_channel" as const, + label: "Message in Channel", + description: "When a message is posted in specified channel(s)", + }, ]frontend/src/components/workflow/WorkflowBuilder.tsx (1)
2293-2302: Populate Slack config before opening the editorWhen clicking an existing Slack Trigger node, the code only toggles the sidebar visibility but doesn't populate
slackTriggerInitialConfigorslackTriggerToolId. This causes the editor to show defaults instead of the node's saved settings, potentially wiping previous configuration on update.This issue was previously flagged in past review comments for lines 2293-2297.
Apply this diff to hydrate the config before opening:
case "slack_trigger": - // Open Slack Trigger config sidebar - setShowSlackTriggerConfigUI(true) - setSelectedSlackTriggerNodeId(node.id) - break + { + // Open Slack Trigger config sidebar + const slackTools = (node.data?.tools as Tool[] | undefined) || [] + const slackTool = slackTools[0] + const slackConfig = + (slackTool?.config as SlackTriggerConfig | undefined) || + (slackTool?.val as SlackTriggerConfig | undefined) || + ((step as any)?.config as SlackTriggerConfig | undefined) + + setSlackTriggerInitialConfig(slackConfig) + setSlackTriggerToolId(slackTool?.id) + setShowSlackTriggerConfigUI(true) + setSelectedSlackTriggerNodeId(node.id) + break + } case "slack_message": - setShowSlackMessageConfigUI(true) - setSelectedSlackMessageNodeId(node.id) - break + { + const slackMsgTools = (node.data?.tools as Tool[] | undefined) || [] + const slackMsgTool = slackMsgTools[0] + const slackMsgConfig = + (slackMsgTool?.config as SlackMessageConfig | undefined) || + (slackMsgTool?.val as SlackMessageConfig | undefined) || + ((step as any)?.config as SlackMessageConfig | undefined) + + // Populate initial config for Slack Message if editing existing node + setShowSlackMessageConfigUI(true) + setSelectedSlackMessageNodeId(node.id) + break + }Note: For Slack Message, you'll also need to pass the config to
SlackMessageConfigUIvia aninitialConfigprop (currently the component accepts it but WorkflowBuilder doesn't pass it).
🧹 Nitpick comments (7)
frontend/src/components/workflow/WhatHappensNextUI.tsx (1)
123-127: Consider refactoring the condition for better maintainability.The condition now has five action IDs checked with OR operators, making it lengthy and harder to maintain when adding new actions.
Consider refactoring to:
+ const clickableActions = ["ai_agent", "email", "http_request", "select_agents", "slack_message"] onClick={() => { - if (action.id === "ai_agent" || action.id === "email" || action.id === "http_request" || action.id === "select_agents" || action.id === "slack_message") { + if (clickableActions.includes(action.id)) { // For AI Agent, Email, HTTP Request, Select Agents, and Slack Message, trigger custom event to open respective ConfigUI onSelectAction(action.id) onClose() // Close WhatHappensNextUI } }}frontend/src/components/workflow/WorkflowNode.tsx (1)
81-85: Add type guard for icon prop to prevent runtime errors.The
React.cloneElementcall assumesiconis a valid React element. If the icon prop is not a proper ReactElement, this will throw a runtime error.Consider adding a type guard:
- {React.cloneElement(icon as React.ReactElement, { + {React.isValidElement(icon) && React.cloneElement(icon, { width: iconSizeUnconfigured, height: iconSizeUnconfigured, color: iconColor })}Apply the same fix at line 177 in the configured state.
server/api/workflow.ts (1)
5826-5841: Consider GET for metadata endpoint (optional)Fetching metadata is semantically a GET, not POST. If routing allows, expose GET /workflow/tools/slack/metadata for idempotent caching at CDN/proxy.
server/integrations/slack/client.ts (2)
1597-1652: Avoid DB hit on every “/” command; cache workflow triggers brieflyFetching workflow triggers on each command adds latency and load. Cache per (workspaceId,userId) with a short TTL (e.g., 30–60s).
// module scope const wfCache = new Map<string, { expires: number; data: SlackTriggerTool[] }>() const wfKey = (u: number, w: number) => `${w}:${u}` const getWf = async (u: number, w: number) => { const k = wfKey(u, w) const hit = wfCache.get(k) const now = Date.now() if (hit && hit.expires > now) return hit.data const data = await getSlackTriggersInWorkflows(db, u, w) wfCache.set(k, { expires: now + 60_000, data }) return data } // replace direct getSlackTriggersInWorkflows(...) with: await getWf(dbUser.id, dbUser.workspaceId)
1504-1583: Workflow execution path is a stub (“DUMMY EXECUTION”)If this is intentional for the first cut, please track a follow-up to call the actual workflow execution API, pass the mapped tool/workflow IDs, and surface the executionId back to Slack.
frontend/src/components/workflow/SlackMessageConfigUI.tsx (1)
132-142: Consider revising channel clearing logicThe current implementation clears the selected channel whenever the input value is empty. This could be problematic if a user clicks into the input field (perhaps to search for a different channel) – any typing would immediately clear their current selection before they've actually chosen a replacement.
Consider only clearing the selection explicitly via the X button (lines 239-244) and not automatically when the user types into the search input.
const handleChannelInputChange = (value: string) => { setChannelInput(value) - - // If input is cleared, clear the selected channel - if (!value.trim()) { - setConfig({ - ...config, - channelId: "" - }) - } }frontend/src/components/workflow/WorkflowBuilder.tsx (1)
707-711: Simplify config extraction to improve type safetyThe config extraction pattern uses multiple fallbacks with
anytype assertions, which was flagged in previous review comments as complex and not ideal for type safety. Consider simplifying this to be more explicit and type-safe.Based on learnings
- const config = - (step as any).config || - (hasSlackTriggerTool && tools?.[0]?.config) || - (hasSlackTriggerTool && tools?.[0]?.val) || - {} + const stepWithConfig = step as Step & { config?: Partial<SlackTriggerConfig> } + const toolConfig = (hasSlackTriggerTool && (tools?.[0]?.config || tools?.[0]?.val)) as Partial<SlackTriggerConfig> | undefined + const config: Partial<SlackTriggerConfig> = stepWithConfig.config || toolConfig || {}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
frontend/src/components/workflow/SlackMessageConfigUI.tsx(1 hunks)frontend/src/components/workflow/SlackTriggerConfigUI.tsx(1 hunks)frontend/src/components/workflow/WhatHappensNextUI.tsx(3 hunks)frontend/src/components/workflow/WorkflowBuilder.tsx(19 hunks)frontend/src/components/workflow/WorkflowNode.tsx(1 hunks)frontend/src/components/workflow/api/ApiHandlers.ts(1 hunks)frontend/src/routes/_authenticated/workflow.tsx(0 hunks)server/api/workflow.ts(3 hunks)server/db/workflowTool.ts(3 hunks)server/integrations/slack/client.ts(7 hunks)server/server.ts(2 hunks)server/types/workflowTypes.ts(1 hunks)
💤 Files with no reviewable changes (1)
- frontend/src/routes/_authenticated/workflow.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- server/types/workflowTypes.ts
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-05-28T10:47:41.020Z
Learnt from: naSim087
Repo: xynehq/xyne PR: 484
File: server/integrations/google/sync.ts:222-222
Timestamp: 2025-05-28T10:47:41.020Z
Learning: The functions `handleGoogleDriveChange` and `getDriveChanges` in `server/integrations/google/sync.ts` are intentionally exported for future changes, even though they are not currently being imported by other modules.
Applied to files:
server/server.ts
📚 Learning: 2025-06-10T05:40:04.427Z
Learnt from: naSim087
Repo: xynehq/xyne PR: 525
File: frontend/src/routes/_authenticated/admin/integrations/slack.tsx:141-148
Timestamp: 2025-06-10T05:40:04.427Z
Learning: In frontend/src/routes/_authenticated/admin/integrations/slack.tsx, the ConnectAction enum and related connectAction state (lines 141-148, 469-471) are intentionally kept for future development, even though they appear unused in the current implementation.
Applied to files:
frontend/src/components/workflow/WhatHappensNextUI.tsxfrontend/src/components/workflow/SlackMessageConfigUI.tsx
📚 Learning: 2025-07-28T12:07:13.546Z
Learnt from: vipul-maheshwari
Repo: xynehq/xyne PR: 686
File: server/utils/chainUtils.ts:65-69
Timestamp: 2025-07-28T12:07:13.546Z
Learning: In the chainUtils.ts file, the correct type for message parameters is `SelectMessage[]` rather than `Message[]` when working with conversation message arrays.
Applied to files:
frontend/src/components/workflow/SlackTriggerConfigUI.tsx
📚 Learning: 2025-09-08T16:18:07.128Z
Learnt from: Aayushjshah
Repo: xynehq/xyne PR: 787
File: server/api/chat/mapper.ts:631-636
Timestamp: 2025-09-08T16:18:07.128Z
Learning: In server/api/chat/mapper.ts, the filter_query parameter for getSlackThreads should remain required: false (optional) in the mapper definition, even if it differs from the AgentTool definition in server/api/chat/tools.ts. This is an intentional design decision where parameter requirements can differ between the mapper and tool layers.
Applied to files:
server/integrations/slack/client.ts
📚 Learning: 2025-08-08T10:58:41.288Z
Learnt from: MayankBansal2004
Repo: xynehq/xyne PR: 719
File: server/api/chat/agents.ts:2056-2058
Timestamp: 2025-08-08T10:58:41.288Z
Learning: In server/db/schema/messages.ts, the messages table defines tokensUsed as integer and cost as numeric. Due to Drizzle’s numeric mapping, cost is handled as a string at the TypeScript layer, while tokensUsed remains a number. Therefore, insertMessage should persist cost as a string and tokensUsed as a number across server/api/chat/agents.ts.
Applied to files:
server/integrations/slack/client.ts
📚 Learning: 2025-11-06T09:49:42.236Z
Learnt from: Asrani-Aman
Repo: xynehq/xyne PR: 1163
File: server/api/chat/agents.ts:3941-3991
Timestamp: 2025-11-06T09:49:42.236Z
Learning: In server/api/chat/agents.ts dual RAG path and similar agent message flows, when `agentForDb.appIntegrations` is parsed, the outer function (agents.ts) only needs to extract high-level routing info like `agentAppEnums`. The detailed per-app selections (`selectedItems`) are intentionally extracted inside the inner RAG functions (`generateAnswerFromDualRag`, `UnderstandMessageAndAnswer` in chat.ts) by parsing the `agentPrompt` parameter. This is the established architectural pattern: outer function handles routing, inner function handles detailed selection parsing via `parseAppSelections(agentPromptData.appIntegrations)` and passes `selectedItem` to `searchVespaAgent` for per-app item scoping.
Applied to files:
server/integrations/slack/client.ts
📚 Learning: 2025-09-02T16:41:31.729Z
Learnt from: devesh-juspay
Repo: xynehq/xyne PR: 758
File: server/db/schema/workflowTools.ts:9-14
Timestamp: 2025-09-02T16:41:31.729Z
Learning: In the workflowTools.ts schema, the user prefers to add notNull() constraint to the type field but does not want onDelete cascade behavior for the workflowTemplateId foreign key reference.
Applied to files:
server/db/workflowTool.ts
📚 Learning: 2025-10-28T07:51:03.144Z
Learnt from: Sithaarth24
Repo: xynehq/xyne PR: 1127
File: server/db/schema/workflows.ts:320-320
Timestamp: 2025-10-28T07:51:03.144Z
Learning: In the workflow template architecture, the API layer handles conversion between external UUIDs (used by frontend) and internal integer IDs (used in database). Frontend types expect UUID strings for workflow template IDs, while internal schemas like createWorkflowStepTemplateSchema expect integers. The publicWorkflowTemplateSchema maps external_id → id for API responses.
Applied to files:
server/db/workflowTool.ts
🧬 Code graph analysis (8)
server/api/workflow.ts (2)
server/db/schema/channels.ts (1)
channels(32-69)server/integrations/slack/client.ts (1)
listSlackChannels(2378-2395)
server/server.ts (1)
server/api/workflow.ts (1)
getSlackMetadataApi(5829-5841)
frontend/src/components/workflow/SlackMessageConfigUI.tsx (1)
frontend/src/components/workflow/WorkflowIcons.tsx (1)
SlackIcon(494-526)
frontend/src/components/workflow/SlackTriggerConfigUI.tsx (2)
frontend/src/components/workflow/api/ApiHandlers.ts (1)
workflowToolsAPI(396-1090)frontend/src/components/workflow/WorkflowIcons.tsx (1)
SlackIcon(494-526)
server/integrations/slack/client.ts (2)
server/integrations/slack/types.ts (2)
DbUser(23-31)SlackTriggerTool(70-77)server/db/workflowTool.ts (1)
getSlackTriggersInWorkflows(125-179)
frontend/src/components/workflow/api/ApiHandlers.ts (1)
frontend/src/api.ts (1)
api(5-5)
frontend/src/components/workflow/WorkflowBuilder.tsx (3)
frontend/src/components/workflow/WorkflowIcons.tsx (3)
WebhookIcon(167-185)FormDocumentIcon(318-337)SlackIcon(494-526)frontend/src/components/workflow/SlackTriggerConfigUI.tsx (2)
SlackTriggerConfig(6-11)SlackTriggerConfigUI(41-512)frontend/src/components/workflow/SlackMessageConfigUI.tsx (2)
SlackMessageConfig(6-11)SlackMessageConfigUI(29-397)
server/db/workflowTool.ts (3)
server/types.ts (1)
TxnOrClient(340-340)server/db/schema/workflows.ts (3)
workflowTemplate(65-89)workflowTool(117-135)workflowStepTemplate(92-114)server/db/schema/userWorkflowPermissions.ts (1)
userWorkflowPermissions(23-54)
🔇 Additional comments (14)
frontend/src/components/workflow/WhatHappensNextUI.tsx (1)
11-11: LGTM! Slack Message action properly integrated.The MessageSquare icon import and slack_message action definition follow the established pattern and integrate cleanly with the existing action list.
Also applies to: 50-55
server/db/workflowTool.ts (2)
147-154: LGTM! Join condition uses idiomatic Drizzle ORM.The
inArray(workflowTool.id, workflowStepTemplate.toolIds)join condition is the proper Drizzle ORM approach for array containment checks, providing good type safety.
125-179: No actionable changes required.The permission model is correct and consistent throughout the codebase. Slack trigger workflows are properly scoped:
- Public workflows (isPublic=true) → accessible to all workspace members
- Private workflows → accessible only to users with explicit permission entries
This pattern is used consistently across
getSlackTriggersInWorkflows,getWorkflows, and related functions. The leftJoin + OR condition correctly implements this two-tier access model. If workspace-wide access is desired for a trigger, mark it public; otherwise, use permission entries to control visibility. The implementation is secure and intentional.frontend/src/components/workflow/WorkflowNode.tsx (3)
4-29: LGTM! Well-structured interface with clear documentation.The WorkflowNodeProps interface is comprehensive and well-documented, making the component easy to use.
111-141: LGTM! Add Next Step button properly implemented.The button correctly handles events (stopPropagation + preventDefault) and uses conditional rendering appropriately.
148-265: LGTM! Configured state renders properly with all required elements.The configured state rendering includes all necessary UI elements (icon, title, description, handles, optional button) and maintains consistency with the unconfigured state.
server/server.ts (1)
327-327: LGTM! Slack metadata route properly integrated.The import and route follow the established pattern for workflow tool metadata endpoints, maintaining consistency with similar Jira routes.
Also applies to: 1510-1510
frontend/src/components/workflow/api/ApiHandlers.ts (2)
1086-1088: LGTM! Error handling is consistent.Error logging and re-throwing follows the established pattern in this file.
1065-1089: ****The review comment is based on a false premise. The server-side
getSlackMetadataApicallslistSlackChannels(), which already returns an array of objects with{ id: string; name: string }structure. The endpoint wraps this as{ success: true, data: { channels } }. The frontend code correctly extractsresult.data?.channels, which is already in the expected format—no transformation is needed. The misleading comment should be removed or clarified to reflect that the channels are already properly formatted.Likely an incorrect or invalid review comment.
frontend/src/components/workflow/SlackMessageConfigUI.tsx (2)
55-72: Good pattern: fetching channels on visibilityThe effect correctly fetches Slack channels when the component becomes visible, with proper error handling and loading states. This ensures fresh data for each configuration session.
89-116: Validation logic is thoroughThe validation function properly checks all required fields and enforces appropriate length limits (message: 4000, title: 100, description: 500), providing clear error messages for users.
frontend/src/components/workflow/WorkflowBuilder.tsx (3)
3959-4093: Slack Message follows correct patternThe
handleSlackMessageConfigSaveimplementation correctly follows the pattern of other "What Happens Next" actions (AI Agent, Email, HTTP Request) by creating/updating local node state without backend API calls. These action nodes are persisted when the entire workflow template is saved, which is appropriate for workflow steps rather than trigger configurations.
1379-1385: Slack Trigger successfully integrated into triggers listThe Slack Trigger entry is properly added to the TriggersSidebar with appropriate icon, description, and enabled state. This makes it discoverable to users alongside other trigger types.
4728-4770: UI components properly wired with correct propsBoth
SlackTriggerConfigUIandSlackMessageConfigUIare correctly integrated with all necessary props (isVisible, onBack, onClose, onSave, initialConfig, toolId, showBackButton, builder) following the established patterns from other configuration sidebars.
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (5)
frontend/src/components/workflow/WorkflowBuilder.tsx (3)
3539-3544: Remove console.log in production UI flow.Replace with the existing Snackbar/logger pattern to avoid noisy console output.
- console.log("Webhook saved to backend:", savedWebhookData) + // saved to backend; update UI below ... - console.log("Webhook updated in backend:", savedWebhookData) + // updated in backend; update UI belowAlso applies to: 3602-3605
707-711: Simplify Slack Trigger config source selection.Prefer a single clear source-of-truth order: step.config, then tools[0].config. Dropping tools[0].val reduces confusion.
- const config = - (step as any).config || - (hasSlackTriggerTool && tools?.[0]?.config) || - (hasSlackTriggerTool && tools?.[0]?.val) || - {} + const config = + (step as any).config || + (hasSlackTriggerTool && tools?.[0]?.config) || + {}
2293-2303: Hydrate Slack Trigger editor on node click to avoid wiping saved config.Opening an existing Slack Trigger sets the sidebar flags but does not set slackTriggerInitialConfig or slackTriggerToolId, so the editor shows defaults and edits overwrite existing settings. Hydrate from node.data.tools[0] (fallback to step.config) before opening.
- case "slack_trigger": - // Open Slack Trigger config sidebar - setShowSlackTriggerConfigUI(true) - setSelectedSlackTriggerNodeId(node.id) - break + case "slack_trigger": { + const slackTools = (node.data?.tools as Tool[] | undefined) || [] + const slackTool = slackTools[0] + const slackConfig = + (slackTool?.config as SlackTriggerConfig | undefined) || + (slackTool?.value as SlackTriggerConfig | undefined) || + ((step as any)?.config as SlackTriggerConfig | undefined) + setSlackTriggerInitialConfig(slackConfig) + setSlackTriggerToolId(slackTool?.id) + setSelectedSlackTriggerNodeId(node.id) + setShowSlackTriggerConfigUI(true) + break + }Based on learnings
server/integrations/slack/client.ts (2)
1667-1700: Avoid repeated DB scans; add short‑TTL cache for workflow triggers.getSlackTriggersInWorkflows is called on every slash. Cache by workspaceId+channel for a few seconds to reduce load.
+// simple in-memory cache { key: { data, expiresAt } } +const wfCache = new Map<string, { data: SlackTriggerTool[]; expiresAt: number }>() @@ - const workflowTriggers: SlackTriggerTool[] = await getSlackTriggersInWorkflows( + const cacheKey = `${dbUser.workspaceId}:${channel}` + const cached = wfCache.get(cacheKey) + const now = Date.now() + const workflowTriggers: SlackTriggerTool[] = cached && cached.expiresAt > now + ? cached.data + : await getSlackTriggersInWorkflows( db, dbUser.id, dbUser.workspaceId, channel - ) + ).then(res => { + wfCache.set(cacheKey, { data: res, expiresAt: now + 5_000 }) + return res + })
2427-2466: Guard against missing client and paginate conversations.list.webClient! can be null and only one page is fetched. Add a null check, iterate cursors, and exclude archived.
-export const listSlackChannels = async ( +export const listSlackChannels = async ( options: { cursor?: string, limit?: number } = {} ): Promise<{ channels: { id: string, name: string, }[], hasMore: boolean, nextCursor?: string }> => { - const { cursor, limit = 200 } = options - - const response = await webClient!.conversations.list({ - cursor, - limit, - exclude_archived: true, - types: 'public_channel,private_channel' - }) - - if (!response.channels) { - Logger.error("channels not received from conversations") - throw new Error("Channels absent in conversation response") - } - - const channels = response.channels - .filter(ch => (ch.name && ch.id)) - .map(ch => ({ - id: ch.id!, - name: ch.name! - })) - - return { - channels, - hasMore: !!response.response_metadata?.next_cursor, - nextCursor: response.response_metadata?.next_cursor - } + if (!webClient) { + Logger.warn("Slack client not initialized for listSlackChannels") + throw new Error("Slack integration is not configured") + } + const { limit = 200 } = options + const out: Array<{ id: string; name: string }> = [] + let cursor = options.cursor + do { + const resp = await webClient.conversations.list({ + cursor, + limit, + exclude_archived: true, + types: "public_channel,private_channel", + } as any) + const page = (resp.channels || []) + .filter((ch: any) => ch?.id && ch?.name) + .map((ch: any) => ({ id: ch.id as string, name: ch.name as string })) + out.push(...page) + cursor = (resp.response_metadata as any)?.next_cursor || undefined + } while (cursor) + return { channels: out, hasMore: false, nextCursor: undefined }
🧹 Nitpick comments (9)
server/integrations/slack/workflowSlackUtils.ts (3)
45-83: Consider wrapping workflowExecution + stepExecution creation in a DB transactionRight now the template lookup, workflowExecution insert, stepExecution inserts, and rootWorkflowStepExeId update (Lines 53–61, 66–83, 95–133) run as separate statements on
db. If anything fails mid‑way, you can end up with an execution record without all its steps, or steps withoutrootWorkflowStepExeIdset.Using
db.transactionfor this block would keep the execution and its steps consistent (all‑or‑nothing) before you callexecuteWorkflowChain, while still letting the chain use the committed state.Also applies to: 95-133
161-180: Avoid duplicating workflow completion logic already handled in executeWorkflowChainAfter
executeWorkflowChainreturns, you re‑query all step executions and, if all areCOMPLETED, explicitly mark the workflowExecution asCOMPLETED(Lines 161–180). Per the existingexecuteWorkflowChainbehaviour, it already rechecks all steps and marks the workflowExecution asCOMPLETEDwhen appropriate.This duplication is harmless but adds extra queries and another place to maintain completion rules. You can rely on
executeWorkflowChainfor status updates and here only read status (and step counts) for reporting, or drop the completion update here entirely.
135-137: Optional: de‑duplicate toolIds before querying workflow toolsWhen building
allToolIds(Lines 135–136), the same toolId can appear in multiple steps. Passing duplicates intogetWorkflowToolsByIdsis functionally fine (the DBINclause will dedupe), but you can reduce query payload slightly by doingArray.from(new Set(allToolIds))first.frontend/src/components/workflow/api/ApiHandlers.ts (1)
1065-1105: Reuse extractResponseData and tighten typing for Slack metadata helperThe implementation correctly builds query params and parses
{ success, data }from/workflow/tools/slack/metadata(Lines 1068–1100). Two small improvements:
- You can reuse
extractResponseDatato keep response‑handling consistent across APIs, e.g.:const data = await extractResponseData<{ channels: Array<{ id: string; name: string }> hasMore: boolean nextCursor?: string }>(response) return { channels: data.channels ?? [], hasMore: data.hasMore ?? false, nextCursor: data.nextCursor, }
- Instead of
const query: any = {}, type it asRecord<string, string>for better inference and to match how other APIs build query objects.frontend/src/components/workflow/WorkflowBuilder.tsx (1)
3854-3864: Use stable id for Slack Trigger node (optional).Other triggers use fixed ids ("form-submission", "webhook-trigger", "jira-trigger"). Consider "slack-trigger" for consistency and easier lookups.
- const newNodeId = String(nodeCounter + 1) - setNodeCounter(nodeCounter + 1) + const newNodeId = "slack-trigger" + setNodeCounter(prev => prev + 1)server/integrations/slack/client.ts (4)
1-1: Remove unused type import.ConversationsListResponse isn’t used.
-import { WebClient, type ConversationsListResponse } from "@slack/web-api" +import { WebClient } from "@slack/web-api"
1424-1500: Validate workspace before listing workflows and handle empty access early.Mirror the guards used in /agents to avoid confusing errors.
const handleWorkflowsCommand = async ( @@ ) => { try { + if (!dbUser.workspaceId || dbUser.workspaceId <= 0) { + const msg = "There's an issue with your workspace configuration. Please contact your administrator." + return isDM + ? client.chat.postMessage({ channel, text: msg }) + : client.chat.postEphemeral({ channel, user, text: msg, thread_ts }) + } const workflowTriggers = await getSlackTriggersInWorkflows(
1505-1631: Slug matching robustness.workflowName normalization uses “replace(/-/g, ' ')” on the command but equality check uses raw db name lowercased. To avoid false negatives, compare using a shared slugify.
- const matchedTrigger = workflowTriggers.find((wf) => wf.workflowName.toLowerCase() === workflowName) + const slug = (s: string) => s.trim().toLowerCase().replace(/\s+/g, "-") + const matchedTrigger = workflowTriggers.find((wf) => slug(wf.workflowName) === slug(workflowName))
2492-2528: Return consistent errors from postSlackMessage and consider username support.Current implementation is fine; optional: document that username may be ignored for bot tokens. No code change required.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
frontend/src/components/workflow/WorkflowBuilder.tsx(19 hunks)frontend/src/components/workflow/api/ApiHandlers.ts(1 hunks)frontend/src/components/workflow/slack/SlackChannlesInput.tsx(1 hunks)frontend/src/components/workflow/slack/SlackMessageConfigUI.tsx(1 hunks)frontend/src/components/workflow/slack/SlackTriggerConfigUI.tsx(1 hunks)server/api/workflow.ts(4 hunks)server/db/workflowTool.ts(3 hunks)server/integrations/slack/client.ts(8 hunks)server/integrations/slack/workflowSlackUtils.ts(1 hunks)server/server.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- server/db/workflowTool.ts
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: Sithaarth24
Repo: xynehq/xyne PR: 1204
File: frontend/src/components/workflow/WorkflowBuilder.tsx:3851-3938
Timestamp: 2025-11-12T06:07:09.854Z
Learning: In the WorkflowBuilder component, workflow node configurations (including Slack Trigger, AI Agent, Email, HTTP Request, Form, etc.) should only update local React state when users configure them. Backend persistence happens through a single batch save operation when the user clicks the "Save Changes" button (handleSaveChanges function). Only Jira currently has immediate backend persistence (will be changed to match this pattern).
📚 Learning: 2025-08-11T14:10:56.008Z
Learnt from: naSim087
Repo: xynehq/xyne PR: 725
File: server/server.ts:784-811
Timestamp: 2025-08-11T14:10:56.008Z
Learning: In the xyne application (server/server.ts), the following endpoints are intentionally accessible to regular authenticated users (not just admins):
- POST /oauth/create - allows users to create OAuth providers
- POST /slack/ingest_more_channel - allows users to ingest Slack channels
- POST /slack/start_ingestion - allows users to start Slack ingestion
- DELETE /oauth/connector/delete - allows users to delete OAuth connectors
- POST /connector/update_status - allows users to update connector status
- GET /connectors/all - allows users to view all connectors
- GET /oauth/global-slack-provider - allows users to check for global Slack provider
These endpoints enable self-service integration management for users.
Applied to files:
server/server.ts
📚 Learning: 2025-11-12T06:07:09.854Z
Learnt from: Sithaarth24
Repo: xynehq/xyne PR: 1204
File: frontend/src/components/workflow/WorkflowBuilder.tsx:3851-3938
Timestamp: 2025-11-12T06:07:09.854Z
Learning: In the WorkflowBuilder component, workflow node configurations (including Slack Trigger, AI Agent, Email, HTTP Request, Form, etc.) should only update local React state when users configure them. Backend persistence happens through a single batch save operation when the user clicks the "Save Changes" button (handleSaveChanges function). Only Jira currently has immediate backend persistence (will be changed to match this pattern).
Applied to files:
server/server.tsfrontend/src/components/workflow/slack/SlackTriggerConfigUI.tsxserver/integrations/slack/client.tsfrontend/src/components/workflow/slack/SlackMessageConfigUI.tsxfrontend/src/components/workflow/WorkflowBuilder.tsx
📚 Learning: 2025-07-28T12:07:13.546Z
Learnt from: vipul-maheshwari
Repo: xynehq/xyne PR: 686
File: server/utils/chainUtils.ts:65-69
Timestamp: 2025-07-28T12:07:13.546Z
Learning: In the chainUtils.ts file, the correct type for message parameters is `SelectMessage[]` rather than `Message[]` when working with conversation message arrays.
Applied to files:
frontend/src/components/workflow/slack/SlackTriggerConfigUI.tsx
📚 Learning: 2025-09-08T16:18:07.128Z
Learnt from: Aayushjshah
Repo: xynehq/xyne PR: 787
File: server/api/chat/mapper.ts:631-636
Timestamp: 2025-09-08T16:18:07.128Z
Learning: In server/api/chat/mapper.ts, the filter_query parameter for getSlackThreads should remain required: false (optional) in the mapper definition, even if it differs from the AgentTool definition in server/api/chat/tools.ts. This is an intentional design decision where parameter requirements can differ between the mapper and tool layers.
Applied to files:
server/integrations/slack/client.ts
📚 Learning: 2025-08-08T10:58:41.288Z
Learnt from: MayankBansal2004
Repo: xynehq/xyne PR: 719
File: server/api/chat/agents.ts:2056-2058
Timestamp: 2025-08-08T10:58:41.288Z
Learning: In server/db/schema/messages.ts, the messages table defines tokensUsed as integer and cost as numeric. Due to Drizzle’s numeric mapping, cost is handled as a string at the TypeScript layer, while tokensUsed remains a number. Therefore, insertMessage should persist cost as a string and tokensUsed as a number across server/api/chat/agents.ts.
Applied to files:
server/integrations/slack/client.ts
📚 Learning: 2025-11-06T09:49:42.273Z
Learnt from: Asrani-Aman
Repo: xynehq/xyne PR: 1163
File: server/api/chat/agents.ts:3941-3991
Timestamp: 2025-11-06T09:49:42.273Z
Learning: In server/api/chat/agents.ts dual RAG path and similar agent message flows, when `agentForDb.appIntegrations` is parsed, the outer function (agents.ts) only needs to extract high-level routing info like `agentAppEnums`. The detailed per-app selections (`selectedItems`) are intentionally extracted inside the inner RAG functions (`generateAnswerFromDualRag`, `UnderstandMessageAndAnswer` in chat.ts) by parsing the `agentPrompt` parameter. This is the established architectural pattern: outer function handles routing, inner function handles detailed selection parsing via `parseAppSelections(agentPromptData.appIntegrations)` and passes `selectedItem` to `searchVespaAgent` for per-app item scoping.
Applied to files:
server/integrations/slack/client.ts
🧬 Code graph analysis (9)
frontend/src/components/workflow/api/ApiHandlers.ts (1)
frontend/src/api.ts (1)
api(5-5)
server/integrations/slack/workflowSlackUtils.ts (4)
server/logger/index.ts (2)
getLogger(36-93)Subsystem(15-15)server/db/schema/workflows.ts (4)
workflowTemplate(65-89)workflowExecution(138-163)workflowStepTemplate(92-114)workflowStepExecution(166-192)server/db/workflowTool.ts (1)
getWorkflowToolsByIds(108-120)server/api/workflow.ts (1)
executeWorkflowChain(1158-1710)
server/server.ts (1)
server/api/workflow.ts (1)
getSlackMetadataApi(5937-5955)
frontend/src/components/workflow/slack/SlackTriggerConfigUI.tsx (2)
frontend/src/components/workflow/WorkflowIcons.tsx (1)
SlackIcon(494-526)frontend/src/components/workflow/slack/SlackChannlesInput.tsx (1)
SlackChannelInput(18-240)
server/integrations/slack/client.ts (3)
server/integrations/slack/types.ts (2)
DbUser(23-31)SlackTriggerTool(70-77)server/db/workflowTool.ts (1)
getSlackTriggersInWorkflows(125-186)server/integrations/slack/workflowSlackUtils.ts (1)
executeWorkflowWithSlackTrigger(45-199)
frontend/src/components/workflow/slack/SlackChannlesInput.tsx (1)
frontend/src/components/workflow/api/ApiHandlers.ts (1)
workflowToolsAPI(396-1106)
frontend/src/components/workflow/slack/SlackMessageConfigUI.tsx (2)
frontend/src/components/workflow/WorkflowIcons.tsx (1)
SlackIcon(494-526)frontend/src/components/workflow/slack/SlackChannlesInput.tsx (1)
SlackChannelInput(18-240)
server/api/workflow.ts (1)
server/integrations/slack/client.ts (2)
postSlackMessage(2492-2528)listSlackChannels(2427-2466)
frontend/src/components/workflow/WorkflowBuilder.tsx (3)
frontend/src/components/workflow/WorkflowIcons.tsx (3)
WebhookIcon(167-185)FormDocumentIcon(318-337)SlackIcon(494-526)frontend/src/components/workflow/slack/SlackTriggerConfigUI.tsx (2)
SlackTriggerConfig(6-11)SlackTriggerConfigUI(41-370)frontend/src/components/workflow/slack/SlackMessageConfigUI.tsx (2)
SlackMessageConfig(5-10)SlackMessageConfigUI(28-269)
🔇 Additional comments (3)
server/integrations/slack/workflowSlackUtils.ts (1)
144-157: Initial previousResults shape looks reasonable for Slack trigger payloadThe initial
previousResultsyou pass intoexecuteWorkflowChain(Lines 144–157) includes the Slack trigger payload under the root step’s name, along with atoolType: 'slack_trigger'. This aligns with how downstream tools typically consume accumulated results and should be sufficient for Slack‑aware tools to pick up the trigger context.server/server.ts (1)
327-328: Slack metadata endpoint wiring looks consistent and correctThe
getSlackMetadataApiimport and.get("/workflow/tools/slack/metadata", getSlackMetadataApi)route (Lines 327–328, 1510–1511) align with the Hono client path (api.workflow.tools.slack.metadata) and sit behind the existingAuthMiddleware, matching the pattern of other workflow tool routes.Also applies to: 1510-1511
frontend/src/components/workflow/WorkflowBuilder.tsx (1)
4727-4750: Pass initialConfig/toolId to Slack Trigger UI.The state variables exist but are never set on edit without the hydration above. Ensure props are wired; after applying the node-click hydration, these props work as intended. No code change needed here beyond the hydration fix.
|
Can you please add a demo? |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
server/api/workflow.ts (1)
5972-5993: Return proper HTTP errors for Slack metadata and gate on Slack being enabledThis handler currently logs and returns a generic JSON error:
export const getSlackMetadataApi = async (c: Context) => { try { const cursor = c.req.query("cursor") || undefined const limit = Number(c.req.query("limit")) || 200 const result = await listSlackChannels({ cursor, limit }) return c.json({ success: true, data: result }) } catch (error) { Logger.error(error, "Failed to get slack metadata") return c.json({ success: false, error: "Failed to get slack metadata" }, 500) } }Issues:
- It doesn’t fail fast when Slack isn’t configured (e.g., missing token/client).
- It diverges from the rest of this file, which typically throws an
HTTPExceptionwithgetErrorMessage(error)incatchblocks.You can align it with other APIs and make failure modes clearer by:
- Using an
isSlackEnabled()guard from the Slack client (if available).- Throwing
HTTPExceptioninstead of returning ad‑hoc JSON from the catch.For example:
-import { listSlackChannels, postSlackMessage } from "@/integrations/slack/client" +import { listSlackChannels, postSlackMessage, isSlackEnabled } from "@/integrations/slack/client" @@ export const getSlackMetadataApi = async (c: Context) => { try { + if (!isSlackEnabled()) { + throw new HTTPException(503, { message: "Slack integration is not configured" }) + } + const cursor = c.req.query("cursor") || undefined const limit = Number(c.req.query("limit")) || 200 const result = await listSlackChannels({ cursor, limit }) return c.json({ success: true, data: result }) } catch (error) { Logger.error(error, "Failed to get slack metadata") - return c.json({ - success: false, - error: "Failed to get slack metadata" - }, 500) + throw new HTTPException(500, { + message: getErrorMessage(error), + }) } }This matches the surrounding API style and guarantees callers always get a consistent HTTP‑level error.
🧹 Nitpick comments (1)
server/db/workflowTool.ts (1)
122-183: Reconfirm owner access & channel config assumptions in Slack trigger queryThe query looks good overall, but two edge cases are worth double‑checking:
Owner access without explicit permission rows
The access predicate is:and( eq(workflowTool.type, ToolType.SLACK_TRIGGER), eq(workflowTemplate.workspaceId, workspaceId), or( eq(workflowTemplate.isPublic, true), eq(userWorkflowPermissions.userId, userId), ), )This will surface:
- Public workflows in the workspace, and
- Workflows where the user has an entry in
userWorkflowPermissions.If owners don’t always get a
userWorkflowPermissionsrow for their own private workflows, their own non‑public Slack triggers won’t appear here. If that situation is possible, consider extending theorwitheq(workflowTemplate.userId, userId)to guarantee owners see their own triggers as well.Channel IDs shape assumptions
The final filter assumestoolConfig.channelIdsis astring[]:const channelIds = ((wf.toolConfig as any).channelIds as string[]) || []If this is ever mis‑shaped (single string, null, etc.), the result will silently drop that workflow. A small guard like
Array.isArray(...) ? ... : [](and/or returning an explicit config error upstream) would make this more robust.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
frontend/src/components/workflow/slack/SlackChannlesInput.tsx(1 hunks)server/api/workflow.ts(4 hunks)server/db/workflowTool.ts(3 hunks)server/integrations/slack/workflowSlackUtils.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/components/workflow/slack/SlackChannlesInput.tsx
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: Sithaarth24
Repo: xynehq/xyne PR: 1204
File: frontend/src/components/workflow/WorkflowBuilder.tsx:3851-3938
Timestamp: 2025-11-12T06:07:09.854Z
Learning: In the WorkflowBuilder component, workflow node configurations (including Slack Trigger, AI Agent, Email, HTTP Request, Form, etc.) should only update local React state when users configure them. Backend persistence happens through a single batch save operation when the user clicks the "Save Changes" button (handleSaveChanges function). Only Jira currently has immediate backend persistence (will be changed to match this pattern).
📚 Learning: 2025-11-12T06:07:09.854Z
Learnt from: Sithaarth24
Repo: xynehq/xyne PR: 1204
File: frontend/src/components/workflow/WorkflowBuilder.tsx:3851-3938
Timestamp: 2025-11-12T06:07:09.854Z
Learning: In the WorkflowBuilder component, workflow node configurations (including Slack Trigger, AI Agent, Email, HTTP Request, Form, etc.) should only update local React state when users configure them. Backend persistence happens through a single batch save operation when the user clicks the "Save Changes" button (handleSaveChanges function). Only Jira currently has immediate backend persistence (will be changed to match this pattern).
Applied to files:
server/integrations/slack/workflowSlackUtils.ts
📚 Learning: 2025-07-04T07:00:46.837Z
Learnt from: naSim087
Repo: xynehq/xyne PR: 638
File: server/integrations/slack/index.ts:793-824
Timestamp: 2025-07-04T07:00:46.837Z
Learning: In server/integrations/slack/index.ts, the dual IngestionState instances follow a specific pattern: `ingestionState` stores the current state that gets updated during processing, while `ingestionOldState` stores the old/previously saved state used for resumption logic when restarting ingestion.
Applied to files:
server/integrations/slack/workflowSlackUtils.ts
📚 Learning: 2025-09-08T16:18:07.128Z
Learnt from: Aayushjshah
Repo: xynehq/xyne PR: 787
File: server/api/chat/mapper.ts:631-636
Timestamp: 2025-09-08T16:18:07.128Z
Learning: In server/api/chat/mapper.ts, the filter_query parameter for getSlackThreads should remain required: false (optional) in the mapper definition, even if it differs from the AgentTool definition in server/api/chat/tools.ts. This is an intentional design decision where parameter requirements can differ between the mapper and tool layers.
Applied to files:
server/api/workflow.ts
📚 Learning: 2025-05-28T10:47:41.020Z
Learnt from: naSim087
Repo: xynehq/xyne PR: 484
File: server/integrations/google/sync.ts:222-222
Timestamp: 2025-05-28T10:47:41.020Z
Learning: The functions `handleGoogleDriveChange` and `getDriveChanges` in `server/integrations/google/sync.ts` are intentionally exported for future changes, even though they are not currently being imported by other modules.
Applied to files:
server/db/workflowTool.ts
📚 Learning: 2025-10-28T07:51:03.144Z
Learnt from: Sithaarth24
Repo: xynehq/xyne PR: 1127
File: server/db/schema/workflows.ts:320-320
Timestamp: 2025-10-28T07:51:03.144Z
Learning: In the workflow template architecture, the API layer handles conversion between external UUIDs (used by frontend) and internal integer IDs (used in database). Frontend types expect UUID strings for workflow template IDs, while internal schemas like createWorkflowStepTemplateSchema expect integers. The publicWorkflowTemplateSchema maps external_id → id for API responses.
Applied to files:
server/db/workflowTool.ts
📚 Learning: 2025-09-02T16:41:31.729Z
Learnt from: devesh-juspay
Repo: xynehq/xyne PR: 758
File: server/db/schema/workflowTools.ts:9-14
Timestamp: 2025-09-02T16:41:31.729Z
Learning: In the workflowTools.ts schema, the user prefers to add notNull() constraint to the type field but does not want onDelete cascade behavior for the workflowTemplateId foreign key reference.
Applied to files:
server/db/workflowTool.ts
🧬 Code graph analysis (3)
server/integrations/slack/workflowSlackUtils.ts (3)
server/logger/index.ts (2)
getLogger(36-93)Subsystem(15-15)server/db/workflowTool.ts (1)
getWorkflowToolsByIds(108-120)server/api/workflow.ts (1)
executeWorkflowChain(1158-1710)
server/api/workflow.ts (1)
server/integrations/slack/client.ts (2)
postSlackMessage(2492-2528)listSlackChannels(2427-2466)
server/db/workflowTool.ts (3)
server/types.ts (1)
TxnOrClient(340-340)server/db/schema/workflows.ts (3)
workflowTemplate(65-89)workflowTool(117-135)workflowStepTemplate(92-114)server/db/schema/userWorkflowPermissions.ts (1)
userWorkflowPermissions(23-54)
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
frontend/src/components/workflow/WorkflowBuilder.tsx (3)
718-720: Fix channel field mismatch in Slack Trigger rendering.Lines 718 and 720 use
config.channelId(singular), butSlackTriggerConfigdefineschannelIds: string[](plural array). This prevents channel information from ever displaying in the node.Apply this fix:
- const isConfigured = Boolean(config.title || step.name || config.triggerType || config.description) + const isConfigured = Boolean( + config.title || + step.name || + config.triggerType || + config.description || + (config.channelIds && config.channelIds.length > 0) + ) const triggerLabel = config.triggerType ? triggerTypeLabels[config.triggerType as keyof typeof triggerTypeLabels] : "Slack Trigger" - const channelInfo = config.channelId ? ` in ${config.channelId}` : "" + const channelInfo = + Array.isArray(config.channelIds) && config.channelIds.length > 0 + ? ` in ${config.channelIds.length === 1 ? config.channelIds[0] : config.channelIds.join(", ")}` + : ""
753-759: Fix channel field mismatch in Slack Message rendering.Lines 753 and 759 use
config.channel(singular), butSlackMessageConfigdefineschannelIds: string[](plural array). This prevents channel information from displaying correctly.Apply this fix:
- const isConfigured = Boolean(config.title || step.name || config.message || config.channel) + const isConfigured = Boolean( + config.title || + step.name || + config.message || + (config.channelIds && config.channelIds.length > 0) + ) return ( <WorkflowNode icon={<SlackIcon />} title={config.title || step.name || "Slack Message"} - description={config.description || `Send message to Slack${config.channel ? ` in ${config.channel}` : ""}`} + description={ + config.description || + `Send message to Slack${ + Array.isArray(config.channelIds) && config.channelIds.length > 0 + ? ` in ${config.channelIds.length === 1 ? config.channelIds[0] : config.channelIds.join(", ")}` + : "" + }` + }
2293-2297: Populate Slack Trigger config before opening the editor.When clicking an existing Slack Trigger node, the editor opens with blank values because
slackTriggerInitialConfigandslackTriggerToolIdare not populated. This causes users to lose their previous configuration when editing.Apply this fix (following the pattern used for Jira at lines 2258-2291):
case "slack_trigger": - // Open Slack Trigger config sidebar + // Open Slack Trigger config sidebar - fetch config from node + const slackTriggerTools = node?.data?.tools as any[] + const slackTriggerTool = slackTriggerTools?.[0] as any + if (slackTriggerTool?.id) { + // Populate config from node data + setSlackTriggerInitialConfig({ + ...slackTriggerTool?.config, + ...slackTriggerTool?.value, + }) + setSlackTriggerToolId(slackTriggerTool.id) + } else { + // No tool ID yet (shouldn't happen for existing nodes) + setSlackTriggerInitialConfig(undefined) + setSlackTriggerToolId(undefined) + } setShowSlackTriggerConfigUI(true) setSelectedSlackTriggerNodeId(node.id) break
🧹 Nitpick comments (2)
server/db/workflowTool.ts (1)
122-137: Consider defining a named type for the return value.While the explicit return type is helpful, defining a named interface or type (e.g.,
SlackTriggerWorkflow) would improve code reusability and maintainability, especially since this structure is likely used in other parts of the codebase (e.g.,SlackTriggerToolinserver/integrations/slack/types.ts).Example:
export type SlackTriggerWorkflow = { workflowId: string workflowName: string workflowDescription: string | null toolId: string toolConfig: any toolValue: any } export const getSlackTriggersInWorkflows = async ( txn: TxnOrClient, userId: number, workspaceId: number, channel: string ): Promise<SlackTriggerWorkflow[]> => { // ... }frontend/src/components/workflow/WorkflowBuilder.tsx (1)
707-711: Optional: Simplify config extraction with better typing.The multiple fallback logic with type assertions could be made clearer and type-safe. Consider extracting this pattern into a helper function or using a more structured approach:
const getSlackTriggerConfig = (step: Step, tools?: Tool[]): Partial<SlackTriggerConfig> => { const stepConfig = (step as any).config as Partial<SlackTriggerConfig> | undefined const toolConfig = tools?.[0]?.config as Partial<SlackTriggerConfig> | undefined const toolVal = tools?.[0]?.val as Partial<SlackTriggerConfig> | undefined return stepConfig || toolConfig || toolVal || {} } const config = getSlackTriggerConfig(step, hasSlackTriggerTool ? tools : undefined)This makes the intent clearer and avoids repeated type assertions.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
frontend/src/components/workflow/WorkflowBuilder.tsx(19 hunks)server/api/workflow.ts(5 hunks)server/db/workflowTool.ts(3 hunks)server/integrations/slack/workflowSlackUtils.ts(1 hunks)server/server.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- server/server.ts
- server/integrations/slack/workflowSlackUtils.ts
- server/api/workflow.ts
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: Sithaarth24
Repo: xynehq/xyne PR: 1204
File: frontend/src/components/workflow/WorkflowBuilder.tsx:3851-3938
Timestamp: 2025-11-12T06:07:09.854Z
Learning: In the WorkflowBuilder component, workflow node configurations (including Slack Trigger, AI Agent, Email, HTTP Request, Form, etc.) should only update local React state when users configure them. Backend persistence happens through a single batch save operation when the user clicks the "Save Changes" button (handleSaveChanges function). Only Jira currently has immediate backend persistence (will be changed to match this pattern).
📚 Learning: 2025-05-28T10:47:41.020Z
Learnt from: naSim087
Repo: xynehq/xyne PR: 484
File: server/integrations/google/sync.ts:222-222
Timestamp: 2025-05-28T10:47:41.020Z
Learning: The functions `handleGoogleDriveChange` and `getDriveChanges` in `server/integrations/google/sync.ts` are intentionally exported for future changes, even though they are not currently being imported by other modules.
Applied to files:
server/db/workflowTool.ts
📚 Learning: 2025-10-28T07:51:03.144Z
Learnt from: Sithaarth24
Repo: xynehq/xyne PR: 1127
File: server/db/schema/workflows.ts:320-320
Timestamp: 2025-10-28T07:51:03.144Z
Learning: In the workflow template architecture, the API layer handles conversion between external UUIDs (used by frontend) and internal integer IDs (used in database). Frontend types expect UUID strings for workflow template IDs, while internal schemas like createWorkflowStepTemplateSchema expect integers. The publicWorkflowTemplateSchema maps external_id → id for API responses.
Applied to files:
server/db/workflowTool.ts
📚 Learning: 2025-09-02T16:41:31.729Z
Learnt from: devesh-juspay
Repo: xynehq/xyne PR: 758
File: server/db/schema/workflowTools.ts:9-14
Timestamp: 2025-09-02T16:41:31.729Z
Learning: In the workflowTools.ts schema, the user prefers to add notNull() constraint to the type field but does not want onDelete cascade behavior for the workflowTemplateId foreign key reference.
Applied to files:
server/db/workflowTool.ts
📚 Learning: 2025-11-12T06:07:09.854Z
Learnt from: Sithaarth24
Repo: xynehq/xyne PR: 1204
File: frontend/src/components/workflow/WorkflowBuilder.tsx:3851-3938
Timestamp: 2025-11-12T06:07:09.854Z
Learning: In the WorkflowBuilder component, workflow node configurations (including Slack Trigger, AI Agent, Email, HTTP Request, Form, etc.) should only update local React state when users configure them. Backend persistence happens through a single batch save operation when the user clicks the "Save Changes" button (handleSaveChanges function). Only Jira currently has immediate backend persistence (will be changed to match this pattern).
Applied to files:
frontend/src/components/workflow/WorkflowBuilder.tsx
📚 Learning: 2025-07-28T12:07:13.546Z
Learnt from: vipul-maheshwari
Repo: xynehq/xyne PR: 686
File: server/utils/chainUtils.ts:65-69
Timestamp: 2025-07-28T12:07:13.546Z
Learning: In the chainUtils.ts file, the correct type for message parameters is `SelectMessage[]` rather than `Message[]` when working with conversation message arrays.
Applied to files:
frontend/src/components/workflow/WorkflowBuilder.tsx
📚 Learning: 2025-09-17T10:17:16.374Z
Learnt from: MayankBansal2004
Repo: xynehq/xyne PR: 853
File: frontend/src/components/ChatBox.tsx:696-726
Timestamp: 2025-09-17T10:17:16.374Z
Learning: MayankBansal2004 prefers the current model validation approach in ChatBox.tsx that validates saved models against allModelsWithO3 rather than filtering by current capability, considering this a "default mechanism thing" for the frontend that is working fine as designed.
Applied to files:
frontend/src/components/workflow/WorkflowBuilder.tsx
🧬 Code graph analysis (2)
server/db/workflowTool.ts (3)
server/types.ts (1)
TxnOrClient(340-340)server/db/schema/workflows.ts (3)
workflowTemplate(65-89)workflowTool(117-135)workflowStepTemplate(92-114)server/db/schema/userWorkflowPermissions.ts (1)
userWorkflowPermissions(23-54)
frontend/src/components/workflow/WorkflowBuilder.tsx (4)
frontend/src/components/workflow/WorkflowIcons.tsx (3)
WebhookIcon(167-185)FormDocumentIcon(318-337)SlackIcon(494-526)frontend/src/components/workflow/slack/SlackTriggerConfigUI.tsx (2)
SlackTriggerConfig(6-11)SlackTriggerConfigUI(41-370)frontend/src/components/workflow/slack/SlackMessageConfigUI.tsx (2)
SlackMessageConfig(5-10)SlackMessageConfigUI(28-269)frontend/src/components/workflow/Types.ts (1)
Tool(58-63)
🔇 Additional comments (2)
server/db/workflowTool.ts (2)
1-1: LGTM!The new imports (
sql,or,workflowTemplate,workflowStepTemplate,userWorkflowPermissions) are properly added and utilized in the newgetSlackTriggersInWorkflowsfunction.Also applies to: 10-11, 14-14
164-167: Permission logic correctly implemented.The
or()condition properly handles both public workflows and explicitly shared workflows. The left join onuserWorkflowPermissionscombined with the OR condition ensures that:
- Public workflows (
isPublic = true) are accessible to all workspace users- Private workflows are only accessible if the user has an explicit permission entry
This correctly addresses the past review concern about workspace workflow visibility.
Summary by CodeRabbit