chore: add comprehensive API wrapper documentation

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

- Update README.md with enhanced project information

- Update development.md with expanded documentation context

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

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

628
README.md
View file

@ -1,52 +1,632 @@
# Trustcafe API Wrapper
# TrustCafé API Wrapper
A Python wrapper for the TrustCafé API, providing a convenient interface to interact with TrustCafé features programmatically.
## Table of Contents
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Authentication](#authentication)
- [API Reference](#api-reference)
- [Jobs](#jobs)
- [Wrappers](#wrappers)
- [Custom Requests](#custom-requests)
- [Environment Setup](#environment-setup)
- [Debug Mode](#debug-mode)
- [Examples](#examples)
- [Development](#development)
- [Testing](#testing)
## Prerequisites
You will need:
1. API Client and Secret
2. Python 3.11 or something sensible or above
3. A method for environment variables like [python-dotenv](https://pypi.org/project/python-dotenv/)
Before you begin, ensure you have:
### API client key and secret from the site
- **Python 3.11 or higher** installed
- **API Client ID and Secret** from TrustCafé
- **python-dotenv** (for `.env` file support) or ability to set environment variables
### Getting API Credentials
1. Visit your TrustCafé account page:
- [Production API Access](https://www.trustcafe.io/en/myaccount/apiaccess)
- [Alpha Environment API Access](https://alpha.wts2.net/en/myaccount/apiaccess)
1. https://www.trustcafe.io/en/myaccount/apiaccess for production and https://alpha.wts2.net/en/myaccount/apiaccess for alpha (this will change in the future)
2. Click "Create new client credentials key pair"
3. Choose all of the scopes or select which you would like to restrict the key to
4. Scroll down and press Save
5. Copy the client key and secret to your environment, the secret cannot be retrieved without making a new one
3. Choose the scopes you need (or select all for full access)
4. Click "Save"
5. Copy the **client_id** and **client_secret** to your environment:
- The secret cannot be retrieved later — make sure to save it securely
- Do not commit these credentials to version control
## Installation
```bash
# Using pip
pip install trustcafeapiwrapper
OR
# Using uv (recommended)
uv add trustcafeapiwrapper
```
## Usage
## Quick Start
The simplest way to get started:
```python
import trustcafeapiwrapper, os
import trustcafeapiwrapper
import os
# Initialize the API client
API = trustcafeapiwrapper.APIClient(
client_id=os.getenv("client_id"),
client_secret=os.getenv("client_secret"),
env="alpha" # alpha | production.
client_id=os.getenv("TRUSTCAFE_CLIENT_ID"),
client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"),
env="alpha", # Options: "alpha" or "production"
debug=False
)
profile = API.run_job('userprofile.get', "simon-little")
# Authenticate and get user profile
API.handle_token()
profile = API.run_job('userprofile.get', "your-username")
print(profile)
```
## Authentication
The API wrapper handles OAuth2 client-credentials authentication automatically.
### Automatic Token Management
```python
API.handle_token()
```
**What it does:**
- Checks for an existing token file (`token_data_{env}.json`)
- If found, verifies if the token is still valid
- If expired or not found, obtains a new access token (~3 month duration)
- Saves the token to a local file for future requests
### Manual Token Management
For more control over token storage:
```python
def getMyToken():
# Retrieve token from your custom storage (database, file, etc.)
return {
"access_token": "your_token_here",
"access_token_timeout": 9999999999 # Unix timestamp
}
def saveMyToken(token_data):
# Save to your preferred storage
with open("my_token_cache.json", "w") as f:
json.dump(token_data, f)
return True
# Set the token manually or pass to initialize
API = APIClient(
client_id=os.getenv("client_id"),
client_secret=os.getenv("client_secret"),
env="production",
debug=False
)
# Use existing token or get a new one
if not API.is_token_valid():
saveMyToken(API.sign_in())
```
### Token Validation
```python
if API.is_token_valid():
print("Token is valid")
else:
API.handle_token() # Get a new token
```
## Environment Setup
The wrapper supports two environments:
| Environment | URL | Purpose |
|------------|-----|---------|
| `alpha` | `alpha.wts2.net` | Development/staging environment |
| `production` | `trustcafe.io` | Production environment |
```python
# Alpha environment (default)
API = APIClient(
client_id="your-client-id",
client_secret="your-client-secret",
env="alpha",
debug=False
)
# Production environment
API = APIClient(
client_id="your-client-id",
client_secret="your-client-secret",
env="production",
debug=False
)
# Change environment dynamically
API.set_environment("production")
```
## API Reference
### Jobs
Jobs are pre-built functions that map to specific API endpoints. They return the raw response from the API.
#### Post Operations
```python
# Get a specific post
post = API.run_job('post.get', "post-slug-or-id")
# Create a post
post = API.run_job('post.create', {
"postText": "This is a test post",
"parent": {
"pk": "maintrunk#maintrunk",
"sk": "maintrunk#maintrunk"
}
})
# List all posts
posts = API.run_job('post.listall')
# List public posts
public_posts = API.run_job('post.listpublic')
# List posts by a specific branch
branch_posts = API.run_job('post.listbybranch', "branch-name")
# List posts by a user profile
user_posts = API.run_job('post.listbyuserprofile', "username")
# Update a post
updated_post = API.run_job('post.update', {
"pk": "post-id",
"sk": "post-id",
"newPostText": "Updated text"
})
# Delete a post
deleted = API.run_job('post.listremoved', "post-id")
```
#### Comment Operations
```python
# Create a comment
comment = API.run_job('comment.create', {
"commentText": "This is a comment",
"parent": {
"pk": "post-id",
"sk": "post-id"
}
})
# Get comments by post ID
comments = API.run_job('comment.listtbypostid', "post-id")
```
#### User Profile Operations
```python
# Get user profile
profile = API.run_job('userprofile.get', "username")
```
#### Follow Operations
```python
# Follow a user
followed = API.run_job('follow.follow', "username-to-follow")
```
#### Vote Operations
```python
# Cast a vote
voted = API.run_job('vote.votecast', {
"pk": "post-id",
"sk": "post-id",
"voteType": "up" # or "down"
})
```
#### Reaction Operations
```python
# React to something (post, comment, etc.)
reactions = API.run_job('reaction.reacttosomething', {
"objectPK": "post-id",
"objectSK": "post-id",
"objectType": "post",
"reactionType": "like" # various types supported
})
# Get reactions by parent
post_reactions = API.run_job('reaction.getbyparent', "parent-id")
# List reactions
reactions_list = API.run_job('reaction.listbyparent', "parent-id")
```
#### Notification Operations
```python
# List all notifications
notifications = API.run_job('notification.listnotifications')
# Mark all as read
API.run_job('notification.markallasread')
```
#### Trust Operations
```python
# Create or update trust relationship
trust = API.run_job('trust.createorupdate', {
"pk": "trust_id",
"sk": "trust_id",
"trustType": "positive"
})
# List trust by user initialization
trusts = API.run_job('trust.listbyusersinit', "username")
# List trust by user has
trusts = API.run_job('trust.listbyuserhas', "username")
```
#### Branch Operations
```python
# Get a specific branch
branch = API.run_job('branch.get', "branch-name")
# List branches by name
branches = API.run_job('branch.listbyname', "prefix")
```
#### Feed Operations
```python
# Get café feed
feed = API.run_job('feed.cafefeed')
# Get following feed
following_feed = API.run_job('feed.following')
```
#### Block Operations
```python
# Block a user (function exists but needs implementation)
# TODO: Add job function for block operations
```
#### Mute Operations
```python
# Mute functionality (to be added)
# TODO: Add job function for mute operations
```
### Wrappers
Wrappers are high-level functions that simplify common operations by handling payload preparation and job execution automatically.
#### Using a Wrapper
```python
API.handle_token()
# Create a post with a wrapper (recommended for simplicity)
from trustcafeapiwrapper.wrappers.post.create_post import create_post
API.wrapped(create_post(
"This is a test post created via the create_post wrapper function.",
"This is a test post created via the wrapper.",
parent_path="/", # Root path (optional)
blur_label=None, # Optional blurring
card_url=None, # Optional card URL
collaborative=False # Optional collaboration flag
))
# Create a comment
from trustcafeapiwrapper.wrappers.comment.create_comment import create_comment
API.wrapped(create_comment(
"This is a comment",
parent_path="/" # Parent path (required)
))
```
## More
- [Development "Things to think about do"](https://gitlab.com/trustcafe/trustcafe-api-wrapper/-/blob/main/development.md)
- [Documentation](https://gitlab.com/trustcafe/trustcafe-api-wrapper/-/blob/main/documentation.md)
**About Wrappers:**
- Wrappers simplify common operations
- They prepare the payload and job execution automatically
- More wrappers are being added for consistency
- For flexibility, use Jobs or Custom Requests
### Using `run_job` vs. Wrappers
| Feature | `run_job` | Wrappers |
|---------|----------|----------|
| Simplicity | Higher | Highest for common tasks |
| Flexibility | Maximum | Optimized for specific use cases |
| Discovery | Need to know job format | Self-documenting payloads |
| Customization | Full control | Pre-defined options |
**Recommendation:**
- Use **Wrappers** for common, well-documented operations
- Use **Jobs** when you need fine-grained control
- Use **Custom Requests** when neither fits your needs
## Custom Requests
For operations not covered by jobs or wrappers, use the `make_request` method:
```python
# Make a raw API request
response = API.make_request(
method="GET",
endpoint="content",
path="post/some-custom-path",
authenticate=True,
query_params={"param1": "value1"}
)
```
**Parameters:**
- `method` (str): HTTP method - "GET", "POST", "PUT", "DELETE"
- `endpoint` (str): API endpoint - "content", "auth", "audrey", etc.
- `path` (str): API path after endpoint
- `authenticate` (bool): Whether to include authentication token
- `query_params` (dict): Optional query parameters
**Example - Custom Request:**
```python
# Get posts from a specific branch with filters
posts = API.make_request(
"GET",
"content",
"post/ref-subwiki/music",
query_params={"limit": 10, "offset": 0}
)
```
## Debug Mode
Enable debug mode to see all API requests and responses:
```python
API = APIClient(
client_id=os.getenv("client_id"),
client_secret=os.getenv("client_secret"),
env="production",
debug=True # Enable debug mode
)
# Now API calls will print request details
API.handle_token()
```
## Examples
### Example 1: Basic Post Creation
```python
import trustcafeapiwrapper
import os
# Setup
API = trustcafeapiwrapper.APIClient(
client_id=os.getenv("TRUSTCAFE_CLIENT_ID"),
client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"),
env="alpha",
debug=False
)
# Handle authentication
API.handle_token()
# Create a post in the music branch
from trustcafeapiwrapper.wrappers.post.create_post import create_post
API.wrapped(create_post(
"New music discussion post",
parent_path="/music",
blur_label=None,
card_url=None,
collaborative=False
))
```
### Example 2: Following a User and Getting Their Profile
```python
import trustcafeapiwrapper
import os
API = trustcafeapiwrapper.APIClient(
client_id=os.getenv("TRUSTCAFE_CLIENT_ID"),
client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"),
env="production"
)
# Follow a user
follow = API.run_job('follow.follow', "philosopher-jon")
print(f"Followed user: {follow}")
# Get their profile
profile = API.run_job('userprofile.get', "philosopher-jon")
print(f"Profile data: {profile}")
```
### Example 3: Managing Notifications
```python
import trustcafeapiwrapper
import os
API = trustcafeapiwrapper.APIClient(
client_id=os.getenv("TRUSTCAFE_CLIENT_ID"),
client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"),
env="production"
)
API.handle_token()
# Check for new notifications
notifications = API.run_job('notification.listnotifications')
print(f"Unread notifications: {notifications}")
# Mark all as read
API.run_job('notification.markallasread')
print("All notifications marked as read")
```
### Example 4: Creating a Collaborative Post
```python
import trustcafeapiwrapper
import os
API = trustcafeapiwrapper.APIClient(
client_id=os.getenv("TRUSTCAFE_CLIENT_ID"),
client_secret=os.getenv("TRUSTCAFE_CLIENT_SECRET"),
env="production"
)
API.handle_token()
# Create a collaborative post
from trustcafeapiwrapper.wrappers.post.create_post import create_post
API.wrapped(create_post(
"Research paper draft - collaborative editing",
parent_path="/research",
collaborative=True # Enables collaborative editing
))
```
## Development
### Project Structure
```
trustcafe-api-wrapper/
├── src/
│ └── trustcafeapiwrapper/
│ ├── apiclient.py # Core API client
│ ├── jobs/ # API job functions
│ │ ├── post/
│ │ ├── comment/
│ │ ├── userprofile/
│ │ ├── follow/
│ │ └── ... (all jobs)
│ ├── wrappers/ # High-level wrappers
│ │ ├── post/
│ │ ├── comment/
│ │ └── ...
│ └── utils/ # Helper functions
├── tests/ # Test files
├── README.md # This file
├── documentation.md # Basic documentation
├── development.md # Project notes
└── pyproject.toml # Project configuration
```
### Adding New Jobs
1. Create a new file in `src/trustcafeapiwrapper/jobs/`
2. Define a function matching the job name
3. Export it from the module's `__init__.py`
4. Document the parameters and return value
**Example - Adding a Job:**
```python
# src/trustcafeapiwrapper/jobs/example/new_job.py
def my_new_job(API, param1: str, param2: int) -> dict:
"""
Create a new job function.
Args:
API: The APIClient instance
param1 (str): Description of parameter 1
param2 (int): Description of parameter 2
Returns:
dict: The API response
"""
response = API.make_request(
"POST",
"endpoint",
"path/to/resource",
data={"param1": param1, "param2": param2}
)
return response
```
### Known Limitations
- **Incomplete Jobs**: Not all available API endpoints have wrapper functions yet
- **Enums**: Some enum types are not strictly typed
- **Block Operations**: Block functionality exists in backend but not yet exposed
- **Mute Operations**: Mute functionality pending implementation
### Future Enhancements
- Complete coverage of all TrustCafé API endpoints
- Type hints for better IDE support
- Async support for better performance
- Comprehensive test suite
- Pydantic models for request/response validation
## Testing
Run the test suite:
```bash
pytest
```
Or run specific tests:
```bash
# Test the API client
python -m pytest tests/apiclient.py
# Run all tests
python -m pytest tests/
```
## License
[Depends on TrustCafé project license]
## Support
For issues, questions, or contributions:
- [TrustCafé Website](https://trustcafe.io)
- [GitLab Repository](https://gitlab.com/trustcafe/trustcafe-api-wrapper)
- Contact the WikiTribune team
## Version History
- **0.1.0.13** - Current version with ongoing development
---
**Note:** This wrapper is for internal WikiTribune/TrustCafé use. Public API endpoints are subject to change.

View file

@ -1,37 +1,788 @@
# Things to think about do
## Debates
1. Hating the name `jobs` and `run_job`. Requests is an
existing package dependency though. `tasks` seems to broard. I'm sure
there's probably an already existing name that I can't articulate how
to find. `apirequests`, `rqsts`, `apicalls`?
2. Should these `job` be called with dot notation? Or with slashes? Or
something else? Is it okay to have these as strings?
3. Should we do validation in the `jobs` or let the server do that all?
4. `wrapped` is an awkward name. What's more proper? But also obvious?
5. Should have consistent format, ie `create_{noun}`, and
`delete_{noun}`, rather than just verbing ie `comment`, `post` as this
is ambiguousetc
6. Known/displayed entity names or as-is/DB names, ie: `trust` or
`reltrust`? We call trust as reltrust in the database and in the
backend, relfollow for follow, but don't have relblock, instead it's
userblock. It's a bit inconsistent and not very obvious at all.
Feels helpful to make it more obvious inside the wrapper but is also
potentially misleading.
7. It makes more sense to organise like: `user.follow` rather than
`follow.follow`, and `post.vote` instead of `vote.vote`, but what about
`post.comment.vote` vs `comment.vote`, what about
`post.comment.comment`, they seem weird.
## ToDo:
1. Make more jobs
2. Make more wrappers
3. Write more documentation
Mute
Block
Branch Mute
# TrustCafé API Wrapper - Development Guide
This document covers development considerations, known limitations, and contribution guidelines for the TrustCafé API wrapper.
## Table of Contents
1. [Project Structure](#project-structure)
2. [Design Decisions](#design-decisions)
3. [Known Limitations](#known-limitations)
4. [Missing Features](#missing-features)
5. [Future Enhancements](#future-enhancements)
6. [Contributing](#contributing)
7. [Testing Guide](#testing-guide)
8. [Extending the Wrapper](#extending-the-wrapper)
## Project Structure
```
trustcafe-api-wrapper/
├── README.md # Main documentation
├── documentation.md # Basic usage documentation
├── development.md # This file - development notes
├── pyproject.toml # Project configuration
├── setup.py # Legacy setup file
├── development.md
├── .gitlab-ci.yml # CI/CD configuration
└── src/
└── trustcafeapiwrapper/
├── __init__.py # Package exports
├── apiclient.py # Core API client (218 lines)
├── jobs/ # API job functions
│ ├── __init__.py
│ ├── post/
│ ├── comment/
│ ├── userprofile/
│ ├── follow/
│ ├── vote/
│ ├── reaction/
│ ├── notification/
│ ├── trust/
│ ├── branch/
│ └── feed/
├── wrappers/ # High-level wrappers
│ ├── __init__.py
│ ├── post/
│ └── comment/
└── utils/ # Helper functions
├── __init__.py
├── make_comment_sk.py
├── make_post_sk.py
├── get_post_pksk.py
├── get_userprofile_pksk_from_slug.py
├── get_user_slug_from_path.py
├── get_entity_from_str.py
├── get_parent_pksk_from_path.py
└── get_child_spksk_from_paths.py
```
## Design Decisions
### 1. Naming Conventions
**Jobs vs. Wrappers:**
| Aspect | Jobs | Wrappers |
|--------|------|----------|
| Naming | Verb (`create_x`, `get_x`) | Noun phrase (`create_post`, `update_post`) |
| Pattern | Inconsistent | Standardized (`create_noun`, `update_noun`) |
| Usage | Flexible | Optimized for common cases |
| Type | Function | Function returning dict with job spec |
**Question: Should these be called `jobs`, `tasks`, `apirequests`, `rqsts`, or `apicalls`?**
- **Chosen:** `jobs` - Existing dependency (requests)
- **Alternatives considered:** `tasks` (too broad), `apirequests` (confusing with HTTP library)
**Thread with discussion:** See GitLab issues
### 2. Job Execution Method
**Question: Should jobs be called with `dot notation` (e.g., `userprofile.get`) or `slashes` (e.g., `userprofile/get`)?**
- **Chosen:** Dot notation for module style
- **Flexibility:** Supports both strings for dynamic loading and functions for static use
- **Pattern:**
```python
# String-based (dynamic) - works for variable jobs
API.run_job('userprofile.get', username)
# Function-based (static) - good for explicit calls
API.run_job(userprofile_get, username)
```
### 3. Parameter Validation
**Question: Should validation happen in jobs or let the server do it?**
- **Chosen:** Server-side validation with pydantic at APIClient level
- **Reasoning:**
- Server provides authoritative validation
- Reduced redundancy
- Known API responses
- **Trade-off:** Misuse symptoms appear when server rejects invalid data
### 4. Wrapper Architecture
**Question: What's the proper name for the wrapper system?**
- **Chosen:** `wrapped()` as the calling method
- **Alternatives considered:** None after review
- **Pros:** Self-documenting parameter handling
- **Cons:** Awkward name
### 5. Verbosity in Noun Names
**Question: Use `post.vote` vs `vote.cast`?**
- **Chosen:** `post.vote` for entity-based paths
- **Rationale:**
- Better semantic structure: `object.action`
- Clearer context (what are we voting on?)
- Consistent with TrustCafé's own endpoints
**Examples:**
- ✅ `post.comment` (comment on a post)
- ✅ `comment.vote` (vote on a comment)
- ⚠️ vs `comment.comment` (commenting on a comment)
### 6. Database vs. Display Names
**Question: Use `trust` or RelTrust in code?**
- **Chosen:** Display names (`trust`, `follow`, `block`)
- **Misleading risk:** Database uses RelTrust, RelFollow, UserBlock
- **Plan:** Internal mapping in utility functions
**Database Inconsistencies:**
- `trust``RelTrust`
- `follow``RelFollow`
- `block``UserBlock` (not `RelBlock`)
- `mute``Mute`
### 7. Entity Hierarchy Organization
**Question: Organize as `user.follow` or `follow.follow`?**
- **Chosen:** `user.follow` (object-centric) for most cases
- **Complex cases:** `comment.vote`, `post.comment.comment` (both possible)
**Plan:**
- Simple cases: Entity-based (`user.follow`, `post.comment`)
- Complex cases: Action-centric (`comment.vote`)
- Organize by common pattern, not dogmatically
## Known Limitations
### 1. Incomplete Job Coverage
**Status:** Partial coverage of API endpoints
**Currently Implemented:**
- ✅ Post operations: create, get, update, listall, listpublic, listbybranch, listbyuserprofile, listremoved
- ✅ Comment operations: create, listtbypostid
- ✅ UserProfile: get
- ✅ Follow: follow
- ✅ Vote: votecast
- ✅ Reaction: reacttosomething, getbyparent, listbyparent
- ✅ Notification: listnotifications, markallasread
- ✅ Trust: createorupdate, listbyusersinit, listbyuserhas
- ✅ Branch: get, listbyname
- ✅ Feed: cafefeed, following
- ⚠️ Block: job created but not connected to server
- ⚠️ Mute: job created but not connected to server
**Missing/Incomplete:**
- ❌ Post status operations (delete, pin, etc.)
- ❌ Full post metadata operations
- ❌ Advanced comment features
- ❌ User management operations
- ❌ Search functionality
- ❌ Moderation operations
- ❌ Analytics endpoints
---
### 2. Enums Not Strictly Typed
**Issue:** Some parameter types are not enforced
**Example:**
```python
# Acceptable (but should be):
API.run_job('vote.votecast', {..., "voteType": "UP"}) # Should error?
# Better to use type hints:
vote_type: Literal["up", "down"]
```
**Affected APIs:**
- voteType (should be "up" or "down")
- reactionType (should match TrustCafé enum values)
- trustType (should be known set)
**Solution:** Add pydantic models for requests and responses
---
### 3. No Async Support
**Issue:** All requests are synchronous (blocking)
**Impact:**
- Serial request execution
- Long waits for multiple calls
- Limited throughput
**Solution:**
- Add async/await support using `aiohttp`
- Provide sync wrapper that calls async
- Consider for future major version
---
### 4. Limited Timeout Configuration
**Issue:** Hardcoded 20-second timeout
**Current Code:**
```python
# apiclient.py line 87
response = session.request(method.upper(), url, json=data, headers=headers, timeout=20)
```
**Problem:**
- No way to configure timeout per request
- Default works but is a guess
- Network issues hard to diagnose without flexibility
**Solution:**
```python
# Add timeout parameter
def make_request(self, method, endpoint, path, data=None,
authenticate=True, query_params=None,
timeout=20): # Add timeout parameter
```
---
### 5. No Detailed Error Types
**Issue:** All errors are generic `Exception` or `ValueError`
**Current Code:**
```python
except Exception as e:
raise ConnectionError(f"An error occurred while making the request: {e}")
```
**Limitations:**
- No authentication-specific errors
- No validation errors
- No permission errors
- Error type doesn't give context
**Solution:** Create custom error classes:
```python
class APIError(Exception):
"""Base API error"""
class AuthenticationError(APIError):
"""Authentication failures"""
class ValidationError(APIError):
"""Invalid parameter values"""
```
---
### 6. Token File Handling
**Issue:** Token file stored in working directory (influenced by os.getcwd())
**Problem:**
- Cross-platform issues
- Permission problems
- Can't control location from outside
**Current Code:**
```python
token_data_path = f"token_data_{self.environment}.json"
```
**Solution:**
```python
# User-configurable path
import os
default_path = os.path.join(os.getcwd(), f"token_data_{self.environment}.json")
API.handle_token(token_data_path=os.getenv("TRUSTCAFE_TOKEN_PATH", default_path))
```
---
### 7. Database-Display Name Mismatch
**Issue:** Internal vs. external naming不一致
**Examples:**
- Display: `trust` ↔ Database: `RelTrust`
- Display: `follow` ↔ Database: `RelFollow`
- Display: `block` ↔ Database: `UserBlock` (not `RelBlock`)
- Display: `mute` ↔ Database: `Mute`
**Solution:**
- Map most common names (trust, follow, mute)
- Add mappings in utility functions
- Document naming inconsistencies
- Consider renaming display names for consistency
---
### 8. Unused Dependencies
**Cleanup opportunity:** Some dependencies may not be used
**Current Dependencies:**
```toml
dependencies = [
"dotenv>=0.9.9", # Used in development.md example
"pydantic>=2.12.5", # Used for SecretStr
"requests>=2.33.1", # Used for HTTP requests
"simplejson>=3.20.2", # Used for JSON parsing
]
```
**Potential Removal:**
- `simplejson`: Can use Python's built-in json
- **Note:** Keep for now to minimize risk
---
### 9. Hard-coded Endpoints
**Issue:** API base URLs are hard-coded
**Current Code:**
```python
endpoints = {
"alpha": {
"audrey": "https://eso1of8gqd.execute-api.us-east-1.amazonaws.com/alpha/",
# ...
},
"production": {
# ...
}
}
```
**Problem:**
- Environment changes will break
- No hot-reload
- Same URLs across versions
**Solution:**
```python
# Could be environment variables
endpoints = {
"alpha": {
"audrey": os.getenv("AUDREY_ALPHA_URL", "default-alpha-url"),
},
# ...
}
```
## Missing Features
### High Priority
1. **Complete Post Operations**
- Post status: pin, unpin, archive, delete
- Post editing: editing mode handling
- Post metadata bulk operations
- Post history/versioning
2. **User Management Jobs**
- Block/unblock users
- Mute/unmute users
- User profile management
- User settings
3. **Search Functionality**
- Full-text search
- Advanced filters
- Query builder
4. **Comment Enhancements**
- Comment editing
- Comment deletion
- Comment threading
- Comment moderation
---
### Medium Priority
5. **Reaction UI**
- Multiple response types (heart, fire, thumbs-up, etc.)
- Reaction history
- Reaction statistics
6. **Trust System**
- Trust ranking algorithms
- Trust network visualization
- Block followers
- Trust relationships with others' trust
7. **Notification Enhancements**
- Notification callbacks/webhooks
- Notification types documentation
- Notification preferences
- Notification filtering
8. **Feed Customization**
- Filter classes
- Schedule-based feeds
- Feed templates
---
### Low Priority
9. **Analytics**
- User engagement metrics
- Content popularity
- API usage analytics
- Performance metrics
10. **Batch Operations**
- Bulk post creation
- Bulk comment actions
- Bulk follow operations
11. **Spatial/Media**
- Location support
- Media attachments handling
- Image/video upload
## Future Enhancements
### Architecture Improvements
1. **Type Safety**
- Pydantic models for all inputs and outputs
- Strict type checking
- Auto-completion support
- Better IDE integration
2. **Async Support**
- Async wrappers for all operations
- Pooling for concurrency
- Automatic retries with exponential backoff
- Connection pooling
3. **Configuration Management**
- Centralized config file
- Environment-specific configs
- Secrets management integration
- Hot-reload capability
4. **Better Testing**
- Comprehensive test suite
- Integration tests
- Mock API server
- Property-based testing
### Developer Features
5. **CLI Tool**
- `trustcafe` command-line interface
- Interactive workflows
- Dry-run mode
- Export/import operations
6. **Code Generation**
- Auto-generate jobs from API spec
- Wrapper generators
- Type definitions from OpenAPI spec
7. **Plugin System**
- Extension points
- Custom jobs
- Custom middleware
- Hooks system
---
### User Experience Improvements
8. **Better Documentation**
- API sandbox/demo site
- Data models and schemas
- Migration guides
- FAQ section
9. **Developer Experience**
- SDK-style TypeScript version
- Better examples
- Quick start guides
- Video tutorials
10. **Monitoring and observability**
- Request/response logging
- Metrics and tracing
- Health checks
- Alerting hooks
## Contributing
### Adding New Jobs
```python
# 1. Create job file in src/trustcafeapiwrapper/jobs/
# Example: src/trustcafeapiwrapper/jobs/example/my_job.py
from trustcafeapiwrapper.apiclient import APIClient
def my_job(API: APIClient, param1: str, param2: int) -> dict:
"""
Create your job here.
Args:
API: The APIClient instance
param1: Description of param1
param2: Description of param2
Returns:
dict: Whatever the API returns
"""
response = API.make_request(
"GET",
"endpoint",
f"path/to/resource/{param1}",
authenticate=True
)
return response
# 2. Export from __init__.py (if needed)
# Keep __init__.py empty per current structure
```
### Adding New Wrappers
```python
# Example: src/trustcafeapiwrapper/wrappers/example/my_wrapper.py
from trustcafeapiwrapper.utils.get_parent_pksk_from_path import get_parent_pksk_from_path
def my_wrapper(
param1: str,
param2: int,
optional_param: str = None
):
"""
Create a wrapper that makes a job easier.
Args:
param1: Required parameter description
param2: Required parameter description
optional_param: Optional parameter with default
Returns:
dict: Job specification for wrapped execution
"""
parent_pksk = get_parent_pksk_from_path("/")
return {
"job_function": "example.my_job",
"payload": {
"param1": param1,
"param2": param2,
"optional_param": optional_param,
}
}
# 3. Export from __init__.py (if needed)
```
### Testing
```python
# Create test file: tests/test_my_job.py
import unittest
from unittest.mock import patch
from trustcafeapiwrapper.apiclient import APIClient
from trustcafeapiwrapper.jobs.example.my_job import my_job
class TestMyJob(unittest.TestCase):
def setUp(self):
self.api_client = APIClient(
client_id="test-id",
client_secret="test-secret",
debug=False
)
@patch('trustcafeapiwrapper.apiclient.requests.Session.request')
def test_my_job(self, mock_request):
mock_request.return_value.json.return_value = {"result": "success"}
mock_request.return_value.raise_for_status.return_value = None
result = my_job(self.api_client, "param1", 123)
self.assertEqual(result["result"], "success")
if __name__ == '__main__':
unittest.main()
```
### Code Style
- Follow PEP 8
- Use type hints
- Write comprehensive docstrings
- Add error handling
- Include examples in docstrings
### Testing Requirements
- All new features must have tests
- Test edge cases
- Mock external API calls
- Achieve 80%+ test coverage
## Testing Guide
### Current Test Structure
```
tests/
├── apiclient.py # API client tests
├── test_api_client.py # Legacy test file
```
### Running Tests
```bash
# Run all tests
pytest
# Run specific test file
pytest tests/apiclient.py
# Run with coverage
pytest --cov=src/trustcafeapiwrapper
# Run with verbose output
pytest -v
# Run specific test
pytest tests/apiclient.py::TestAPIClient::test_initialization
```
### Test Best Practices
1. **Mock External Calls**
```python
@patch('trustcafeapiwrapper.apiclient.requests.Session.request')
def test_implementation(self, mock_request):
mock_request.return_value.json.return_value = {"mock": "response"}
# Test logic
```
2. **Test All Paths**
```python
def test_error_handling():
# Success case
assert result == expected
# Error cases
with pytest.raises(ValueError):
handle_error_param
```
3. **Keep Tests Independent**
- Each test should work alone
- No shared state between tests
- Use setUp for common setup
4. **Clear Test Names**
```python
# Good
def test_userprofile_get_with_invalid_username_raises_error(self):
# Test description
# Bad
def test_invalid(self):
# Unclear what this tests
```
## Extending the Wrapper
### Adding Configuration
```python
# Add to pyproject.toml
[tool.poetry.dependencies]
trustcafeapiwrapper = { path = "." }
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest-cov>=4.0.0",
"httpx>=0.24.0",
]
test = [
"pytest>=7.0.0",
]
```
### Adding New Endpoints
Edit `src/trustcafeapiwrapper/apiclient.py`:
```python
endpoints = {
"alpha": {
# ... existing endpoints ...
"newendpoint": "https://api.example.com/alpha/",
},
}
```
### Adding Utility Functions
```python
# src/trustcafeapiwrapper/utils/my_utilty.py
def my_utility_function(input_data: str) -> dict:
"""Utility function for common data transformations"""
# Implementation
return transformed_data
```
---
## Getting Started with Development
1. **Fork the repository**
- Add your fork as a remote
- Keep pull requests to main
2. **Set up development environment**
```bash
# Clone your fork
git clone https://gitlab.com/your-username/trustcafe-api-wrapper.git
cd trustcafe-api-wrapper
# Install dependencies
pip install -e ".[dev]"
# Run tests
pytest
```
3. **Create feature branch**
```bash
git checkout -b feature/your-feature-name
```
4. **Make changes**
- Add code
- Add tests
- Update documentation
5. **Submit PR**
- Branch must be up-to-date with main
- Include helpful description
- Reference related issues
---
## Resources
- **Stavanger AI**: See the development.md debate notes
- **GitLab Repository**: https://gitlab.com/trustcafe/trustcafe-api-wrapper
- **WikiTribune Team**: Contact via company channels
- **Packaging**: pyproject.toml for dependencies and metadata
---
**Stay Updated**: Read development.md for ongoing decisions and discussions. This file is intentionally loose to capture evolving thoughts and debates as the project progresses.

847
docs/API_REFERENCE.md Normal file
View file

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

767
docs/CUSTOM_REQUESTS.md Normal file
View file

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

469
docs/GETTING_STARTED.md Normal file
View file

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

162
docs/INDEX.md Normal file
View file

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

763
docs/TROUBLESHOOTING.md Normal file
View file

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

509
docs/WRAPPERS.md Normal file
View file

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