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:
parent
14346ed02e
commit
fd1a1566e9
8 changed files with 4909 additions and 61 deletions
847
docs/API_REFERENCE.md
Normal file
847
docs/API_REFERENCE.md
Normal 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
767
docs/CUSTOM_REQUESTS.md
Normal 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
469
docs/GETTING_STARTED.md
Normal 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
162
docs/INDEX.md
Normal 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
763
docs/TROUBLESHOOTING.md
Normal 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
509
docs/WRAPPERS.md
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue