Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
034ed2c928 | ||
|
|
f2d65f8914 | ||
|
|
f4bff86b70 |
@@ -3,7 +3,6 @@
|
|||||||
**Feature:** US: Manage Trello Boards
|
**Feature:** US: Manage Trello Boards
|
||||||
**Issue:** #2
|
**Issue:** #2
|
||||||
**Branch:** `feature/manage-boards`
|
**Branch:** `feature/manage-boards`
|
||||||
**Doc:** 002
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
# Trello Plugin — Manage Trello Cards
|
||||||
|
|
||||||
|
**Feature:** US: Manage Trello Cards
|
||||||
|
**Issue:** #4
|
||||||
|
**Branch:** `feature/manage-cards`
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Extends the Trello plugin with full card management capabilities: create, view, update, move, archive, assign members, comments, and checklist management.
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
### `trello_create_card`
|
||||||
|
Create a new card on a selected list.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
| Param | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `name` | string | Yes | Card title |
|
||||||
|
| `list_id` | string | Yes | List ID |
|
||||||
|
| `desc` | string | No | Card description |
|
||||||
|
| `due` | string | No | Due date (ISO 8601) |
|
||||||
|
|
||||||
|
**Returns:** Card ID, name, URL, and short URL.
|
||||||
|
|
||||||
|
### `trello_card_details`
|
||||||
|
View detailed information about a card.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
| Param | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `card_id` | string | Yes | Card ID |
|
||||||
|
|
||||||
|
**Returns:** Card details including title, description, due date, members, checklists, comments, URL.
|
||||||
|
|
||||||
|
### `trello_update_card`
|
||||||
|
Update a card's core attributes (title, description, due date).
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
| Param | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `card_id` | string | Yes | Card ID |
|
||||||
|
| `name` | string | No | New title |
|
||||||
|
| `desc` | string | No | New description |
|
||||||
|
| `due` | string | No | New due date (ISO 8601) |
|
||||||
|
|
||||||
|
**Returns:** Updated card details.
|
||||||
|
|
||||||
|
### `trello_move_card`
|
||||||
|
Move a card to a different list.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
| Param | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `card_id` | string | Yes | Card ID |
|
||||||
|
| `list_id` | string | Yes | Target list ID |
|
||||||
|
|
||||||
|
**Returns:** Success message with new list location.
|
||||||
|
|
||||||
|
### `trello_archive_card`
|
||||||
|
Archive a card.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
| Param | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `card_id` | string | Yes | Card ID |
|
||||||
|
|
||||||
|
**Returns:** Success message.
|
||||||
|
|
||||||
|
### `trello_assign_member`
|
||||||
|
Add a member to a card.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
| Param | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `card_id` | string | Yes | Card ID |
|
||||||
|
| `member_id` | string | Yes | Trello member ID |
|
||||||
|
|
||||||
|
**Returns:** Success message.
|
||||||
|
|
||||||
|
### `trello_remove_member`
|
||||||
|
Remove a member from a card.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
| Param | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `card_id` | string | Yes | Card ID |
|
||||||
|
| `member_id` | string | Yes | Trello member ID |
|
||||||
|
|
||||||
|
**Returns:** Success message.
|
||||||
|
|
||||||
|
### `trello_add_comment`
|
||||||
|
Add a comment to a card.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
| Param | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `card_id` | string | Yes | Card ID |
|
||||||
|
| `text` | string | Yes | Comment text |
|
||||||
|
|
||||||
|
**Returns:** Comment ID and success.
|
||||||
|
|
||||||
|
### `trello_delete_comment`
|
||||||
|
Delete a comment from a card.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
| Param | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `card_id` | string | Yes | Card ID |
|
||||||
|
| `comment_id` | string | Yes | Comment/action ID |
|
||||||
|
|
||||||
|
**Returns:** Success message.
|
||||||
|
|
||||||
|
### `trello_add_checklist_item`
|
||||||
|
Add a checklist item to a card.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
| Param | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `card_id` | string | Yes | Card ID |
|
||||||
|
| `name` | string | Yes | Checklist item text |
|
||||||
|
| `checklist_id` | string | No | Specific checklist ID (if card has multiple) |
|
||||||
|
|
||||||
|
**Returns:** Checklist item details.
|
||||||
|
|
||||||
|
### `trello_toggle_checklist_item`
|
||||||
|
Mark a checklist item as complete or incomplete.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
| Param | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `card_id` | string | Yes | Card ID |
|
||||||
|
| `item_id` | string | Yes | Checklist item ID |
|
||||||
|
| `checked` | bool | Yes | True = complete, False = incomplete |
|
||||||
|
|
||||||
|
**Returns:** Updated item state.
|
||||||
|
|
||||||
|
### `trello_delete_checklist_item`
|
||||||
|
Remove a checklist item.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
| Param | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `card_id` | string | Yes | Card ID |
|
||||||
|
| `item_id` | string | Yes | Checklist item ID |
|
||||||
|
|
||||||
|
**Returns:** Success message.
|
||||||
|
|
||||||
|
## Trello API Endpoints
|
||||||
|
|
||||||
|
| Purpose | Method | Endpoint |
|
||||||
|
|---------|--------|----------|
|
||||||
|
| Create card | POST | `/1/cards` |
|
||||||
|
| Get card | GET | `/1/cards/{id}` |
|
||||||
|
| Update card | PUT | `/1/cards/{id}` |
|
||||||
|
| Add member | POST | `/1/cards/{id}/idMembers` |
|
||||||
|
| Remove member | DELETE | `/1/cards/{id}/idMembers/{memberId}` |
|
||||||
|
| Add comment | POST | `/1/cards/{id}/actions/comments` |
|
||||||
|
| Delete comment | DELETE | `/1/cards/{id}/actions/{commentId}/comments` |
|
||||||
|
| Add checklist item | POST | `/1/cards/{id}/checklistItems` |
|
||||||
|
| Update checklist item | PUT | `/1/cards/{id}/checklistItem/{itemId}/state` |
|
||||||
|
| Delete checklist item | DELETE | `/1/cards/{id}/checklistItems/{itemId}`
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
**Feature:** US: Manage Trello Lists
|
**Feature:** US: Manage Trello Lists
|
||||||
**Issue:** #3
|
**Issue:** #3
|
||||||
**Branch:** `feature/manage-lists`
|
**Branch:** `feature/manage-lists`
|
||||||
**Doc:** 003
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
**Feature:** US: Authenticate and Connect to Trello Account
|
**Feature:** US: Authenticate and Connect to Trello Account
|
||||||
**Issue:** #1
|
**Issue:** #1
|
||||||
**Branch:** `feature/trello-auth`
|
**Branch:** `feature/trello-auth`
|
||||||
**Doc:** 001
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -8,17 +8,29 @@ from trello_plugin.tools import (
|
|||||||
PLUGIN_TOOLS,
|
PLUGIN_TOOLS,
|
||||||
PLUGIN_VERSION,
|
PLUGIN_VERSION,
|
||||||
check_requirements,
|
check_requirements,
|
||||||
|
trello_add_checklist_item,
|
||||||
|
trello_add_comment,
|
||||||
trello_archive_board,
|
trello_archive_board,
|
||||||
|
trello_archive_card,
|
||||||
trello_archive_list,
|
trello_archive_list,
|
||||||
|
trello_assign_member,
|
||||||
trello_board_details,
|
trello_board_details,
|
||||||
|
trello_card_details,
|
||||||
trello_create_board,
|
trello_create_board,
|
||||||
|
trello_create_card,
|
||||||
trello_create_list,
|
trello_create_list,
|
||||||
|
trello_delete_checklist_item,
|
||||||
|
trello_delete_comment,
|
||||||
trello_disconnect,
|
trello_disconnect,
|
||||||
trello_list_boards,
|
trello_list_boards,
|
||||||
|
trello_move_card,
|
||||||
trello_move_list,
|
trello_move_list,
|
||||||
trello_open_board,
|
trello_open_board,
|
||||||
|
trello_remove_member,
|
||||||
trello_rename_board,
|
trello_rename_board,
|
||||||
trello_rename_list,
|
trello_rename_list,
|
||||||
|
trello_toggle_checklist_item,
|
||||||
|
trello_update_card,
|
||||||
trello_verify_credentials,
|
trello_verify_credentials,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,4 +52,16 @@ __all__ = [
|
|||||||
"trello_rename_list",
|
"trello_rename_list",
|
||||||
"trello_archive_list",
|
"trello_archive_list",
|
||||||
"trello_move_list",
|
"trello_move_list",
|
||||||
|
"trello_create_card",
|
||||||
|
"trello_card_details",
|
||||||
|
"trello_update_card",
|
||||||
|
"trello_move_card",
|
||||||
|
"trello_archive_card",
|
||||||
|
"trello_assign_member",
|
||||||
|
"trello_remove_member",
|
||||||
|
"trello_add_comment",
|
||||||
|
"trello_delete_comment",
|
||||||
|
"trello_add_checklist_item",
|
||||||
|
"trello_toggle_checklist_item",
|
||||||
|
"trello_delete_checklist_item",
|
||||||
]
|
]
|
||||||
@@ -163,6 +163,10 @@ class TrelloClient:
|
|||||||
"""Make an authenticated PUT request."""
|
"""Make an authenticated PUT request."""
|
||||||
return self._request("PUT", path, json_body=json_body)
|
return self._request("PUT", path, json_body=json_body)
|
||||||
|
|
||||||
|
def _delete(self, path: str) -> ApiResponse:
|
||||||
|
"""Make an authenticated DELETE request."""
|
||||||
|
return self._request("DELETE", path)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Public API — Auth & Connection
|
# Public API — Auth & Connection
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
@@ -571,6 +575,398 @@ class TrelloClient:
|
|||||||
"message": result.get("message", "Failed to move list."),
|
"message": result.get("message", "Failed to move list."),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Public API — Card Management
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def create_card(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
list_id: str,
|
||||||
|
desc: str = "",
|
||||||
|
due: str | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Create a new card on a Trello list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: Card title.
|
||||||
|
list_id: Trello list ID.
|
||||||
|
desc: Card description.
|
||||||
|
due: Due date in ISO 8601 format.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with card details on success.
|
||||||
|
"""
|
||||||
|
body: dict[str, Any] = {"name": name, "idList": list_id}
|
||||||
|
if desc:
|
||||||
|
body["desc"] = desc
|
||||||
|
if due:
|
||||||
|
body["due"] = due
|
||||||
|
|
||||||
|
result = self._post("/cards", json_body=body)
|
||||||
|
if _is_success(result):
|
||||||
|
card = result["data"]
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"card": {
|
||||||
|
"id": card.get("id"),
|
||||||
|
"name": card.get("name"),
|
||||||
|
"url": card.get("url"),
|
||||||
|
"short_url": card.get("shortUrl"),
|
||||||
|
"id_list": card.get("idList"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": result.get("message", "Failed to create card."),
|
||||||
|
}
|
||||||
|
|
||||||
|
def card_details(self, card_id: str) -> dict[str, Any]:
|
||||||
|
"""Fetch detailed information about a card.
|
||||||
|
|
||||||
|
Returns card attributes, members, checklists, and comments.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
card_id: Trello card ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with full card details.
|
||||||
|
"""
|
||||||
|
result = self._get(
|
||||||
|
f"/cards/{card_id}",
|
||||||
|
params={
|
||||||
|
"fields": "id,name,desc,due,dueComplete,url,shortUrl,idList,closed",
|
||||||
|
"members": "true",
|
||||||
|
"member_fields": "id,username,fullName",
|
||||||
|
"checklists": "all",
|
||||||
|
"actions": "commentCard",
|
||||||
|
"actions_limit": "50",
|
||||||
|
"list": "true",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if not _is_success(result):
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": result.get("message", "Failed to fetch card details."),
|
||||||
|
}
|
||||||
|
|
||||||
|
card = result["data"]
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"card": {
|
||||||
|
"id": card.get("id"),
|
||||||
|
"name": card.get("name"),
|
||||||
|
"desc": card.get("desc", ""),
|
||||||
|
"due": card.get("due"),
|
||||||
|
"due_complete": card.get("dueComplete", False),
|
||||||
|
"url": card.get("url"),
|
||||||
|
"short_url": card.get("shortUrl"),
|
||||||
|
"closed": card.get("closed", False),
|
||||||
|
"list": {"id": card.get("idList"), "name": (card.get("list") or {}).get("name", "")},
|
||||||
|
"members": [
|
||||||
|
{"id": m.get("id"), "username": m.get("username"), "full_name": m.get("fullName")}
|
||||||
|
for m in card.get("members", [])
|
||||||
|
],
|
||||||
|
"checklists": [
|
||||||
|
{
|
||||||
|
"id": cl.get("id"),
|
||||||
|
"name": cl.get("name"),
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": item.get("id"),
|
||||||
|
"name": item.get("name"),
|
||||||
|
"state": item.get("state"),
|
||||||
|
}
|
||||||
|
for item in cl.get("checkItems", [])
|
||||||
|
],
|
||||||
|
}
|
||||||
|
for cl in card.get("checklists", [])
|
||||||
|
],
|
||||||
|
"comments": [
|
||||||
|
{
|
||||||
|
"id": a.get("id"),
|
||||||
|
"text": a.get("data", {}).get("text", ""),
|
||||||
|
"member": a.get("memberCreator", {}).get("username", "unknown"),
|
||||||
|
"date": a.get("date"),
|
||||||
|
}
|
||||||
|
for a in card.get("actions", [])
|
||||||
|
if a.get("type") == "commentCard"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def update_card(
|
||||||
|
self,
|
||||||
|
card_id: str,
|
||||||
|
name: str | None = None,
|
||||||
|
desc: str | None = None,
|
||||||
|
due: str | None = None,
|
||||||
|
) -> dict[str, Any]:
|
||||||
|
"""Update a card's core attributes.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
card_id: Trello card ID.
|
||||||
|
name: New title (omit to keep current).
|
||||||
|
desc: New description (omit to keep current).
|
||||||
|
due: New due date ISO 8601, or empty string to clear.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with updated card details.
|
||||||
|
"""
|
||||||
|
body: dict[str, Any] = {}
|
||||||
|
if name is not None:
|
||||||
|
body["name"] = name
|
||||||
|
if desc is not None:
|
||||||
|
body["desc"] = desc
|
||||||
|
if due is not None:
|
||||||
|
body["due"] = due
|
||||||
|
|
||||||
|
if not body:
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"message": "No changes specified.",
|
||||||
|
}
|
||||||
|
|
||||||
|
result = self._put(f"/cards/{card_id}", json_body=body)
|
||||||
|
if _is_success(result):
|
||||||
|
card = result["data"]
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"card": {
|
||||||
|
"id": card.get("id"),
|
||||||
|
"name": card.get("name"),
|
||||||
|
"desc": card.get("desc", ""),
|
||||||
|
"due": card.get("due"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": result.get("message", "Failed to update card."),
|
||||||
|
}
|
||||||
|
|
||||||
|
def move_card(self, card_id: str, list_id: str) -> dict[str, Any]:
|
||||||
|
"""Move a card to a different list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
card_id: Trello card ID.
|
||||||
|
list_id: Target list ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with success message.
|
||||||
|
"""
|
||||||
|
result = self._put(f"/cards/{card_id}", json_body={"idList": list_id})
|
||||||
|
if _is_success(result):
|
||||||
|
card = result["data"]
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"message": f"Card '{card.get('name', card_id)}' moved to list '{card.get('idList', list_id)}'.",
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": result.get("message", "Failed to move card."),
|
||||||
|
}
|
||||||
|
|
||||||
|
def archive_card(self, card_id: str) -> dict[str, Any]:
|
||||||
|
"""Archive a card.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
card_id: Trello card ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with success message.
|
||||||
|
"""
|
||||||
|
result = self._put(f"/cards/{card_id}", json_body={"closed": True})
|
||||||
|
if _is_success(result):
|
||||||
|
card = result["data"]
|
||||||
|
return {"success": True, "message": f"Card '{card.get('name', card_id)}' archived."}
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": result.get("message", "Failed to archive card."),
|
||||||
|
}
|
||||||
|
|
||||||
|
def assign_member(self, card_id: str, member_id: str) -> dict[str, Any]:
|
||||||
|
"""Add a member to a card.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
card_id: Trello card ID.
|
||||||
|
member_id: Trello member ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with success message.
|
||||||
|
"""
|
||||||
|
result = self._post(f"/cards/{card_id}/idMembers", json_body={"value": member_id})
|
||||||
|
if _is_success(result):
|
||||||
|
return {"success": True, "message": f"Member {member_id} assigned to card."}
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": result.get("message", "Failed to assign member."),
|
||||||
|
}
|
||||||
|
|
||||||
|
def remove_member(self, card_id: str, member_id: str) -> dict[str, Any]:
|
||||||
|
"""Remove a member from a card.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
card_id: Trello card ID.
|
||||||
|
member_id: Trello member ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with success message.
|
||||||
|
"""
|
||||||
|
result = self._delete(f"/cards/{card_id}/idMembers/{member_id}")
|
||||||
|
if _is_success(result):
|
||||||
|
return {"success": True, "message": f"Member {member_id} removed from card."}
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": result.get("message", "Failed to remove member."),
|
||||||
|
}
|
||||||
|
|
||||||
|
def add_comment(self, card_id: str, text: str) -> dict[str, Any]:
|
||||||
|
"""Add a comment to a card.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
card_id: Trello card ID.
|
||||||
|
text: Comment text.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with comment details.
|
||||||
|
"""
|
||||||
|
result = self._post(
|
||||||
|
f"/cards/{card_id}/actions/comments",
|
||||||
|
json_body={"text": text},
|
||||||
|
)
|
||||||
|
if _is_success(result):
|
||||||
|
action = result["data"]
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"comment": {
|
||||||
|
"id": action.get("id"),
|
||||||
|
"text": action.get("data", {}).get("text", text),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": result.get("message", "Failed to add comment."),
|
||||||
|
}
|
||||||
|
|
||||||
|
def delete_comment(self, card_id: str, comment_id: str) -> dict[str, Any]:
|
||||||
|
"""Delete a comment from a card.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
card_id: Trello card ID.
|
||||||
|
comment_id: Action/comment ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with success message.
|
||||||
|
"""
|
||||||
|
result = self._delete(f"/cards/{card_id}/actions/{comment_id}/comments")
|
||||||
|
if _is_success(result):
|
||||||
|
return {"success": True, "message": "Comment deleted."}
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": result.get("message", "Failed to delete comment."),
|
||||||
|
}
|
||||||
|
|
||||||
|
def add_checklist_item(self, card_id: str, name: str, checklist_id: str | None = None) -> dict[str, Any]:
|
||||||
|
"""Add a checklist item to a card.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
card_id: Trello card ID.
|
||||||
|
name: Checklist item text.
|
||||||
|
checklist_id: Specific checklist (fetches first if omitted).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with item details.
|
||||||
|
"""
|
||||||
|
resolved_checklist_id = checklist_id
|
||||||
|
if not resolved_checklist_id:
|
||||||
|
# Fetch checklists to find the first one
|
||||||
|
details = self.card_details(card_id)
|
||||||
|
if not details.get("success"):
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": "Card has no checklists. Add one via Trello web first.",
|
||||||
|
}
|
||||||
|
checklists = details["card"].get("checklists", [])
|
||||||
|
if not checklists:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": "Card has no checklists. Add one via Trello web first.",
|
||||||
|
}
|
||||||
|
resolved_checklist_id = checklists[0]["id"]
|
||||||
|
|
||||||
|
result = self._post(
|
||||||
|
f"/cards/{card_id}/checklistItems",
|
||||||
|
json_body={
|
||||||
|
"idChecklist": resolved_checklist_id,
|
||||||
|
"name": name,
|
||||||
|
"checked": False,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if _is_success(result):
|
||||||
|
item = result["data"]
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"item": {
|
||||||
|
"id": item.get("id"),
|
||||||
|
"name": item.get("name"),
|
||||||
|
"state": item.get("state", "incomplete"),
|
||||||
|
"id_checklist": item.get("idChecklist"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": result.get("message", "Failed to add checklist item."),
|
||||||
|
}
|
||||||
|
|
||||||
|
def toggle_checklist_item(self, card_id: str, item_id: str, checked: bool) -> dict[str, Any]: # noqa: FBT001
|
||||||
|
"""Mark a checklist item as complete or incomplete.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
card_id: Trello card ID.
|
||||||
|
item_id: Checklist item ID.
|
||||||
|
checked: True for complete, False for incomplete.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with updated item state.
|
||||||
|
"""
|
||||||
|
state = "complete" if checked else "incomplete"
|
||||||
|
result = self._put(
|
||||||
|
f"/cards/{card_id}/checklistItem/{item_id}",
|
||||||
|
json_body={"state": state},
|
||||||
|
)
|
||||||
|
if _is_success(result):
|
||||||
|
item = result["data"]
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"item": {
|
||||||
|
"id": item.get("id"),
|
||||||
|
"name": item.get("name"),
|
||||||
|
"state": item.get("state"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": result.get("message", "Failed to update checklist item."),
|
||||||
|
}
|
||||||
|
|
||||||
|
def delete_checklist_item(self, card_id: str, item_id: str) -> dict[str, Any]:
|
||||||
|
"""Remove a checklist item.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
card_id: Trello card ID.
|
||||||
|
item_id: Checklist item ID.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict with success message.
|
||||||
|
"""
|
||||||
|
result = self._delete(f"/cards/{card_id}/checklistItems/{item_id}")
|
||||||
|
if _is_success(result):
|
||||||
|
return {"success": True, "message": "Checklist item deleted."}
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": result.get("message", "Failed to delete checklist item."),
|
||||||
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check_requirements() -> bool:
|
def check_requirements() -> bool:
|
||||||
"""Check if the required environment variables are set."""
|
"""Check if the required environment variables are set."""
|
||||||
|
|||||||
@@ -260,6 +260,259 @@ def trello_move_list(list_id: str, pos: str = "bottom") -> str:
|
|||||||
return _respond(result)
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Tools — Card Management
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def trello_create_card(name: str, list_id: str, desc: str = "", due: str | None = None) -> str:
|
||||||
|
"""Create a new card on a Trello list.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name : str
|
||||||
|
Card title.
|
||||||
|
list_id : str
|
||||||
|
List ID.
|
||||||
|
desc : str, optional
|
||||||
|
Card description.
|
||||||
|
due : str, optional
|
||||||
|
Due date in ISO 8601 format.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
JSON with card details.
|
||||||
|
"""
|
||||||
|
client = _get_client()
|
||||||
|
result = client.create_card(name=name, list_id=list_id, desc=desc, due=due)
|
||||||
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
|
def trello_card_details(card_id: str) -> str:
|
||||||
|
"""View detailed information about a Trello card.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
card_id : str
|
||||||
|
Card ID.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
JSON with full card details including members, checklists, and comments.
|
||||||
|
"""
|
||||||
|
client = _get_client()
|
||||||
|
result = client.card_details(card_id=card_id)
|
||||||
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
|
def trello_update_card(card_id: str, name: str | None = None, desc: str | None = None, due: str | None = None) -> str:
|
||||||
|
"""Update a card's core attributes (title, description, due date).
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
card_id : str
|
||||||
|
Card ID.
|
||||||
|
name : str, optional
|
||||||
|
New title.
|
||||||
|
desc : str, optional
|
||||||
|
New description.
|
||||||
|
due : str, optional
|
||||||
|
New due date ISO 8601.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
JSON with updated card details.
|
||||||
|
"""
|
||||||
|
client = _get_client()
|
||||||
|
result = client.update_card(card_id=card_id, name=name, desc=desc, due=due)
|
||||||
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
|
def trello_move_card(card_id: str, list_id: str) -> str:
|
||||||
|
"""Move a card to a different list.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
card_id : str
|
||||||
|
Card ID.
|
||||||
|
list_id : str
|
||||||
|
Target list ID.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
JSON with success message.
|
||||||
|
"""
|
||||||
|
client = _get_client()
|
||||||
|
result = client.move_card(card_id=card_id, list_id=list_id)
|
||||||
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
|
def trello_archive_card(card_id: str) -> str:
|
||||||
|
"""Archive a Trello card.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
card_id : str
|
||||||
|
Card ID.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
JSON with success message.
|
||||||
|
"""
|
||||||
|
client = _get_client()
|
||||||
|
result = client.archive_card(card_id=card_id)
|
||||||
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
|
def trello_assign_member(card_id: str, member_id: str) -> str:
|
||||||
|
"""Add a member to a Trello card.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
card_id : str
|
||||||
|
Card ID.
|
||||||
|
member_id : str
|
||||||
|
Trello member ID.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
JSON with success message.
|
||||||
|
"""
|
||||||
|
client = _get_client()
|
||||||
|
result = client.assign_member(card_id=card_id, member_id=member_id)
|
||||||
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
|
def trello_remove_member(card_id: str, member_id: str) -> str:
|
||||||
|
"""Remove a member from a Trello card.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
card_id : str
|
||||||
|
Card ID.
|
||||||
|
member_id : str
|
||||||
|
Trello member ID.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
JSON with success message.
|
||||||
|
"""
|
||||||
|
client = _get_client()
|
||||||
|
result = client.remove_member(card_id=card_id, member_id=member_id)
|
||||||
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
|
def trello_add_comment(card_id: str, text: str) -> str:
|
||||||
|
"""Add a comment to a Trello card.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
card_id : str
|
||||||
|
Card ID.
|
||||||
|
text : str
|
||||||
|
Comment text.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
JSON with comment ID.
|
||||||
|
"""
|
||||||
|
client = _get_client()
|
||||||
|
result = client.add_comment(card_id=card_id, text=text)
|
||||||
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
|
def trello_delete_comment(card_id: str, comment_id: str) -> str:
|
||||||
|
"""Delete a comment from a Trello card.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
card_id : str
|
||||||
|
Card ID.
|
||||||
|
comment_id : str
|
||||||
|
Comment/action ID.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
JSON with success message.
|
||||||
|
"""
|
||||||
|
client = _get_client()
|
||||||
|
result = client.delete_comment(card_id=card_id, comment_id=comment_id)
|
||||||
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
|
def trello_add_checklist_item(card_id: str, name: str, checklist_id: str | None = None) -> str:
|
||||||
|
"""Add a checklist item to a Trello card.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
card_id : str
|
||||||
|
Card ID.
|
||||||
|
name : str
|
||||||
|
Checklist item text.
|
||||||
|
checklist_id : str, optional
|
||||||
|
Specific checklist ID (auto-discovers the first if omitted).
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
JSON with item details.
|
||||||
|
"""
|
||||||
|
client = _get_client()
|
||||||
|
result = client.add_checklist_item(card_id=card_id, name=name, checklist_id=checklist_id)
|
||||||
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
|
def trello_toggle_checklist_item(card_id: str, item_id: str, checked: bool) -> str: # noqa: FBT001
|
||||||
|
"""Mark a checklist item as complete or incomplete.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
card_id : str
|
||||||
|
Card ID.
|
||||||
|
item_id : str
|
||||||
|
Checklist item ID.
|
||||||
|
checked : bool
|
||||||
|
True = complete, False = incomplete.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
JSON with updated item state.
|
||||||
|
"""
|
||||||
|
client = _get_client()
|
||||||
|
result = client.toggle_checklist_item(card_id=card_id, item_id=item_id, checked=checked)
|
||||||
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
|
def trello_delete_checklist_item(card_id: str, item_id: str) -> str:
|
||||||
|
"""Remove a checklist item from a Trello card.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
card_id : str
|
||||||
|
Card ID.
|
||||||
|
item_id : str
|
||||||
|
Checklist item ID.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
str
|
||||||
|
JSON with success message.
|
||||||
|
"""
|
||||||
|
client = _get_client()
|
||||||
|
result = client.delete_checklist_item(card_id=card_id, item_id=item_id)
|
||||||
|
return _respond(result)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Plugin metadata (used by Hermes plugin loader)
|
# Plugin metadata (used by Hermes plugin loader)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -280,6 +533,18 @@ PLUGIN_TOOLS = [
|
|||||||
trello_rename_list,
|
trello_rename_list,
|
||||||
trello_archive_list,
|
trello_archive_list,
|
||||||
trello_move_list,
|
trello_move_list,
|
||||||
|
trello_create_card,
|
||||||
|
trello_card_details,
|
||||||
|
trello_update_card,
|
||||||
|
trello_move_card,
|
||||||
|
trello_archive_card,
|
||||||
|
trello_assign_member,
|
||||||
|
trello_remove_member,
|
||||||
|
trello_add_comment,
|
||||||
|
trello_delete_comment,
|
||||||
|
trello_add_checklist_item,
|
||||||
|
trello_toggle_checklist_item,
|
||||||
|
trello_delete_checklist_item,
|
||||||
]
|
]
|
||||||
PLUGIN_REQUIRES_ENV = ["TRELLO_API_KEY", "TRELLO_TOKEN"]
|
PLUGIN_REQUIRES_ENV = ["TRELLO_API_KEY", "TRELLO_TOKEN"]
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -325,5 +325,5 @@ class TestPluginMetadata:
|
|||||||
assert PLUGIN_NAME == "trello-plugin"
|
assert PLUGIN_NAME == "trello-plugin"
|
||||||
assert isinstance(PLUGIN_DESCRIPTION, str)
|
assert isinstance(PLUGIN_DESCRIPTION, str)
|
||||||
assert isinstance(PLUGIN_VERSION, str)
|
assert isinstance(PLUGIN_VERSION, str)
|
||||||
assert len(PLUGIN_TOOLS) == 12
|
assert len(PLUGIN_TOOLS) == 24
|
||||||
assert all(callable(t) for t in PLUGIN_TOOLS)
|
assert all(callable(t) for t in PLUGIN_TOOLS)
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
"""Tests for the Trello plugin — card management feature."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.usefixtures("clear_env")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def clear_env(monkeypatch: pytest.MonkeyPatch) -> Any:
|
||||||
|
monkeypatch.delenv("TRELLO_API_KEY", raising=False)
|
||||||
|
monkeypatch.delenv("TRELLO_TOKEN", raising=False)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# TrelloClient — create_card
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TestCreateCard:
|
||||||
|
def test_success(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.post("https://api.trello.com/1/cards", json={"id": "c1", "name": "My Card", "url": "https://trello.com/c/c1", "shortUrl": "https://trello.com/c/abc", "idList": "l1"})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.create_card(name="My Card", list_id="l1")
|
||||||
|
assert result["success"]
|
||||||
|
assert result["card"]["name"] == "My Card"
|
||||||
|
|
||||||
|
def test_with_description_and_due(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.post("https://api.trello.com/1/cards", json={"id": "c1", "name": "Card", "url": "", "shortUrl": "", "idList": "l1"})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.create_card(name="Card", list_id="l1", desc="Desc", due="2026-06-01T00:00:00Z")
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_missing_credentials(self) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
client = TrelloClient(api_key="", token="")
|
||||||
|
result = client.create_card(name="Card", list_id="l1")
|
||||||
|
assert not result["success"]
|
||||||
|
assert "TRELLO_API_KEY" in result["message"]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# TrelloClient — card_details
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TestCardDetails:
|
||||||
|
def test_success(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.get(
|
||||||
|
"https://api.trello.com/1/cards/c1",
|
||||||
|
json={
|
||||||
|
"id": "c1", "name": "My Card", "desc": "Description", "due": None,
|
||||||
|
"dueComplete": False, "url": "https://trello.com/c/c1", "shortUrl": "https://trello.com/c/abc",
|
||||||
|
"idList": "l1", "closed": False, "list": {"id": "l1", "name": "To Do"},
|
||||||
|
"members": [{"id": "m1", "username": "user1", "fullName": "User One"}],
|
||||||
|
"checklists": [{"id": "cl1", "name": "Checklist", "checkItems": [{"id": "ci1", "name": "Item", "state": "incomplete"}]}],
|
||||||
|
"actions": [{"id": "a1", "type": "commentCard", "data": {"text": "Nice card"}, "memberCreator": {"username": "user1"}, "date": "2026-05-01T00:00:00Z"}],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.card_details(card_id="c1")
|
||||||
|
assert result["success"]
|
||||||
|
assert result["card"]["name"] == "My Card"
|
||||||
|
assert len(result["card"]["members"]) == 1
|
||||||
|
assert len(result["card"]["checklists"]) == 1
|
||||||
|
assert len(result["card"]["comments"]) == 1
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# TrelloClient — update_card
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TestUpdateCard:
|
||||||
|
def test_update_name(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.put("https://api.trello.com/1/cards/c1", json={"id": "c1", "name": "Updated", "desc": "", "due": None})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.update_card(card_id="c1", name="Updated")
|
||||||
|
assert result["success"]
|
||||||
|
assert result["card"]["name"] == "Updated"
|
||||||
|
|
||||||
|
def test_no_changes(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.update_card(card_id="c1")
|
||||||
|
assert result["success"]
|
||||||
|
assert "No changes" in result["message"]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# TrelloClient — move_card, archive_card
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TestMoveCard:
|
||||||
|
def test_success(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.put("https://api.trello.com/1/cards/c1", json={"id": "c1", "name": "Card", "idList": "l2"})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.move_card(card_id="c1", list_id="l2")
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
|
||||||
|
class TestArchiveCard:
|
||||||
|
def test_success(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.put("https://api.trello.com/1/cards/c1", json={"id": "c1", "name": "Card", "closed": True})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.archive_card(card_id="c1")
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# TrelloClient — member operations
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TestMemberOps:
|
||||||
|
def test_assign_member(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.post("https://api.trello.com/1/cards/c1/idMembers", json={"id": "c1"}, status_code=200)
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.assign_member(card_id="c1", member_id="m1")
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_remove_member(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.delete("https://api.trello.com/1/cards/c1/idMembers/m1", status_code=200, json={})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.remove_member(card_id="c1", member_id="m1")
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# TrelloClient — comments
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TestComments:
|
||||||
|
def test_add_comment(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.post("https://api.trello.com/1/cards/c1/actions/comments", json={"id": "a1", "data": {"text": "Great!"}})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.add_comment(card_id="c1", text="Great!")
|
||||||
|
assert result["success"]
|
||||||
|
assert result["comment"]["id"] == "a1"
|
||||||
|
|
||||||
|
def test_delete_comment(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.delete("https://api.trello.com/1/cards/c1/actions/a1/comments", status_code=200, json={})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.delete_comment(card_id="c1", comment_id="a1")
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# TrelloClient — checklists
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TestChecklistItems:
|
||||||
|
def test_add_item_with_checklist_id(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.post("https://api.trello.com/1/cards/c1/checklistItems", json={"id": "ci1", "name": "Task", "state": "incomplete", "idChecklist": "cl1"})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.add_checklist_item(card_id="c1", name="Task", checklist_id="cl1")
|
||||||
|
assert result["success"]
|
||||||
|
assert result["item"]["name"] == "Task"
|
||||||
|
|
||||||
|
def test_add_item_auto_discover(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
# card_details call for auto-discover
|
||||||
|
requests_mock.get(
|
||||||
|
"https://api.trello.com/1/cards/c1",
|
||||||
|
json={
|
||||||
|
"id": "c1", "name": "Card", "desc": "", "due": None,
|
||||||
|
"dueComplete": False, "url": "", "shortUrl": "", "idList": "l1", "closed": False,
|
||||||
|
"list": {"id": "l1", "name": "List"},
|
||||||
|
"members": [], "checklists": [{"id": "cl1", "name": "Checklist", "checkItems": []}],
|
||||||
|
"actions": [],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
requests_mock.post("https://api.trello.com/1/cards/c1/checklistItems", json={"id": "ci1", "name": "Auto", "state": "incomplete", "idChecklist": "cl1"})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.add_checklist_item(card_id="c1", name="Auto")
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_toggle_complete(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.put("https://api.trello.com/1/cards/c1/checklistItem/ci1", json={"id": "ci1", "name": "Task", "state": "complete"})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.toggle_checklist_item(card_id="c1", item_id="ci1", checked=True)
|
||||||
|
assert result["success"]
|
||||||
|
assert result["item"]["state"] == "complete"
|
||||||
|
|
||||||
|
def test_toggle_incomplete(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.put("https://api.trello.com/1/cards/c1/checklistItem/ci1", json={"id": "ci1", "name": "Task", "state": "incomplete"})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.toggle_checklist_item(card_id="c1", item_id="ci1", checked=False)
|
||||||
|
assert result["success"]
|
||||||
|
assert result["item"]["state"] == "incomplete"
|
||||||
|
|
||||||
|
def test_delete_item(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.client import TrelloClient
|
||||||
|
requests_mock.delete("https://api.trello.com/1/cards/c1/checklistItems/ci1", status_code=200, json={})
|
||||||
|
client = TrelloClient(api_key="k", token="t")
|
||||||
|
result = client.delete_checklist_item(card_id="c1", item_id="ci1")
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Tool functions
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TestCardToolFunctions:
|
||||||
|
def test_trello_create_card_tool(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.tools import trello_create_card
|
||||||
|
os.environ["TRELLO_API_KEY"] = "k"
|
||||||
|
os.environ["TRELLO_TOKEN"] = "t"
|
||||||
|
requests_mock.post("https://api.trello.com/1/cards", json={"id": "c1", "name": "Card", "url": "", "shortUrl": "", "idList": "l1"})
|
||||||
|
result = json.loads(trello_create_card(name="Card", list_id="l1"))
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_trello_card_details_tool(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.tools import trello_card_details
|
||||||
|
os.environ["TRELLO_API_KEY"] = "k"
|
||||||
|
os.environ["TRELLO_TOKEN"] = "t"
|
||||||
|
requests_mock.get("https://api.trello.com/1/cards/c1", json={"id": "c1", "name": "Card", "desc": "", "due": None, "dueComplete": False, "url": "", "shortUrl": "", "idList": "l1", "closed": False, "list": {"id": "l1", "name": "List"}, "members": [], "checklists": [], "actions": []})
|
||||||
|
result = json.loads(trello_card_details(card_id="c1"))
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_trello_update_card_tool(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.tools import trello_update_card
|
||||||
|
os.environ["TRELLO_API_KEY"] = "k"
|
||||||
|
os.environ["TRELLO_TOKEN"] = "t"
|
||||||
|
requests_mock.put("https://api.trello.com/1/cards/c1", json={"id": "c1", "name": "New", "desc": "", "due": None})
|
||||||
|
result = json.loads(trello_update_card(card_id="c1", name="New"))
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_trello_move_card_tool(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.tools import trello_move_card
|
||||||
|
os.environ["TRELLO_API_KEY"] = "k"
|
||||||
|
os.environ["TRELLO_TOKEN"] = "t"
|
||||||
|
requests_mock.put("https://api.trello.com/1/cards/c1", json={"id": "c1", "name": "Card", "idList": "l2"})
|
||||||
|
result = json.loads(trello_move_card(card_id="c1", list_id="l2"))
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_trello_archive_card_tool(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.tools import trello_archive_card
|
||||||
|
os.environ["TRELLO_API_KEY"] = "k"
|
||||||
|
os.environ["TRELLO_TOKEN"] = "t"
|
||||||
|
requests_mock.put("https://api.trello.com/1/cards/c1", json={"id": "c1", "name": "Card", "closed": True})
|
||||||
|
result = json.loads(trello_archive_card(card_id="c1"))
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_trello_assign_member_tool(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.tools import trello_assign_member
|
||||||
|
os.environ["TRELLO_API_KEY"] = "k"
|
||||||
|
os.environ["TRELLO_TOKEN"] = "t"
|
||||||
|
requests_mock.post("https://api.trello.com/1/cards/c1/idMembers", json={}, status_code=200)
|
||||||
|
result = json.loads(trello_assign_member(card_id="c1", member_id="m1"))
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_trello_remove_member_tool(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.tools import trello_remove_member
|
||||||
|
os.environ["TRELLO_API_KEY"] = "k"
|
||||||
|
os.environ["TRELLO_TOKEN"] = "t"
|
||||||
|
requests_mock.delete("https://api.trello.com/1/cards/c1/idMembers/m1", status_code=200, json={})
|
||||||
|
result = json.loads(trello_remove_member(card_id="c1", member_id="m1"))
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_trello_add_comment_tool(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.tools import trello_add_comment
|
||||||
|
os.environ["TRELLO_API_KEY"] = "k"
|
||||||
|
os.environ["TRELLO_TOKEN"] = "t"
|
||||||
|
requests_mock.post("https://api.trello.com/1/cards/c1/actions/comments", json={"id": "a1", "data": {"text": "Nice"}})
|
||||||
|
result = json.loads(trello_add_comment(card_id="c1", text="Nice"))
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_trello_delete_comment_tool(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.tools import trello_delete_comment
|
||||||
|
os.environ["TRELLO_API_KEY"] = "k"
|
||||||
|
os.environ["TRELLO_TOKEN"] = "t"
|
||||||
|
requests_mock.delete("https://api.trello.com/1/cards/c1/actions/a1/comments", status_code=200, json={})
|
||||||
|
result = json.loads(trello_delete_comment(card_id="c1", comment_id="a1"))
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_trello_add_checklist_item_tool(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.tools import trello_add_checklist_item
|
||||||
|
os.environ["TRELLO_API_KEY"] = "k"
|
||||||
|
os.environ["TRELLO_TOKEN"] = "t"
|
||||||
|
requests_mock.post("https://api.trello.com/1/cards/c1/checklistItems", json={"id": "ci1", "name": "Task", "state": "incomplete", "idChecklist": "cl1"})
|
||||||
|
result = json.loads(trello_add_checklist_item(card_id="c1", name="Task", checklist_id="cl1"))
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_trello_toggle_checklist_item_tool(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.tools import trello_toggle_checklist_item
|
||||||
|
os.environ["TRELLO_API_KEY"] = "k"
|
||||||
|
os.environ["TRELLO_TOKEN"] = "t"
|
||||||
|
requests_mock.put("https://api.trello.com/1/cards/c1/checklistItem/ci1", json={"id": "ci1", "name": "Task", "state": "complete"})
|
||||||
|
result = json.loads(trello_toggle_checklist_item(card_id="c1", item_id="ci1", checked=True))
|
||||||
|
assert result["success"]
|
||||||
|
|
||||||
|
def test_trello_delete_checklist_item_tool(self, requests_mock: Any) -> None:
|
||||||
|
from trello_plugin.tools import trello_delete_checklist_item
|
||||||
|
os.environ["TRELLO_API_KEY"] = "k"
|
||||||
|
os.environ["TRELLO_TOKEN"] = "t"
|
||||||
|
requests_mock.delete("https://api.trello.com/1/cards/c1/checklistItems/ci1", status_code=200, json={})
|
||||||
|
result = json.loads(trello_delete_checklist_item(card_id="c1", item_id="ci1"))
|
||||||
|
assert result["success"]
|
||||||
Reference in New Issue
Block a user