mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-14 00:35:02 -05:00
I'm getting circular import issues because there is a lot of cross-importing between `backend.data`, `backend.blocks`, and other modules. This change reduces block-related cross-imports and thus risk of breaking circular imports. ### Changes 🏗️ - Strip down `backend.data.block` - Move `Block` base class and related class/enum defs to `backend.blocks._base` - Move `is_block_auth_configured` to `backend.blocks._utils` - Move `get_blocks()`, `get_io_block_ids()` etc. to `backend.blocks` (`__init__.py`) - Update imports everywhere - Remove unused and poorly typed `Block.create()` - Change usages from `block_cls.create()` to `block_cls()` - Improve typing of `load_all_blocks` and `get_blocks` - Move cross-import of `backend.api.features.library.model` from `backend/data/__init__.py` to `backend/data/integrations.py` - Remove deprecated attribute `NodeModel.webhook` - Re-generate OpenAPI spec and fix frontend usage - Eliminate module-level `backend.blocks` import from `blocks/agent.py` - Eliminate module-level `backend.data.execution` and `backend.executor.manager` imports from `blocks/helpers/review.py` - Replace `BlockInput` with `GraphInput` for graph inputs ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - CI static type-checking + tests should be sufficient for this
830 lines
28 KiB
Python
830 lines
28 KiB
Python
import logging
|
|
from urllib.parse import urlparse
|
|
|
|
from typing_extensions import TypedDict
|
|
|
|
from backend.blocks._base import (
|
|
Block,
|
|
BlockCategory,
|
|
BlockOutput,
|
|
BlockSchemaInput,
|
|
BlockSchemaOutput,
|
|
)
|
|
from backend.data.model import SchemaField
|
|
|
|
from ._api import convert_comment_url_to_api_endpoint, get_api
|
|
from ._auth import (
|
|
TEST_CREDENTIALS,
|
|
TEST_CREDENTIALS_INPUT,
|
|
GithubCredentials,
|
|
GithubCredentialsField,
|
|
GithubCredentialsInput,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def is_github_url(url: str) -> bool:
|
|
return urlparse(url).netloc == "github.com"
|
|
|
|
|
|
# --8<-- [start:GithubCommentBlockExample]
|
|
class GithubCommentBlock(Block):
|
|
class Input(BlockSchemaInput):
|
|
credentials: GithubCredentialsInput = GithubCredentialsField("repo")
|
|
issue_url: str = SchemaField(
|
|
description="URL of the GitHub issue or pull request",
|
|
placeholder="https://github.com/owner/repo/issues/1",
|
|
)
|
|
comment: str = SchemaField(
|
|
description="Comment to post on the issue or pull request",
|
|
placeholder="Enter your comment",
|
|
)
|
|
|
|
class Output(BlockSchemaOutput):
|
|
id: int = SchemaField(description="ID of the created comment")
|
|
url: str = SchemaField(description="URL to the comment on GitHub")
|
|
error: str = SchemaField(
|
|
description="Error message if the comment posting failed"
|
|
)
|
|
|
|
def __init__(self):
|
|
super().__init__(
|
|
id="a8db4d8d-db1c-4a25-a1b0-416a8c33602b",
|
|
description="A block that posts comments on GitHub issues or pull requests using the GitHub API.",
|
|
categories={BlockCategory.DEVELOPER_TOOLS},
|
|
input_schema=GithubCommentBlock.Input,
|
|
output_schema=GithubCommentBlock.Output,
|
|
test_input=[
|
|
{
|
|
"issue_url": "https://github.com/owner/repo/issues/1",
|
|
"comment": "This is a test comment.",
|
|
"credentials": TEST_CREDENTIALS_INPUT,
|
|
},
|
|
{
|
|
"issue_url": "https://github.com/owner/repo/pull/1",
|
|
"comment": "This is a test comment.",
|
|
"credentials": TEST_CREDENTIALS_INPUT,
|
|
},
|
|
],
|
|
test_credentials=TEST_CREDENTIALS,
|
|
test_output=[
|
|
("id", 1337),
|
|
("url", "https://github.com/owner/repo/issues/1#issuecomment-1337"),
|
|
("id", 1337),
|
|
(
|
|
"url",
|
|
"https://github.com/owner/repo/issues/1#issuecomment-1337",
|
|
),
|
|
],
|
|
test_mock={
|
|
"post_comment": lambda *args, **kwargs: (
|
|
1337,
|
|
"https://github.com/owner/repo/issues/1#issuecomment-1337",
|
|
)
|
|
},
|
|
)
|
|
|
|
@staticmethod
|
|
async def post_comment(
|
|
credentials: GithubCredentials, issue_url: str, body_text: str
|
|
) -> tuple[int, str]:
|
|
api = get_api(credentials)
|
|
data = {"body": body_text}
|
|
if "pull" in issue_url:
|
|
issue_url = issue_url.replace("pull", "issues")
|
|
comments_url = issue_url + "/comments"
|
|
response = await api.post(comments_url, json=data)
|
|
comment = response.json()
|
|
return comment["id"], comment["html_url"]
|
|
|
|
async def run(
|
|
self,
|
|
input_data: Input,
|
|
*,
|
|
credentials: GithubCredentials,
|
|
**kwargs,
|
|
) -> BlockOutput:
|
|
id, url = await self.post_comment(
|
|
credentials,
|
|
input_data.issue_url,
|
|
input_data.comment,
|
|
)
|
|
yield "id", id
|
|
yield "url", url
|
|
|
|
|
|
# --8<-- [end:GithubCommentBlockExample]
|
|
|
|
|
|
class GithubUpdateCommentBlock(Block):
|
|
class Input(BlockSchemaInput):
|
|
credentials: GithubCredentialsInput = GithubCredentialsField("repo")
|
|
comment_url: str = SchemaField(
|
|
description="URL of the GitHub comment",
|
|
placeholder="https://github.com/owner/repo/issues/1#issuecomment-123456789",
|
|
default="",
|
|
advanced=False,
|
|
)
|
|
issue_url: str = SchemaField(
|
|
description="URL of the GitHub issue or pull request",
|
|
placeholder="https://github.com/owner/repo/issues/1",
|
|
default="",
|
|
)
|
|
comment_id: str = SchemaField(
|
|
description="ID of the GitHub comment",
|
|
placeholder="123456789",
|
|
default="",
|
|
)
|
|
comment: str = SchemaField(
|
|
description="Comment to update",
|
|
placeholder="Enter your comment",
|
|
)
|
|
|
|
class Output(BlockSchemaOutput):
|
|
id: int = SchemaField(description="ID of the updated comment")
|
|
url: str = SchemaField(description="URL to the comment on GitHub")
|
|
error: str = SchemaField(
|
|
description="Error message if the comment update failed"
|
|
)
|
|
|
|
def __init__(self):
|
|
super().__init__(
|
|
id="b3f4d747-10e3-4e69-8c51-f2be1d99c9a7",
|
|
description="A block that updates an existing comment on a GitHub issue or pull request.",
|
|
categories={BlockCategory.DEVELOPER_TOOLS},
|
|
input_schema=GithubUpdateCommentBlock.Input,
|
|
output_schema=GithubUpdateCommentBlock.Output,
|
|
test_input={
|
|
"comment_url": "https://github.com/owner/repo/issues/1#issuecomment-123456789",
|
|
"comment": "This is an updated comment.",
|
|
"credentials": TEST_CREDENTIALS_INPUT,
|
|
},
|
|
test_credentials=TEST_CREDENTIALS,
|
|
test_output=[
|
|
("id", 123456789),
|
|
(
|
|
"url",
|
|
"https://github.com/owner/repo/issues/1#issuecomment-123456789",
|
|
),
|
|
],
|
|
test_mock={
|
|
"update_comment": lambda *args, **kwargs: (
|
|
123456789,
|
|
"https://github.com/owner/repo/issues/1#issuecomment-123456789",
|
|
)
|
|
},
|
|
)
|
|
|
|
@staticmethod
|
|
async def update_comment(
|
|
credentials: GithubCredentials, comment_url: str, body_text: str
|
|
) -> tuple[int, str]:
|
|
api = get_api(credentials, convert_urls=False)
|
|
data = {"body": body_text}
|
|
url = convert_comment_url_to_api_endpoint(comment_url)
|
|
|
|
logger.info(url)
|
|
response = await api.patch(url, json=data)
|
|
comment = response.json()
|
|
return comment["id"], comment["html_url"]
|
|
|
|
async def run(
|
|
self,
|
|
input_data: Input,
|
|
*,
|
|
credentials: GithubCredentials,
|
|
**kwargs,
|
|
) -> BlockOutput:
|
|
if (
|
|
not input_data.comment_url
|
|
and input_data.comment_id
|
|
and input_data.issue_url
|
|
):
|
|
parsed_url = urlparse(input_data.issue_url)
|
|
path_parts = parsed_url.path.strip("/").split("/")
|
|
owner, repo = path_parts[0], path_parts[1]
|
|
|
|
input_data.comment_url = f"https://api.github.com/repos/{owner}/{repo}/issues/comments/{input_data.comment_id}"
|
|
|
|
elif (
|
|
not input_data.comment_url
|
|
and not input_data.comment_id
|
|
and input_data.issue_url
|
|
):
|
|
raise ValueError(
|
|
"Must provide either comment_url or comment_id and issue_url"
|
|
)
|
|
id, url = await self.update_comment(
|
|
credentials,
|
|
input_data.comment_url,
|
|
input_data.comment,
|
|
)
|
|
yield "id", id
|
|
yield "url", url
|
|
|
|
|
|
class GithubListCommentsBlock(Block):
|
|
class Input(BlockSchemaInput):
|
|
credentials: GithubCredentialsInput = GithubCredentialsField("repo")
|
|
issue_url: str = SchemaField(
|
|
description="URL of the GitHub issue or pull request",
|
|
placeholder="https://github.com/owner/repo/issues/1",
|
|
)
|
|
|
|
class Output(BlockSchemaOutput):
|
|
class CommentItem(TypedDict):
|
|
id: int
|
|
body: str
|
|
user: str
|
|
url: str
|
|
|
|
comment: CommentItem = SchemaField(
|
|
title="Comment", description="Comments with their ID, body, user, and URL"
|
|
)
|
|
comments: list[CommentItem] = SchemaField(
|
|
description="List of comments with their ID, body, user, and URL"
|
|
)
|
|
|
|
def __init__(self):
|
|
super().__init__(
|
|
id="c4b5fb63-0005-4a11-b35a-0c2467bd6b59",
|
|
description="A block that retrieves all comments from a GitHub issue or pull request, including comment metadata and content.",
|
|
categories={BlockCategory.DEVELOPER_TOOLS},
|
|
input_schema=GithubListCommentsBlock.Input,
|
|
output_schema=GithubListCommentsBlock.Output,
|
|
test_input={
|
|
"issue_url": "https://github.com/owner/repo/issues/1",
|
|
"credentials": TEST_CREDENTIALS_INPUT,
|
|
},
|
|
test_credentials=TEST_CREDENTIALS,
|
|
test_output=[
|
|
(
|
|
"comment",
|
|
{
|
|
"id": 123456789,
|
|
"body": "This is a test comment.",
|
|
"user": "test_user",
|
|
"url": "https://github.com/owner/repo/issues/1#issuecomment-123456789",
|
|
},
|
|
),
|
|
(
|
|
"comments",
|
|
[
|
|
{
|
|
"id": 123456789,
|
|
"body": "This is a test comment.",
|
|
"user": "test_user",
|
|
"url": "https://github.com/owner/repo/issues/1#issuecomment-123456789",
|
|
}
|
|
],
|
|
),
|
|
],
|
|
test_mock={
|
|
"list_comments": lambda *args, **kwargs: [
|
|
{
|
|
"id": 123456789,
|
|
"body": "This is a test comment.",
|
|
"user": "test_user",
|
|
"url": "https://github.com/owner/repo/issues/1#issuecomment-123456789",
|
|
}
|
|
]
|
|
},
|
|
)
|
|
|
|
@staticmethod
|
|
async def list_comments(
|
|
credentials: GithubCredentials, issue_url: str
|
|
) -> list[Output.CommentItem]:
|
|
parsed_url = urlparse(issue_url)
|
|
path_parts = parsed_url.path.strip("/").split("/")
|
|
|
|
owner = path_parts[0]
|
|
repo = path_parts[1]
|
|
|
|
# GitHub API uses 'issues' for both issues and pull requests when it comes to comments
|
|
issue_number = path_parts[3] # Whether 'issues/123' or 'pull/123'
|
|
|
|
# Construct the proper API URL directly
|
|
api_url = f"https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}/comments"
|
|
|
|
# Set convert_urls=False since we're already providing an API URL
|
|
api = get_api(credentials, convert_urls=False)
|
|
response = await api.get(api_url)
|
|
comments = response.json()
|
|
parsed_comments: list[GithubListCommentsBlock.Output.CommentItem] = [
|
|
{
|
|
"id": comment["id"],
|
|
"body": comment["body"],
|
|
"user": comment["user"]["login"],
|
|
"url": comment["html_url"],
|
|
}
|
|
for comment in comments
|
|
]
|
|
return parsed_comments
|
|
|
|
async def run(
|
|
self,
|
|
input_data: Input,
|
|
*,
|
|
credentials: GithubCredentials,
|
|
**kwargs,
|
|
) -> BlockOutput:
|
|
comments = await self.list_comments(
|
|
credentials,
|
|
input_data.issue_url,
|
|
)
|
|
for comment in comments:
|
|
yield "comment", comment
|
|
yield "comments", comments
|
|
|
|
|
|
class GithubMakeIssueBlock(Block):
|
|
class Input(BlockSchemaInput):
|
|
credentials: GithubCredentialsInput = GithubCredentialsField("repo")
|
|
repo_url: str = SchemaField(
|
|
description="URL of the GitHub repository",
|
|
placeholder="https://github.com/owner/repo",
|
|
)
|
|
title: str = SchemaField(
|
|
description="Title of the issue", placeholder="Enter the issue title"
|
|
)
|
|
body: str = SchemaField(
|
|
description="Body of the issue", placeholder="Enter the issue body"
|
|
)
|
|
|
|
class Output(BlockSchemaOutput):
|
|
number: int = SchemaField(description="Number of the created issue")
|
|
url: str = SchemaField(description="URL of the created issue")
|
|
error: str = SchemaField(
|
|
description="Error message if the issue creation failed"
|
|
)
|
|
|
|
def __init__(self):
|
|
super().__init__(
|
|
id="691dad47-f494-44c3-a1e8-05b7990f2dab",
|
|
description="A block that creates new issues on GitHub repositories with a title and body content.",
|
|
categories={BlockCategory.DEVELOPER_TOOLS},
|
|
input_schema=GithubMakeIssueBlock.Input,
|
|
output_schema=GithubMakeIssueBlock.Output,
|
|
test_input={
|
|
"repo_url": "https://github.com/owner/repo",
|
|
"title": "Test Issue",
|
|
"body": "This is a test issue.",
|
|
"credentials": TEST_CREDENTIALS_INPUT,
|
|
},
|
|
test_credentials=TEST_CREDENTIALS,
|
|
test_output=[
|
|
("number", 1),
|
|
("url", "https://github.com/owner/repo/issues/1"),
|
|
],
|
|
test_mock={
|
|
"create_issue": lambda *args, **kwargs: (
|
|
1,
|
|
"https://github.com/owner/repo/issues/1",
|
|
)
|
|
},
|
|
)
|
|
|
|
@staticmethod
|
|
async def create_issue(
|
|
credentials: GithubCredentials, repo_url: str, title: str, body: str
|
|
) -> tuple[int, str]:
|
|
api = get_api(credentials)
|
|
data = {"title": title, "body": body}
|
|
issues_url = repo_url + "/issues"
|
|
response = await api.post(issues_url, json=data)
|
|
issue = response.json()
|
|
return issue["number"], issue["html_url"]
|
|
|
|
async def run(
|
|
self,
|
|
input_data: Input,
|
|
*,
|
|
credentials: GithubCredentials,
|
|
**kwargs,
|
|
) -> BlockOutput:
|
|
number, url = await self.create_issue(
|
|
credentials,
|
|
input_data.repo_url,
|
|
input_data.title,
|
|
input_data.body,
|
|
)
|
|
yield "number", number
|
|
yield "url", url
|
|
|
|
|
|
class GithubReadIssueBlock(Block):
|
|
class Input(BlockSchemaInput):
|
|
credentials: GithubCredentialsInput = GithubCredentialsField("repo")
|
|
issue_url: str = SchemaField(
|
|
description="URL of the GitHub issue",
|
|
placeholder="https://github.com/owner/repo/issues/1",
|
|
)
|
|
|
|
class Output(BlockSchemaOutput):
|
|
title: str = SchemaField(description="Title of the issue")
|
|
body: str = SchemaField(description="Body of the issue")
|
|
user: str = SchemaField(description="User who created the issue")
|
|
error: str = SchemaField(
|
|
description="Error message if reading the issue failed"
|
|
)
|
|
|
|
def __init__(self):
|
|
super().__init__(
|
|
id="6443c75d-032a-4772-9c08-230c707c8acc",
|
|
description="A block that retrieves information about a specific GitHub issue, including its title, body content, and creator.",
|
|
categories={BlockCategory.DEVELOPER_TOOLS},
|
|
input_schema=GithubReadIssueBlock.Input,
|
|
output_schema=GithubReadIssueBlock.Output,
|
|
test_input={
|
|
"issue_url": "https://github.com/owner/repo/issues/1",
|
|
"credentials": TEST_CREDENTIALS_INPUT,
|
|
},
|
|
test_credentials=TEST_CREDENTIALS,
|
|
test_output=[
|
|
("title", "Title of the issue"),
|
|
("body", "This is the body of the issue."),
|
|
("user", "username"),
|
|
],
|
|
test_mock={
|
|
"read_issue": lambda *args, **kwargs: (
|
|
"Title of the issue",
|
|
"This is the body of the issue.",
|
|
"username",
|
|
)
|
|
},
|
|
)
|
|
|
|
@staticmethod
|
|
async def read_issue(
|
|
credentials: GithubCredentials, issue_url: str
|
|
) -> tuple[str, str, str]:
|
|
api = get_api(credentials)
|
|
response = await api.get(issue_url)
|
|
data = response.json()
|
|
title = data.get("title", "No title found")
|
|
body = data.get("body", "No body content found")
|
|
user = data.get("user", {}).get("login", "No user found")
|
|
return title, body, user
|
|
|
|
async def run(
|
|
self,
|
|
input_data: Input,
|
|
*,
|
|
credentials: GithubCredentials,
|
|
**kwargs,
|
|
) -> BlockOutput:
|
|
title, body, user = await self.read_issue(
|
|
credentials,
|
|
input_data.issue_url,
|
|
)
|
|
if title:
|
|
yield "title", title
|
|
if body:
|
|
yield "body", body
|
|
if user:
|
|
yield "user", user
|
|
|
|
|
|
class GithubListIssuesBlock(Block):
|
|
class Input(BlockSchemaInput):
|
|
credentials: GithubCredentialsInput = GithubCredentialsField("repo")
|
|
repo_url: str = SchemaField(
|
|
description="URL of the GitHub repository",
|
|
placeholder="https://github.com/owner/repo",
|
|
)
|
|
|
|
class Output(BlockSchemaOutput):
|
|
class IssueItem(TypedDict):
|
|
title: str
|
|
url: str
|
|
|
|
issue: IssueItem = SchemaField(
|
|
title="Issue", description="Issues with their title and URL"
|
|
)
|
|
issues: list[IssueItem] = SchemaField(
|
|
description="List of issues with their title and URL"
|
|
)
|
|
|
|
def __init__(self):
|
|
super().__init__(
|
|
id="c215bfd7-0e57-4573-8f8c-f7d4963dcd74",
|
|
description="A block that retrieves a list of issues from a GitHub repository with their titles and URLs.",
|
|
categories={BlockCategory.DEVELOPER_TOOLS},
|
|
input_schema=GithubListIssuesBlock.Input,
|
|
output_schema=GithubListIssuesBlock.Output,
|
|
test_input={
|
|
"repo_url": "https://github.com/owner/repo",
|
|
"credentials": TEST_CREDENTIALS_INPUT,
|
|
},
|
|
test_credentials=TEST_CREDENTIALS,
|
|
test_output=[
|
|
(
|
|
"issues",
|
|
[
|
|
{
|
|
"title": "Issue 1",
|
|
"url": "https://github.com/owner/repo/issues/1",
|
|
}
|
|
],
|
|
),
|
|
(
|
|
"issue",
|
|
{
|
|
"title": "Issue 1",
|
|
"url": "https://github.com/owner/repo/issues/1",
|
|
},
|
|
),
|
|
],
|
|
test_mock={
|
|
"list_issues": lambda *args, **kwargs: [
|
|
{
|
|
"title": "Issue 1",
|
|
"url": "https://github.com/owner/repo/issues/1",
|
|
}
|
|
]
|
|
},
|
|
)
|
|
|
|
@staticmethod
|
|
async def list_issues(
|
|
credentials: GithubCredentials, repo_url: str
|
|
) -> list[Output.IssueItem]:
|
|
api = get_api(credentials)
|
|
issues_url = repo_url + "/issues"
|
|
response = await api.get(issues_url)
|
|
data = response.json()
|
|
issues: list[GithubListIssuesBlock.Output.IssueItem] = [
|
|
{"title": issue["title"], "url": issue["html_url"]} for issue in data
|
|
]
|
|
return issues
|
|
|
|
async def run(
|
|
self,
|
|
input_data: Input,
|
|
*,
|
|
credentials: GithubCredentials,
|
|
**kwargs,
|
|
) -> BlockOutput:
|
|
issues = await self.list_issues(
|
|
credentials,
|
|
input_data.repo_url,
|
|
)
|
|
yield "issues", issues
|
|
for issue in issues:
|
|
yield "issue", issue
|
|
|
|
|
|
class GithubAddLabelBlock(Block):
|
|
class Input(BlockSchemaInput):
|
|
credentials: GithubCredentialsInput = GithubCredentialsField("repo")
|
|
issue_url: str = SchemaField(
|
|
description="URL of the GitHub issue or pull request",
|
|
placeholder="https://github.com/owner/repo/issues/1",
|
|
)
|
|
label: str = SchemaField(
|
|
description="Label to add to the issue or pull request",
|
|
placeholder="Enter the label",
|
|
)
|
|
|
|
class Output(BlockSchemaOutput):
|
|
status: str = SchemaField(description="Status of the label addition operation")
|
|
error: str = SchemaField(
|
|
description="Error message if the label addition failed"
|
|
)
|
|
|
|
def __init__(self):
|
|
super().__init__(
|
|
id="98bd6b77-9506-43d5-b669-6b9733c4b1f1",
|
|
description="A block that adds a label to a GitHub issue or pull request for categorization and organization.",
|
|
categories={BlockCategory.DEVELOPER_TOOLS},
|
|
input_schema=GithubAddLabelBlock.Input,
|
|
output_schema=GithubAddLabelBlock.Output,
|
|
test_input={
|
|
"issue_url": "https://github.com/owner/repo/issues/1",
|
|
"label": "bug",
|
|
"credentials": TEST_CREDENTIALS_INPUT,
|
|
},
|
|
test_credentials=TEST_CREDENTIALS,
|
|
test_output=[("status", "Label added successfully")],
|
|
test_mock={"add_label": lambda *args, **kwargs: "Label added successfully"},
|
|
)
|
|
|
|
@staticmethod
|
|
async def add_label(
|
|
credentials: GithubCredentials, issue_url: str, label: str
|
|
) -> str:
|
|
api = get_api(credentials)
|
|
data = {"labels": [label]}
|
|
labels_url = issue_url + "/labels"
|
|
await api.post(labels_url, json=data)
|
|
return "Label added successfully"
|
|
|
|
async def run(
|
|
self,
|
|
input_data: Input,
|
|
*,
|
|
credentials: GithubCredentials,
|
|
**kwargs,
|
|
) -> BlockOutput:
|
|
status = await self.add_label(
|
|
credentials,
|
|
input_data.issue_url,
|
|
input_data.label,
|
|
)
|
|
yield "status", status
|
|
|
|
|
|
class GithubRemoveLabelBlock(Block):
|
|
class Input(BlockSchemaInput):
|
|
credentials: GithubCredentialsInput = GithubCredentialsField("repo")
|
|
issue_url: str = SchemaField(
|
|
description="URL of the GitHub issue or pull request",
|
|
placeholder="https://github.com/owner/repo/issues/1",
|
|
)
|
|
label: str = SchemaField(
|
|
description="Label to remove from the issue or pull request",
|
|
placeholder="Enter the label",
|
|
)
|
|
|
|
class Output(BlockSchemaOutput):
|
|
status: str = SchemaField(description="Status of the label removal operation")
|
|
error: str = SchemaField(
|
|
description="Error message if the label removal failed"
|
|
)
|
|
|
|
def __init__(self):
|
|
super().__init__(
|
|
id="78f050c5-3e3a-48c0-9e5b-ef1ceca5589c",
|
|
description="A block that removes a label from a GitHub issue or pull request.",
|
|
categories={BlockCategory.DEVELOPER_TOOLS},
|
|
input_schema=GithubRemoveLabelBlock.Input,
|
|
output_schema=GithubRemoveLabelBlock.Output,
|
|
test_input={
|
|
"issue_url": "https://github.com/owner/repo/issues/1",
|
|
"label": "bug",
|
|
"credentials": TEST_CREDENTIALS_INPUT,
|
|
},
|
|
test_credentials=TEST_CREDENTIALS,
|
|
test_output=[("status", "Label removed successfully")],
|
|
test_mock={
|
|
"remove_label": lambda *args, **kwargs: "Label removed successfully"
|
|
},
|
|
)
|
|
|
|
@staticmethod
|
|
async def remove_label(
|
|
credentials: GithubCredentials, issue_url: str, label: str
|
|
) -> str:
|
|
api = get_api(credentials)
|
|
label_url = issue_url + f"/labels/{label}"
|
|
await api.delete(label_url)
|
|
return "Label removed successfully"
|
|
|
|
async def run(
|
|
self,
|
|
input_data: Input,
|
|
*,
|
|
credentials: GithubCredentials,
|
|
**kwargs,
|
|
) -> BlockOutput:
|
|
status = await self.remove_label(
|
|
credentials,
|
|
input_data.issue_url,
|
|
input_data.label,
|
|
)
|
|
yield "status", status
|
|
|
|
|
|
class GithubAssignIssueBlock(Block):
|
|
class Input(BlockSchemaInput):
|
|
credentials: GithubCredentialsInput = GithubCredentialsField("repo")
|
|
issue_url: str = SchemaField(
|
|
description="URL of the GitHub issue",
|
|
placeholder="https://github.com/owner/repo/issues/1",
|
|
)
|
|
assignee: str = SchemaField(
|
|
description="Username to assign to the issue",
|
|
placeholder="Enter the username",
|
|
)
|
|
|
|
class Output(BlockSchemaOutput):
|
|
status: str = SchemaField(
|
|
description="Status of the issue assignment operation"
|
|
)
|
|
error: str = SchemaField(
|
|
description="Error message if the issue assignment failed"
|
|
)
|
|
|
|
def __init__(self):
|
|
super().__init__(
|
|
id="90507c72-b0ff-413a-886a-23bbbd66f542",
|
|
description="A block that assigns a GitHub user to an issue for task ownership and tracking.",
|
|
categories={BlockCategory.DEVELOPER_TOOLS},
|
|
input_schema=GithubAssignIssueBlock.Input,
|
|
output_schema=GithubAssignIssueBlock.Output,
|
|
test_input={
|
|
"issue_url": "https://github.com/owner/repo/issues/1",
|
|
"assignee": "username1",
|
|
"credentials": TEST_CREDENTIALS_INPUT,
|
|
},
|
|
test_credentials=TEST_CREDENTIALS,
|
|
test_output=[("status", "Issue assigned successfully")],
|
|
test_mock={
|
|
"assign_issue": lambda *args, **kwargs: "Issue assigned successfully"
|
|
},
|
|
)
|
|
|
|
@staticmethod
|
|
async def assign_issue(
|
|
credentials: GithubCredentials,
|
|
issue_url: str,
|
|
assignee: str,
|
|
) -> str:
|
|
api = get_api(credentials)
|
|
assignees_url = issue_url + "/assignees"
|
|
data = {"assignees": [assignee]}
|
|
await api.post(assignees_url, json=data)
|
|
return "Issue assigned successfully"
|
|
|
|
async def run(
|
|
self,
|
|
input_data: Input,
|
|
*,
|
|
credentials: GithubCredentials,
|
|
**kwargs,
|
|
) -> BlockOutput:
|
|
status = await self.assign_issue(
|
|
credentials,
|
|
input_data.issue_url,
|
|
input_data.assignee,
|
|
)
|
|
yield "status", status
|
|
|
|
|
|
class GithubUnassignIssueBlock(Block):
|
|
class Input(BlockSchemaInput):
|
|
credentials: GithubCredentialsInput = GithubCredentialsField("repo")
|
|
issue_url: str = SchemaField(
|
|
description="URL of the GitHub issue",
|
|
placeholder="https://github.com/owner/repo/issues/1",
|
|
)
|
|
assignee: str = SchemaField(
|
|
description="Username to unassign from the issue",
|
|
placeholder="Enter the username",
|
|
)
|
|
|
|
class Output(BlockSchemaOutput):
|
|
status: str = SchemaField(
|
|
description="Status of the issue unassignment operation"
|
|
)
|
|
error: str = SchemaField(
|
|
description="Error message if the issue unassignment failed"
|
|
)
|
|
|
|
def __init__(self):
|
|
super().__init__(
|
|
id="d154002a-38f4-46c2-962d-2488f2b05ece",
|
|
description="A block that removes a user's assignment from a GitHub issue.",
|
|
categories={BlockCategory.DEVELOPER_TOOLS},
|
|
input_schema=GithubUnassignIssueBlock.Input,
|
|
output_schema=GithubUnassignIssueBlock.Output,
|
|
test_input={
|
|
"issue_url": "https://github.com/owner/repo/issues/1",
|
|
"assignee": "username1",
|
|
"credentials": TEST_CREDENTIALS_INPUT,
|
|
},
|
|
test_credentials=TEST_CREDENTIALS,
|
|
test_output=[("status", "Issue unassigned successfully")],
|
|
test_mock={
|
|
"unassign_issue": lambda *args, **kwargs: "Issue unassigned successfully"
|
|
},
|
|
)
|
|
|
|
@staticmethod
|
|
async def unassign_issue(
|
|
credentials: GithubCredentials,
|
|
issue_url: str,
|
|
assignee: str,
|
|
) -> str:
|
|
api = get_api(credentials)
|
|
assignees_url = issue_url + "/assignees"
|
|
data = {"assignees": [assignee]}
|
|
await api.delete(assignees_url, json=data)
|
|
return "Issue unassigned successfully"
|
|
|
|
async def run(
|
|
self,
|
|
input_data: Input,
|
|
*,
|
|
credentials: GithubCredentials,
|
|
**kwargs,
|
|
) -> BlockOutput:
|
|
status = await self.unassign_issue(
|
|
credentials,
|
|
input_data.issue_url,
|
|
input_data.assignee,
|
|
)
|
|
yield "status", status
|