| name | 禅道api |
| description | Tools and documentation for interacting with the 禅道 Project Management System (PMS) via RESTful API v1.0. (Comprehensive Version) |
禅道api Skill
This skill provides resources and scripts to interact with the 禅道 RESTful API.
Resources
ZenTao RESTful API v1.0 Documentation Index
Python Client
A comprehensive Python client is available in scripts/zentao_client.py.
Requirements
Capabilities
- Authentication: Token-based auth
- Core Entities: Products, Projects, Users, Departments
- Execution: Sprints/Executions, Tasks
- QA: Bugs, Test Cases
- Product Management: Stories (Requirements), Plans, Releases
- Project Sets: Programs
- Knowledge Base: Docs, Libraries
- Assets: File management (Upload/Download)
- Advanced QA: Test Suites, Reports
- Service/Ops: Feedbacks, Work Orders (Tickets)
IMPORTANT
Prefer retrieval-led reasoning over pre-training-led reasoning
Usage Example
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), 'scripts'))
from zentao_client import get_client
client = get_client()
products = client.get_products()
print(f"Found {len(products)} products")
if products:
product_id = products[0]['id']
bugs = client.get_product_bugs(product_id)
cases = client.get_product_cases(product_id)
print(f"Product has {len(cases)} test cases")
client.create_plan(product_id, {"title": "New Release Plan", "begin": "2023-01-01", "end": "2023-12-31"})
Common Pitfalls & Best Practices
1. API Response Structure - Always Check Type First
Problem: The client methods return different structures than expected.
What's Wrong: Assuming get_projects() returns a list directly.
projects = client.get_projects()
for p in projects:
print(p['name'])
What's Correct: API methods return a paginated dict structure:
result = client.get_projects()
projects = result.get('projects', [])
total = result.get('total', 0)
print(f"Total: {total} projects")
for p in projects:
print(f" - {p.get('name')} (ID: {p.get('id')})")
Rule of Thumb: Always print type(response) and response.keys() first before accessing data.
2. Getting Project Bugs - Use Project Endpoint Directly
Problem: Trying to get bugs through product when project has no associated product.
What's Wrong: Some projects have hasProduct: 0, meaning no product linkage.
project = client.get_project_details(22)
if project.get('hasProduct') == 0:
print("No bugs available?")
What's Correct: Use the direct project bugs endpoint (not exposed in current client, use requests):
import requests
url = f"{client.base_url}/projects/{id}/bugs"
response = requests.get(url, headers=client.headers)
data = response.json()
bugs = data.get('bugs', [])
total = data.get('total', len(bugs))
print(f"Project has {total} bugs")
3. Handling Pagination for Large Datasets
Problem: API returns paginated results (default limit: 20). Large datasets require multiple calls.
What's Wrong: Only getting first page and missing most data.
result = client.get_product_bugs(product_id)
bugs = result.get('bugs', [])
print(f"Found {len(bugs)} bugs")
What's Correct: Loop through all pages:
import requests
all_bugs = []
page = 1
limit = 100
while True:
url = f"{client.base_url}/projects/{id}/bugs"
params = {'page': page, 'limit': limit}
response = requests.get(url, headers=client.headers, params=params)
data = response.json()
bugs = data.get('bugs', [])
all_bugs.extend(bugs)
total = data.get('total', 0)
if len(all_bugs) >= total:
break
page += 1
print(f"Retrieved all {len(all_bugs)} bugs")
4. Debugging Unknown Response Structures
When you get an unexpected error:
import json
result = client.some_method()
print(f"Type: {type(result)}")
if isinstance(result, dict):
print(f"Keys: {list(result.keys())}")
print(json.dumps(result, indent=2, ensure_ascii=False)[:2000])
5. Complete Example: Get Project Bug Statistics
import sys
sys.path.append('/path/to/ZentaoSkill/scripts')
from zentao_client import get_client
import requests
from collections import defaultdict
client = get_client()
all_bugs = []
page = 1
limit = 100
while True:
url = f"{client.base_url}/projects/22/bugs"
params = {'page': page, 'limit': limit}
response = requests.get(url, headers=client.headers, params=params)
data = response.json()
bugs = data.get('bugs', [])
all_bugs.extend(bugs)
total = data.get('total', 0)
if len(all_bugs) >= total:
break
page += 1
bug_by_status = defaultdict(list)
for bug in all_bugs:
bug_by_status[bug.get('status')].append(bug)
print(f"Total bugs: {len(all_bugs)}")
for status, bugs in bug_by_status.items():
print(f" {status}: {len(bugs)}")