Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions autogpt_platform/backend/backend/blocks/linear/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,68 @@ async def try_search_issues(self, term: str) -> list[Issue]:
return [Issue(**issue) for issue in issues["searchIssues"]["nodes"]]
except LinearAPIException as e:
raise e

async def try_get_issues(
self, project: str, status: str, is_assigned: bool, include_comments: bool
) -> list[Issue]:
try:
query = """
query IssuesByProjectStatusAndAssignee(
$projectName: String!
$statusName: String!
$isAssigned: Boolean!
$includeComments: Boolean! = false
) {
issues(
filter: {
project: { name: { eq: $projectName } }
state: { name: { eq: $statusName } }
assignee: { null: $isAssigned }
}
) {
nodes {
id
title
identifier
description
createdAt
priority
assignee {
id
name
}
project {
id
name
}
state {
id
name
}
comments @include(if: $includeComments) {
nodes {
id
body
createdAt
user {
id
name
}
}
}
}
}
}
"""

variables: dict[str, Any] = {
"projectName": project,
"statusName": status,
"isAssigned": not is_assigned,
"includeComments": include_comments,
}

issues = await self.query(query, variables)
return [Issue(**issue) for issue in issues["issues"]["nodes"]]
except LinearAPIException as e:
raise e
103 changes: 103 additions & 0 deletions autogpt_platform/backend/backend/blocks/linear/issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,106 @@ async def run(
yield "error", str(e)
except Exception as e:
yield "error", f"Unexpected error: {str(e)}"


class LinearGetProjectIssuesBlock(Block):
"""Block for getting issues from a Linear project filtered by status and assignee"""

class Input(BlockSchemaInput):
credentials: CredentialsMetaInput = linear.credentials_field(
description="Linear credentials with read permissions",
required_scopes={LinearScope.READ},
)
project: str = SchemaField(description="Name of the project to get issues from")
status: str = SchemaField(
description="Status/state name to filter issues by (e.g., 'In Progress', 'Done')"
)
is_assigned: bool = SchemaField(
description="Filter by assignee status - True to get assigned issues, False to get unassigned issues",
default=False,
)
include_comments: bool = SchemaField(
description="Whether to include comments in the response",
default=False,
)

class Output(BlockSchemaOutput):
issues: list[Issue] = SchemaField(
description="List of issues matching the criteria"
)

def __init__(self):
super().__init__(
id="c7d3f1e8-45a9-4b2c-9f81-3e6a8d7c5b1a",
description="Gets issues from a Linear project filtered by status and assignee",
input_schema=self.Input,
output_schema=self.Output,
categories={BlockCategory.PRODUCTIVITY, BlockCategory.ISSUE_TRACKING},
test_input={
"project": "Test Project",
"status": "In Progress",
"is_assigned": False,
"include_comments": False,
"credentials": TEST_CREDENTIALS_INPUT_OAUTH,
},
test_credentials=TEST_CREDENTIALS_OAUTH,
test_output=[
(
"issues",
[
Issue(
id="abc123",
identifier="TST-123",
title="Test issue",
description="Test description",
priority=1,
)
],
),
],
test_mock={
"get_project_issues": lambda *args, **kwargs: [
Issue(
id="abc123",
identifier="TST-123",
title="Test issue",
description="Test description",
priority=1,
)
]
},
)

@staticmethod
async def get_project_issues(
credentials: OAuth2Credentials | APIKeyCredentials,
project: str,
status: str,
is_assigned: bool,
include_comments: bool,
) -> list[Issue]:
client = LinearClient(credentials=credentials)
response: list[Issue] = await client.try_get_issues(
project=project,
status=status,
is_assigned=is_assigned,
include_comments=include_comments,
)
return response

async def run(
self,
input_data: Input,
*,
credentials: OAuth2Credentials | APIKeyCredentials,
**kwargs,
) -> BlockOutput:
"""Execute getting project issues"""
issues = await self.get_project_issues(
credentials=credentials,
project=input_data.project,
status=input_data.status,
is_assigned=input_data.is_assigned,
include_comments=input_data.include_comments,
)
yield "issues", issues
29 changes: 20 additions & 9 deletions autogpt_platform/backend/backend/blocks/linear/models.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
from backend.sdk import BaseModel


class User(BaseModel):
id: str
name: str


class Comment(BaseModel):
id: str
body: str
createdAt: str | None = None
user: User | None = None


class CreateCommentInput(BaseModel):
Expand All @@ -20,22 +27,26 @@ class CreateCommentResponseWrapper(BaseModel):
commentCreate: CreateCommentResponse


class Project(BaseModel):
id: str
name: str
description: str | None = None
priority: int | None = None
progress: float | None = None
content: str | None = None


class Issue(BaseModel):
id: str
identifier: str
title: str
description: str | None
priority: int
project: Project | None = None
createdAt: str | None = None
comments: list[Comment] | None = None
assignee: User | None = None


class CreateIssueResponse(BaseModel):
issue: Issue


class Project(BaseModel):
id: str
name: str
description: str
priority: int
progress: float
content: str | None
Loading