21e4d3effd
The reorder_card method was using the API route
/api/v1.0/boards/{boardId}/stacks/{stackId}/cards/{cardId}/reorder
which has a parameter conflict: the URL's {stackId} (current stack)
overrides the body's stackId (target stack) in Nextcloud's routing.
This caused cards to stay in their original stack even when the API
reported success.
Switched to the non-API route /cards/{cardId}/reorder which correctly
reads stackId from the request body, matching the behavior of the
working curl command reported in the issue.
Also added the required OCS-APIRequest headers that were missing.
Fixes #469
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
617 lines
22 KiB
Python
617 lines
22 KiB
Python
from typing import Any, Dict, List, Optional
|
|
|
|
from nextcloud_mcp_server.client.base import BaseNextcloudClient
|
|
from nextcloud_mcp_server.models.deck import (
|
|
DeckACL,
|
|
DeckAttachment,
|
|
DeckBoard,
|
|
DeckCard,
|
|
DeckComment,
|
|
DeckConfig,
|
|
DeckLabel,
|
|
DeckSession,
|
|
DeckStack,
|
|
)
|
|
|
|
|
|
class DeckClient(BaseNextcloudClient):
|
|
"""Client for Nextcloud Deck app operations."""
|
|
|
|
app_name = "deck"
|
|
|
|
def _get_deck_headers(
|
|
self, additional_headers: Optional[Dict[str, str]] = None
|
|
) -> Dict[str, str]:
|
|
"""Get standard headers required for Deck API calls."""
|
|
headers = {"OCS-APIRequest": "true", "Content-Type": "application/json"}
|
|
if additional_headers:
|
|
headers.update(additional_headers)
|
|
return headers
|
|
|
|
# Boards
|
|
async def get_boards(
|
|
self, details: bool = False, if_modified_since: Optional[str] = None
|
|
) -> List[DeckBoard]:
|
|
additional_headers = {}
|
|
if if_modified_since:
|
|
additional_headers["If-Modified-Since"] = if_modified_since
|
|
headers = self._get_deck_headers(additional_headers)
|
|
params = {"details": "true"} if details else {}
|
|
response = await self._make_request(
|
|
"GET", "/apps/deck/api/v1.0/boards", headers=headers, params=params
|
|
)
|
|
return [DeckBoard(**board) for board in response.json()]
|
|
|
|
async def create_board(self, title: str, color: str) -> DeckBoard:
|
|
json_data = {"title": title, "color": color}
|
|
headers = self._get_deck_headers()
|
|
response = await self._make_request(
|
|
"POST", "/apps/deck/api/v1.0/boards", json=json_data, headers=headers
|
|
)
|
|
return DeckBoard(**response.json())
|
|
|
|
async def get_board(self, board_id: int) -> DeckBoard:
|
|
headers = self._get_deck_headers()
|
|
response = await self._make_request(
|
|
"GET", f"/apps/deck/api/v1.0/boards/{board_id}", headers=headers
|
|
)
|
|
return DeckBoard(**response.json())
|
|
|
|
async def update_board(
|
|
self,
|
|
board_id: int,
|
|
title: Optional[str] = None,
|
|
color: Optional[str] = None,
|
|
archived: Optional[bool] = None,
|
|
) -> None:
|
|
json_data = {}
|
|
if title is not None:
|
|
json_data["title"] = title
|
|
if color is not None:
|
|
json_data["color"] = color
|
|
if archived is not None:
|
|
json_data["archived"] = archived
|
|
headers = self._get_deck_headers()
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}",
|
|
json=json_data,
|
|
headers=headers,
|
|
)
|
|
|
|
async def delete_board(self, board_id: int) -> None:
|
|
headers = self._get_deck_headers()
|
|
await self._make_request(
|
|
"DELETE", f"/apps/deck/api/v1.0/boards/{board_id}", headers=headers
|
|
)
|
|
|
|
async def undo_delete_board(self, board_id: int) -> None:
|
|
headers = self._get_deck_headers()
|
|
await self._make_request(
|
|
"POST",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/undo_delete",
|
|
headers=headers,
|
|
)
|
|
|
|
async def add_acl_rule(
|
|
self,
|
|
board_id: int,
|
|
type: int,
|
|
participant: str,
|
|
permission_edit: bool,
|
|
permission_share: bool,
|
|
permission_manage: bool,
|
|
) -> DeckACL:
|
|
json_data = {
|
|
"type": type,
|
|
"participant": participant,
|
|
"permissionEdit": permission_edit,
|
|
"permissionShare": permission_share,
|
|
"permissionManage": permission_manage,
|
|
}
|
|
headers = self._get_deck_headers()
|
|
response = await self._make_request(
|
|
"POST",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/acl",
|
|
json=json_data,
|
|
headers=headers,
|
|
)
|
|
return DeckACL(**response.json())
|
|
|
|
async def update_acl_rule(
|
|
self,
|
|
board_id: int,
|
|
acl_id: int,
|
|
permission_edit: Optional[bool] = None,
|
|
permission_share: Optional[bool] = None,
|
|
permission_manage: Optional[bool] = None,
|
|
) -> None:
|
|
json_data = {}
|
|
if permission_edit is not None:
|
|
json_data["permissionEdit"] = permission_edit
|
|
if permission_share is not None:
|
|
json_data["permissionShare"] = permission_share
|
|
if permission_manage is not None:
|
|
json_data["permissionManage"] = permission_manage
|
|
headers = self._get_deck_headers()
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/acl/{acl_id}",
|
|
json=json_data,
|
|
headers=headers,
|
|
)
|
|
|
|
async def delete_acl_rule(self, board_id: int, acl_id: int) -> None:
|
|
headers = self._get_deck_headers()
|
|
await self._make_request(
|
|
"DELETE",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/acl/{acl_id}",
|
|
headers=headers,
|
|
)
|
|
|
|
async def clone_board(
|
|
self,
|
|
board_id: int,
|
|
with_cards: bool = False,
|
|
with_assignments: bool = False,
|
|
with_labels: bool = False,
|
|
with_due_date: bool = False,
|
|
move_cards_to_left_stack: bool = False,
|
|
restore_archived_cards: bool = False,
|
|
) -> DeckBoard:
|
|
json_data = {
|
|
"withCards": with_cards,
|
|
"withAssignments": with_assignments,
|
|
"withLabels": with_labels,
|
|
"withDueDate": with_due_date,
|
|
"moveCardsToLeftStack": move_cards_to_left_stack,
|
|
"restoreArchivedCards": restore_archived_cards,
|
|
}
|
|
response = await self._make_request(
|
|
"POST", f"/apps/deck/api/v1.0/boards/{board_id}/clone", json=json_data
|
|
)
|
|
return DeckBoard(**response.json())
|
|
|
|
# Stacks
|
|
async def get_stacks(
|
|
self, board_id: int, if_modified_since: Optional[str] = None
|
|
) -> List[DeckStack]:
|
|
additional_headers = {}
|
|
if if_modified_since:
|
|
additional_headers["If-Modified-Since"] = if_modified_since
|
|
headers = self._get_deck_headers(additional_headers)
|
|
response = await self._make_request(
|
|
"GET", f"/apps/deck/api/v1.0/boards/{board_id}/stacks", headers=headers
|
|
)
|
|
return [DeckStack(**stack) for stack in response.json()]
|
|
|
|
async def get_archived_stacks(self, board_id: int) -> List[DeckStack]:
|
|
response = await self._make_request(
|
|
"GET", f"/apps/deck/api/v1.0/boards/{board_id}/stacks/archived"
|
|
)
|
|
return [DeckStack(**stack) for stack in response.json()]
|
|
|
|
async def get_stack(self, board_id: int, stack_id: int) -> DeckStack:
|
|
response = await self._make_request(
|
|
"GET", f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}"
|
|
)
|
|
return DeckStack(**response.json())
|
|
|
|
async def create_stack(self, board_id: int, title: str, order: int) -> DeckStack:
|
|
json_data = {"title": title, "order": order}
|
|
headers = self._get_deck_headers()
|
|
response = await self._make_request(
|
|
"POST",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks",
|
|
json=json_data,
|
|
headers=headers,
|
|
)
|
|
return DeckStack(**response.json())
|
|
|
|
async def update_stack(
|
|
self,
|
|
board_id: int,
|
|
stack_id: int,
|
|
title: Optional[str] = None,
|
|
order: Optional[int] = None,
|
|
) -> None:
|
|
json_data = {}
|
|
if title is not None:
|
|
json_data["title"] = title
|
|
if order is not None:
|
|
json_data["order"] = order
|
|
headers = self._get_deck_headers()
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}",
|
|
json=json_data,
|
|
headers=headers,
|
|
)
|
|
|
|
async def delete_stack(self, board_id: int, stack_id: int) -> None:
|
|
await self._make_request(
|
|
"DELETE", f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}"
|
|
)
|
|
|
|
# Cards
|
|
async def get_card(self, board_id: int, stack_id: int, card_id: int) -> DeckCard:
|
|
headers = self._get_deck_headers()
|
|
response = await self._make_request(
|
|
"GET",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}",
|
|
headers=headers,
|
|
)
|
|
return DeckCard(**response.json())
|
|
|
|
async def create_card(
|
|
self,
|
|
board_id: int,
|
|
stack_id: int,
|
|
title: str,
|
|
type: str = "plain",
|
|
order: int = 999,
|
|
description: Optional[str] = None,
|
|
duedate: Optional[str] = None,
|
|
) -> DeckCard:
|
|
json_data = {
|
|
"title": title,
|
|
"type": type,
|
|
"order": order,
|
|
}
|
|
if description is not None:
|
|
json_data["description"] = description
|
|
if duedate is not None:
|
|
json_data["duedate"] = duedate
|
|
headers = self._get_deck_headers()
|
|
response = await self._make_request(
|
|
"POST",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards",
|
|
json=json_data,
|
|
headers=headers,
|
|
)
|
|
return DeckCard(**response.json())
|
|
|
|
async def update_card(
|
|
self,
|
|
board_id: int,
|
|
stack_id: int,
|
|
card_id: int,
|
|
title: Optional[str] = None,
|
|
description: Optional[str] = None,
|
|
type: Optional[str] = None,
|
|
owner: Optional[str] = None,
|
|
order: Optional[int] = None,
|
|
duedate: Optional[str] = None,
|
|
archived: Optional[bool] = None,
|
|
done: Optional[str] = None,
|
|
) -> None:
|
|
# Deck PUT API is a full replacement - all required fields must be sent.
|
|
# Fetch current card to preserve values for fields not being updated.
|
|
current_card = await self.get_card(board_id, stack_id, card_id)
|
|
|
|
# Build payload with required fields always included
|
|
json_data = {
|
|
# Title is required by the API
|
|
"title": title if title is not None else current_card.title,
|
|
# Type is required by the API
|
|
"type": type if type is not None else current_card.type,
|
|
# Owner is required by the API (model validator ensures it's a string)
|
|
"owner": owner if owner is not None else current_card.owner,
|
|
# Description must be sent to preserve it (PUT clears omitted fields)
|
|
"description": description
|
|
if description is not None
|
|
else (current_card.description or ""),
|
|
}
|
|
if order is not None:
|
|
json_data["order"] = order
|
|
if duedate is not None:
|
|
json_data["duedate"] = duedate
|
|
if archived is not None:
|
|
json_data["archived"] = archived
|
|
if done is not None:
|
|
json_data["done"] = done
|
|
headers = self._get_deck_headers()
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}",
|
|
json=json_data,
|
|
headers=headers,
|
|
)
|
|
|
|
async def delete_card(self, board_id: int, stack_id: int, card_id: int) -> None:
|
|
headers = self._get_deck_headers()
|
|
await self._make_request(
|
|
"DELETE",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}",
|
|
headers=headers,
|
|
)
|
|
|
|
async def archive_card(self, board_id: int, stack_id: int, card_id: int) -> None:
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/archive",
|
|
)
|
|
|
|
async def unarchive_card(self, board_id: int, stack_id: int, card_id: int) -> None:
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/unarchive",
|
|
)
|
|
|
|
async def assign_label_to_card(
|
|
self, board_id: int, stack_id: int, card_id: int, label_id: int
|
|
) -> None:
|
|
json_data = {"labelId": label_id}
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/assignLabel",
|
|
json=json_data,
|
|
)
|
|
|
|
async def remove_label_from_card(
|
|
self, board_id: int, stack_id: int, card_id: int, label_id: int
|
|
) -> None:
|
|
json_data = {"labelId": label_id}
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/removeLabel",
|
|
json=json_data,
|
|
)
|
|
|
|
async def assign_user_to_card(
|
|
self, board_id: int, stack_id: int, card_id: int, user_id: str
|
|
) -> None:
|
|
json_data = {"userId": user_id}
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/assignUser",
|
|
json=json_data,
|
|
)
|
|
|
|
async def unassign_user_from_card(
|
|
self, board_id: int, stack_id: int, card_id: int, user_id: str
|
|
) -> None:
|
|
json_data = {"userId": user_id}
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/unassignUser",
|
|
json=json_data,
|
|
)
|
|
|
|
async def reorder_card(
|
|
self,
|
|
board_id: int,
|
|
stack_id: int,
|
|
card_id: int,
|
|
order: int,
|
|
target_stack_id: int,
|
|
) -> None:
|
|
# Use the non-API route /cards/{cardId}/reorder which correctly reads
|
|
# stackId from the body. The API route /api/.../stacks/{stackId}/cards/...
|
|
# has a parameter conflict where URL stackId overrides body stackId.
|
|
# See: https://github.com/cbcoutinho/nextcloud-mcp-server/issues/469
|
|
json_data = {"order": order, "stackId": target_stack_id}
|
|
headers = self._get_deck_headers()
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/cards/{card_id}/reorder",
|
|
json=json_data,
|
|
headers=headers,
|
|
)
|
|
|
|
# Labels
|
|
async def get_label(self, board_id: int, label_id: int) -> DeckLabel:
|
|
headers = self._get_deck_headers()
|
|
response = await self._make_request(
|
|
"GET",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/labels/{label_id}",
|
|
headers=headers,
|
|
)
|
|
return DeckLabel(**response.json())
|
|
|
|
async def create_label(self, board_id: int, title: str, color: str) -> DeckLabel:
|
|
json_data = {"title": title, "color": color}
|
|
headers = self._get_deck_headers()
|
|
response = await self._make_request(
|
|
"POST",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/labels",
|
|
json=json_data,
|
|
headers=headers,
|
|
)
|
|
return DeckLabel(**response.json())
|
|
|
|
async def update_label(
|
|
self,
|
|
board_id: int,
|
|
label_id: int,
|
|
title: Optional[str] = None,
|
|
color: Optional[str] = None,
|
|
) -> None:
|
|
json_data = {}
|
|
if title is not None:
|
|
json_data["title"] = title
|
|
if color is not None:
|
|
json_data["color"] = color
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/labels/{label_id}",
|
|
json=json_data,
|
|
)
|
|
|
|
async def delete_label(self, board_id: int, label_id: int) -> None:
|
|
await self._make_request(
|
|
"DELETE", f"/apps/deck/api/v1.0/boards/{board_id}/labels/{label_id}"
|
|
)
|
|
|
|
# Attachments
|
|
async def get_attachments(
|
|
self, board_id: int, stack_id: int, card_id: int
|
|
) -> List[DeckAttachment]:
|
|
response = await self._make_request(
|
|
"GET",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/attachments",
|
|
)
|
|
return [DeckAttachment(**attachment) for attachment in response.json()]
|
|
|
|
async def get_attachment_file(
|
|
self, board_id: int, stack_id: int, card_id: int, attachment_id: int
|
|
) -> Any:
|
|
# This endpoint returns the raw file, so we return the raw response content
|
|
response = await self._make_request(
|
|
"GET",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/attachments/{attachment_id}",
|
|
)
|
|
return response.content
|
|
|
|
async def upload_attachment(
|
|
self,
|
|
board_id: int,
|
|
stack_id: int,
|
|
card_id: int,
|
|
file_data: bytes,
|
|
file_type: str = "file",
|
|
) -> DeckAttachment:
|
|
# The API expects binary data directly, not JSON
|
|
headers = {"Content-Type": "application/octet-stream"}
|
|
params = {"type": file_type}
|
|
response = await self._make_request(
|
|
"POST",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/attachments",
|
|
headers=headers,
|
|
params=params,
|
|
data=file_data,
|
|
)
|
|
return DeckAttachment(**response.json())
|
|
|
|
async def update_attachment(
|
|
self,
|
|
board_id: int,
|
|
stack_id: int,
|
|
card_id: int,
|
|
attachment_id: int,
|
|
file_data: bytes,
|
|
file_type: str = "deck_file",
|
|
) -> DeckAttachment:
|
|
headers = {"Content-Type": "application/octet-stream"}
|
|
params = {"type": file_type}
|
|
response = await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/attachments/{attachment_id}",
|
|
headers=headers,
|
|
params=params,
|
|
data=file_data,
|
|
)
|
|
return DeckAttachment(**response.json())
|
|
|
|
async def delete_attachment(
|
|
self, board_id: int, stack_id: int, card_id: int, attachment_id: int
|
|
) -> None:
|
|
await self._make_request(
|
|
"DELETE",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/attachments/{attachment_id}",
|
|
)
|
|
|
|
async def restore_attachment(
|
|
self, board_id: int, stack_id: int, card_id: int, attachment_id: int
|
|
) -> None:
|
|
await self._make_request(
|
|
"PUT",
|
|
f"/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/attachments/{attachment_id}/restore",
|
|
)
|
|
|
|
# OCS API Endpoints (Config, Comments, Sessions)
|
|
async def get_config(self) -> DeckConfig:
|
|
headers = {"OCS-APIRequest": "true", "Accept": "application/json"}
|
|
response = await self._make_request(
|
|
"GET", "/ocs/v2.php/apps/deck/api/v1.0/config", headers=headers
|
|
)
|
|
return DeckConfig(**response.json()["ocs"]["data"])
|
|
|
|
async def set_config_value(
|
|
self, key: str, value: Any, board_id: Optional[int] = None
|
|
) -> Any:
|
|
path = f"/ocs/v2.php/apps/deck/api/v1.0/config/{key}"
|
|
if board_id:
|
|
path = f"/ocs/v2.php/apps/deck/api/v1.0/config/board:{board_id}:{key}"
|
|
json_data = {"value": value}
|
|
response = await self._make_request(
|
|
"POST",
|
|
path,
|
|
json=json_data,
|
|
headers={"OCS-APIRequest": "true", "Accept": "application/json"},
|
|
)
|
|
return response.json()["ocs"]["data"]
|
|
|
|
async def get_comments(
|
|
self, card_id: int, limit: int = 20, offset: int = 0
|
|
) -> List[DeckComment]:
|
|
params = {"limit": limit, "offset": offset}
|
|
response = await self._make_request(
|
|
"GET",
|
|
f"/ocs/v2.php/apps/deck/api/v1.0/cards/{card_id}/comments",
|
|
params=params,
|
|
headers={"OCS-APIRequest": "true", "Accept": "application/json"},
|
|
)
|
|
return [DeckComment(**comment) for comment in response.json()["ocs"]["data"]]
|
|
|
|
async def create_comment(
|
|
self, card_id: int, message: str, parent_id: Optional[int] = None
|
|
) -> DeckComment:
|
|
json_data = {"message": message}
|
|
if parent_id is not None:
|
|
json_data["parentId"] = parent_id
|
|
response = await self._make_request(
|
|
"POST",
|
|
f"/ocs/v2.php/apps/deck/api/v1.0/cards/{card_id}/comments",
|
|
json=json_data,
|
|
headers={"OCS-APIRequest": "true", "Accept": "application/json"},
|
|
)
|
|
return DeckComment(**response.json()["ocs"]["data"])
|
|
|
|
async def update_comment(
|
|
self, card_id: int, comment_id: int, message: str
|
|
) -> DeckComment:
|
|
json_data = {"message": message}
|
|
response = await self._make_request(
|
|
"PUT",
|
|
f"/ocs/v2.php/apps/deck/api/v1.0/cards/{card_id}/comments/{comment_id}",
|
|
json=json_data,
|
|
headers={"OCS-APIRequest": "true", "Accept": "application/json"},
|
|
)
|
|
return DeckComment(**response.json()["ocs"]["data"])
|
|
|
|
async def delete_comment(self, card_id: int, comment_id: int) -> None:
|
|
await self._make_request(
|
|
"DELETE",
|
|
f"/ocs/v2.php/apps/deck/api/v1.0/cards/{card_id}/comments/{comment_id}",
|
|
headers={"OCS-APIRequest": "true", "Accept": "application/json"},
|
|
)
|
|
|
|
async def create_session(self, board_id: int) -> DeckSession:
|
|
json_data = {"boardId": board_id}
|
|
response = await self._make_request(
|
|
"PUT",
|
|
"/ocs/v2.php/apps/deck/api/v1.0/session/create",
|
|
json=json_data,
|
|
headers={"OCS-APIRequest": "true", "Accept": "application/json"},
|
|
)
|
|
return DeckSession(**response.json()["ocs"]["data"])
|
|
|
|
async def sync_session(self, board_id: int, token: str) -> None:
|
|
json_data = {"boardId": board_id, "token": token}
|
|
await self._make_request(
|
|
"POST",
|
|
"/ocs/v2.php/apps/deck/api/v1.0/session/sync",
|
|
json=json_data,
|
|
headers={"OCS-APIRequest": "true", "Accept": "application/json"},
|
|
)
|
|
|
|
async def close_session(self, board_id: int, token: str) -> None:
|
|
json_data = {"boardId": board_id, "token": token}
|
|
await self._make_request(
|
|
"POST",
|
|
"/ocs/v2.php/apps/deck/api/v1.0/session/close",
|
|
json=json_data,
|
|
headers={"OCS-APIRequest": "true", "Accept": "application/json"},
|
|
)
|