chore: add comprehensive API wrapper documentation

- Add docs/ directory with 6 comprehensive documentation files:
  * INDEX.md - Main navigation and documentation index
  * GETTING_STARTED.md - Setup and configuration guide
  * API_REFERENCE.md - Complete API documentation
  * WRAPPERS.md - High-level wrappers guide
  * CUSTOM_REQUESTS.md - Advanced usage patterns
  * TROUBLESHOOTING.md - Problem-solving guide

- Update README.md with enhanced project information

- Update development.md with expanded documentation context

Extends previous session's documentation work with complete
developer and user-facing documentation.

Closes previous documentation issue.
This commit is contained in:
BarnacleBoy 2026-04-18 01:44:49 +00:00
parent 14346ed02e
commit fd1a1566e9
8 changed files with 4909 additions and 61 deletions

847
docs/API_REFERENCE.md Normal file
View file

@ -0,0 +1,847 @@
# TrustCafé API Reference
Complete reference guide for all available jobs in the TrustCafé API wrapper.
## Table of Contents
- [Preparation](#preparation)
- [Authentication Jobs](#authentication-jobs)
- [Post Jobs](#post-jobs)
- [Comment Jobs](#comment-jobs)
- [UserProfile Jobs](#userprofile-jobs)
- [Follow Jobs](#follow-jobs)
- [Vote Jobs](#vote-jobs)
- [Reaction Jobs](#reaction-jobs)
- [Notification Jobs](#notification-jobs)
- [Trust Jobs](#trust-jobs)
- [Branch Jobs](#branch-jobs)
- [Feed Jobs](#feed-jobs)
- [Block Jobs](#block-jobs)
- [Mute Jobs](#mute-jobs)
- [Error Handling](#error-handling)
## Preparation
Before using jobs, initialize the API client:
```python
import trustcafeapiwrapper
import os
API = trustcafeapiwrapper.APIClient(
client_id=os.getenv("TRUSTCAFE_CLIENT_ID"),
client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"),
env="alpha", # or "production"
debug=False
)
# Authenticate
API.handle_token()
```
## Authentication Jobs
### sign_in()
Obtains a new access token using OAuth2 client-credentials flow.
```python
token_data = API.sign_in()
```
**Returns:**
```python
{
"access_token": "string (3-month duration)",
"access_token_timeout": "integer (Unix timestamp)"
}
```
**Parameters:** None
**Error Cases:**
- Invalid client credentials
- Incorrect environment
- Network errors
---
## Post Jobs
Fetch, create, update, and manage posts.
### post.get()
Fetches a specific post by ID or slug.
```python
post_data = API.run_job('post.get', "post-slug-or-id")
```
**Parameters:**
- `post_slug` (str): The post identifier (slug or ID)
**Returns:** dict - Post data
**Example:**
```python
post = API.run_job('post.get', "my-first-post")
print(post.get('postText'))
```
---
### post.create()
Creates a new post.
```python
post = API.run_job('post.create', payload)
```
**Parameters:**
- `postText` (str): The text content of the post
- `parent` (dict, required): Parent object containing pk and sk
- `pk` (str): Partition key
- `sk` (str): Sort key
- `blurLabel` (str, optional): Label for blurring content
- `cardUrl` (str, optional): URL for link preview card
- `collaborative` (bool, optional): Enable collaborative editing (default: False)
**Returns:** dict - Created post data
**Example:**
```python
post = API.run_job('post.create', {
"postText": "This is a new post",
"parent": {
"pk": "maintrunk#maintrunk",
"sk": "maintrunk#maintrunk"
}
})
```
---
### post.update()
Updates an existing post.
```python
updated_post = API.run_job('post.update', payload)
```
**Parameters:**
- `pk` (str): Post partition key
- `sk` (str): Post sort key
- `newPostText` (str, optional): New post content
- Other optional fields to update
**Returns:** dict - Updated post data
**Example:**
```python
post = API.run_job('post.update', {
"pk": "post-id-123",
"sk": "post-id-123",
"newPostText": "Updated post content"
})
```
*Note: Full field list requires review of API documentation.*
---
### post.listall()
Lists all posts.
```python
posts = API.run_job('post.listall')
```
**Parameters:** None
**Returns:** dict - List of all posts
**Example:**
```python
all_posts = API.run_job('post.listall')
print(f"Total posts: {len(all_posts.get('Items', []))}")
```
---
### post.listpublic()
Lists public posts only.
```python
public_posts = API.run_job('post.listpublic')
```
**Parameters:** None
**Returns:** dict - List of public posts
**Example:**
```python
public_posts = API.run_job('post.listpublic')
for post in public_posts.get('Items', []):
print(post.get('pk'))
```
---
### post.listbybranch()
Lists posts in a specific branch.
```python
branch_posts = API.run_job('post.listbybranch', branch_name)
```
**Parameters:**
- `branchName` (str): The branch identifier
**Returns:** dict - List of posts in the branch
**Example:**
```python
music_posts = API.run_job('post.listbybranch', "music")
```
---
### post.listbyuserprofile()
Lists posts by a specific user profile.
```python
user_posts = API.run_job('post.listbyuserprofile', username)
```
**Parameters:**
- `username` (str): The username of the user
**Returns:** dict - List of posts by user
**Example:**
```python
journals = API.run_job('post.listbyuserprofile', "philosopher-jon")
```
---
### post.listremoved()
Lists removed/deleted posts.
```python
removed = API.run_job('post.listremoved', post_id)
```
**Parameters:**
- `postId` (str): The post identifier
**Returns:** dict - Removed post data
**Example:**
```python
removed_post = API.run_job('post.listremoved', "deleted-post-id")
```
---
## Comment Jobs
Manage comments on posts.
### comment.create()
Creates a new comment.
```python
comment = API.run_job('comment.create', payload)
```
**Parameters:**
- `commentText` (str): Comment content
- `parent` (dict, required): Parent object
- `pk` (str): Partition key
- `sk` (str): Sort key
- Other optional fields as API requires
**Returns:** dict - Created comment data
**Example:**
```python
comment = API.run_job('comment.create', {
"commentText": "Great post!",
"parent": {
"pk": "post-id",
"sk": "post-id"
}
})
```
---
### comment.listtbypostid()
Lists comments for a specific post.
```python
comments = API.run_job('comment.listtbypostid', post_id)
```
**Parameters:**
- `postId` (str): The post ID
**Returns:** dict - List of comments
**Example:**
```python
comments = API.run_job('comment.listtbypostid', "current-post-id")
for comment in comments.get('Items', []):
print(comment.get('commentText'))
```
---
## UserProfile Jobs
Retrieve user profile information.
### userprofile.get()
Retrieves a user profile.
```python
profile = API.run_job('userprofile.get', username)
```
**Parameters:**
- `username` (str): The username to retrieve
**Returns:** dict - User profile data
**Example:**
```python
profile = API.run_job('userprofile.get', "philosopher-jon")
print(profile.get('username'))
print(profile.get('trustScore'))
```
---
## Follow Jobs
Manage follow relationships.
### follow.follow()
Follows a user.
```python
result = API.run_job('follow.follow', username)
```
**Parameters:**
- `username` (str): The username to follow
**Returns:** dict - Follow status
**Example:**
```python
followed = API.run_job('follow.follow', "another-user")
print(f"Follow status: {followed}")
```
---
## Vote Jobs
Cast votes on posts/content.
### vote.votecast()
Casts a vote on a post.
```python
vote = API.run_job('vote.votecast', payload)
```
**Parameters:**
- `pk` (str): Object partition key
- `sk` (str): Object sort key
- `voteType` (str): Vote type - "up" or "down"
**Returns:** dict - Vote status
**Example:**
```python
voted = API.run_job('vote.votecast', {
"pk": "post-id",
"sk": "post-id",
"voteType": "up"
})
```
---
## Reaction Jobs
Manage reactions to posts, comments, and other content.
### reaction.reacttosomething()
React to something (post, comment, etc.).
```python
reaction = API.run_job('reaction.reacttosomething', payload)
```
**Parameters:**
- `objectPK` (str): Object partition key
- `objectSK` (str): Object sort key
- `objectType` (str): Type of object ("post", "comment", etc.)
- `reactionType` (str): Reaction type (depends on API)
**Returns:** dict - Reaction status
**Example:**
```python
reacted = API.run_job('reaction.reacttosomething', {
"objectPK": "post-id",
"objectSK": "post-id",
"objectType": "post",
"reactionType": "like"
})
```
---
### reaction.getbyparent()
Get reactions by parent object.
```python
reactions = API.run_job('reaction.getbyparent', parent_id)
```
**Parameters:**
- `objectId` (str): Parent object ID
**Returns:** dict - List of reactions
---
### reaction.listbyparent()
List all reactions for a parent object.
```python
reactions = API.run_job('reaction.listbyparent', parent_id)
```
**Parameters:**
- `parentPK` (str): Parent partition key
- `parentSK` (str): Parent sort key
**Returns:** dict - List of reactions
---
## Notification Jobs
Manage user notifications.
### notification.listnotifications()
Lists all notifications.
```python
notifications = API.run_job('notification.listnotifications')
```
**Parameters:** None
**Returns:** dict - List of notifications
**Example:**
```python
notifications = API.run_job('notification.listnotifications')
for notif in notifications.get('Items', []):
print(f"{notif.get('notificationType')}: {notif.get('text')}")
```
---
### notification.markallasread()
Marks all notifications as read.
```python
API.run_job('notification.markallasread')
```
**Parameters:** None
**Returns:** dict - Mark as read status
**Example:**
```python
API.run_job('notification.markallasread')
```
---
## Trust Jobs
Manage trust relationships.
### trust.createorupdate()
Create or update a trust relationship.
```python
trust = API.run_job('trust.createorupdate', payload)
```
**Parameters:**
- `pk` (str): Trust identifier (partition key)
- `sk` (str): Trust identifier (sort key)
- `trustType` (str): Trust type ("positive", "negative", etc.)
- Other optional fields
**Returns:** dict - Trust relationship status
**Example:**
```python
trust = API.run_job('trust.createorupdate', {
"pk": "trust-id-123",
"sk": "trust-id-123",
"trustType": "positive"
})
```
---
### trust.listbyusersinit()
Lists trust relationships initialized by a user.
```python
trusts = API.run_job('trust.listbyusersinit', username)
```
**Parameters:**
- `username` (str): The user whose trust relationships to list
**Returns:** dict - List of trust relationships
**Example:**
```python
my_trusts = API.run_job('trust.listbyusersinit', "your-username")
```
---
### trust.listbyuserhas()
Lists trust relationships where a user was the target.
```python
trusts = API.run_job('trust.listbyuserhas', username)
```
**Parameters:**
- `username` (str): The user who was targeted
**Returns:** dict - List of trust relationships
---
## Branch Jobs
Manage branches (platform branches or sub-wikis).
### branch.get()
Get a specific branch.
```python
branch = API.run_job('branch.get', branch_name)
```
**Parameters:**
- `branchName` (str): The branch identifier
**Returns:** dict - Branch data
**Example:**
```python
branch = API.run_job('branch.get', "main")
print(branch.get('branchName'))
```
---
### branch.listbyname()
List branches by name prefix.
```python
branches = API.run_job('branch.listbyname', prefix)
```
**Parameters:**
- `prefix` (str): String prefix for branch names
**Returns:** dict - List of matching branches
**Example:**
```python
music_branches = API.run_job('branch.listbyname', "music")
```
---
## Feed Jobs
Get content feeds from TrustCafé.
### feed.cafefeed()
Get the café-wide feed.
```python
feed = API.run_job('feed.cafefeed')
```
**Parameters:** None
**Returns:** dict - Café feed content
**Example:**
```python
cafefeed = API.run_job('feed.cafefeed')
print(f"Feed items: {len(cafefeed.get('Items', []))}")
```
---
### feed.following()
Get feed for users you follow.
```python
feed = API.run_job('feed.following')
```
**Parameters:** None
**Returns:** dict - Following feed content
**Example:**
```python
following_feed = API.run_job('feed.following')
print(f"Following feed items: {len(following_feed.get('Items', []))}")
```
---
## Block Jobs
Block users or content.
### block.[notimplemented]
Block functionality is defined in the backend but not yet implemented in the wrapper.
**Status:** TODO
**Implementation needed:**
- Job function creation
- Payload structure definition
- Error handling
---
## Mute Jobs
Mute users or content.
### mute.[notimplemented]
Mute functionality is pending implementation.
**Status:** TODO
**Implementation needed:**
- Job function creation
- Payload structure definition
- Error handling
---
## Error Handling
### Common Error Cases
**Authentication Errors**
```python
try:
profile = API.run_job('userprofile.get', "username")
except Exception as e:
print(f"Authentication required: {e}")
API.handle_token()
profile = API.run_job('userprofile.get', "username")
```
**Invalid Parameters**
```python
try:
post = API.run_job('post.create', {
"postText": "Test",
# Missing required 'parent' field
})
except Exception as e:
print(f"Validation error: {e}")
```
**Network Errors**
```python
try:
result = API.make_request("GET", "content", "some/path")
except Exception as e:
print(f"Network error: {e}")
# Wait and retry
import time
time.sleep(1)
result = API.make_request("GET", "content", "some/path")
```
### Error Response Structure
API errors typically return:
```json
{
"error": "string",
"message": "string",
"details": {} // Optional details
}
```
---
## Response Structure
Most API responses follow a similar structure:
```python
{
"Items": [ // For list operations
{ /* item data */ }
],
"Count": int, // Number of items
"LastEvaluatedKey": { /* pagination key */ }, // For pagination
"ResponseMetadata": { /* AWS SDK metadata */ }
}
```
### Pagination
For large result sets, results may include pagination:
```python
def get_all_posts():
posts = []
paginator = None
while True:
response = API.run_job('post.listpublic', params)
posts.extend(response.get('Items', []))
has_more = 'LastEvaluatedKey' in response
if not has_more:
break
# Set params for next page
params = {
'ExclusiveStartKey': response['LastEvaluatedKey']
}
return posts
```
---
## Best Practices
### 1. Use Type Hints
```python
# Good
from typing import Optional, Dict, Any
def get_post(api: trustcafeapiwrapper.APIClient, post_id: str) -> Dict[str, Any]:
return api.run_job('post.get', post_id)
# Bad
def get_post(api, post_id):
return api.run_job('post.get', post_id)
```
### 2. Validate Responses
```python
post = API.run_job('post.get', "post-id")
if not post or 'Items' not in post:
raise ValueError("Invalid post data received")
item = post['Items'][0]
if not item:
raise ValueError("Post item is empty")
```
### 3. Error Logging
```python
import logging
logger = logging.getLogger(__name__)
try:
profile = API.run_job('userprofile.get', username)
logger.info(f"Successfully retrieved profile for {username}")
except Exception as e:
logger.error(f"Failed to get profile for {username}: {e}")
raise
```
### 4. Timeout Configurations
The API client has a default timeout of 20 seconds:
```python
# The make_request method accepts a timeout parameter
# However, current implementation doesn't expose it directly
# Consider updating the APIClient to accept custom timeouts
```
### 5. Rate Limiting
Implement rate limiting for production use (see main README for example).
---
## Next Steps
- [Wrappers Guide](wrappers.md) - High-level wrappers for common tasks
- [Custom Requests Guide](custom_requests.md) - Making advanced API calls
- [Troubleshooting Guide](troubleshooting.md) - Common issues and solutions

767
docs/CUSTOM_REQUESTS.md Normal file
View file

@ -0,0 +1,767 @@
# Custom Requests Guide
This guide covers making advanced API calls using the TrustCafé API wrapper's direct request functionality. This is for when jobs or wrappers don't provide the level of control you need.
## Table of Contents
- [Overview](#overview)
- [Basic Usage](#basic-usage)
- [Authentication](#authentication)
- [Common Patterns](#common-patterns)
- [Advanced Usage](#advanced-usage)
- [Pagination](#pagination)
- [Error Handling](#error-handling)
- [Examples](#examples)
## Overview
### What is make_request?
`make_request` is the core method of the `APIClient` class that allows you to make raw API calls to TrustCafé endpoints directly. It bypasses jobs and wrappers, giving you complete control over:
- HTTP methods (GET, POST, PUT, DELETE)
- API endpoints
- Request paths
- Request bodies
- Query parameters
- Authentication
### When to Use Custom Requests
**Use `make_request` when:**
- No wrapper or job exists for your operation
- You need a custom HTTP endpoint
- Building complex queries programmatically
- Using specialized API features
- Need maximum flexibility
**Don't use when:**
- Standard operation is covered by wrappers (too much boilerplate)
- Simpler option exists (jobs/wrappers)
- You're a beginner (wrappers/jobs are easier)
## Basic Usage
### Fundamentals
```python
import trustcafeapiwrapper
import os
API = trustcafeapiwrapper.APIClient(
client_id=os.getenv("CLIENT_ID"),
client_secret=os.getenv("CLIENT_SECRET"),
env="alpha",
debug=True
)
API.handle_token()
```
### The method signature
```python
response = API.make_request(
method="GET",
endpoint="content",
path="post/some-id",
data=None,
authenticate=True,
query_params=None
)
```
### Parameters Explained
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `method` | str | ✅ Yes | - | HTTP method: "GET", "POST", "PUT", "DELETE" |
| `endpoint` | str | ✅ Yes | - | API service: "content", "auth", "audrey", etc. |
| `path` | str | ✅ Yes | - | API path after endpoint |
| `data` | dict | ❌ No | `None` | Request body for POST/PUT/DELETE |
| `authenticate` | bool | ❌ No | `True` | Include authentication token |
| `query_params` | dict | ❌ No | `None` | Query parameters for GET requests |
### Simple GET Request
```python
# Get a post
response = API.make_request(
"GET",
"content",
"post/my-post-id"
)
print(response)
```
### POST Request
```python
# Create a new resource
response = API.make_request(
"POST",
"content",
"post/new",
data={
"postText": "My custom post",
"parent": {
"pk": "maintrunk#maintrunk",
"sk": "maintrunk#maintrunk"
}
}
)
print(response)
```
### PUT Request
```python
# Update an existing resource
response = API.make_request(
"PUT",
"content",
"post/my-post-id",
data={
"pk": "my-post-id",
"sk": "my-post-id",
"newPostText": "Updated content"
}
)
print(response)
```
### DELETE Request
```python
# Delete a resource
response = API.make_request(
"DELETE",
"content",
"post/my-post-id"
)
print(response)
```
## Authentication
### By default, authentication is automatic:
```python
# This includes the access token in the Authorization header
response = API.make_request(
"GET",
"content",
"post/my-post-id",
authenticate=True # Default
)
```
### Making guest requests (no authentication):
```python
# Useful for public endpoints
response = API.make_request(
"GET",
"content",
"post/public-post",
authenticate=False # No token needed
)
```
### Manual token injection (advanced):
```python
# Sometimes you may need to bypass the token management
response = API.make_request(
"GET",
"content",
"post/some-path",
headers={
"Authorization": "Bearer your-token-manually-set"
}
)
```
## Common Patterns
### Pagination
TrustCafé responses may include pagination for large result sets:
```python
def get_all_posts(limit=100):
"""Fetch all posts with pagination"""
def fetch_page(start_key=None):
"""Fetch a single page of posts"""
params = {"Limit": limit}
if start_key:
params["ExclusiveStartKey"] = start_key
response = API.make_request(
"GET",
"content",
"post/listall",
query_params=params,
authenticate=True
)
return response
all_posts = []
start_key = None
while True:
page = fetch_page(start_key)
items = page.get('Items', [])
all_posts.extend(items)
# Check if there are more pages
has_more = 'LastEvaluatedKey' in page
if not has_more or len(items) == 0:
break
# Set start key for next page
start_key = page['LastEvaluatedKey']
return all_posts
# Usage
API.handle_token()
all_posts = get_all_posts(limit=50)
print(f"Total posts: {len(all_posts)}")
```
### Filtering with Query Parameters
```python
# Get posts created after a specific date
response = API.make_request(
"GET",
"content",
"post/listpublic",
query_params={
"creationDateAfter": "2024-01-01T00:00:00Z"
},
authenticate=True
)
# Get posts in a specific branch
response = API.make_request(
"GET",
"content",
"post/ref-subwiki/music",
query_params={
"maxResults": 20,
"showDeleted": False
},
authenticate=True
)
```
### Building Dynamic Paths
```python
def get_post_by_slug(slug):
"""Get a post by slugified ID"""
response = API.make_request(
"GET",
"content",
f"post/id/{slug}",
authenticate=True
)
return response
# Usage
post = get_post_by_slug("my-custom-slug-123")
```
```python
def get_comments_for_post(post_id):
"""Get comments for a specific post"""
response = API.make_request(
"GET",
"content",
f"comment/bypostid/{post_id}",
authenticate=True
)
return response
```
## Advanced Usage
### Multiple Endpoints
TrustCafé has multiple API service endpoints:
```python
# Content API
content_response = API.make_request(
"GET",
"content",
"post/listall",
query_params={"Limit": 10}
)
# Auth API (for tokens)
auth_response = API.make_request(
"POST",
"auth",
"token",
data={
"client_id": "your-id",
"client_secret": "your-secret",
"grant_type": "client_credentials"
},
authenticate=False
)
# Audrey API (various services)
audrey_response = API.make_request(
"GET",
"audrey",
"some/endpoint",
authenticate=True
)
```
### Complex Payloads
```python
# Building complex payloads for updates
def update_post_completion(post_id, completion_status):
"""Update post with new completion status"""
response = API.make_request(
"PUT",
"content",
f"post/{post_id}",
data={
"pk": post_id,
"sk": post_id,
"postText": "Updated post with completion status",
"completion": completion_status
},
authenticate=True
)
return response
# Usage
update_post_completion(
"current-post-pk",
{
"isComplete": True,
"completedBy": "username",
"completedAt": "2024-01-01"
}
)
```
### Batch Operations
```python
def batch_create_posts(posts_data):
"""Create multiple posts in a single batch"""
# Build batch request
batch_data = {
"Post": [
{
"postText": post["text"],
"parent": {
"pk": post["parent_pk"],
"sk": post["parent_sk"]
}
}
for post in posts_data
]
}
response = API.make_request(
"POST",
"content",
"post/batch-create",
data=batch_data,
authenticate=True
)
return response
```
### Error Handling for Custom Requests
```python
def safe_api_request(method, endpoint, path, data=None):
"""
Safely execute an API request with proper error handling.
"""
try:
response = API.make_request(
method,
endpoint,
path,
data=data,
authenticate=True
)
# Check for unexpected errors
if 'error' in response:
raise Exception(f"API Error: {response['error']}")
return response
except Exception as e:
print(f"Request failed: {e}")
# Handle error: retry, notify, log, etc.
raise
```
## Pagination
### Pagination Strategy
When handling paginated results, always check for pagination metadata:
```python
def fetch_all_content():
"""Fetch all content with pagination handling"""
all_content = []
next_token = None
while True:
params = {
"Limit": 100
}
if next_token:
params["ExclusiveStartKey"] = next_token
try:
response = API.make_request(
"GET",
"content",
"content/list",
query_params=params,
authenticate=True
)
except Exception as e:
print(f"Error fetching page: {e}")
break
# Process current page
items = response.get('Items', [])
all_content.extend(items)
# Check for next page
has_more = 'LastEvaluatedKey' in response
if not has_more or len(items) < 100:
print(f"Total items retrieved: {len(all_content)}")
break
next_token = response['LastEvaluatedKey']
print(f"Fetching next page (token: {next_token[:10]}...)")
return all_content
```
### Processing Large Datasets
```python
def process_content_in_batches(batch_size=100):
"""Process content in manageable batches"""
batch_number = 0
while True:
batch_number += 1
print(f"Processing batch {batch_number}...")
try:
response = API.make_request(
"GET",
"content",
"content/list",
query_params={
"Limit": batch_size
},
authenticate=True
)
items = response.get('Items', [])
if not items:
print("No more items to process")
break
# Process each item in the batch
for item in items:
# Your processing logic here
process_item(item)
# Check for more items
has_more = 'LastEvaluatedKey' in response
if not has_more:
break
except Exception as e:
print(f"Error in batch {batch_number}: {e}")
# Retry logic could go here
break
print(f"Completed processing {batch_number} batches")
```
## Error Handling
### Common Error Scenarios
```python
def handle_api_request(method, endpoint, path, data=None):
"""Robust API request handler"""
try:
# Make request
response = API.make_request(
method,
endpoint,
path,
data=data,
authenticate=True
)
# Check for API-level errors
if 'error' in response:
raise APIError(
endpoint=endpoint,
path=path,
api_error=response['error'],
message=response.get('message', '')
)
return response
except APIError as e:
print(f"API Error: {e}")
# Retry logic or fallback could go here
raise
except Exception as e:
print(f"Unexpected error: {e}")
# Log the error
log_error(e)
# Could retry or use fallback
raise
```
### Retry Logic
```python
import time
import logging
logger = logging.getLogger(__name__)
def retry_on_failure(request_func, max_retries=3, retry_delay=1):
"""
Retry a request function on failure.
Args:
request_func: Function that makes the API request
max_retries: Maximum number of retry attempts
retry_delay: Seconds to wait between retries
Returns:
The successful response
"""
for attempt in range(max_retries):
try:
return request_func()
except Exception as e:
logger.warning(
f"Request failed (attempt {attempt + 1}/{max_retries}): {e}"
)
if attempt < max_retries - 1:
logger.info(f"Retrying in {retry_delay} seconds...")
time.sleep(retry_delay)
else:
logger.error("Max retries reached. Giving up.")
raise
# This line should never be reached due to raise above
raise Exception("Unexpected state in retry function")
```
## Examples
### Example 1: Complex Query
```python
def search_posts_advanced(search_term, branch="all", limit=50):
"""
Advanced search for posts with filtering.
Note: This is an EXAMPLE - you'll need to adjust to match
TrustCafé's actual search/query API structure.
"""
# Build search query
query_data = {
"search": {
"term": search_term,
"filters": {
"branches": [branch] if branch != "all" else None
}
}
}
response = API.make_request(
"POST",
"content",
"search",
data=query_data,
authenticate=True
)
return response
```
### Example 2: Custom Analytics Query
```python
def get_post_engagement_metrics(post_id):
"""
Get engagement metrics for a post.
Note: This requires mapping to TrustCafé's actual endpoint
and data structure for engagement metrics.
"""
response = API.make_request(
"GET",
"content",
f"post/{post_id}/analytics",
query_params={
"metrics": [
"views",
"likes",
"comments",
"shares"
],
"timeRange": "7d"
},
authenticate=True
)
return response
```
### Example 3: Multi-step Transaction
```python
def create_post_and_related_content(post_text, related_items):
"""
Create a post and related content in a transaction-like operation.
"""
# Step 1: Create the main post
post_response = API.make_request(
"POST",
"content",
"post",
data={
"postText": post_text,
"parent": {
"pk": "maintrunk#maintrunk",
"sk": "maintrunk#maintrunk"
}
},
authenticate=True
)
post_id = post_response.get('pk')
# Step 2: Create related content (if applicable)
if related_items:
for item in related_items:
API.make_request(
"POST",
"content",
"related-item",
data={
"postId": post_id,
"externalId": item['id'],
"type": item['type'],
"url": item['url']
},
authenticate=True
)
return post_response
```
### Example 4: Conditional Operations
```python
def conditional_update_post(post_id, updates, condition=None):
"""
Update a post only if a condition is met.
Note: This maps to CousinDB/GSI-style conditional updates.
Implementation depends on TrustCafé's specific API.
"""
update_data = {
"pk": post_id,
"sk": post_id,
**updates
}
if condition:
update_data["condition"] = {
"expression": condition["expression"],
"values": condition["values"]
}
response = API.make_request(
"PUT",
"content",
f"post/{post_id}",
data=update_data,
authenticate=True
)
return response
```
---
## Summary
`make_request` provides maximum flexibility for making API calls, but with greater complexity comes the need for careful:
- **Error handling** - Always wrap in try/except
- **Logging** - Track request/response for debugging
- **Testing** - Verify your custom endpoints
- **Documentation** - Document any custom patterns you discover
**For most use cases, wrappers and jobs provide the right balance of simplicity and control. Use `make_request` when standard wrappers don't fit your needs.**
---
## Next Steps
- [API Reference](api_reference.md) - Complete list of jobs
- [Wrappers Guide](wrappers.md) - High-level wrappers for common operations
- [Examples](../README.md#examples) - Real-world usage examples
- [Troubleshooting](troubleshooting.md) - Common issues and solutions

469
docs/GETTING_STARTED.md Normal file
View file

@ -0,0 +1,469 @@
# TrustCafé API Wrapper - Getting Started Guide
This guide will help you quickly set up the TrustCafé API wrapper and start making API calls.
## Table of Contents
1. [Installation](#installation)
2. [API Credentials Setup](#api-credentials-setup)
3. [Environment Configuration](#environment-configuration)
4. [First API Call](#first-api-call)
5. [Token Management](#token-management)
6. [Best Practices](#best-practices)
7. [Next Steps](#next-steps)
## Installation
### Prerequisites
Ensure you have:
- Python 3.11 or higher installed
- pip or uv package manager
### Installing via pip
```bash
pip install trustcafeapiwrapper
```
### Installing via uv (Recommended)
```bash
uv add trustcafeapiwrapper
```
### Installation Verification
```python
import trustcafeapiwrapper
print(trustcafeapiwrapper.__version__) # Should print the version
```
## API Credentials Setup
### Step 1: Generate Client Credentials
1. Visit the TrustCafé API Access page:
- **Production**: [https://trustcafe.io/en/myaccount/apiaccess](https://trustcafe.io/en/myaccount/apiaccess)
- **Alpha/Development**: [https://alpha.wts2.net/en/myaccount/apiaccess](https://alpha.wts2.net/en/myaccount/apiaccess)
2. Click "Create new client credentials key pair"
3. Select your desired scopes:
- **Full Access**: Select all scopes for maximum flexibility
- **Limited Access**: Choose specific scopes if you only need certain features
4. Click "Save"
5. Record your credentials:
- **Client ID**: Your unique application identifier
- **Client Secret**: Your application secret (save this securely!)
### Step 2: Store Credentials Securely
**Option A: Environment Variables (Recommended)**
Use environment variables for production code:
```bash
# .env file (add to .gitignore)
export TRUSTCAFE_CLIENT_ID="your-client-id-here"
export TRUSTCAFE_CLIENT_SECRET="your-client-secret-here"
export TRUSTCAFE_ENV="production"
```
Load the environment variables in your Python code:
```python
import os
from dotenv import load_dotenv
load_dotenv()
client_id = os.getenv("TRUSTCAFE_CLIENT_ID")
client_secret = os.getenv("TRUSTCAFE_CLIENT_SECRET")
env = os.getenv("TRUSTCAFE_ENV", "alpha")
```
**Option B: Configuration Files**
For development, you can use configuration files:
```python
# config.py
CLIENT_ID = "your-client-id"
CLIENT_SECRET = "your-client-secret"
ENVIRONMENT = "alpha"
```
**Option C: Secrets Management (Production)**
For production deployments, use a secrets management system:
- AWS Secrets Manager
- HashiCorp Vault
- Kubernetes Secrets
- Environment-specific config files in CI/CD
**Important: Never commit credentials to version control!**
## Environment Configuration
The TrustCafé API wrapper supports two environments:
### Alpha Environment (Development)
- **Purpose**: Testing, development, staging
- **URL**: `alpha.wts2.net`
- **Best for**: Development and testing before production
```python
API = APIClient(
client_id=os.getenv("TRUSTCAFE_CLIENT_ID"),
client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"),
env="alpha" # Alpha environment
)
```
### Production Environment
- **Purpose**: Actual production usage
- **URL**: `trustcafe.io`
- **Best for**: Production deployments
```python
API = APIClient(
client_id=os.getenv("TRUSTCAFE_CLIENT_ID"),
client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"),
env="production" # Production environment
)
```
### Switching Environments
```python
# Initialize with alpha
API = APIClient(client_id="id", client_secret="secret", env="alpha")
# Later, switch to production
API.set_environment("production")
```
## First API Call
### Example: Get Your Profile
```python
import trustcafeapiwrapper
import os
from dotenv import load_dotenv
load_dotenv()
# Initialize the API client
API = trustcafeapiwrapper.APIClient(
client_id=os.getenv("TRUSTCAFE_CLIENT_ID"),
client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"),
env="alpha", # or "production"
debug=True # Set to True to see request/response details
)
# Authenticate and get an access token
API.handle_token()
# Get your profile using a job
profile = API.run_job('userprofile.get', "your-username")
print("Profile Data:")
print(profile)
```
### Example: Create Your First Post
```python
from trustcafeapiwrapper.wrappers.post.create_post import create_post
# Create a new post
API.wrapped(create_post(
"Hey there! This is my first post via the API.",
parent_path="/", # Root path
blur_label=None, # Optional: blur content
card_url=None, # Optional: link preview card
collaborative=False # Optional: enable collaboration
))
print("Post created successfully!")
```
## Token Management
### Automatic Token Management
The wrapper automatically manages access tokens:
```python
# First request - gets and saves token
API.handle_token()
# Subsequent requests - uses cached token if still valid
API.handle_token() # Returns cached token
```
**How it works:**
1. Checks for `token_data_{env}.json` file
2. Verifies token expiration (tokens last ~3 months)
3. Obtains new token if expired
4. Saves token to file for caching
### Manual Token Control
For more control:
```python
# Check if token is valid before making requests
if not API.is_token_valid():
API.sign_in() # Get new token
# Or manually set token
API.set_token({
"access_token": "your_access_token",
"access_token_timeout": 9999999999 # Unix timestamp
})
# Verify token validity
if API.is_token_valid():
print("Token is valid - safe to make requests")
else:
print("Token expired - get a new one")
```
### Environment-Specific Tokens
Different environments use separate token files:
- `token_data_alpha.json` - Alpha environment tokens
- `token_data_production.json` - Production environment tokens
This prevents token mixing between environments.
## Best Practices
### 1. Environment Variables
Always use environment variables for credentials:
```bash
# .env file
TRUSTCAFE_CLIENT_ID=
TRUSTCAFE_CLIENT_SECRET=
TRUSTCAFE_ENV=alpha
```
### 2. Error Handling
Always wrap API calls in try-except blocks:
```python
try:
API.handle_token()
profile = API.run_job('userprofile.get', "username")
except Exception as e:
print(f"Error: {e}")
# Handle error appropriately
```
### 3. Token Refresh Strategy
For long-running applications:
```python
# Before making API calls
if not API.is_token_valid():
API.handle_token()
# Optionally pre-fetch critical data
user_profile = API.run_job('userprofile.get', "username")
```
### 4. Logging
Implement appropriate logging:
```python
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
try:
API.handle_token()
profile = API.run_job('userprofile.get', "username")
logger.info(f"Successfully retrieved profile: {profile.get('username')}")
except Exception as e:
logger.error(f"Failed to get profile: {e}")
raise
```
### 5. Testing Credentials
Always test in alpha before production:
```python
# Test with alpha first
API = APIClient(client_id="test-id", client_secret="test-secret", env="alpha")
# Verify token works
results = API.handle_token()
print("Alpha environment works!")
# Then configure production
API = APIClient(
client_id=os.getenv("PROD_CLIENT_ID"),
client_secret=os.getenv("PROD_CLIENT_SECRET"),
env="production"
)
```
### 6. Rate Limiting Considerations
The TrustCafé API may have rate limits. Implement if needed:
```python
import time
from functools import wraps
def rate_limit(max_calls=10, period=60):
def decorator(func):
calls = [0]
times = [time.time()]
@wraps(func)
def wrapper(*args, **kwargs):
now = time.time()
calls[0] += 1
# Reset if period has passed
if times[0] + period < now:
times[0] = now
calls[0] = 1
# Sleep if necessary
if calls[0] > max_calls:
waited = time.time() - times[0] + 1
time.sleep(waited)
times[0] = time.time()
return func(*args, **kwargs)
return wrapper
return decorator
# Apply rate limiting
@rate_limit(max_calls=30, period=60)
def api_call():
return API.make_request(...)
```
### 7. Debug Mode in Development
Use debug mode during development:
```python
API = APIClient(
client_id="your-id",
client_secret="your-secret",
env="internal",
debug=True # Prints request/response details
)
```
## Common Configuration Patterns
### Production Deployment
```python
# config.py
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
# Production credentials from environment
CLIENT_ID = os.getenv("TRUSTCAFE_PROD_CLIENT_ID")
CLIENT_SECRET = os.getenv("TRUSTCAFE_PROD_CLIENT_SECRET")
ENVIRONMENT = os.getenv("TRUSTCAFE_ENV", "production")
# Other config
DEBUG = os.getenv("DEBUG", "False") == "True"
```
```python
# app.py
from config import Config
import trustcafeapiwrapper
API = trustcafeapiwrapper.APIClient(
client_id=Config.CLIENT_ID,
client_secret=Config.CLIENT_SECRET,
env=Config.ENVIRONMENT,
debug=Config.DEBUG
)
API.handle_token()
```
### Development/Local
```python
# config_dev.py
CLASS Config:
CLIENT_ID = "dev-client-id"
CLIENT_SECRET = "dev-client-secret"
ENVIRONMENT = "alpha"
DEBUG = True
```
```
# .env file
DEBUG=True
LOG_LEVEL=INFO
```
## Troubleshooting Setup Issues
### Issue: "Client ID not found"
**Solution**: Ensure you've generated client credentials from the TrustCafé admin page.
### Issue: "Client secret cannot be retrieved"
**Solution**: Client secrets can only be retrieved when first created. Create a new pair if needed.
### Issue: "Invalid client credentials"
**Solution**: Double-check client_id and client_secret. Verify you're using the correct environment.
### Issue: "Token expired"
**Solution**: Call `API.handle_token()` to get a new token. Check your token file is writable.
### Issue: "Permission denied" when writing token file
**Solution**: Ensure the working directory is writable. Use absolute paths or configure a writable location.
## Next Steps
Now that you're set up:
1. **Read the API Reference**: Learn about all available jobs and wrappers
- [API Reference](api_reference.md)
2. **Explore Wrappers**: Try the high-level wrappers for common tasks
- [Wrappers Guide](wrappers.md)
3. **Make Custom Requests**: Learn to make advanced API calls manually
- [Custom Requests Guide](custom_requests.md)
4. **Handle Errors**: Learn about common errors and how to handle them
- [Troubleshooting Guide](troubleshooting.md)
5. **Contribute**: Help complete the wrapper with additional jobs
- [Development Guide](development.md)

162
docs/INDEX.md Normal file
View file

@ -0,0 +1,162 @@
# TrustCafé API Wrapper - Documentation Index
Complete documentation for the TrustCafé API wrapper.
## Getting Started
**New to the wrapper?** Start here.
- [Getting Started Guide](GETTING_STARTED.md) - Complete setup and configuration guide
- [README.md](../README.md) - Overview package information and installation
## Core Usage
**Learn how to use the API wrapper effectively.**
- [Wrappers Guide](WRAPPERS.md) - High-level wrappers for common operations
- [API Reference](API_REFERENCE.md) - Complete API job reference
- [Custom Requests Guide](CUSTOM_REQUESTS.md) - Making advanced API calls manually
## Development
**Contributing or extending the wrapper.**
- [Development Guide](../development.md) - Development notes, design decisions, and known limitations
- [Examples](../README.md#examples) - Real-world usage examples
## Support and Troubleshooting
**Having issues?** Find solutions here.
- [Troubleshooting Guide](TROUBLESHOOTING.md) - Common issues and solutions
---
## Quick Links
### For New Users
1. [Installation](../README.md#installation)
2. [Authentication Setup](GETTING_STARTED.md#api-credentials-setup)
3. [First API Call](GETTING_STARTED.md#first-api-call)
4. [Basic Usage](../README.md#quick-start)
### For In-Depth Learning
1. [Wrappers](WRAPPERS.md) - Start here for easiest usage
2. [API Reference](API_REFERENCE.md) - Deep dive into all jobs
3. [Creating Custom Wrappers](WRAPPERS.md#creating-custom-wrappers)
### For Developers
1. [Extending the Wrapper](../development.md#extending-the-wrapper)
2. [Testing Guide](../development.md#testing-guide)
3. [Contributing](../development.md#contributing)
4. [Known Limitations](../development.md#known-limitations)
---
## Documentation Contents
### Getting Started (GETTING_STARTED.md)
- Installation steps
- API credentials setup
- Environment configuration
- First API call example
- Token management best practices
- Setup patterns for different environments
### Wrappers Guide (WRAPPERS.md)
- Overview of wrapper system
- Post wrapper documentation (create_post, update_post)
- Comment wrapper documentation (create_comment)
- Wrapper best practices
- Wrapper vs Job comparison
- Creating custom wrappers
### API Reference (API_REFERENCE.md)
- All available Jobs
- Complete job parameters and return values
- Authentication job documentation
- Post, Comment, UserProfile, Follow, Vote jobs
- Reaction, Notification, Trust, Branch jobs
- Feed operations
- Error handling patterns
- Pagination guide
### Custom Requests Guide (CUSTOM_REQUESTS.md)
- Using the `make_request` method
- Advanced usage patterns
- Pagination strategies
- Batch operations
- Error handling for custom calls
- Complex query examples
### Troubleshooting Guide (TROUBLESHOOTING.md)
- Authentication issues
- Connection problems
- Token management errors
- API call issues
- Environment issues
- Data handling problems
- Performance issues
- Debugging strategies
### Development Guide (development.md)
- Project structure
- Design decisions and debates
- Known limitations
- Missing features
- Future enhancements
- Contributing guidelines
- Testing guide
- Extension instructions
---
## Support
If you can't find the answer you're looking for:
1. **Check the FAQs section** - Common questions and answers
2. **Review the Troubleshooting guide** - For specific error handling
3. **Contact WikiTribune team** - Via company channels
4. **File an issue on GitLab** - [Repository Issues](https://gitlab.com/trustcafe/trustcafe-api-wrapper/-/issues)
---
## Changelog
Document version updates should be listed here.
### v0.2.0 (Current)
- Complete rewrite and enhancement of all documentation
- Added comprehensive API reference
- Added wrappers guide
- Added custom requests guide
- Added detailed troubleshooting section
- Enhanced development documentation
- Improved README.md structure and examples
### v0.1.0.13
- Initial release with basic documentation
- Minimum set of jobs and wrappers
---
## Contributing to Documentation
If you find any documentation issues:
1. **Typos or errors** - Correct in the markdown file
2. **Outdated examples** - Update with correct code
3. **Missing information** - Add to the appropriate section
4. **Unclear explanations** - Improve wording for clarity
When contributing documentation remember:
- Keep examples working
- Maintain consistent formatting
- Use proper markdown syntax
- Link to related documentation
- Update this INDEX.md if structure changes
---
**Last Updated:** April 17, 2026

763
docs/TROUBLESHOOTING.md Normal file
View file

@ -0,0 +1,763 @@
# Troubleshooting Guide
Common issues and solutions when using the TrustCafé API wrapper.
## Table of Contents
- [Authentication Issues](#authentication-issues)
- [Connection Issues](#connection-issues)
- [Token Management Issues](#token-management-issues)
- [API Call Issues](#api-call-issues)
- [Environment Issues](#environment-issues)
- [Data Handling Issues](#data-handling-issues)
- [Performance Issues](#performance-issues)
- [Debugging Strategies](#debugging-strategies)
## Authentication Issues
### Issue: "Invalid client credentials"
**Symptoms:**
```
ValueError: Client credentials invalid
```
**Causes:**
- Incorrect client_id or client_secret
- Using alpha credentials on production environment (or vice versa)
- Credentials haven't been saved yet from the TrustCafé admin panel
**Solutions:**
1. Verify credentials from the admin panel
2. Ensure environment matches credentials:
```python
# Check your credentials are for the right environment
print(f"Environment: {env}") # Should match where credentials were created
```
3. Generate new credentials if needed:
- Visit: https://www.trustcafe.io/en/myaccount/apiaccess
- Click "Create new client credentials key pair"
- Save the new credentials
4. Verify credentials aren't expired (trustcafe-side expiration)
---
### Issue: "Token expired"
**Symptoms:**
```
Exception: Token has expired
```
**Causes:**
- Access token duration has expired (~3 months from creation)
- Time mismatch between systems
**Solutions:**
1. Automatically refresh token:
```python
# Check token validity and refresh if needed
if not API.is_token_valid():
API.handle_token()
```
2. Pre-load tokens before long operations:
```python
# Ensure token is fresh before making many requests
API.handle_token()
# Now all API calls will succeed
```
3. Provide longer expiration time for testing:
```python
# Manual token with long expiration
API.set_token({
"access_token": "your-token",
"access_token_timeout": 9999999999 # Far future date
})
```
---
### Issue: "Permission denied" when making requests
**Symptoms:**
```
ForbiddenError: Insufficient permissions for this API endpoint
```
**Causes:**
- Client credentials lack required scopes
- Not authenticated (no access token)
**Solutions:**
1. Verify scopes on the admin panel
2. Ensure authentication is enabled in all requests:
```python
# Make sure to authenticate
response = API.make_request(
"GET",
"content",
"some/path",
authenticate=True # Ensure this is True
)
# For guest requests (if allowed)
response = API.make_request(
"GET",
"content",
"some/path",
authenticate=False
)
```
---
## Connection Issues
### Issue: "No internet connection" or "Connection refused"
**Symptoms:**
```
ConnectionError: An error occurred while making the request
```
**Causes:**
- Network connectivity issues
- Firewall blocking requests
- API service unavailable
**Solutions:**
1. Check network connectivity:
```bash
# Test connectivity to alpha/production endpoints
ping alpha.wts2.net
ping trustcafe.io
```
2. Verify firewall rules allow HTTPS requests
3. Try alternative network (mobile hotspot vs. Wi-Fi)
4. Check if TrustCafé services are up:
- Production: https://trustcafe.io
- Alpha: https://alpha.wts2.net
5. Add retry logic:
```python
import time
def retry_request(request_func, max_retries=3):
for attempt in range(max_retries):
try:
return request_func()
except ConnectionError as e:
if attempt < max_retries - 1:
time.sleep(1)
else:
raise
```
---
### Issue: "DNS resolution failed"
**Symptoms:**
```
requests.exceptions.ConnectionError: DNS resolution failed
```
**Causes:**
- DNS server issues
- VPN interfering with DNS
- Incorrect hostname
**Solutions:**
1. Check DNS:
```bash
nslookup alpha.wts2.net
nslookup trustcafe.io
```
2. Try changing DNS server:
```bash
# Use Google DNS (8.8.8.8, 8.8.4.4)
# Or Cloudflare (1.1.1.1, 1.0.0.1)
```
3. Disable VPN temporarily
4. Check configuration for correct hostnames (alpha.wts2.net vs trustcafe.io)
---
## Token Management Issues
### Issue: "Token file not found"
**Symptoms:**
```
FileNotFoundError: Token data file 'token_data_alpha.json' not found
```
**Causes:**
- First-time run
- Token directory not created
- Wrong path specified
**Solutions:**
1. API `<environment>_handle_token` automatically creates file on first use
2. Ensure working directory is writable
3. Specify custom token file path:
```python
API.handle_token(token_data_path="my/custom/path/token_data.json")
```
4. Manually create token file:
```python
token_data = API.sign_in()
with open("token_data_alpha.json", "w") as f:
json.dump(token_data, f)
```
---
### Issue: "Permission denied: cannot write token file"
**Symptoms:**
```
PermissionError: [Errno 13] Permission denied: 'token_data_alpha.json'
```
**Causes:**
- Working directory not writable
- File locked by another process
- Insufficient permissions
**Solutions:**
1. Ensure writable directory:
```python
import os
# Use a writable directory
writable_dir = os.path.expanduser("~") # Home directory
API.handle_token(token_data_path=f"{writable_dir}/tokens/token_data.json")
```
2. Check file permissions:
```bash
ls -la token_data_alpha.json
chmod 644 token_data_alpha.json
```
3. Try with a custom directory:
```python
import tempfile
# Use temp directory
with tempfile.TemporaryDirectory() as tmpdir:
API.handle_token(token_data_path=f"{tmpdir}/token_data.json")
```
---
### Issue: "Token file corrupted"
**Symptoms:**
```
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
```
**Causes:**
- File corruption during write
- File truncated by other process
**Solutions:**
1. Delete corrupted token file:
```bash
rm token_data_alpha.json
```
2. Let wrapper recreate it
3. Check for concurrent token writes:
```python
from threading import Lock
token_lock = Lock()
def safe_handle_token():
with token_lock:
API.handle_token()
```
---
## API Call Issues
### Issue: "400 Bad Request" or "Invalid parameter"
**Symptoms:**
```
ValueError: Invalid parameter or data format
400 Bad Request
```
**Causes:**
- Missing required parameters
- Incorrect parameter format
- Parameter validation failure
**Solutions:**
1. Check parameter requirements in API Reference:
- See [API Reference](api_reference.md) for each job
2. Verify parameter types:
```python
# Use correct types
API.run_job('post.create', {
"postText": "text string", # Should be string
"blurLabel": None, # None is okay
# Wrong: "blurLabel": "" (empty string)
})
```
3. Verify required parameters:
```python
# Example for post.create
required_fields = ['postText', 'parent']
if 'parent' not in data:
raise ValueError("Required field 'parent' is missing")
```
4. Use debug mode to see request:
```python
API = APIClient(debug=True)
# Now you'll see the exact data being sent
API.run_job('post.create', data)
```
---
### Issue: "404 Not Found"
**Symptoms:**
```
FileNotFoundError: Endpoint not found
404 Not Found
```
**Causes:**
- Wrong endpoint or path
- Resource doesn't exist
- Branch/post not found
**Solutions:**
1. Verify the path is correct
2. Check if resource exists:
```python
# Verify branch exists
branch = API.run_job('branch.get', "branch-name")
if not branch:
print("Branch does not exist")
# Verify post exists
post = API.run_job('post.get', "post-id")
if not post:
print("Post does not exist")
```
3. Check for spelling mistakes in paths
4. Use debug mode to see exact path being sent:
```python
API = APIClient(debug=True)
API.run_job('post.create', data) # See exact request path
```
---
### Issue: "500 Internal Server Error"
**Symptoms:**
```
ConnectionError: An error occurred
500 Internal Server Error
```
**Causes:**
- TrustCafé server error
- Database issues on server side
- Unhandled edge case
**Solutions:**
1. Wait a moment and retry
2. Check TrustCafé status
3. Try at a different time
4. Consider this transient error:
```python
import time
def retry_server_error(func, max_retries=3):
for attempt in range(max_retries):
try:
return func()
except ConnectionError as e:
if str(e).find("500") != -1:
if attempt < max_retries - 1:
time.sleep(2 ** attempt) # Exponential backoff
else:
raise
```
---
### Issue: "Rate limit exceeded"
**Symptoms:**
```
429 Too Many Requests
```
**Causes:**
- Too many API calls too quickly
- Gratuitous API usage
**Solutions:**
1. Implement rate limiting (see main README for example)
2. Space out your API calls:
```python
import time
def spaced_request(request_func, delay=1):
result = request_func()
if hasattr(request_func, 'call_count'):
request_func.call_count += 1
if request_func.call_count > 30:
time.sleep(2)
return result
```
3. Use pagination for large result sets
4. Consider caching results when possible
---
## Environment Issues
### Issue: "Environment 'invalid' is not valid"
**Symptoms:**
```
ValueError: Environment 'staging' is not valid. Must be one of: ['alpha', 'production']
```
**Causes:**
- Typo in environment name
- Passing only environment once
- Environment changed incorrectly
**Solutions:**
1. Use correct environment names:
- "alpha" (development/staging)
- "production" (live)
2. Avoid modifying environment mid-session after API init
3. Reinitialize if you need to switch:
```python
# Create new API client for new environment
API_prod = APIClient(
client_id="prod-id",
client_secret="prod-secret",
env="production"
)
# Old API still has old environment
print(API.environment) # "alpha"
```
---
### Issue: "Endpoint 'invalid' is not defined"
**Symptoms:**
```
ValueError: Endpoint 'noservices' is not defined in the API client.
```
**Causes:**
- Typo in endpoint name
- Custom endpoint not configured
- Server-side endpoint changed
**Solutions:**
1. Check available endpoints in APIClient code
2. Use correct endpoints: "content", "auth", "audrey", etc.
3. Review documentation for correct API structure
---
## Data Handling Issues
### Issue: "JSON decode error" or "Response is not valid JSON"
**Symptoms:**
```
ValueError: Response is not valid JSON: Expecting value: line 1 column 1 (char 0)
```
**Causes:**
- Server returning non-JSON response
- Network error causing incomplete response
- Empty response
**Solutions:**
1. Add error handling:
```python
try:
response = API.make_request("GET", "content", "path")
response_json = response.json()
except json.JSONDecodeError as e:
print(f"Failed to parse JSON: {e}")
print(f"Response was: {response}")
```
2. Check if server is responding correctly
3. Verify network connectivity
4. Check for errors in response:
```python
if 'error' in response:
raise Exception(f"API Error: {response['error']}")
```
---
### Issue: Empty response or missing data
**Symptoms:**
```
expected to find Items in response, but didn't
```
**Causes:**
- Query returns no results
- Pagination not handled
- Response structure misunderstanding
**Solutions:**
1. Check for empty results:
```python
response = API.make_request("GET", "content", "some/path")
if 'Items' not in response or not response.get('Items'):
print("No results found")
```
2. Check pagination status:
```python
if 'LastEvaluatedKey' in response:
print("More results available, need pagination")
```
3. Verify response structure understanding
---
## Performance Issues
### Issue: "Timeout" errors
**Symptoms:**
```
requests.exceptions.Timeout: Request timed out
```
**Causes:**
- Network slowness
- Large result sets
- Slow server response
**Solutions:**
1. Increase timeout:
```python
# Unfortunately, current implementation doesn't expose timeout
# This would need updating in apiclient.py
# If you control code, you can modify the make_request method
# to accept a timeout parameter
```
2. Reduce request complexity
3. Implement pagination for large datasets
4. Use async if available
---
### Issue: Slow token refresh
**Symptoms:**
- Initial request slow
- Token refresh takes too long
**Causes:**
- Network latency to auth endpoint
- Server-side delays
**Solutions:**
1. Pre-load token:
```python
# Get token before doing anything time-sensitive
API.handle_token()
# All subsequent calls will use cached token
```
2. Use manual token for frequent operations:
```python
API.set_token({
"access_token": token,
"access_token_timeout": 9999999999
})
```
---
## Debugging Strategies
### Enable Debug Mode
```python
API = APIClient(
client_id="your-id",
client_secret="your-secret",
env="alpha",
debug=True # Enables verbose logging
)
```
This will print:
- Request method
- Full URL
- Headers
- Request payload
### Add Logging
```python
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
try:
result = API.run_job('post.create', data)
logger.debug(f"Success: {result}")
except Exception as e:
logger.error(f"Failed: {e}", exc_info=True)
```
### Verify Request Flow
```python
def trace_request(func, *args, **kwargs):
"""Trace each request"""
import inspect
# Get function name
func_name = inspect.getmember(BuiltinFunctionType, func).__name__
print(f"Calling: {func_name}")
print(f"Args: {args}")
print(f"Kwargs: {kwargs}")
result = func(*args, **kwargs)
print(f"Result: {result}")
return result
```
### Network Inspection
```python
import requests
# Direct inspection of requests
import logging
requests_log = logging.getLogger("urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
```
---
## Getting Help
If you've tried all of the above and still have issues:
1. **Check the Code**: Review the latest version on GitLab
- [GitLab Repository](https://gitlab.com/trustcafe/trustcafe-api-wrapper)
2. **Check TrustCafé Status**: Verify services are up
- [Production](https://trustcafe.io)
- [Alpha](https://alpha.wts2.net)
3. **Review Contributions**: Check for known issues
- Open an issue on GitLab
- Check if a fix is already in progress
4. **Contact Support**: For resolved but unreleased issues
- WikiTribune team
- Build Product Slack/Discord
---
## Quick Reference
Common error codes:
- **400 Bad Request**: Invalid parameters (check API Reference)
- **401 Unauthorized**: Invalid/missing token (call `handle_token()`)
- **403 Forbidden**: Insufficient permissions (check scopes)
- **404 Not Found**: Wrong path or resource doesn't exist
- **429 Too Many Requests**: Rate limit exceeded (apply rate limiting)
- **500 Internal Server Error**: Service error (wait and retry)
- **Timeout**: Network issues (check connectivity)
---
**Remember**: Most issues are easily solved with proper error handling, verification of credentials, and rate limiting.

509
docs/WRAPPERS.md Normal file
View file

@ -0,0 +1,509 @@
# TrustCafé API Wrapped Functions Guide
This guide covers the high-level wrapper functions provided by the TrustCafé API wrapper. Wrappers simplify common operations by handling payload preparation and job execution automatically.
## Table of Contents
- [Overview](#overview)
- [Post Wrappers](#post-wrappers)
- [Comment Wrappers](#comment-wrappers)
- [Wrappers Best Practices](#wrappers-best-practices)
- [Wrapper vs Job](#wrapper-vs-job)
## Overview
### What are Wrappers?
Wrappers are pre-built functions that encapsulate common TrustCafé API operations. They:
- **Simplify usage**: Less boilerplate code
- **Handle payload preparation**: Automatically structure requests
- **Prevent errors**: Built-in validation and defaults
- **Self-documenting**: Clear parameter names and descriptions
### When to Use Wrappers
**Use Wrappers for:**
- Creating and managing content (posts, comments)
- Standard operations with predictable inputs
- Reducing code complexity
- Frequently used patterns
**Use Jobs or Custom Requests when:**
- More control is needed
- Custom parameter combinations
- Operations not covered by wrappers
- Uncommon workflows
### Quick Example
```python
# Using a wrapper (RECOMMENDED)
from trustcafeapiwrapper.wrappers.post.create_post import create_post
API.wrapped(create_post(
"This is my post content",
parent_path="/music"
))
# Using a job (for custom control)
API.run_job('post.create', {
"postText": "Different content",
"parent": {
"pk": "parents-key",
"sk": "parents-key"
}
})
```
## Post Wrappers
### create_post()
Creates a new post with simplified parameters.
```python
from trustcafeapiwrapper.wrappers.post.create_post import create_post
API.wrapped(create_post(
post_text="Post content here",
parent_path="/branch-name",
blur_label=None,
card_url=None,
collaborative=False
))
```
**Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `post_text` | str | **Required** | The text content of the post |
| `parent_path` | str | `"/"` | Branch path where the post will be created |
| `blur_label` | str | `None` | Optional label for blurring content |
| `card_url` | str | `None` | Optional URL for card preview |
| `collaborative` | bool | `False` | Enable collaborative editing |
**Returns:** dict - Job execution result, contains success information
**Example - Basic Post:**
```python
from trustcafeapiwrapper.wrappers.post.create_post import create_post
API.handle_token()
API.wrapped(create_post(
"Welcome to TrustCafé!",
parent_path="/"
))
```
**Example - Post in a Specific Branch:**
```python
API.wrapped(create_post(
"Music discussion thread",
parent_path="/music",
blur_label=None,
card_url=None,
collaborative=False
))
```
**Example - Blurred Post:**
```python
API.wrapped(create_post(
"Sensitive information",
parent_path="/",
blur_label="secret", # Will blur until labeled
card_url=None,
collaborative=False
))
```
**Example - Collaborative Post:**
```python
API.wrapped(create_post(
"Research paper draft - collaborative editing",
parent_path="/research",
blur_label=None,
card_url=None,
collaborative=True # Enables real-time collaboration
))
```
Example of performing multi-step integration with create_post and a linked resource:
```python
# Create post
created = API.wrapped(create_post("Paper title / author", parent_path="/research"))
# If response includes pk/sk, attach a linked item (structure depends on TrustCafé's link feature)
API.run_job('some.link.create', {
"fromPK": "paper",
"fromSK": created.get('pk'),
"toPK": "external-source",
"toSK": created.get('sk')
})
```
### update_post()
Updates an existing post.
```python
from trustcafeapiwrapper.wrappers.post.update_post import update_post
API.wrapped(update_post(
post_id="post-uuid",
new_post_text="New post content"
))
```
**Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `post_id` | str | **Required** | The post to update (pk/sk) |
| `new_post_text` | str | **Required** | The new post content |
| Optional fields per the API (e.g., blurLabel, cardUrl, etc.)
**Returns:** dict - Updated post data
**Important:** The post_id parameter should be set to the post's pk/sk. The wrapper internally maps the update parameters to the API's update payload structure.
**Example - Update Text:**
```python
from trustcafeapiwrapper.wrappers.post.update_post import update_post
API.handle_token()
API.wrapped(update_post(
post_id="current-pk-same-as-sk",
new_post_text="Updated content here"
))
```
**Example - Update with Optional Fields:**
```python
API.wrapped(update_post(
post_id="current-pk-same-as-sk",
new_post_text="Updated with new card",
card_url="https://example.com/resource"
))
```
**Example - Update Blur Label:**
```python
API.wrapped(update_post(
post_id="current-pk-same-as-sk",
blur_label="sensitive"
))
```
See update_post.py for mapping of updated fields to the server payload.
## Comment Wrappers
### create_comment()
Creates a new comment on a post.
```python
from trustcafeapiwrapper.wrappers.comment.create_comment import create_comment
API.wrapped(create_comment(
comment_text="This is a comment",
parent_path="/"
))
```
**Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `comment_text` | str | **Required** | The comment content |
| `parent_path` | str | **Required** | Path to the parent post where comment will be created |
**Returns:** dict - Job execution result, contains success information
**Example - Basic Comment:**
```python
from trustcafeapiwrapper.wrappers.comment.create_comment import create_comment
API.handle_token()
API.wrapped(create_comment(
"Great post! I really enjoyed reading it.",
parent_path="/"
))
```
**Example - Comment on a Post:**
```python
# First get the post to find its path
post = API.run_job('post.get', "target-post-id")
# Then create a comment
API.wrapped(create_comment(
"I agree with your point about this.",
parent_path="/music" # Path to the post
))
```
**Example - Collaborative Comment:**
```python
API.wrapped(create_comment(
"Let's work on this together!",
parent_path="/current-post"
))
```
## Wrappers Best Practices
### 1. Provide Context in Parent Paths
**Good - Explicit paths:**
```python
API.wrapped(create_post(
"Content",
parent_path="/music/discussion" # Clear and specific
))
```
**Bad - Empty paths:**
```python
API.wrapped(create_post(
"Content",
parent_path="/" # Default root - less clear intention
))
```
### 2. Handle Phone Numbers in Content
When phone numbers appear in text, leave them as-is. The API wraps the input string, so `555-1234` stays `555-1234`. No HTML is added.
### 3. Path Normalization
Paths are used directly without extra normalization:
```python
# Use with leading slash
API.wrapped(create_post(
"Content",
parent_path="/music"
))
# Or without (both should work)
API.wrapped(create_post(
"Content",
parent_path="music"
))
```
### 4. Validate Parent Exists
It's good practice to verify the parent path exists:
```python
# Check if branch exists
branches = API.run_job('branch.listbyname', "music")
if branches.get('Items'):
# Branch exists, proceed with post
API.wrapped(create_post(
"Content",
parent_path="/music"
))
else:
print("Branch 'music' does not exist")
```
### 5. Handle Responses
```python
from trustcafeapiwrapper.wrappers.post.create_post import create_post
response = API.wrapped(create_post(
"My post",
parent_path="/music"
))
# Check for success
if response and 'success' in response:
print("Post created successfully!")
print(f"Post ID: {response.get('pk')}")
else:
print("Failed to create post")
print(f"Error: {response.get('error')}")
```
### 6. Error Handling
```python
from trustcafeapiwrapper.wrappers.post.create_post import create_post
import logging
logger = logging.getLogger(__name__)
try:
API.handle_token() # Ensure authenticated
API.wrapped(create_post(
"Post content",
parent_path="/music"
))
logger.info("Post created successfully")
except Exception as e:
logger.error(f"Failed to create post: {e}")
# Handle error - maybe retry, notify admin, etc.
```
## Wrapper vs Job
| Feature | Wrappers | Jobs |
|---------|----------|------|
| **Simplicity** | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| **Flexibility** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| **Default Values** | ✅ Yes | ❌ No |
| **Parameter Validation** | ✅ Yes | ❌ No |
| **Common Operations** | ✅ Many | ❌ None |
| **Custom Operations** | ❌ No | ✅ Yes |
| **Learning Curve** | Low | High |
### Replacement Examples
**Example 1: Create Post**
```python
# Wrapper - Simple
API.wrapped(create_post(
"Content",
parent_path="/music"
))
# Job - More complex
API.run_job('post.create', {
"postText": "Content",
"parent": {
"pk": "encode(parent_path)",
"sk": "encode(parent_path)"
}
})
```
**Example 2: Update Post**
```python
# Wrapper
API.wrapped(update_post(
post_id="current-pk-same-as-sk",
new_post_text="New content"
))
# Job
API.run_job('post.update', {
"pk": "current-pk-same-as-sk",
"sk": "current-pk-same-as-sk",
"newPostText": "New content"
})
```
**Example 3: Create Comment**
```python
# Wrapper
API.wrapped(create_comment(
"My comment",
parent_path="/post-id"
))
# Job
API.run_job('comment.create', {
"commentText": "My comment",
"parent": {
"pk": "post-id",
"sk": "post-id"
}
})
```
### When to Use Each
**Use Wrappers When:**
- Common operation (create/update post/comment)
- Multiple optional parameters
- Want concise, readable code
- Acceptable default behavior
**Use Jobs When:**
- Rarely used operation
- Very specific parameter combinations
- Custom job names unsupported by wrappers
- Need maximum flexibility
## Creating Custom Wrappers
You can create your own wrappers for repeated operations:
```python
# Custom post wrapper
def create_in_branch(API, branch: str, content: str, create_card: bool = False):
"""
Helper wrapper for creating posts in specific branches.
Args:
API: The APIClient instance
branch: Branch name (e.g., "music", "science")
content: Post content
create_card: Whether to create a card URL
Returns:
dict: API response
"""
card_url = "https://example.com/card" if create_card else None
from trustcafeapiwrapper.wrappers.post.create_post import create_post
return API.wrapped(create_post(
content,
parent_path=f"/{branch}",
card_url=card_url
))
# Usage
API.handle_token()
result = create_in_branch(API, "music", "New music discussion")
# Or with card
result = create_in_branch(API, "science", "Research paper abstract", create_card=True)
```
---
## Summary
Wrappers provide a convenient, safe way to perform common operations with the TrustCafé API. For most use cases, wrappers reduce boilerplate and prevent configuration errors. However, jobs offer greater flexibility when standard wrappers don't meet your needs.
**Choose wisely:**
- Wrappers for standard operations and simplicity
- Jobs for custom operations and maximum control
- Custom wrappers for repeated patterns
---
## Next Steps
- [API Reference](api_reference.md) - Complete list of all API jobs
- [Custom Requests Guide](custom_requests.md) - Making advanced API calls manually
- [Examples](../README.md#examples) - Real-world usage examples