trustcafe-api-wrapper/docs/CUSTOM_REQUESTS.md
BarnacleBoy a223bcb27a doc: audit pass — fix env= bug, params, pagination, missing wrappers, broken examples
- env= -> environment= across all docs (Pydantic silently ignores env=)
- Remove __version__ check (doesn't exist in module)
- Fix APIClient(debug=True) missing required client_id/secret
- Fix post.listremoved usage (takes no postId, just lists removed posts)
- Fix JS true/false -> Python True/False in code blocks
- Fix parameter names: branchName->branch_slug, username->user_slug, postId->post_id
- Add missing lastEvaluatedKey param docs for list jobs
- Note post.listpublic as unauthenticated endpoint
- Fix pagination examples (LastEvaluatedKey, not ExclusiveStartKey dict)
- Remove non-existent headers param from make_request example
- Fix branch.listbyname example (takes no name arg)
- Add 4 missing wrapper docs: follow, react, trust, votecast
- Fix broken internal links (case-sensitive filenames)
- Fix <environment>_handle_token template artifact
- Add CODE_BUGS.md documenting 4 code bugs found during audit
2026-04-18 22:58:33 +00:00

761 lines
16 KiB
Markdown

# 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"),
environment="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 handling (advanced):
```python
# If you need to bypass token management, set the access_token directly on the API instance
API.access_token = "your-token-manually-set"
response = API.make_request(
"GET",
"content",
"post/some-path"
)
```
## Common Patterns
### Pagination
TrustCafé responses may include pagination for large result sets:
```python
def get_all_posts(limit=100):
"""Fetch all posts with pagination using make_request directly."""
def fetch_page(last_evaluated_key=None):
"""Fetch a single page of posts"""
response = API.make_request(
"GET",
"content",
"post",
query_params=last_evaluated_key,
authenticate=True
)
return response
all_posts = []
last_key = None
while True:
page = fetch_page(last_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
# Pass the LastEvaluatedKey directly as query_params for next page
last_key = page['LastEvaluatedKey']
return all_posts
# Usage
API.handle_token()
all_posts = get_all_posts()
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