From 69042148d58174212cf67e2065f62a9162db15a3 Mon Sep 17 00:00:00 2001 From: abhishekpatil4 Date: Thu, 1 May 2025 22:43:04 +0530 Subject: [PATCH 01/17] feat: add Composio Lienar component --- .../langflow/components/composio/__init__.py | 4 +- .../components/composio/linear_api.py | 80 +++++++++++++++++++ src/frontend/src/icons/linear/index.tsx | 9 +++ src/frontend/src/icons/linear/linear.jsx | 11 +++ src/frontend/src/icons/linear/linear.svg | 9 +++ src/frontend/src/utils/styleUtils.ts | 3 + 6 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 src/backend/base/langflow/components/composio/linear_api.py create mode 100644 src/frontend/src/icons/linear/index.tsx create mode 100644 src/frontend/src/icons/linear/linear.jsx create mode 100644 src/frontend/src/icons/linear/linear.svg diff --git a/src/backend/base/langflow/components/composio/__init__.py b/src/backend/base/langflow/components/composio/__init__.py index 66148b0597fd..212084adfa84 100644 --- a/src/backend/base/langflow/components/composio/__init__.py +++ b/src/backend/base/langflow/components/composio/__init__.py @@ -1,7 +1,5 @@ from .composio_api import ComposioAPIComponent from .gmail_composio import ComposioGmailAPIComponent +from .linear_api import LinearAPIComponent __all__ = [ - "ComposioAPIComponent", - "ComposioGmailAPIComponent", -] diff --git a/src/backend/base/langflow/components/composio/linear_api.py b/src/backend/base/langflow/components/composio/linear_api.py new file mode 100644 index 000000000000..3b88c7235c26 --- /dev/null +++ b/src/backend/base/langflow/components/composio/linear_api.py @@ -0,0 +1,80 @@ +from typing import Any + +from composio import Action + +from langflow.base.composio.composio_base import ComposioBaseComponent +from langflow.inputs import ( + BoolInput, + IntInput, + MessageTextInput, +) +from langflow.logging import logger + + +class ComposioLinearAPIComponent(ComposioBaseComponent): + display_name: str = "Linear" + description: str = "Linear API" + icon = "Linear" + documentation: str = "https://docs.composio.dev" + app_name = "linear" + + _actions_data: dict = {} + + _all_fields = {field for action_data in _actions_data.values() for field in action_data["action_fields"]} + + _bool_variables = {} + + inputs = [ + *ComposioBaseComponent._base_inputs, + ] + + def execute_action(self): + """Execute action and return response as Message.""" + toolset = self._build_wrapper() + + try: + self._build_action_maps() + display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else self.action + action_key = self._display_to_key_map.get(display_name) + if not action_key: + msg = f"Invalid action: {display_name}" + raise ValueError(msg) + + enum_name = getattr(Action, action_key) + params = {} + if action_key in self._actions_data: + for field in self._actions_data[action_key]["action_fields"]: + value = getattr(self, field) + + if value is None or value == "": + continue + + if field in self._bool_variables: + value = bool(value) + + param_name = field.replace(action_key + "_", "") + + params[param_name] = value + + result = toolset.execute_action( + action=enum_name, + params=params, + ) + if not result.get("successful"): + return {"error": result.get("error", "No response")} + + return result.get("data", []) + except Exception as e: + logger.error(f"Error executing action: {e}") + display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else str(self.action) + msg = f"Failed to execute {display_name}: {e!s}" + raise ValueError(msg) from e + + def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict: + return super().update_build_config(build_config, field_value, field_name) + + def set_default_tools(self): + self._default_tools = { + self.sanitize_action_name("").replace(" ", "-"), + self.sanitize_action_name("").replace(" ", "-"), + } \ No newline at end of file diff --git a/src/frontend/src/icons/linear/index.tsx b/src/frontend/src/icons/linear/index.tsx new file mode 100644 index 000000000000..731648d90995 --- /dev/null +++ b/src/frontend/src/icons/linear/index.tsx @@ -0,0 +1,9 @@ +import React, { forwardRef } from "react"; +import LinearIconSVG from "./linear"; + +export const LinearIcon = forwardRef< + SVGSVGElement, + React.PropsWithChildren<{}> +>((props, ref) => { + return ; +}); \ No newline at end of file diff --git a/src/frontend/src/icons/linear/linear.jsx b/src/frontend/src/icons/linear/linear.jsx new file mode 100644 index 000000000000..e0454f41dc4a --- /dev/null +++ b/src/frontend/src/icons/linear/linear.jsx @@ -0,0 +1,11 @@ +const Icon = (props) => ( + + + + + + + + +); +export default Icon; diff --git a/src/frontend/src/icons/linear/linear.svg b/src/frontend/src/icons/linear/linear.svg new file mode 100644 index 000000000000..689dbe52188e --- /dev/null +++ b/src/frontend/src/icons/linear/linear.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index cb7a2d8bff20..aa4c6bf2e13b 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -1,3 +1,4 @@ +import { LinearIcon } from "../icons/Linear"; import { ENABLE_DATASTAX_LANGFLOW } from "@/customization/feature-flags"; import { BotMessageSquareIcon } from "@/icons/BotMessageSquare"; import { GradientSave } from "@/icons/GradientSparkles"; @@ -228,6 +229,7 @@ export const SIDEBAR_CATEGORIES = [ ]; export const SIDEBAR_BUNDLES = [ + { display_name: "Linear", name: "linear", icon: "Linear" }, { display_name: "Amazon", name: "amazon", icon: "Amazon" }, { display_name: "Gmail", name: "gmail", icon: "Gmail" }, // Add apify @@ -320,6 +322,7 @@ export const nodeIconToDisplayIconMap: Record = { ChatInput: "MessagesSquare", ChatOutput: "MessagesSquare", //Integration Icons + Linear: LinearIcon, AIML: "AI/ML", AgentQL: "AgentQL", AirbyteJSONLoader: "Airbyte", From dc97d92fc2688461fe1a11402ed1c01b34df0cfe Mon Sep 17 00:00:00 2001 From: abhishekpatil4 Date: Mon, 12 May 2025 20:48:19 +0530 Subject: [PATCH 02/17] feat: add Composio Linear component --- .../langflow/components/composio/__init__.py | 6 +- .../components/composio/linear_api.py | 468 +++++++++++++++++- src/frontend/src/icons/linear/index.tsx | 2 +- src/frontend/src/icons/linear/linear.jsx | 35 +- src/frontend/src/utils/styleUtils.ts | 2 +- 5 files changed, 489 insertions(+), 24 deletions(-) diff --git a/src/backend/base/langflow/components/composio/__init__.py b/src/backend/base/langflow/components/composio/__init__.py index 212084adfa84..1ed912dd8551 100644 --- a/src/backend/base/langflow/components/composio/__init__.py +++ b/src/backend/base/langflow/components/composio/__init__.py @@ -1,5 +1,9 @@ from .composio_api import ComposioAPIComponent from .gmail_composio import ComposioGmailAPIComponent -from .linear_api import LinearAPIComponent +from .linear_api import ComposioLinearAPIComponent __all__ = [ + "ComposioAPIComponent", + "ComposioGmailAPIComponent", + "ComposioLinearAPIComponent", +] diff --git a/src/backend/base/langflow/components/composio/linear_api.py b/src/backend/base/langflow/components/composio/linear_api.py index 3b88c7235c26..8b5da5959213 100644 --- a/src/backend/base/langflow/components/composio/linear_api.py +++ b/src/backend/base/langflow/components/composio/linear_api.py @@ -4,7 +4,6 @@ from langflow.base.composio.composio_base import ComposioBaseComponent from langflow.inputs import ( - BoolInput, IntInput, MessageTextInput, ) @@ -17,17 +16,460 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): icon = "Linear" documentation: str = "https://docs.composio.dev" app_name = "linear" - - _actions_data: dict = {} - + + _actions_data: dict = { + "LINEAR_CREATE_LINEAR_ATTACHMENT": { + "display_name": "Create Linear Attachment", + "action_fields": [ + "LINEAR_CREATE_LINEAR_ATTACHMENT_issue_id", + "LINEAR_CREATE_LINEAR_ATTACHMENT_subtitle", + "LINEAR_CREATE_LINEAR_ATTACHMENT_title", + "LINEAR_CREATE_LINEAR_ATTACHMENT_url", + ], + }, + "LINEAR_CREATE_LINEAR_COMMENT": { + "display_name": "Create Linear Comment", + "action_fields": ["LINEAR_CREATE_LINEAR_COMMENT_body", "LINEAR_CREATE_LINEAR_COMMENT_issue_id"], + }, + "LINEAR_CREATE_LINEAR_ISSUE": { + "display_name": "Create Linear Issue", + "action_fields": [ + "LINEAR_CREATE_LINEAR_ISSUE_assignee_id", + "LINEAR_CREATE_LINEAR_ISSUE_description", + "LINEAR_CREATE_LINEAR_ISSUE_due_date", + "LINEAR_CREATE_LINEAR_ISSUE_estimate", + "LINEAR_CREATE_LINEAR_ISSUE_label_ids", + "LINEAR_CREATE_LINEAR_ISSUE_parent_id", + "LINEAR_CREATE_LINEAR_ISSUE_priority", + "LINEAR_CREATE_LINEAR_ISSUE_project_id", + "LINEAR_CREATE_LINEAR_ISSUE_state_id", + "LINEAR_CREATE_LINEAR_ISSUE_team_id", + "LINEAR_CREATE_LINEAR_ISSUE_title", + ], + }, + "LINEAR_CREATE_LINEAR_ISSUE_DETAILS": { + "display_name": "Get Create Issue Default Params", + "action_fields": ["LINEAR_CREATE_LINEAR_ISSUE_DETAILS_team_id"], + }, + "LINEAR_CREATE_LINEAR_LABEL": { + "display_name": "Create Label", + "action_fields": [ + "LINEAR_CREATE_LINEAR_LABEL_color", + "LINEAR_CREATE_LINEAR_LABEL_description", + "LINEAR_CREATE_LINEAR_LABEL_name", + "LINEAR_CREATE_LINEAR_LABEL_team_id", + ], + }, + "LINEAR_DELETE_LINEAR_ISSUE": { + "display_name": "Delete Issue", + "action_fields": ["LINEAR_DELETE_LINEAR_ISSUE_issue_id"], + }, + "LINEAR_GET_ATTACHMENTS": { + "display_name": "Donwload Issue Attachments", + "action_fields": [ + "LINEAR_GET_ATTACHMENTS_attachment_id", + "LINEAR_GET_ATTACHMENTS_file_name", + "LINEAR_GET_ATTACHMENTS_issue_id", + ], + }, + "LINEAR_GET_CYCLES_BY_TEAM_ID": { + "display_name": "Get Cycles By Team", + "action_fields": ["LINEAR_GET_CYCLES_BY_TEAM_ID_team_id"], + }, + "LINEAR_GET_LINEAR_ISSUE": { + "display_name": "Get Linear Issue", + "action_fields": ["LINEAR_GET_LINEAR_ISSUE_issue_id"], + }, + "LINEAR_LIST_LINEAR_CYCLES": {"display_name": "Get All Cycles", "action_fields": []}, + "LINEAR_LIST_LINEAR_ISSUES": { + "display_name": "Get Issues By Project", + "action_fields": [ + "LINEAR_LIST_LINEAR_ISSUES_after", + "LINEAR_LIST_LINEAR_ISSUES_first", + "LINEAR_LIST_LINEAR_ISSUES_project_id", + ], + }, + "LINEAR_LIST_LINEAR_LABELS": { + "display_name": "Get Labels By Team", + "action_fields": ["LINEAR_LIST_LINEAR_LABELS_team_id"], + }, + "LINEAR_LIST_LINEAR_PROJECTS": {"display_name": "List Linear Projects", "action_fields": []}, + "LINEAR_LIST_LINEAR_STATES": { + "display_name": "Get States By Team", + "action_fields": ["LINEAR_LIST_LINEAR_STATES_team_id"], + }, + "LINEAR_LIST_LINEAR_TEAMS": { + "display_name": "Get Teams By Project", + "action_fields": ["LINEAR_LIST_LINEAR_TEAMS_project_id"], + }, + "LINEAR_REMOVE_ISSUE_LABEL": { + "display_name": "Remove Label From Linear Issue", + "action_fields": ["LINEAR_REMOVE_ISSUE_LABEL_issue_id", "LINEAR_REMOVE_ISSUE_LABEL_label_id"], + }, + "LINEAR_RUN_QUERY_OR_MUTATION": { + "display_name": "Run Query Or Mutation", + "action_fields": [ + "LINEAR_RUN_QUERY_OR_MUTATION_query_or_mutation", + "LINEAR_RUN_QUERY_OR_MUTATION_variables", + ], + }, + "LINEAR_UPDATE_ISSUE": { + "display_name": "Update Issue", + "action_fields": [ + "LINEAR_UPDATE_ISSUE_assignee_id", + "LINEAR_UPDATE_ISSUE_description", + "LINEAR_UPDATE_ISSUE_due_date", + "LINEAR_UPDATE_ISSUE_estimate", + "LINEAR_UPDATE_ISSUE_issue_id", + "LINEAR_UPDATE_ISSUE_label_ids", + "LINEAR_UPDATE_ISSUE_parent_id", + "LINEAR_UPDATE_ISSUE_priority", + "LINEAR_UPDATE_ISSUE_project_id", + "LINEAR_UPDATE_ISSUE_state_id", + "LINEAR_UPDATE_ISSUE_team_id", + "LINEAR_UPDATE_ISSUE_title", + ], + }, + } + _all_fields = {field for action_data in _actions_data.values() for field in action_data["action_fields"]} - - _bool_variables = {} - + inputs = [ *ComposioBaseComponent._base_inputs, + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ATTACHMENT_issue_id", + display_name="Issue Id", + info="ID of the issue to attach to", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ATTACHMENT_subtitle", + display_name="Subtitle", + info="Subtitle of the attachment", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ATTACHMENT_title", + display_name="Title", + info="Title of the attachment", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ATTACHMENT_url", + display_name="Url", + info="URL of the attachment", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_COMMENT_body", + display_name="Body", + info="Content of the comment", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_COMMENT_issue_id", + display_name="Issue Id", + info="ID of the issue to comment on", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ISSUE_assignee_id", + display_name="Assignee Id", + info="ID of the assignee", + show=False, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ISSUE_description", + display_name="Description", + info="Description of the issue", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ISSUE_due_date", + display_name="Due Date", + info="Due date of the issue in the comma separated format YYYY,MM,DD,hh,mm,ss. For example, 2024,10,27,12,58,00.", # noqa: E501 + show=False, + ), + IntInput( + name="LINEAR_CREATE_LINEAR_ISSUE_estimate", + display_name="Estimate", + info="The Int scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.", # noqa: E501 + show=False, + value=0, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ISSUE_label_ids", + display_name="Label Ids", + info="List of label IDs", + show=False, + value=[], + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ISSUE_parent_id", + display_name="Parent Id", + info="ID of the parent issue", + show=False, + ), + IntInput( + name="LINEAR_CREATE_LINEAR_ISSUE_priority", + display_name="Priority", + info="The priority of the issue. 0 = No priority, 1 = Urgent, 2 = High, 3 = Normal, 4 = Low.", + show=False, + value=0, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ISSUE_project_id", + display_name="Project Id", + info="ID of the project", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ISSUE_state_id", + display_name="State Id", + info="ID of the issue state", + show=False, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ISSUE_team_id", + display_name="Team Id", + info="ID of the team", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ISSUE_title", + display_name="Title", + info="Title of the issue", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_ISSUE_DETAILS_team_id", + display_name="Team Id", + info="ID of the team for which to fetch details", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_LABEL_color", + display_name="Color", + info="Color of the label (hex code)", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_LABEL_description", + display_name="Description", + info="Description of the label", + show=False, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_LABEL_name", + display_name="Name", + info="Name of the label", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_CREATE_LINEAR_LABEL_team_id", + display_name="Team Id", + info="ID of the team to create the label for", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_DELETE_LINEAR_ISSUE_issue_id", + display_name="Issue Id", + info="The ID of the issue to delete (can be UUID or shorthand ID like 'LIN-123')", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_GET_ATTACHMENTS_attachment_id", + display_name="Attachment Id", + info="ID of the attachment to be fetched", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_GET_ATTACHMENTS_file_name", + display_name="File Name", + info="Name of the file to be saved by. The file downloaded will be saved with this name. Make sure to include the file extension.", # noqa: E501 + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_GET_ATTACHMENTS_issue_id", + display_name="Issue Id", + info="ID of the issue for which to fetch details", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_GET_CYCLES_BY_TEAM_ID_team_id", + display_name="Team Id", + info="ID of the team for which to list cycles", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_GET_LINEAR_ISSUE_issue_id", + display_name="Issue Id", + info="ID of the issue for which to fetch details", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_LIST_LINEAR_ISSUES_after", + display_name="After", + info="Cursor to start from", + show=False, + ), + IntInput( + name="LINEAR_LIST_LINEAR_ISSUES_first", + display_name="First", + info="Number of issues to return", + show=False, + value=10, + ), + MessageTextInput( + name="LINEAR_LIST_LINEAR_ISSUES_project_id", + display_name="Project Id", + info="ID of the project for which to list issues. If this is provided the issues returned will be filtered by the given project ID.", # noqa: E501 + show=False, + ), + MessageTextInput( + name="LINEAR_LIST_LINEAR_LABELS_team_id", + display_name="Team Id", + info="ID of the team for which to list labels", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_LIST_LINEAR_STATES_team_id", + display_name="Team Id", + info="ID of the team for which to list states", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_LIST_LINEAR_TEAMS_project_id", + display_name="Project Id", + info="ID of the project for which to list teams", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_REMOVE_ISSUE_LABEL_issue_id", + display_name="Issue Id", + info="The ID of the Linear issue from which to remove the label", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_REMOVE_ISSUE_LABEL_label_id", + display_name="Label Id", + info="The ID of the label to remove from the issue", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_RUN_QUERY_OR_MUTATION_query_or_mutation", + display_name="Query Or Mutation", + info="Query or mutation to run", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_RUN_QUERY_OR_MUTATION_variables", + display_name="Variables", + info="Variables to pass to the query or mutation", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_UPDATE_ISSUE_assignee_id", + display_name="Assignee Id", + info="ID of the assignee", + show=False, + ), + MessageTextInput( + name="LINEAR_UPDATE_ISSUE_description", + display_name="Description", + info="New description for the issue", + show=False, + ), + MessageTextInput( + name="LINEAR_UPDATE_ISSUE_due_date", + display_name="Due Date", + info="Due date of the issue in the comma separated format YYYY,MM,DD,hh,mm,ss. For example, 2024,10,27,12,58,00.", # noqa: E501 + show=False, + ), + IntInput( + name="LINEAR_UPDATE_ISSUE_estimate", + display_name="Estimate", + info="Time estimate for the issue in minutes", + show=False, + ), + MessageTextInput( + name="LINEAR_UPDATE_ISSUE_issue_id", + display_name="Issue Id", + info="ID of the issue to update", + show=False, + required=True, + ), + MessageTextInput( + name="LINEAR_UPDATE_ISSUE_label_ids", + display_name="Label Ids", + info="List of label IDs to assign to the issue", + show=False, + ), + MessageTextInput( + name="LINEAR_UPDATE_ISSUE_parent_id", + display_name="Parent Id", + info="ID of the parent issue", + show=False, + ), + IntInput( + name="LINEAR_UPDATE_ISSUE_priority", + display_name="Priority", + info="The priority of the issue. 0 = No priority, 1 = Urgent, 2 = High, 3 = Normal, 4 = Low.", + show=False, + ), + MessageTextInput( + name="LINEAR_UPDATE_ISSUE_project_id", + display_name="Project Id", + info="ID of the project", + show=False, + ), + MessageTextInput( + name="LINEAR_UPDATE_ISSUE_state_id", + display_name="State Id", + info="ID of the issue state", + show=False, + ), + MessageTextInput( + name="LINEAR_UPDATE_ISSUE_team_id", + display_name="Team Id", + info="ID of the team", + show=False, + ), + MessageTextInput( + name="LINEAR_UPDATE_ISSUE_title", + display_name="Title", + info="New title for the issue", + show=False, + ), ] - + def execute_action(self): """Execute action and return response as Message.""" toolset = self._build_wrapper() @@ -49,8 +491,8 @@ def execute_action(self): if value is None or value == "": continue - if field in self._bool_variables: - value = bool(value) + if field in ["LINEAR_CREATE_LINEAR_ISSUE_label_ids", "LINEAR_UPDATE_ISSUE_label_ids"] and value: + value = [item.strip() for item in value.split(",")] param_name = field.replace(action_key + "_", "") @@ -75,6 +517,6 @@ def update_build_config(self, build_config: dict, field_value: Any, field_name: def set_default_tools(self): self._default_tools = { - self.sanitize_action_name("").replace(" ", "-"), - self.sanitize_action_name("").replace(" ", "-"), - } \ No newline at end of file + self.sanitize_action_name("LINEAR_CREATE_LINEAR_ISSUE").replace(" ", "-"), + self.sanitize_action_name("LINEAR_GET_LINEAR_ISSUE").replace(" ", "-"), + } diff --git a/src/frontend/src/icons/linear/index.tsx b/src/frontend/src/icons/linear/index.tsx index 731648d90995..9ffa622ce199 100644 --- a/src/frontend/src/icons/linear/index.tsx +++ b/src/frontend/src/icons/linear/index.tsx @@ -6,4 +6,4 @@ export const LinearIcon = forwardRef< React.PropsWithChildren<{}> >((props, ref) => { return ; -}); \ No newline at end of file +}); diff --git a/src/frontend/src/icons/linear/linear.jsx b/src/frontend/src/icons/linear/linear.jsx index e0454f41dc4a..e1f4ea8529d2 100644 --- a/src/frontend/src/icons/linear/linear.jsx +++ b/src/frontend/src/icons/linear/linear.jsx @@ -1,11 +1,30 @@ const Icon = (props) => ( - - - - - - - - + + + + + + + + ); export default Icon; diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index aa4c6bf2e13b..cd5bb11d93f9 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -1,4 +1,3 @@ -import { LinearIcon } from "../icons/Linear"; import { ENABLE_DATASTAX_LANGFLOW } from "@/customization/feature-flags"; import { BotMessageSquareIcon } from "@/icons/BotMessageSquare"; import { GradientSave } from "@/icons/GradientSparkles"; @@ -6,6 +5,7 @@ import { TwitterLogoIcon } from "@radix-ui/react-icons"; import dynamicIconImports from "lucide-react/dynamicIconImports"; import { lazy } from "react"; import { FaApple, FaDiscord, FaGithub } from "react-icons/fa"; +import { LinearIcon } from "../icons/Linear"; import { lucideIcons } from "../icons/lucideIcons"; export const BG_NOISE = From b58a4ebd32a7749945da168a61732aa2fdf422d7 Mon Sep 17 00:00:00 2001 From: abhishekpatil4 Date: Mon, 12 May 2025 22:56:11 +0530 Subject: [PATCH 03/17] fix: icons --- src/backend/base/langflow/components/composio/__init__.py | 2 +- .../components/composio/{linear_api.py => linear_composio.py} | 0 src/frontend/src/icons/lazyIconImports.ts | 2 ++ src/frontend/src/icons/linear/linear.svg | 1 - src/frontend/src/utils/styleUtils.ts | 2 -- 5 files changed, 3 insertions(+), 4 deletions(-) rename src/backend/base/langflow/components/composio/{linear_api.py => linear_composio.py} (100%) diff --git a/src/backend/base/langflow/components/composio/__init__.py b/src/backend/base/langflow/components/composio/__init__.py index 2a098f603abd..8aa348302d21 100644 --- a/src/backend/base/langflow/components/composio/__init__.py +++ b/src/backend/base/langflow/components/composio/__init__.py @@ -2,7 +2,7 @@ from .gmail_composio import ComposioGmailAPIComponent from .googlecalendar_composio import ComposioGoogleCalendarAPIComponent from .slack_composio import ComposioSlackAPIComponent -from .linear_api import ComposioLinearAPIComponent +from .linear_composio import ComposioLinearAPIComponent __all__ = [ "ComposioAPIComponent", diff --git a/src/backend/base/langflow/components/composio/linear_api.py b/src/backend/base/langflow/components/composio/linear_composio.py similarity index 100% rename from src/backend/base/langflow/components/composio/linear_api.py rename to src/backend/base/langflow/components/composio/linear_composio.py diff --git a/src/frontend/src/icons/lazyIconImports.ts b/src/frontend/src/icons/lazyIconImports.ts index 8cec685ebff2..51e89f9695c1 100644 --- a/src/frontend/src/icons/lazyIconImports.ts +++ b/src/frontend/src/icons/lazyIconImports.ts @@ -105,6 +105,8 @@ export const lazyIconsMapping = { })), Gmail: () => import("@/icons/gmail").then((mod) => ({ default: mod.GmailIcon })), + Linear: () => + import("@/icons/linear").then((mod) => ({ default: mod.LinearIcon })), Googlecalendar: () => import("@/icons/googlecalendar").then((mod) => ({ default: mod.GooglecalendarIcon, diff --git a/src/frontend/src/icons/linear/linear.svg b/src/frontend/src/icons/linear/linear.svg index 689dbe52188e..b0df72009709 100644 --- a/src/frontend/src/icons/linear/linear.svg +++ b/src/frontend/src/icons/linear/linear.svg @@ -1,4 +1,3 @@ - diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index 10c5c3e7a15a..ae7c8eee1f15 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -5,7 +5,6 @@ import { TwitterLogoIcon } from "@radix-ui/react-icons"; import dynamicIconImports from "lucide-react/dynamicIconImports"; import { lazy } from "react"; import { FaApple, FaDiscord, FaGithub } from "react-icons/fa"; -import { LinearIcon } from "../icons/Linear"; export const BG_NOISE = "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoYRxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg==)"; @@ -326,7 +325,6 @@ export const nodeIconToDisplayIconMap: Record = { ChatInput: "MessagesSquare", ChatOutput: "MessagesSquare", //Integration Icons - Linear: LinearIcon, AIML: "AI/ML", AgentQL: "AgentQL", AirbyteJSONLoader: "Airbyte", From 0b2fadaa4db3665d0148fdbed8eb8d03403701ca Mon Sep 17 00:00:00 2001 From: abhishekpatil4 Date: Mon, 12 May 2025 22:57:18 +0530 Subject: [PATCH 04/17] fix: format --- src/backend/base/langflow/components/composio/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/base/langflow/components/composio/__init__.py b/src/backend/base/langflow/components/composio/__init__.py index 8aa348302d21..254e56dc7923 100644 --- a/src/backend/base/langflow/components/composio/__init__.py +++ b/src/backend/base/langflow/components/composio/__init__.py @@ -1,13 +1,13 @@ from .composio_api import ComposioAPIComponent from .gmail_composio import ComposioGmailAPIComponent from .googlecalendar_composio import ComposioGoogleCalendarAPIComponent -from .slack_composio import ComposioSlackAPIComponent from .linear_composio import ComposioLinearAPIComponent +from .slack_composio import ComposioSlackAPIComponent __all__ = [ "ComposioAPIComponent", "ComposioGmailAPIComponent", "ComposioGoogleCalendarAPIComponent", - "ComposioSlackAPIComponent", "ComposioLinearAPIComponent", + "ComposioSlackAPIComponent", ] From 7c35dc7110bc30fb90e9f7652b5343f65b3aad5c Mon Sep 17 00:00:00 2001 From: abhishekpatil4 Date: Tue, 13 May 2025 12:35:09 +0530 Subject: [PATCH 05/17] fix: remove invalid default value --- src/backend/base/langflow/components/composio/linear_composio.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend/base/langflow/components/composio/linear_composio.py b/src/backend/base/langflow/components/composio/linear_composio.py index 8b5da5959213..cb602ac0f968 100644 --- a/src/backend/base/langflow/components/composio/linear_composio.py +++ b/src/backend/base/langflow/components/composio/linear_composio.py @@ -209,7 +209,6 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): display_name="Label Ids", info="List of label IDs", show=False, - value=[], ), MessageTextInput( name="LINEAR_CREATE_LINEAR_ISSUE_parent_id", From e305d5ffced05a289210070423408937e5faeee2 Mon Sep 17 00:00:00 2001 From: abhishekpatil4 Date: Tue, 27 May 2025 21:35:52 +0530 Subject: [PATCH 06/17] feat: add test file --- .../bundles/composio/test_linear.py | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 src/backend/tests/unit/components/bundles/composio/test_linear.py diff --git a/src/backend/tests/unit/components/bundles/composio/test_linear.py b/src/backend/tests/unit/components/bundles/composio/test_linear.py new file mode 100644 index 000000000000..b52f29c19919 --- /dev/null +++ b/src/backend/tests/unit/components/bundles/composio/test_linear.py @@ -0,0 +1,170 @@ +from unittest.mock import MagicMock, patch + +import pytest +from composio import Action +from langflow.components.composio.linear_composio import ComposioLinearAPIComponent +from langflow.schema.dataframe import DataFrame + +from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient + +from .test_base import MockComposioToolSet + + +class MockAction: + LINEAR_CREATE_LINEAR_ISSUE = "LINEAR_CREATE_LINEAR_ISSUE" + LINEAR_GET_LINEAR_ISSUE = "LINEAR_GET_LINEAR_ISSUE" + LINEAR_LIST_LINEAR_ISSUES = "LINEAR_LIST_LINEAR_ISSUES" + + +class TestLinearComponent(ComponentTestBaseWithoutClient): + @pytest.fixture(autouse=True) + def mock_composio_toolset(self): + with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet): + yield + + @pytest.fixture + def component_class(self): + return ComposioLinearAPIComponent + + @pytest.fixture + def default_kwargs(self): + return {"api_key": "", "entity_id": "default", "action": None} + + @pytest.fixture + def file_names_mapping(self): + return [ + {"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST}, + {"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST}, + {"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST}, + {"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST}, + {"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST}, + ] + + def test_init(self, component_class, default_kwargs): + component = component_class(**default_kwargs) + assert component.display_name == "Linear" + assert component.app_name == "linear" + assert "LINEAR_CREATE_LINEAR_ISSUE" in component._actions_data + assert "LINEAR_GET_LINEAR_ISSUE" in component._actions_data + + def test_execute_action_create_issue(self, component_class, default_kwargs, monkeypatch): + monkeypatch.setattr(Action, "LINEAR_CREATE_LINEAR_ISSUE", MockAction.LINEAR_CREATE_LINEAR_ISSUE) + + component = component_class(**default_kwargs) + component.api_key = "test_key" + component.action = [{"name": "Create Linear Issue"}] + component.LINEAR_CREATE_LINEAR_ISSUE_description = "Test Description" + component.LINEAR_CREATE_LINEAR_ISSUE_title = "Test Title" + component.LINEAR_CREATE_LINEAR_ISSUE_team_id = "123" + component.LINEAR_CREATE_LINEAR_ISSUE_project_id = "123" + + component._actions_data = { + "LINEAR_CREATE_LINEAR_ISSUE": { + "display_name": "Create Linear Issue", + "action_fields": [ + "LINEAR_CREATE_LINEAR_ISSUE_assignee_id", + "LINEAR_CREATE_LINEAR_ISSUE_description", + "LINEAR_CREATE_LINEAR_ISSUE_due_date", + "LINEAR_CREATE_LINEAR_ISSUE_estimate", + "LINEAR_CREATE_LINEAR_ISSUE_label_ids", + "LINEAR_CREATE_LINEAR_ISSUE_parent_id", + "LINEAR_CREATE_LINEAR_ISSUE_priority", + "LINEAR_CREATE_LINEAR_ISSUE_project_id", + "LINEAR_CREATE_LINEAR_ISSUE_state_id", + "LINEAR_CREATE_LINEAR_ISSUE_team_id", + "LINEAR_CREATE_LINEAR_ISSUE_title", + ], + }, + } + + result = component.execute_action() + assert result == {"result": "mocked response"} + + def test_execute_action_get_issue(self, component_class, default_kwargs, monkeypatch): + monkeypatch.setattr(Action, "LINEAR_GET_LINEAR_ISSUE", MockAction.LINEAR_GET_LINEAR_ISSUE) + + component = component_class(**default_kwargs) + component.api_key = "test_key" + component.action = [{"name": "Get Linear Issue"}] + component.issue_id = "123" + + component._actions_data = { + "LINEAR_GET_LINEAR_ISSUE": { + "display_name": "Get Linear Issue", + "action_fields": ["LINEAR_GET_LINEAR_ISSUE_issue_id"], + }, + } + + mock_toolset = MagicMock() + mock_toolset.execute_action.return_value = {"successful": True, "data": {"messages": "mocked response"}} + + with patch.object(component, "_build_wrapper", return_value=mock_toolset): + result = component.execute_action() + assert result == {"messages": "mocked response"} + + def test_execute_action_invalid_action(self, component_class, default_kwargs): + component = component_class(**default_kwargs) + component.api_key = "test_key" + component.action = [{"name": "Invalid Action"}] + + with pytest.raises(ValueError, match="Invalid action: Invalid Action"): + component.execute_action() + + def test_as_dataframe(self, component_class, default_kwargs, monkeypatch): + monkeypatch.setattr(Action, "LINEAR_LIST_LINEAR_ISSUES", MockAction.LINEAR_LIST_LINEAR_ISSUES) + + component = component_class(**default_kwargs) + component.api_key = "test_key" + component.action = [{"name": "Get Issues By Project"}] + + mock_issues = [ + { + "id": "a1b7b19d-41d5-2972-b102-faa0b65d2b47", + "title": "Fix frontend", + "description": "Fix error logs on frontend", + "assignee": {"email": "test@gmail.com", "id": "271027-3d3f-415e-a784-6d2810351b", "name": "Test User"}, + "priority": 0, + "project": None, + "state": {"name": "Todo"}, + }, + { + "id": "346745c-00a-4a15-ab55-89e329b556", + "title": "Fix backend", + "description": "Fix error handling on backend", + "assignee": None, + "priority": 0, + "project": None, + "state": {"name": "Backlog"}, + }, + ] + + with patch.object(component, "execute_action", return_value=mock_issues): + result = component.as_dataframe() + + assert isinstance(result, DataFrame) + + assert not result.empty + + data_str = result.to_string() + assert "Fix frontend" in data_str + assert "Fix backend" in data_str + + def test_update_build_config(self, component_class, default_kwargs): + component = component_class(**default_kwargs) + build_config = { + "auth_link": {"value": "", "auth_tooltip": ""}, + "action": { + "options": [], + "helper_text": "", + "helper_text_metadata": {}, + }, + } + + result = component.update_build_config(build_config, "", "api_key") + assert result["auth_link"]["value"] == "" + assert "Please provide a valid Composio API Key" in result["auth_link"]["auth_tooltip"] + assert result["action"]["options"] == [] + + component.api_key = "test_key" + result = component.update_build_config(build_config, "test_key", "api_key") + assert len(result["action"]["options"]) > 0 From 9929d5189d897ab39348a25d3ecc68942bcc8edf Mon Sep 17 00:00:00 2001 From: abhishekpatil4 Date: Tue, 27 May 2025 21:47:58 +0530 Subject: [PATCH 07/17] fix: remove run query action --- .../base/langflow/components/composio/linear_composio.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/backend/base/langflow/components/composio/linear_composio.py b/src/backend/base/langflow/components/composio/linear_composio.py index cb602ac0f968..21534a6f8ea9 100644 --- a/src/backend/base/langflow/components/composio/linear_composio.py +++ b/src/backend/base/langflow/components/composio/linear_composio.py @@ -106,13 +106,6 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): "display_name": "Remove Label From Linear Issue", "action_fields": ["LINEAR_REMOVE_ISSUE_LABEL_issue_id", "LINEAR_REMOVE_ISSUE_LABEL_label_id"], }, - "LINEAR_RUN_QUERY_OR_MUTATION": { - "display_name": "Run Query Or Mutation", - "action_fields": [ - "LINEAR_RUN_QUERY_OR_MUTATION_query_or_mutation", - "LINEAR_RUN_QUERY_OR_MUTATION_variables", - ], - }, "LINEAR_UPDATE_ISSUE": { "display_name": "Update Issue", "action_fields": [ From 921481fbda993a6570c3960a0373b0e4b4efaca6 Mon Sep 17 00:00:00 2001 From: abhishekpatil4 Date: Mon, 2 Jun 2025 09:46:14 +0530 Subject: [PATCH 08/17] SP --- src/frontend/src/utils/styleUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index 8b581fa7659f..1754bb477cda 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -314,6 +314,7 @@ export const categoryIcons: Record = { export const nodeIconToDisplayIconMap: Record = { //Category Icons + Linear: "Linear", input_output: "Cable", inputs: "Download", outputs: "Upload", From f89efbad19a62e045fd9f5dd604e16e493afb3a2 Mon Sep 17 00:00:00 2001 From: abhishekpatil4 Date: Mon, 2 Jun 2025 11:00:37 +0530 Subject: [PATCH 09/17] SP --- src/frontend/src/icons/lazyIconImports.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/icons/lazyIconImports.ts b/src/frontend/src/icons/lazyIconImports.ts index 6a9ad569587b..3ed37b103953 100644 --- a/src/frontend/src/icons/lazyIconImports.ts +++ b/src/frontend/src/icons/lazyIconImports.ts @@ -108,7 +108,7 @@ export const lazyIconsMapping = { Gmail: () => import("@/icons/gmail").then((mod) => ({ default: mod.GmailIcon })), Linear: () => - import("@/icons/linear").then((mod) => ({ default: mod.LinearIcon })), + import("@/icons/Linear").then((mod) => ({ default: mod.LinearIcon })), Googlecalendar: () => import("@/icons/googlecalendar").then((mod) => ({ default: mod.GooglecalendarIcon, From a89aad60f9b72122ecf3e491e58497b15dc5d4bc Mon Sep 17 00:00:00 2001 From: abhishekpatil4 Date: Mon, 2 Jun 2025 19:58:35 +0530 Subject: [PATCH 10/17] fix: add result field to improve response format --- .../components/composio/linear_composio.py | 119 ++++++++---------- 1 file changed, 50 insertions(+), 69 deletions(-) diff --git a/src/backend/base/langflow/components/composio/linear_composio.py b/src/backend/base/langflow/components/composio/linear_composio.py index 21534a6f8ea9..c4c767a86941 100644 --- a/src/backend/base/langflow/components/composio/linear_composio.py +++ b/src/backend/base/langflow/components/composio/linear_composio.py @@ -18,15 +18,6 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): app_name = "linear" _actions_data: dict = { - "LINEAR_CREATE_LINEAR_ATTACHMENT": { - "display_name": "Create Linear Attachment", - "action_fields": [ - "LINEAR_CREATE_LINEAR_ATTACHMENT_issue_id", - "LINEAR_CREATE_LINEAR_ATTACHMENT_subtitle", - "LINEAR_CREATE_LINEAR_ATTACHMENT_title", - "LINEAR_CREATE_LINEAR_ATTACHMENT_url", - ], - }, "LINEAR_CREATE_LINEAR_COMMENT": { "display_name": "Create Linear Comment", "action_fields": ["LINEAR_CREATE_LINEAR_COMMENT_body", "LINEAR_CREATE_LINEAR_COMMENT_issue_id"], @@ -50,6 +41,8 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): "LINEAR_CREATE_LINEAR_ISSUE_DETAILS": { "display_name": "Get Create Issue Default Params", "action_fields": ["LINEAR_CREATE_LINEAR_ISSUE_DETAILS_team_id"], + "get_result_field": True, + "result_field": "team", }, "LINEAR_CREATE_LINEAR_LABEL": { "display_name": "Create Label", @@ -64,23 +57,24 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): "display_name": "Delete Issue", "action_fields": ["LINEAR_DELETE_LINEAR_ISSUE_issue_id"], }, - "LINEAR_GET_ATTACHMENTS": { - "display_name": "Donwload Issue Attachments", - "action_fields": [ - "LINEAR_GET_ATTACHMENTS_attachment_id", - "LINEAR_GET_ATTACHMENTS_file_name", - "LINEAR_GET_ATTACHMENTS_issue_id", - ], - }, "LINEAR_GET_CYCLES_BY_TEAM_ID": { "display_name": "Get Cycles By Team", "action_fields": ["LINEAR_GET_CYCLES_BY_TEAM_ID_team_id"], + "get_result_field": True, + "result_field": "cycles", }, "LINEAR_GET_LINEAR_ISSUE": { "display_name": "Get Linear Issue", "action_fields": ["LINEAR_GET_LINEAR_ISSUE_issue_id"], + "get_result_field": True, + "result_field": "issue", + }, + "LINEAR_LIST_LINEAR_CYCLES": { + "display_name": "Get All Cycles", + "action_fields": [], + "get_result_field": True, + "result_field": "cycles", }, - "LINEAR_LIST_LINEAR_CYCLES": {"display_name": "Get All Cycles", "action_fields": []}, "LINEAR_LIST_LINEAR_ISSUES": { "display_name": "Get Issues By Project", "action_fields": [ @@ -92,15 +86,26 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): "LINEAR_LIST_LINEAR_LABELS": { "display_name": "Get Labels By Team", "action_fields": ["LINEAR_LIST_LINEAR_LABELS_team_id"], + "get_result_field": True, + "result_field": "labels", + }, + "LINEAR_LIST_LINEAR_PROJECTS": { + "display_name": "List Linear Projects", + "action_fields": [], + "get_result_field": True, + "result_field": "projects", }, - "LINEAR_LIST_LINEAR_PROJECTS": {"display_name": "List Linear Projects", "action_fields": []}, "LINEAR_LIST_LINEAR_STATES": { "display_name": "Get States By Team", "action_fields": ["LINEAR_LIST_LINEAR_STATES_team_id"], + "get_result_field": True, + "result_field": "states", }, "LINEAR_LIST_LINEAR_TEAMS": { "display_name": "Get Teams By Project", "action_fields": ["LINEAR_LIST_LINEAR_TEAMS_project_id"], + "get_result_field": True, + "result_field": "teams", }, "LINEAR_REMOVE_ISSUE_LABEL": { "display_name": "Remove Label From Linear Issue", @@ -129,34 +134,6 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): inputs = [ *ComposioBaseComponent._base_inputs, - MessageTextInput( - name="LINEAR_CREATE_LINEAR_ATTACHMENT_issue_id", - display_name="Issue Id", - info="ID of the issue to attach to", - show=False, - required=True, - ), - MessageTextInput( - name="LINEAR_CREATE_LINEAR_ATTACHMENT_subtitle", - display_name="Subtitle", - info="Subtitle of the attachment", - show=False, - required=True, - ), - MessageTextInput( - name="LINEAR_CREATE_LINEAR_ATTACHMENT_title", - display_name="Title", - info="Title of the attachment", - show=False, - required=True, - ), - MessageTextInput( - name="LINEAR_CREATE_LINEAR_ATTACHMENT_url", - display_name="Url", - info="URL of the attachment", - show=False, - required=True, - ), MessageTextInput( name="LINEAR_CREATE_LINEAR_COMMENT_body", display_name="Body", @@ -176,6 +153,7 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): display_name="Assignee Id", info="ID of the assignee", show=False, + advanced=True, ), MessageTextInput( name="LINEAR_CREATE_LINEAR_ISSUE_description", @@ -189,6 +167,7 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): display_name="Due Date", info="Due date of the issue in the comma separated format YYYY,MM,DD,hh,mm,ss. For example, 2024,10,27,12,58,00.", # noqa: E501 show=False, + advanced=True, ), IntInput( name="LINEAR_CREATE_LINEAR_ISSUE_estimate", @@ -196,18 +175,21 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): info="The Int scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.", # noqa: E501 show=False, value=0, + advanced=True, ), MessageTextInput( name="LINEAR_CREATE_LINEAR_ISSUE_label_ids", display_name="Label Ids", info="List of label IDs", show=False, + advanced=True, ), MessageTextInput( name="LINEAR_CREATE_LINEAR_ISSUE_parent_id", display_name="Parent Id", info="ID of the parent issue", show=False, + advanced=True, ), IntInput( name="LINEAR_CREATE_LINEAR_ISSUE_priority", @@ -215,6 +197,7 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): info="The priority of the issue. 0 = No priority, 1 = Urgent, 2 = High, 3 = Normal, 4 = Low.", show=False, value=0, + advanced=True, ), MessageTextInput( name="LINEAR_CREATE_LINEAR_ISSUE_project_id", @@ -228,6 +211,7 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): display_name="State Id", info="ID of the issue state", show=False, + advanced=True, ), MessageTextInput( name="LINEAR_CREATE_LINEAR_ISSUE_team_id", @@ -284,27 +268,6 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): show=False, required=True, ), - MessageTextInput( - name="LINEAR_GET_ATTACHMENTS_attachment_id", - display_name="Attachment Id", - info="ID of the attachment to be fetched", - show=False, - required=True, - ), - MessageTextInput( - name="LINEAR_GET_ATTACHMENTS_file_name", - display_name="File Name", - info="Name of the file to be saved by. The file downloaded will be saved with this name. Make sure to include the file extension.", # noqa: E501 - show=False, - required=True, - ), - MessageTextInput( - name="LINEAR_GET_ATTACHMENTS_issue_id", - display_name="Issue Id", - info="ID of the issue for which to fetch details", - show=False, - required=True, - ), MessageTextInput( name="LINEAR_GET_CYCLES_BY_TEAM_ID_team_id", display_name="Team Id", @@ -337,6 +300,7 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): display_name="Project Id", info="ID of the project for which to list issues. If this is provided the issues returned will be filtered by the given project ID.", # noqa: E501 show=False, + required=True, ), MessageTextInput( name="LINEAR_LIST_LINEAR_LABELS_team_id", @@ -392,6 +356,7 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): display_name="Assignee Id", info="ID of the assignee", show=False, + advanced=True, ), MessageTextInput( name="LINEAR_UPDATE_ISSUE_description", @@ -404,12 +369,14 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): display_name="Due Date", info="Due date of the issue in the comma separated format YYYY,MM,DD,hh,mm,ss. For example, 2024,10,27,12,58,00.", # noqa: E501 show=False, + advanced=True, ), IntInput( name="LINEAR_UPDATE_ISSUE_estimate", display_name="Estimate", info="Time estimate for the issue in minutes", show=False, + advanced=True, ), MessageTextInput( name="LINEAR_UPDATE_ISSUE_issue_id", @@ -423,24 +390,28 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): display_name="Label Ids", info="List of label IDs to assign to the issue", show=False, + advanced=True, ), MessageTextInput( name="LINEAR_UPDATE_ISSUE_parent_id", display_name="Parent Id", info="ID of the parent issue", show=False, + advanced=True, ), IntInput( name="LINEAR_UPDATE_ISSUE_priority", display_name="Priority", info="The priority of the issue. 0 = No priority, 1 = Urgent, 2 = High, 3 = Normal, 4 = Low.", show=False, + advanced=True, ), MessageTextInput( name="LINEAR_UPDATE_ISSUE_project_id", display_name="Project Id", info="ID of the project", show=False, + advanced=True, ), MessageTextInput( name="LINEAR_UPDATE_ISSUE_state_id", @@ -453,6 +424,7 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): display_name="Team Id", info="ID of the team", show=False, + advanced=True, ), MessageTextInput( name="LINEAR_UPDATE_ISSUE_title", @@ -497,7 +469,16 @@ def execute_action(self): if not result.get("successful"): return {"error": result.get("error", "No response")} - return result.get("data", []) + result_data = result.get("data", []) + actions_data = self._actions_data.get(action_key, {}) + # If 'get_result_field' is True and 'result_field' is specified, extract the data + # using 'result_field'. Otherwise, fall back to the entire 'data' field in the response. + if actions_data.get("get_result_field") and actions_data.get("result_field"): + result_data = result_data.get(actions_data.get("result_field"), result.get("data", [])) + if len(result_data) != 1 and not actions_data.get("result_field") and actions_data.get("get_result_field"): + msg = f"Expected a dict with a single key, got {len(result_data)} keys: {result_data.keys()}" + raise ValueError(msg) + return result_data # noqa: TRY300 except Exception as e: logger.error(f"Error executing action: {e}") display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else str(self.action) From f84412fdb470e1168a71fce31acb83755842ca2b Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Mon, 2 Jun 2025 16:51:52 -0500 Subject: [PATCH 11/17] Update src/frontend/src/icons/linear/linear.jsx Co-authored-by: Mike Fortman --- src/frontend/src/icons/linear/linear.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/src/icons/linear/linear.jsx b/src/frontend/src/icons/linear/linear.jsx index e1f4ea8529d2..f70a8987dc85 100644 --- a/src/frontend/src/icons/linear/linear.jsx +++ b/src/frontend/src/icons/linear/linear.jsx @@ -6,6 +6,8 @@ const Icon = (props) => ( height="23px" viewBox="0 0 23 23" version="1.1" + fill="currentColor" + {...props} > Date: Mon, 2 Jun 2025 16:52:14 -0500 Subject: [PATCH 12/17] Update src/frontend/src/icons/linear/linear.jsx Co-authored-by: Mike Fortman --- src/frontend/src/icons/linear/linear.jsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/frontend/src/icons/linear/linear.jsx b/src/frontend/src/icons/linear/linear.jsx index f70a8987dc85..87a09a330702 100644 --- a/src/frontend/src/icons/linear/linear.jsx +++ b/src/frontend/src/icons/linear/linear.jsx @@ -11,19 +11,15 @@ const Icon = (props) => ( > From 35101428899865b13cf25e11d012ccafccb6aff8 Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Mon, 2 Jun 2025 16:53:21 -0500 Subject: [PATCH 13/17] Update src/frontend/src/icons/linear/linear.jsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/frontend/src/icons/linear/linear.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/icons/linear/linear.jsx b/src/frontend/src/icons/linear/linear.jsx index 87a09a330702..e368ef2da167 100644 --- a/src/frontend/src/icons/linear/linear.jsx +++ b/src/frontend/src/icons/linear/linear.jsx @@ -1,7 +1,7 @@ const Icon = (props) => ( Date: Thu, 5 Jun 2025 23:36:15 +0530 Subject: [PATCH 14/17] SP --- .../base/langflow/components/composio/linear_composio.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/base/langflow/components/composio/linear_composio.py b/src/backend/base/langflow/components/composio/linear_composio.py index c4c767a86941..bf9f47064e32 100644 --- a/src/backend/base/langflow/components/composio/linear_composio.py +++ b/src/backend/base/langflow/components/composio/linear_composio.py @@ -406,8 +406,8 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): show=False, advanced=True, ), - MessageTextInput( - name="LINEAR_UPDATE_ISSUE_project_id", + MessageTextIproject_idnput( + name="LINEAR_UPDATE_ISSUE_", display_name="Project Id", info="ID of the project", show=False, From 9c18fb085248c35b025016dee2e45b16854fc064 Mon Sep 17 00:00:00 2001 From: abhishekpatil4 Date: Wed, 18 Jun 2025 01:03:42 +0530 Subject: [PATCH 15/17] fix: format --- .../components/composio/linear_composio.py | 2 +- src/frontend/src/icons/linear/linear.jsx | 16 ++++------------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/backend/base/langflow/components/composio/linear_composio.py b/src/backend/base/langflow/components/composio/linear_composio.py index bf9f47064e32..2035004e272e 100644 --- a/src/backend/base/langflow/components/composio/linear_composio.py +++ b/src/backend/base/langflow/components/composio/linear_composio.py @@ -406,7 +406,7 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): show=False, advanced=True, ), - MessageTextIproject_idnput( + MessageTextInput( name="LINEAR_UPDATE_ISSUE_", display_name="Project Id", info="ID of the project", diff --git a/src/frontend/src/icons/linear/linear.jsx b/src/frontend/src/icons/linear/linear.jsx index e368ef2da167..7745ad83735a 100644 --- a/src/frontend/src/icons/linear/linear.jsx +++ b/src/frontend/src/icons/linear/linear.jsx @@ -10,18 +10,10 @@ const Icon = (props) => ( {...props} > - - - - + + + + ); From 7731bfa769999fe335f086c1b12f1ab170c8dcfc Mon Sep 17 00:00:00 2001 From: abhishekpatil4 Date: Wed, 18 Jun 2025 01:06:49 +0530 Subject: [PATCH 16/17] fix: typo --- .../base/langflow/components/composio/linear_composio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/base/langflow/components/composio/linear_composio.py b/src/backend/base/langflow/components/composio/linear_composio.py index 2035004e272e..c4c767a86941 100644 --- a/src/backend/base/langflow/components/composio/linear_composio.py +++ b/src/backend/base/langflow/components/composio/linear_composio.py @@ -407,7 +407,7 @@ class ComposioLinearAPIComponent(ComposioBaseComponent): advanced=True, ), MessageTextInput( - name="LINEAR_UPDATE_ISSUE_", + name="LINEAR_UPDATE_ISSUE_project_id", display_name="Project Id", info="ID of the project", show=False, From ccd8d18196883ba012fdd015757198a0a49fc1ce Mon Sep 17 00:00:00 2001 From: Uday-sidagana Date: Thu, 19 Jun 2025 11:05:16 +0530 Subject: [PATCH 17/17] fix: LazyIconImports path --- src/frontend/src/icons/lazyIconImports.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/icons/lazyIconImports.ts b/src/frontend/src/icons/lazyIconImports.ts index cf0075c30a1d..aa76c493a0b4 100644 --- a/src/frontend/src/icons/lazyIconImports.ts +++ b/src/frontend/src/icons/lazyIconImports.ts @@ -112,7 +112,7 @@ export const lazyIconsMapping = { Gmail: () => import("@/icons/gmail").then((mod) => ({ default: mod.GmailIcon })), Linear: () => - import("@/icons/Linear").then((mod) => ({ default: mod.LinearIcon })), + import("@/icons/linear").then((mod) => ({ default: mod.LinearIcon })), Outlook: () => import("@/icons/outlook").then((mod) => ({ default: mod.OutlookIcon })), Googlecalendar: () =>