feat(integration): integrate Linear.app#579
feat(integration): integrate Linear.app#579TooonyChen wants to merge 1 commit intoaipotheosis-labs:mainfrom
Conversation
|
Someone is attempting to deploy a commit to the Proxy Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughA new Linear app connector module has been introduced, providing a Python class to interact with the Linear issue tracking system via its GraphQL API. Supporting configuration and function specification files have also been added, detailing OAuth2 setup and the connector protocol for exposed functions related to issues, projects, teams, and users. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant LinearConnector as Linear (AppConnector)
participant LinearAPI as Linear GraphQL API
Client->>LinearConnector: create_issue(params)
LinearConnector->>LinearConnector: _before_execute()
LinearConnector->>LinearConnector: _make_graphql_request(query, variables)
LinearConnector->>LinearAPI: POST /graphql (with OAuth2 token)
LinearAPI-->>LinearConnector: Response (issue data)
LinearConnector-->>Client: Structured issue data
sequenceDiagram
participant Client
participant LinearConnector as Linear (AppConnector)
participant LinearAPI as Linear GraphQL API
Client->>LinearConnector: get_issues(filters)
LinearConnector->>LinearConnector: _before_execute()
LinearConnector->>LinearConnector: _make_graphql_request(query, variables)
LinearConnector->>LinearAPI: POST /graphql (with OAuth2 token)
LinearAPI-->>LinearConnector: Response (issues list)
LinearConnector-->>Client: Structured issues data
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
✨ No issues found! Your code is sparkling clean! ✨ Need help? Join our Discord for support! |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
backend/aci/server/app_connectors/linear.py (2)
528-588: Use parameterized queries for consistency and safety.Although
include_archived(boolean) andlimit(integer) are type-controlled, embedding them directly in the query string is not ideal.Use GraphQL variables:
- query = f""" - query Projects {{ - projects(includeArchived: {str(include_archived).lower()}, first: {limit}) {{ + query = """ + query Projects($includeArchived: Boolean!, $limit: Int!) { + projects(includeArchived: $includeArchived, first: $limit) {And pass as variables:
variables = {"includeArchived": include_archived, "limit": limit} result = self._make_graphql_request(query, variables)
1-709: Consider adding missing functions mentioned in PR objectives.The PR objectives mention functions for comments and attachments operations, but I don't see dedicated methods for these. While
get_issuereturns comments and attachments, there might be value in having dedicated methods for:
- Adding comments to issues
- Managing attachments
Would you like me to help implement these additional methods or open an issue to track this enhancement?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
backend/aci/server/app_connectors/linear.py(1 hunks)backend/apps/linear/app.json(1 hunks)backend/apps/linear/functions.json(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
backend/apps/*/{app.json,functions.json}
📄 CodeRabbit Inference Engine (CLAUDE.md)
Each integration in backend/apps/{app_name}/ must have both app.json and functions.json files
Files:
backend/apps/linear/app.jsonbackend/apps/linear/functions.json
backend/apps/*/app.json
📄 CodeRabbit Inference Engine (CLAUDE.md)
Integration app.json files must define metadata, authentication schemes, categories, and visibility
Files:
backend/apps/linear/app.json
backend/apps/**/*.json
📄 CodeRabbit Inference Engine (CLAUDE.md)
Backend integration definitions should be stored as JSON configs in backend/apps/
Files:
backend/apps/linear/app.jsonbackend/apps/linear/functions.json
backend/apps/*/functions.json
📄 CodeRabbit Inference Engine (CLAUDE.md)
Integration functions.json files must define API endpoints and their schemas
Files:
backend/apps/linear/functions.json
backend/**/*.py
📄 CodeRabbit Inference Engine (CLAUDE.md)
backend/**/*.py: Backend Python code must be compatible with Python 3.12+
Backend must use ruff for formatting and linting
Backend must use mypy for type checking
Files:
backend/aci/server/app_connectors/linear.py
🧠 Learnings (4)
📚 Learning: applies to backend/apps/*/app.json : integration app.json files must define metadata, authentication...
Learnt from: CR
PR: aipotheosis-labs/aci#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-05T09:39:27.629Z
Learning: Applies to backend/apps/*/app.json : Integration app.json files must define metadata, authentication schemes, categories, and visibility
Applied to files:
backend/apps/linear/app.json
📚 Learning: applies to backend/apps/**/*.json : backend integration definitions should be stored as json configs...
Learnt from: CR
PR: aipotheosis-labs/aci#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-05T09:39:27.629Z
Learning: Applies to backend/apps/**/*.json : Backend integration definitions should be stored as JSON configs in backend/apps/
Applied to files:
backend/apps/linear/app.json
📚 Learning: applies to backend/apps/*/{app.json,functions.json} : each integration in backend/apps/{app_name}/ m...
Learnt from: CR
PR: aipotheosis-labs/aci#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-05T09:39:27.629Z
Learning: Applies to backend/apps/*/{app.json,functions.json} : Each integration in backend/apps/{app_name}/ must have both app.json and functions.json files
Applied to files:
backend/apps/linear/app.jsonbackend/apps/linear/functions.json
📚 Learning: applies to backend/apps/*/functions.json : integration functions.json files must define api endpoint...
Learnt from: CR
PR: aipotheosis-labs/aci#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-05T09:39:27.629Z
Learning: Applies to backend/apps/*/functions.json : Integration functions.json files must define API endpoints and their schemas
Applied to files:
backend/apps/linear/app.jsonbackend/apps/linear/functions.json
🔇 Additional comments (2)
backend/apps/linear/app.json (1)
1-26: LGTM! Configuration follows required structure.The app.json file properly defines all required elements according to the coding guidelines: metadata, authentication schemes, categories, and visibility settings.
backend/apps/linear/functions.json (1)
1-236: LGTM! Functions properly defined with complete schemas.All function definitions include the required elements as per coding guidelines: API endpoints with proper JSON schemas, parameter definitions, and visibility settings.
| filter_conditions = [] | ||
|
|
||
| if team_id: | ||
| filter_conditions.append(f'team: {{ id: {{ eq: "{team_id}" }} }}') | ||
| if assignee_id: | ||
| filter_conditions.append(f'assignee: {{ id: {{ eq: "{assignee_id}" }} }}') | ||
| if state: | ||
| filter_conditions.append(f'state: {{ name: {{ eq: "{state}" }} }}') | ||
|
|
||
| filter_str = "" | ||
| if filter_conditions: | ||
| filter_str = f'filter: {{ {", ".join(filter_conditions)} }}' | ||
|
|
There was a problem hiding this comment.
Critical: Potential GraphQL injection vulnerability.
Building GraphQL queries using string concatenation with user input can lead to injection attacks. The team_id, assignee_id, and state parameters are directly embedded into the query string without proper escaping.
Use parameterized queries with variables instead:
- def get_issues(
- self,
- team_id: Optional[str] = None,
- assignee_id: Optional[str] = None,
- state: Optional[str] = None,
- limit: int = 50,
- ) -> Dict[str, Any]:
- """
- Get issues from Linear with optional filtering.
-
- Function name: LINEAR__GET_ISSUES
- """
- # Build filter object
- filter_conditions = []
-
- if team_id:
- filter_conditions.append(f'team: {{ id: {{ eq: "{team_id}" }} }}')
- if assignee_id:
- filter_conditions.append(f'assignee: {{ id: {{ eq: "{assignee_id}" }} }}')
- if state:
- filter_conditions.append(f'state: {{ name: {{ eq: "{state}" }} }}')
-
- filter_str = ""
- if filter_conditions:
- filter_str = f'filter: {{ {", ".join(filter_conditions)} }}'
-
- query = f"""
- query Issues {{
- issues({filter_str}, first: {limit}) {{
+ def get_issues(
+ self,
+ team_id: Optional[str] = None,
+ assignee_id: Optional[str] = None,
+ state: Optional[str] = None,
+ limit: int = 50,
+ ) -> Dict[str, Any]:
+ """
+ Get issues from Linear with optional filtering.
+
+ Function name: LINEAR__GET_ISSUES
+ """
+ query = """
+ query Issues($filter: IssueFilter, $limit: Int!) {
+ issues(filter: $filter, first: $limit) {Then build the filter object as a dictionary and pass it as a variable:
filter_obj = {}
if team_id:
filter_obj["team"] = {"id": {"eq": team_id}}
if assignee_id:
filter_obj["assignee"] = {"id": {"eq": assignee_id}}
if state:
filter_obj["state"] = {"name": {"eq": state}}
variables = {"filter": filter_obj if filter_obj else None, "limit": limit}🤖 Prompt for AI Agents
In backend/aci/server/app_connectors/linear.py around lines 176 to 188, the
current code builds GraphQL filter queries by directly concatenating user inputs
into strings, which risks injection attacks. To fix this, replace the string
concatenation with constructing a filter dictionary using the input parameters
as values, then pass this dictionary as a variable to the GraphQL query. This
involves creating a filter_obj dictionary with keys for team, assignee, and
state only if their values exist, and then passing this filter_obj inside a
variables dictionary to the query execution instead of embedding inputs directly
in the query string.
| filter_str = 'filter: { active: { eq: true } }' if active_only else '' | ||
|
|
||
| query = f""" | ||
| query Users {{ | ||
| users({filter_str}) {{ | ||
| nodes {{ | ||
| id | ||
| name | ||
| displayName | ||
| avatarUrl | ||
| active | ||
| admin | ||
| guest | ||
| }} | ||
| }} | ||
| }} | ||
| """ |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Improve query construction to avoid potential issues.
While the active_only boolean parameter is less risky, constructing GraphQL queries with string concatenation is not a best practice.
Use parameterized queries:
- def get_users(self, active_only: bool = True) -> Dict[str, Any]:
- """
- Get users in the Linear workspace.
-
- Function name: LINEAR__GET_USERS
- """
- filter_str = 'filter: { active: { eq: true } }' if active_only else ''
-
- query = f"""
- query Users {{
- users({filter_str}) {{
+ def get_users(self, active_only: bool = True) -> Dict[str, Any]:
+ """
+ Get users in the Linear workspace.
+
+ Function name: LINEAR__GET_USERS
+ """
+ query = """
+ query Users($filter: UserFilter) {
+ users(filter: $filter) {And use variables:
variables = {"filter": {"active": {"eq": True}} if active_only else None}
result = self._make_graphql_request(query, variables)🤖 Prompt for AI Agents
In backend/aci/server/app_connectors/linear.py around lines 394 to 411, the
GraphQL query is constructed using string concatenation, which can lead to
potential issues. Refactor the code to use parameterized queries by defining the
query with a variable for the filter and passing the filter as a variable in the
request. Specifically, define a variables dictionary that sets the filter to
{"active": {"eq": True}} if active_only is True, otherwise None, and pass this
variables dictionary along with the query to the _make_graphql_request method.
There was a problem hiding this comment.
cubic analysis
4 issues found across 3 files • Review in cubic
React with 👍 or 👎 to teach cubic. You can also tag @cubic-dev-ai to give feedback, ask questions, or re-run the review.
|
|
||
| query = f""" | ||
| query Users {{ | ||
| users({filter_str}) {{ |
There was a problem hiding this comment.
Empty parentheses produce invalid GraphQL when requesting all users without a filter
Prompt for AI agents
Address the following comment on backend/aci/server/app_connectors/linear.py at line 398:
<comment>Empty parentheses produce invalid GraphQL when requesting all users without a filter</comment>
<file context>
@@ -0,0 +1,709 @@
+import json
+from typing import Any, Dict, List, Optional, override
+
+import requests
+
+from aci.common.db.sql_models import LinkedAccount
+from aci.common.exceptions import ACIException
+from aci.common.logging_setup import get_logger
+from aci.common.schemas.security_scheme import (
</file context>
|
|
||
| query = f""" | ||
| query Issues {{ | ||
| issues({filter_str}, first: {limit}) {{ |
There was a problem hiding this comment.
Leading comma in GraphQL issues call produces invalid query when no filters are supplied
Prompt for AI agents
Address the following comment on backend/aci/server/app_connectors/linear.py at line 191:
<comment>Leading comma in GraphQL issues call produces invalid query when no filters are supplied</comment>
<file context>
@@ -0,0 +1,709 @@
+import json
+from typing import Any, Dict, List, Optional, override
+
+import requests
+
+from aci.common.db.sql_models import LinkedAccount
+from aci.common.exceptions import ACIException
+from aci.common.logging_setup import get_logger
+from aci.common.schemas.security_scheme import (
</file context>
| @@ -0,0 +1,709 @@ | |||
| import json | |||
| from typing import Any, Dict, List, Optional, override | |||
There was a problem hiding this comment.
Importing override from typing will break on Python versions earlier than 3.12, causing a runtime ImportError and making the connector unusable
Prompt for AI agents
Address the following comment on backend/aci/server/app_connectors/linear.py at line 2:
<comment>Importing override from typing will break on Python versions earlier than 3.12, causing a runtime ImportError and making the connector unusable</comment>
<file context>
@@ -0,0 +1,709 @@
+import json
+from typing import Any, Dict, List, Optional, override
+
+import requests
</file context>
| filter_conditions = [] | ||
|
|
||
| if team_id: | ||
| filter_conditions.append(f'team: {{ id: {{ eq: "{team_id}" }} }}') |
There was a problem hiding this comment.
Directly embedding user-supplied values into the GraphQL query string opens the door to injection vulnerabilities. Construct a variables object and pass it separately to the query instead of interpolating IDs into the string.
Prompt for AI agents
Address the following comment on backend/aci/server/app_connectors/linear.py at line 179:
<comment>Directly embedding user-supplied values into the GraphQL query string opens the door to injection vulnerabilities. Construct a variables object and pass it separately to the query instead of interpolating IDs into the string.</comment>
<file context>
@@ -0,0 +1,709 @@
+import json
+from typing import Any, Dict, List, Optional, override
+
+import requests
+
+from aci.common.db.sql_models import LinkedAccount
+from aci.common.exceptions import ACIException
+from aci.common.logging_setup import get_logger
+from aci.common.schemas.security_scheme import (
</file context>
🏷️ Ticket
https://www.notion.so/Linear-Integration-23f8378d6a478008b2bcc26232e81e86?source=copy_link
📝 Description
This PR implements a comprehensive Linear integration for the ACI platform, enabling AI agents to interact with Linear's project management features through GraphQL API.
Available Functions:
LINEAR__CREATE_ISSUE- Create new issues with team assignment and priorityLINEAR__GET_ISSUES- List issues with filtering by team, assignee, or stateLINEAR__UPDATE_ISSUE- Update existing issues (title, description, priority, state)LINEAR__GET_TEAMS- Get all workspace teams with workflow states and labelsLINEAR__GET_USERS- List workspace users (with active/inactive filtering)LINEAR__GET_ISSUE- Get detailed issue information including comments and attachmentsLINEAR__GET_PROJECTS- List all projects with progress and status informationLINEAR__GET_PROJECT- Get detailed project information with milestones and team members🎥 Demo (if applicable)
📸 Screenshots (if applicable)
fuzzy test prompts and result screenshots are as follows:
https://www.notion.so/Integration-Linear-app-2478378d6a4780c690add23e65a4b38d?source=copy_link
✅ Checklist
Summary by cubic
Added a full Linear integration, allowing agents to create, update, and query issues, projects, teams, and users through the Linear GraphQL API.
Summary by CodeRabbit