ready for try

This commit is contained in:
2025-07-29 20:16:11 -04:00
parent 29001b02a5
commit 18a82711cb
33 changed files with 7446 additions and 47 deletions

117
docs/CHANGELOG.md Normal file
View File

@@ -0,0 +1,117 @@
# Changelog
All notable changes to the Wiki.js Python SDK will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Project foundation and repository structure
- Python packaging configuration (setup.py, pyproject.toml)
- CI/CD pipeline with GitHub Actions
- Code quality tools (black, isort, flake8, mypy, bandit)
- Comprehensive documentation structure
- Contributing guidelines and community governance
- Issue and PR templates for GitHub
### Changed
- N/A
### Deprecated
- N/A
### Removed
- N/A
### Fixed
- N/A
### Security
- Added automated security scanning with bandit
## Release Planning
### [0.1.0] - Target: 2 weeks from start
**MVP Release - Basic Wiki.js Integration**
#### Planned Features
- Core WikiJSClient with HTTP transport
- API key authentication
- Pages API with full CRUD operations (list, get, create, update, delete)
- Type-safe data models with Pydantic
- Comprehensive error handling
- >85% test coverage
- Complete API documentation
- PyPI package publication
#### Success Criteria
- [ ] Package installable via `pip install wikijs-python-sdk`
- [ ] Basic page operations work with real Wiki.js instance
- [ ] All quality gates pass (tests, coverage, linting, security)
- [ ] Documentation sufficient for basic usage
### [0.2.0] - Target: 4 weeks from start
**Essential Features - Complete API Coverage**
#### Planned Features
- Users API (full CRUD operations)
- Groups API (management and permissions)
- Assets API (file upload and management)
- System API (health checks and info)
- Enhanced error handling with detailed context
- Configuration management (file and environment-based)
- Basic CLI interface
- Performance benchmarks
### [0.3.0] - Target: 7 weeks from start
**Production Ready - Reliability & Performance**
#### Planned Features
- Retry logic with exponential backoff
- Circuit breaker for fault tolerance
- Intelligent caching with multiple backends
- Rate limiting and API compliance
- Performance monitoring and metrics
- Bulk operations for efficiency
- Connection pooling optimization
### [1.0.0] - Target: 11 weeks from start
**Enterprise Grade - Advanced Features**
#### Planned Features
- Full async/await support with aiohttp
- Advanced CLI with interactive mode
- Plugin architecture for extensibility
- Advanced authentication (JWT rotation, OAuth2)
- Enterprise security features
- Webhook support and verification
- Multi-tenancy support
---
## Development Notes
### Versioning Strategy
- **MAJOR**: Breaking changes that require user action
- **MINOR**: New features that are backward compatible
- **PATCH**: Bug fixes and improvements that are backward compatible
### Quality Standards
All releases must meet:
- [ ] >85% test coverage (>90% for minor releases)
- [ ] 100% type coverage on public APIs
- [ ] All quality gates pass (linting, formatting, security)
- [ ] Complete documentation for new features
- [ ] No known critical bugs
### Community Involvement
- Feature requests welcomed through GitHub issues
- Community feedback incorporated into release planning
- Breaking changes require community discussion period
- Beta releases available for testing major features
---
*This changelog is maintained as part of our commitment to transparency and professional development practices. All changes are documented to help users understand what's new, what's changed, and how to upgrade safely.*

342
docs/CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,342 @@
# Contributing to Wiki.js Python SDK
Thank you for your interest in contributing to the Wiki.js Python SDK! This guide will help you get started with contributing to this AI-assisted, community-driven project.
## 🎯 Project Context
This project is being developed entirely with AI assistance (Claude), showcasing professional development practices while building a production-ready SDK for Wiki.js. We welcome contributors of all experience levels!
**Current Status**: MVP Development (Phase 1)
**Focus**: Core functionality and foundational quality
## 🚀 Getting Started
### Prerequisites
- Python 3.8 or higher
- Git
- A GitHub account
- (Optional) A Wiki.js instance for testing
### Development Setup
1. **Fork the Repository**
```bash
# Fork on GitHub, then clone your fork
git clone https://github.com/yourusername/wikijs-python-sdk.git
cd wikijs-python-sdk
```
2. **Set Up Development Environment**
```bash
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install in development mode
pip install -e ".[dev]"
# Install pre-commit hooks
pre-commit install
```
3. **Verify Setup**
```bash
# Run tests
pytest
# Run quality checks
pre-commit run --all-files
```
## 📋 Development Process
### Finding Work
1. **Check Current Priorities**
- Review [CLAUDE.md](CLAUDE.md) for current development tasks
- See [Development Plan](docs/wikijs_sdk_release_plan.md) for roadmap
- Look for issues labeled `good first issue`
2. **Understand Architecture**
- Read [Architecture Overview](docs/wikijs_sdk_architecture.md)
- Review existing code patterns
- Check the [Risk Management](docs/RISK_MANAGEMENT.md) for known issues
### Making Changes
1. **Create Feature Branch**
```bash
git checkout -b feature/your-feature-name
```
2. **Follow Code Standards**
- Write type hints for all public APIs
- Add docstrings to all public methods (Google style)
- Follow existing code patterns
- Keep functions small and focused
3. **Write Tests**
- Add unit tests for new functionality
- Maintain >85% code coverage
- Use descriptive test names
- Include edge cases
4. **Update Documentation**
- Update docstrings for changed methods
- Add examples for new features
- Update README if needed
### Code Quality Standards
#### Python Style
```python
def example_function(param: str, optional: Optional[int] = None) -> bool:
"""Example function following our standards.
Args:
param: Description of the parameter
optional: Optional parameter with default
Returns:
Boolean result of the operation
Raises:
ValueError: If param is empty
Example:
>>> example_function("test")
True
"""
if not param:
raise ValueError("param cannot be empty")
# Implementation here
return True
```
#### Testing Patterns
```python
import pytest
from wikijs import WikiJSClient
from wikijs.exceptions import WikiJSException
class TestWikiJSClient:
"""Test WikiJS client functionality."""
def test_client_initialization_success(self):
"""Test successful client initialization."""
client = WikiJSClient("https://wiki.example.com", "api-key")
assert client.base_url == "https://wiki.example.com"
def test_client_initialization_invalid_url(self):
"""Test client initialization with invalid URL."""
with pytest.raises(ValueError, match="Invalid URL"):
WikiJSClient("invalid-url", "api-key")
```
### Submitting Changes
1. **Run Quality Checks**
```bash
# Format code
black wikijs tests
isort wikijs tests
# Run linting
flake8 wikijs tests
mypy wikijs
# Run tests
pytest --cov=wikijs --cov-fail-under=85
# Security scan
bandit -r wikijs
```
2. **Commit Changes**
```bash
git add .
git commit -m "feat: add new feature description
Detailed description of what was changed and why.
Closes #123"
```
3. **Create Pull Request**
- Push to your fork
- Create PR against `main` branch
- Fill out the PR template completely
- Link related issues
## 🤝 Community Guidelines
### Communication
- **Be respectful** and constructive in all interactions
- **Ask questions** if anything is unclear
- **Help others** when you can
- **Focus on the code**, not the person
### Code Review Process
1. **Maintainer Review**: All PRs reviewed by project maintainer
2. **Feedback**: Address review comments promptly
3. **Discussion**: Open discussion encouraged for design decisions
4. **Approval**: PR approved when all checks pass and review complete
### Response Times
- **Issues**: Response within 48-72 hours
- **Pull Requests**: Initial review within 1 week
- **Questions**: Community-driven with maintainer backup
## 🏗️ Architecture & Patterns
### Project Structure
```
wikijs/
├── __init__.py # Package entry point
├── client.py # Main client class
├── exceptions.py # Exception hierarchy
├── models/ # Data models (Pydantic)
├── auth/ # Authentication handlers
├── endpoints/ # API endpoint implementations
└── utils/ # Utility functions
```
### Key Patterns
- **Dependency Injection**: Use abstract interfaces
- **Type Safety**: Full type hints and validation
- **Error Handling**: Comprehensive exception hierarchy
- **Testing**: Mock external dependencies
- **Documentation**: Example-driven docstrings
## 🧪 Testing Guidelines
### Test Categories
- **Unit Tests**: Test individual components in isolation
- **Integration Tests**: Test component interactions
- **End-to-End**: Test with real Wiki.js instance (CI only)
### Test Organization
```
tests/
├── unit/
│ ├── test_client.py
│ ├── test_models.py
│ └── test_auth.py
├── integration/
│ └── test_api_endpoints.py
└── conftest.py # Shared fixtures
```
### Writing Good Tests
```python
def test_specific_behavior_with_expected_outcome():
"""Test description should be clear and specific."""
# Arrange
client = WikiJSClient("https://example.com", "key")
# Act
result = client.some_method()
# Assert
assert result.expected_property == "expected_value"
```
## 🐛 Bug Reports
### Before Reporting
1. Check existing issues
2. Test with latest version
3. Provide minimal reproduction case
### Bug Report Checklist
- [ ] Clear description of the problem
- [ ] Steps to reproduce
- [ ] Expected vs actual behavior
- [ ] Environment details
- [ ] Error messages/stack traces
- [ ] Minimal code example
## ✨ Feature Requests
### Before Requesting
1. Check if feature already exists
2. Search existing feature requests
3. Consider if it fits project scope
### Feature Request Checklist
- [ ] Clear use case description
- [ ] Proposed API design
- [ ] Implementation considerations
- [ ] Breaking change analysis
## 🚢 Release Process
Releases are managed by maintainers:
1. **Version Bump**: Update version in `wikijs/version.py`
2. **Changelog**: Update `CHANGELOG.md` with changes
3. **Tag Release**: Create git tag `v0.1.0`
4. **Automated**: GitHub Actions handles building and PyPI publishing
## 📚 Documentation
### Types of Documentation
- **API Reference**: Auto-generated from docstrings
- **User Guides**: How-to guides and tutorials
- **Examples**: Real-world usage examples
- **Architecture**: Technical design decisions
### Documentation Standards
- Write for your audience (users vs contributors)
- Include practical examples
- Keep it up-to-date with code changes
- Use clear, concise language
## 🤖 AI Development Context
This project uses Claude AI for development coordination. If you're interested in the AI-assisted development process:
- See [CLAUDE.md](CLAUDE.md) for AI coordination details
- Development tasks are tracked and managed through AI sessions
- Quality standards are maintained through automated tooling
- Community contributions are integrated with AI-assisted development
## ❓ Getting Help
### Questions and Support
- **GitHub Discussions**: General questions and community chat
- **GitHub Issues**: Bug reports and feature requests
- **Documentation**: Check docs for existing answers
- **Code Review**: Learn through the review process
### Common Questions
**Q: How do I set up a test Wiki.js instance?**
A: Check the testing documentation for local setup instructions.
**Q: What should I work on first?**
A: Look for `good first issue` labels or check CLAUDE.md for current priorities.
**Q: How do I add a new API endpoint?**
A: Follow the existing patterns in `wikijs/endpoints/` and add corresponding tests.
## 🙏 Recognition
All contributors are recognized in:
- `CONTRIBUTORS.md` file
- Release notes for significant contributions
- GitHub contributors page
Significant contributors may be invited to become maintainers.
---
**Ready to contribute?**
1. Read our [Governance](docs/GOVERNANCE.md) guidelines
2. Check the [current development status](CLAUDE.md)
3. Look for [good first issues](https://github.com/yourusername/wikijs-python-sdk/labels/good%20first%20issue)
4. Join the discussion!
**Questions?** Don't hesitate to ask in [GitHub Discussions](https://github.com/yourusername/wikijs-python-sdk/discussions) or create an issue.

614
docs/api_reference.md Normal file
View File

@@ -0,0 +1,614 @@
# API Reference
Complete reference for the Wiki.js Python SDK.
## Table of Contents
- [Client](#client)
- [Authentication](#authentication)
- [Pages API](#pages-api)
- [Models](#models)
- [Exceptions](#exceptions)
- [Utilities](#utilities)
---
## Client
### WikiJSClient
The main client class for interacting with Wiki.js API.
```python
from wikijs import WikiJSClient
client = WikiJSClient(
base_url="https://wiki.example.com",
auth="your-api-key",
timeout=30,
verify_ssl=True,
user_agent="Custom-Agent/1.0"
)
```
#### Parameters
- **base_url** (`str`): The base URL of your Wiki.js instance
- **auth** (`str | AuthHandler`): Authentication (API key string or auth handler)
- **timeout** (`int`, optional): Request timeout in seconds (default: 30)
- **verify_ssl** (`bool`, optional): Whether to verify SSL certificates (default: True)
- **user_agent** (`str`, optional): Custom User-Agent header
#### Methods
##### test_connection()
Test connection to Wiki.js instance.
```python
is_connected = client.test_connection()
```
**Returns:** `bool` - True if connection successful
**Raises:**
- `ConfigurationError`: If client is not properly configured
- `ConnectionError`: If cannot connect to server
- `AuthenticationError`: If authentication fails
##### close()
Close the HTTP session and clean up resources.
```python
client.close()
```
#### Context Manager Support
```python
with WikiJSClient("https://wiki.example.com", auth="api-key") as client:
pages = client.pages.list()
# Session automatically closed
```
---
## Authentication
### API Key Authentication
```python
from wikijs.auth import APIKeyAuth
auth = APIKeyAuth("your-api-key")
client = WikiJSClient("https://wiki.example.com", auth=auth)
```
### JWT Authentication
```python
from wikijs.auth import JWTAuth
auth = JWTAuth(
username="your-username",
password="your-password"
)
client = WikiJSClient("https://wiki.example.com", auth=auth)
```
---
## Pages API
Access the Pages API through `client.pages`.
### list()
List pages with optional filtering and pagination.
```python
pages = client.pages.list(
limit=10,
offset=0,
search="query",
tags=["tag1", "tag2"],
locale="en",
author_id=1,
order_by="title",
order_direction="ASC"
)
```
#### Parameters
- **limit** (`int`, optional): Maximum number of pages to return
- **offset** (`int`, optional): Number of pages to skip
- **search** (`str`, optional): Search term to filter pages
- **tags** (`List[str]`, optional): List of tags to filter by
- **locale** (`str`, optional): Locale to filter by
- **author_id** (`int`, optional): Author ID to filter by
- **order_by** (`str`, optional): Field to order by (`title`, `created_at`, `updated_at`, `path`)
- **order_direction** (`str`, optional): Order direction (`ASC` or `DESC`)
**Returns:** `List[Page]` - List of Page objects
**Raises:**
- `APIError`: If the API request fails
- `ValidationError`: If parameters are invalid
### get()
Get a specific page by ID.
```python
page = client.pages.get(123)
```
#### Parameters
- **page_id** (`int`): The page ID
**Returns:** `Page` - Page object
**Raises:**
- `APIError`: If the page is not found or request fails
- `ValidationError`: If page_id is invalid
### get_by_path()
Get a page by its path.
```python
page = client.pages.get_by_path("getting-started", locale="en")
```
#### Parameters
- **path** (`str`): The page path
- **locale** (`str`, optional): The page locale (default: "en")
**Returns:** `Page` - Page object
**Raises:**
- `APIError`: If the page is not found or request fails
- `ValidationError`: If path is invalid
### create()
Create a new page.
```python
from wikijs.models import PageCreate
new_page_data = PageCreate(
title="Getting Started",
path="getting-started",
content="# Welcome\n\nThis is your first page!",
description="Getting started guide",
tags=["guide", "tutorial"],
is_published=True
)
created_page = client.pages.create(new_page_data)
```
#### Parameters
- **page_data** (`PageCreate | dict`): Page creation data
**Returns:** `Page` - Created Page object
**Raises:**
- `APIError`: If page creation fails
- `ValidationError`: If page data is invalid
### update()
Update an existing page.
```python
from wikijs.models import PageUpdate
update_data = PageUpdate(
title="Updated Title",
content="Updated content",
tags=["updated"]
)
updated_page = client.pages.update(123, update_data)
```
#### Parameters
- **page_id** (`int`): The page ID
- **page_data** (`PageUpdate | dict`): Page update data
**Returns:** `Page` - Updated Page object
**Raises:**
- `APIError`: If page update fails
- `ValidationError`: If parameters are invalid
### delete()
Delete a page.
```python
success = client.pages.delete(123)
```
#### Parameters
- **page_id** (`int`): The page ID
**Returns:** `bool` - True if deletion was successful
**Raises:**
- `APIError`: If page deletion fails
- `ValidationError`: If page_id is invalid
### search()
Search for pages by content and title.
```python
results = client.pages.search("search query", limit=10)
```
#### Parameters
- **query** (`str`): Search query string
- **limit** (`int`, optional): Maximum number of results to return
- **locale** (`str`, optional): Locale to search in
**Returns:** `List[Page]` - List of matching Page objects
**Raises:**
- `APIError`: If search fails
- `ValidationError`: If parameters are invalid
### get_by_tags()
Get pages by tags.
```python
pages = client.pages.get_by_tags(
tags=["tutorial", "guide"],
match_all=True,
limit=10
)
```
#### Parameters
- **tags** (`List[str]`): List of tags to search for
- **match_all** (`bool`, optional): If True, pages must have ALL tags (default: True)
- **limit** (`int`, optional): Maximum number of results to return
**Returns:** `List[Page]` - List of matching Page objects
**Raises:**
- `APIError`: If request fails
- `ValidationError`: If parameters are invalid
---
## Models
### Page
Represents a Wiki.js page with all metadata and content.
```python
from wikijs.models import Page
```
#### Properties
- **id** (`int`): Unique page identifier
- **title** (`str`): Page title
- **path** (`str`): Page path/slug
- **content** (`str`): Page content
- **description** (`str`, optional): Page description
- **is_published** (`bool`): Whether page is published
- **is_private** (`bool`): Whether page is private
- **tags** (`List[str]`): Page tags
- **locale** (`str`): Page locale
- **author_id** (`int`, optional): Author ID
- **author_name** (`str`, optional): Author name
- **author_email** (`str`, optional): Author email
- **editor** (`str`, optional): Editor used
- **created_at** (`datetime`): Creation timestamp
- **updated_at** (`datetime`): Last update timestamp
#### Computed Properties
```python
# Word count
word_count = page.word_count
# Reading time (minutes)
reading_time = page.reading_time
# Full URL path
url_path = page.url_path
```
#### Methods
```python
# Extract markdown headings
headings = page.extract_headings()
# Check if page has a tag
has_tutorial_tag = page.has_tag("tutorial")
```
### PageCreate
Data model for creating a new page.
```python
from wikijs.models import PageCreate
page_data = PageCreate(
title="New Page",
path="new-page",
content="Page content",
description="Optional description",
is_published=True,
is_private=False,
tags=["tag1", "tag2"],
locale="en",
editor="markdown"
)
```
#### Required Fields
- **title** (`str`): Page title
- **path** (`str`): Page path/slug
- **content** (`str`): Page content
#### Optional Fields
- **description** (`str`): Page description
- **is_published** (`bool`): Whether to publish immediately (default: True)
- **is_private** (`bool`): Whether page should be private (default: False)
- **tags** (`List[str]`): Page tags (default: [])
- **locale** (`str`): Page locale (default: "en")
- **editor** (`str`): Editor to use (default: "markdown")
### PageUpdate
Data model for updating an existing page.
```python
from wikijs.models import PageUpdate
update_data = PageUpdate(
title="Updated Title",
content="Updated content",
tags=["new-tag"]
)
```
#### Optional Fields (all)
- **title** (`str`): Page title
- **content** (`str`): Page content
- **description** (`str`): Page description
- **is_published** (`bool`): Publication status
- **is_private** (`bool`): Privacy status
- **tags** (`List[str]`): Page tags
---
## Exceptions
### APIError
Base exception for API-related errors.
```python
from wikijs.exceptions import APIError
try:
page = client.pages.get(999)
except APIError as e:
print(f"API error: {e}")
```
### AuthenticationError
Raised when authentication fails.
```python
from wikijs.exceptions import AuthenticationError
try:
client.test_connection()
except AuthenticationError as e:
print(f"Authentication failed: {e}")
```
### ValidationError
Raised when input validation fails.
```python
from wikijs.exceptions import ValidationError
try:
page_data = PageCreate(title="", path="invalid path")
except ValidationError as e:
print(f"Validation error: {e}")
```
### ConfigurationError
Raised when client configuration is invalid.
```python
from wikijs.exceptions import ConfigurationError
try:
client = WikiJSClient("", auth=None)
except ConfigurationError as e:
print(f"Configuration error: {e}")
```
### ConnectionError
Raised when connection to Wiki.js fails.
```python
from wikijs.exceptions import ConnectionError
try:
client.test_connection()
except ConnectionError as e:
print(f"Connection error: {e}")
```
### TimeoutError
Raised when requests timeout.
```python
from wikijs.exceptions import TimeoutError
try:
pages = client.pages.list()
except TimeoutError as e:
print(f"Request timed out: {e}")
```
---
## Utilities
### URL Utilities
```python
from wikijs.utils import normalize_url, build_api_url
# Normalize a base URL
normalized = normalize_url("https://wiki.example.com/")
# Build API endpoint URL
api_url = build_api_url("https://wiki.example.com", "/graphql")
```
### Response Utilities
```python
from wikijs.utils import parse_wiki_response, extract_error_message
# Parse Wiki.js API response
data = parse_wiki_response(response_data)
# Extract error message from HTTP response
error_msg = extract_error_message(http_response)
```
---
## Error Handling Best Practices
### Comprehensive Error Handling
```python
from wikijs import WikiJSClient
from wikijs.exceptions import (
APIError,
AuthenticationError,
ValidationError,
ConnectionError,
TimeoutError
)
try:
client = WikiJSClient("https://wiki.example.com", auth="api-key")
pages = client.pages.list(limit=10)
except AuthenticationError:
print("Invalid API key or authentication failed")
except ValidationError as e:
print(f"Invalid parameters: {e}")
except ConnectionError:
print("Cannot connect to Wiki.js instance")
except TimeoutError:
print("Request timed out")
except APIError as e:
print(f"API error: {e}")
```
### Retry Logic
```python
import time
from wikijs.exceptions import TimeoutError, ConnectionError
def with_retry(func, max_retries=3, delay=1):
for attempt in range(max_retries):
try:
return func()
except (TimeoutError, ConnectionError) as e:
if attempt == max_retries - 1:
raise
time.sleep(delay * (2 ** attempt)) # Exponential backoff
# Usage
pages = with_retry(lambda: client.pages.list())
```
---
## Performance Tips
### Connection Reuse
```python
# Use context manager for automatic cleanup
with WikiJSClient("https://wiki.example.com", auth="api-key") as client:
# Multiple operations reuse the same connection
pages = client.pages.list()
page = client.pages.get(123)
updated = client.pages.update(123, data)
```
### Pagination
```python
# Efficiently paginate through large result sets
def get_all_pages(client, batch_size=50):
offset = 0
all_pages = []
while True:
batch = client.pages.list(limit=batch_size, offset=offset)
if not batch:
break
all_pages.extend(batch)
offset += batch_size
return all_pages
```
### Filtering
```python
# Use server-side filtering instead of client-side
# Good: Filter on server
tutorial_pages = client.pages.get_by_tags(["tutorial"])
# Better: Combine filters
recent_tutorials = client.pages.list(
tags=["tutorial"],
order_by="updated_at",
order_direction="DESC",
limit=10
)
```

721
docs/development.md Normal file
View File

@@ -0,0 +1,721 @@
# Development Guide
Guide for contributors and developers working on the Wiki.js Python SDK.
## Table of Contents
- [Development Setup](#development-setup)
- [Project Structure](#project-structure)
- [Development Workflow](#development-workflow)
- [Testing](#testing)
- [Code Quality](#code-quality)
- [Documentation](#documentation)
- [Release Process](#release-process)
---
## Development Setup
### Prerequisites
- **Python 3.8+** (tested with 3.8, 3.9, 3.10, 3.11, 3.12)
- **Git** for version control
- **Wiki.js instance** for testing (can be local or remote)
### Environment Setup
1. **Clone the repository:**
```bash
git clone https://github.com/yourusername/wikijs-python-sdk.git
cd wikijs-python-sdk
```
2. **Create a virtual environment:**
```bash
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
```
3. **Install development dependencies:**
```bash
pip install -e ".[dev]"
```
4. **Set up pre-commit hooks:**
```bash
pre-commit install
```
5. **Configure environment variables:**
```bash
export WIKIJS_URL='https://your-test-wiki.example.com'
export WIKIJS_API_KEY='your-test-api-key'
```
### Verify Setup
```bash
# Run tests
pytest
# Check code quality
pre-commit run --all-files
# Verify package can be imported
python -c "from wikijs import WikiJSClient; print('✅ Setup successful!')"
```
---
## Project Structure
```
wikijs-python-sdk/
├── wikijs/ # Main package
│ ├── __init__.py # Package exports
│ ├── version.py # Version information
│ ├── client.py # Main WikiJS client
│ ├── exceptions.py # Exception classes
│ ├── auth/ # Authentication handlers
│ │ ├── __init__.py # Auth exports
│ │ ├── base.py # Base auth handler
│ │ ├── api_key.py # API key authentication
│ │ └── jwt.py # JWT authentication
│ ├── endpoints/ # API endpoints
│ │ ├── __init__.py # Endpoint exports
│ │ ├── base.py # Base endpoint class
│ │ └── pages.py # Pages API endpoint
│ ├── models/ # Data models
│ │ ├── __init__.py # Model exports
│ │ ├── base.py # Base model classes
│ │ └── page.py # Page-related models
│ └── utils/ # Utility functions
│ ├── __init__.py # Utility exports
│ └── helpers.py # Helper functions
├── tests/ # Test suite
│ ├── conftest.py # Test configuration
│ ├── auth/ # Authentication tests
│ ├── endpoints/ # Endpoint tests
│ ├── models/ # Model tests
│ └── utils/ # Utility tests
├── docs/ # Documentation
│ ├── api_reference.md # API reference
│ ├── user_guide.md # User guide
│ ├── development.md # This file
│ └── ...
├── examples/ # Usage examples
├── .github/ # GitHub workflows
│ └── workflows/ # CI/CD pipelines
├── pyproject.toml # Project configuration
├── setup.py # Package setup
├── requirements.txt # Runtime dependencies
├── requirements-dev.txt # Development dependencies
└── README.md # Project README
```
### Key Components
#### **Client (`wikijs/client.py`)**
- Main entry point for the SDK
- Manages HTTP sessions and requests
- Handles authentication and error handling
- Provides access to endpoint handlers
#### **Authentication (`wikijs/auth/`)**
- Base authentication handler interface
- Concrete implementations for API key and JWT auth
- Extensible for custom authentication methods
#### **Endpoints (`wikijs/endpoints/`)**
- API endpoint implementations
- Each endpoint handles a specific Wiki.js API area
- Base endpoint class provides common functionality
#### **Models (`wikijs/models/`)**
- Pydantic models for data validation and serialization
- Type-safe data structures
- Input validation and error handling
#### **Utilities (`wikijs/utils/`)**
- Helper functions for common operations
- URL handling, response parsing, etc.
- Shared utility functions
---
## Development Workflow
### Branch Strategy
- **`main`**: Stable, production-ready code
- **`develop`**: Integration branch for new features
- **Feature branches**: `feature/description` for new features
- **Bug fixes**: `fix/description` for bug fixes
- **Hotfixes**: `hotfix/description` for critical fixes
### Workflow Steps
1. **Create a feature branch:**
```bash
git checkout -b feature/new-awesome-feature
```
2. **Make your changes:**
- Write code following our style guidelines
- Add tests for new functionality
- Update documentation as needed
3. **Run quality checks:**
```bash
# Run tests
pytest
# Check code formatting
black --check .
# Check imports
isort --check-only .
# Type checking
mypy wikijs
# Linting
flake8 wikijs
# Security scan
bandit -r wikijs
```
4. **Commit your changes:**
```bash
git add .
git commit -m "feat: add awesome new feature"
```
5. **Push and create PR:**
```bash
git push origin feature/new-awesome-feature
# Create pull request on GitHub
```
### Commit Message Format
We follow [Conventional Commits](https://www.conventionalcommits.org/):
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
**Types:**
- `feat`: A new feature
- `fix`: A bug fix
- `docs`: Documentation only changes
- `style`: Changes that don't affect code meaning
- `refactor`: Code change that neither fixes a bug nor adds a feature
- `test`: Adding missing tests or correcting existing tests
- `chore`: Other changes that don't modify src or test files
**Examples:**
```
feat(auth): add JWT authentication support
fix(client): handle connection timeout properly
docs: update API reference for pages endpoint
test: add comprehensive model validation tests
```
---
## Testing
### Test Organization
- **Unit tests**: Test individual components in isolation
- **Integration tests**: Test component interactions
- **End-to-end tests**: Test complete workflows
- **Mock tests**: Test with mocked external dependencies
### Running Tests
```bash
# Run all tests
pytest
# Run with coverage
pytest --cov=wikijs --cov-report=html
# Run specific test file
pytest tests/test_client.py
# Run specific test
pytest tests/test_client.py::TestWikiJSClient::test_basic_initialization
# Run tests with verbose output
pytest -v
# Run tests and stop on first failure
pytest -x
```
### Writing Tests
#### Test Structure
```python
"""Tests for module_name."""
import pytest
from unittest.mock import Mock, patch
from wikijs.module_name import ClassUnderTest
from wikijs.exceptions import SomeException
class TestClassUnderTest:
"""Test suite for ClassUnderTest."""
@pytest.fixture
def sample_data(self):
"""Sample data for testing."""
return {"key": "value"}
def test_basic_functionality(self, sample_data):
"""Test basic functionality."""
# Arrange
instance = ClassUnderTest()
# Act
result = instance.some_method(sample_data)
# Assert
assert result is not None
assert result.key == "value"
def test_error_handling(self):
"""Test proper error handling."""
instance = ClassUnderTest()
with pytest.raises(SomeException, match="Expected error message"):
instance.method_that_should_fail()
@patch('wikijs.module_name.external_dependency')
def test_with_mocking(self, mock_dependency):
"""Test with mocked dependencies."""
# Setup mock
mock_dependency.return_value = "mocked result"
# Test
instance = ClassUnderTest()
result = instance.method_using_dependency()
# Verify
assert result == "mocked result"
mock_dependency.assert_called_once()
```
#### Test Guidelines
1. **Follow AAA pattern**: Arrange, Act, Assert
2. **Use descriptive test names** that explain what is being tested
3. **Test both success and failure cases**
4. **Mock external dependencies** (HTTP requests, file system, etc.)
5. **Use fixtures** for common test data and setup
6. **Maintain high test coverage** (target: >85%)
### Test Configuration
#### `conftest.py`
```python
"""Shared test configuration and fixtures."""
import pytest
from unittest.mock import Mock
from wikijs import WikiJSClient
@pytest.fixture
def mock_client():
"""Create a mock WikiJS client for testing."""
client = Mock(spec=WikiJSClient)
client.base_url = "https://test-wiki.example.com"
return client
@pytest.fixture
def sample_page_data():
"""Sample page data for testing."""
return {
"id": 123,
"title": "Test Page",
"path": "test-page",
"content": "# Test\n\nContent here.",
"is_published": True,
"tags": ["test"]
}
```
---
## Code Quality
### Code Style
We use several tools to maintain code quality:
- **Black**: Code formatting
- **isort**: Import sorting
- **flake8**: Linting
- **mypy**: Type checking
- **bandit**: Security scanning
### Configuration Files
#### `pyproject.toml`
```toml
[tool.black]
line-length = 88
target-version = ['py38']
include = '\.pyi?$'
[tool.isort]
profile = "black"
multi_line_output = 3
line_length = 88
[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "--strict-markers --disable-warnings"
```
### Pre-commit Hooks
```yaml
# .pre-commit-config.yaml
repos:
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.3.0
hooks:
- id: mypy
additional_dependencies: [types-requests]
```
### Quality Checks
Run these commands before committing:
```bash
# Format code
black .
isort .
# Check formatting
black --check .
isort --check-only .
# Lint code
flake8 wikijs tests
# Type checking
mypy wikijs
# Security scan
bandit -r wikijs
# Run all pre-commit hooks
pre-commit run --all-files
```
---
## Documentation
### Documentation Types
1. **API Reference**: Auto-generated from docstrings
2. **User Guide**: Manual documentation for end users
3. **Development Guide**: This document
4. **Examples**: Practical usage examples
5. **Changelog**: Version history and changes
### Writing Documentation
#### Docstring Format
We use Google-style docstrings:
```python
def create_page(self, page_data: PageCreate) -> Page:
"""Create a new page in the wiki.
Args:
page_data: Page creation data containing title, path, content, etc.
Returns:
The created Page object with assigned ID and metadata.
Raises:
ValidationError: If page data is invalid.
APIError: If the API request fails.
AuthenticationError: If authentication fails.
Example:
>>> from wikijs.models import PageCreate
>>> page_data = PageCreate(
... title="My Page",
... path="my-page",
... content="# Hello World"
... )
>>> created_page = client.pages.create(page_data)
>>> print(f"Created page with ID: {created_page.id}")
"""
```
#### Documentation Guidelines
1. **Be clear and concise** in explanations
2. **Include examples** for complex functionality
3. **Document all public APIs** with proper docstrings
4. **Keep documentation up to date** with code changes
5. **Use consistent formatting** and style
### Building Documentation
```bash
# Install documentation dependencies
pip install -e ".[docs]"
# Build documentation (if using Sphinx)
cd docs
make html
# Serve documentation locally
python -m http.server 8000 -d _build/html
```
---
## Release Process
### Version Management
We use [Semantic Versioning](https://semver.org/):
- **MAJOR**: Incompatible API changes
- **MINOR**: New functionality (backward compatible)
- **PATCH**: Bug fixes (backward compatible)
### Release Steps
1. **Update version number** in `wikijs/version.py`
2. **Update CHANGELOG.md** with new version details
3. **Run full test suite** and ensure all checks pass
4. **Create release commit**:
```bash
git add .
git commit -m "chore: bump version to v1.2.3"
```
5. **Create and push tag**:
```bash
git tag v1.2.3
git push origin main --tags
```
6. **GitHub Actions** will automatically:
- Run tests
- Build package
- Publish to PyPI
- Create GitHub release
### Pre-release Checklist
- [ ] All tests pass
- [ ] Code coverage meets requirements (>85%)
- [ ] Documentation is updated
- [ ] CHANGELOG.md is updated
- [ ] Version number is bumped
- [ ] No breaking changes without major version bump
- [ ] Examples work with new version
### Release Automation
Our CI/CD pipeline automatically handles:
- **Testing**: Run test suite on multiple Python versions
- **Quality checks**: Code formatting, linting, type checking
- **Security**: Vulnerability scanning
- **Building**: Create source and wheel distributions
- **Publishing**: Upload to PyPI on tagged releases
- **Documentation**: Update documentation site
---
## Contributing Guidelines
### Getting Started
1. **Fork the repository** on GitHub
2. **Create a feature branch** from `develop`
3. **Make your changes** following our guidelines
4. **Add tests** for new functionality
5. **Update documentation** as needed
6. **Submit a pull request**
### Pull Request Process
1. **Ensure CI passes** - all tests and quality checks must pass
2. **Update documentation** - include any necessary documentation updates
3. **Add tests** - maintain or improve test coverage
4. **Follow conventions** - use consistent code style and commit messages
5. **Be responsive** - address feedback and review comments promptly
### Code Review Guidelines
As a reviewer:
- **Be constructive** and helpful in feedback
- **Check for correctness** and potential issues
- **Verify tests** cover new functionality
- **Ensure documentation** is adequate
- **Approve when ready** or request specific changes
As an author:
- **Respond promptly** to review feedback
- **Make requested changes** or explain why they're not needed
- **Keep PRs focused** - one feature or fix per PR
- **Test thoroughly** before requesting review
---
## Debugging and Troubleshooting
### Common Development Issues
#### Import Errors
```bash
# Install package in development mode
pip install -e .
# Verify Python path
python -c "import sys; print(sys.path)"
```
#### Test Failures
```bash
# Run specific failing test with verbose output
pytest -xvs tests/path/to/failing_test.py::test_name
# Debug with pdb
pytest --pdb tests/path/to/failing_test.py::test_name
```
#### Type Checking Issues
```bash
# Run mypy on specific file
mypy wikijs/module_name.py
# Show mypy configuration
mypy --config-file
```
### Debugging Tools
#### Logging
```python
import logging
# Enable debug logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('wikijs')
logger.setLevel(logging.DEBUG)
```
#### Python Debugger
```python
import pdb
# Set breakpoint
pdb.set_trace()
# Or use built-in breakpoint() (Python 3.7+)
breakpoint()
```
#### HTTP Debugging
```python
import http.client
# Enable HTTP debugging
http.client.HTTPConnection.debuglevel = 1
```
---
## Resources
### Useful Links
- **[Wiki.js API Documentation](https://docs.js.wiki/dev/api)** - Official API docs
- **[GraphQL](https://graphql.org/learn/)** - GraphQL learning resources
- **[Pydantic](https://pydantic-docs.helpmanual.io/)** - Data validation library
- **[Requests](https://docs.python-requests.org/)** - HTTP library documentation
- **[pytest](https://docs.pytest.org/)** - Testing framework documentation
### Development Tools
- **VS Code Extensions**: Python, Pylance, Black Formatter, isort
- **PyCharm**: Professional Python IDE
- **Postman**: API testing tool
- **GraphQL Playground**: GraphQL query testing
### Community
- **GitHub Discussions**: Ask questions and share ideas
- **GitHub Issues**: Report bugs and request features
- **Pull Requests**: Contribute code improvements
---
## Questions?
If you have questions about development:
1. **Check this documentation** and the API reference
2. **Search existing issues** on GitHub
3. **Ask in GitHub Discussions** for community help
4. **Create an issue** for bugs or feature requests
Happy coding! 🚀

804
docs/user_guide.md Normal file
View File

@@ -0,0 +1,804 @@
# User Guide
Complete guide to using the Wiki.js Python SDK for common tasks and workflows.
## Table of Contents
- [Getting Started](#getting-started)
- [Authentication](#authentication)
- [Working with Pages](#working-with-pages)
- [Advanced Features](#advanced-features)
- [Best Practices](#best-practices)
- [Troubleshooting](#troubleshooting)
---
## Getting Started
### Installation
```bash
pip install wikijs-python-sdk
```
### Basic Setup
```python
from wikijs import WikiJSClient
# Initialize the client
client = WikiJSClient(
base_url="https://your-wiki.example.com",
auth="your-api-key"
)
# Test the connection
if client.test_connection():
print("Connected successfully!")
else:
print("Connection failed")
```
### Your First API Call
```python
# Get all pages
pages = client.pages.list()
print(f"Found {len(pages)} pages")
# Get a specific page
page = client.pages.get(1)
print(f"Page title: {page.title}")
```
---
## Authentication
### API Key Authentication
The simplest way to authenticate is with an API key:
```python
from wikijs import WikiJSClient
client = WikiJSClient(
base_url="https://wiki.example.com",
auth="your-api-key-here"
)
```
**Getting an API Key:**
1. Log into your Wiki.js admin panel
2. Go to Administration → API Keys
3. Create a new API key with appropriate permissions
4. Copy the generated key
### JWT Authentication
For username/password authentication:
```python
from wikijs import WikiJSClient
from wikijs.auth import JWTAuth
auth = JWTAuth(
username="your-username",
password="your-password"
)
client = WikiJSClient(
base_url="https://wiki.example.com",
auth=auth
)
```
### Custom Authentication
You can also create custom authentication handlers:
```python
from wikijs.auth import AuthHandler
class CustomAuth(AuthHandler):
def __init__(self, token):
self.token = token
def get_headers(self):
return {"Authorization": f"Bearer {self.token}"}
def validate_credentials(self):
if not self.token:
raise ValueError("Token is required")
client = WikiJSClient(
base_url="https://wiki.example.com",
auth=CustomAuth("your-custom-token")
)
```
---
## Working with Pages
### Listing Pages
#### Basic Listing
```python
# Get all pages
all_pages = client.pages.list()
# Get first 10 pages
first_10 = client.pages.list(limit=10)
# Get pages 11-20 (pagination)
next_10 = client.pages.list(limit=10, offset=10)
```
#### Filtering and Searching
```python
# Search by content
search_results = client.pages.search("getting started")
# Filter by tags
tutorial_pages = client.pages.get_by_tags(["tutorial", "guide"])
# Filter by author
author_pages = client.pages.list(author_id=1)
# Filter by locale
french_pages = client.pages.list(locale="fr")
```
#### Sorting
```python
# Sort by title (A-Z)
pages_by_title = client.pages.list(
order_by="title",
order_direction="ASC"
)
# Sort by most recently updated
recent_pages = client.pages.list(
order_by="updated_at",
order_direction="DESC",
limit=10
)
# Sort by creation date (oldest first)
oldest_pages = client.pages.list(
order_by="created_at",
order_direction="ASC"
)
```
### Getting Individual Pages
#### By ID
```python
# Get page with ID 123
page = client.pages.get(123)
print(f"Title: {page.title}")
print(f"Content: {page.content}")
```
#### By Path
```python
# Get page by its path
page = client.pages.get_by_path("getting-started")
# Get page in specific locale
french_page = client.pages.get_by_path("guide-utilisateur", locale="fr")
```
### Creating Pages
#### Basic Page Creation
```python
from wikijs.models import PageCreate
# Create a simple page
new_page = PageCreate(
title="My New Page",
path="my-new-page",
content="# Welcome\n\nThis is my new page content!"
)
created_page = client.pages.create(new_page)
print(f"Created page with ID: {created_page.id}")
```
#### Advanced Page Creation
```python
from wikijs.models import PageCreate
# Create a comprehensive page
new_page = PageCreate(
title="Complete Guide to Wiki.js",
path="guides/wikijs-complete-guide",
content="""# Complete Guide to Wiki.js
## Introduction
This guide covers everything you need to know about Wiki.js.
## Getting Started
1. Installation
2. Configuration
3. First steps
## Advanced Topics
- Custom themes
- Plugin development
- API integration
""",
description="A comprehensive guide covering all aspects of Wiki.js",
tags=["guide", "tutorial", "wikijs", "documentation"],
is_published=True,
is_private=False,
locale="en",
editor="markdown"
)
created_page = client.pages.create(new_page)
```
#### Creating from Dictionary
```python
# You can also use a dictionary
page_data = {
"title": "Quick Note",
"path": "quick-note",
"content": "This is a quick note.",
"tags": ["note", "quick"]
}
created_page = client.pages.create(page_data)
```
### Updating Pages
#### Partial Updates
```python
from wikijs.models import PageUpdate
# Update only specific fields
update_data = PageUpdate(
title="Updated Title",
tags=["updated", "modified"]
)
updated_page = client.pages.update(123, update_data)
```
#### Full Content Update
```python
from wikijs.models import PageUpdate
# Update content and metadata
update_data = PageUpdate(
title="Revised Guide",
content="""# Revised Guide
This guide has been completely updated with new information.
## What's New
- Updated examples
- New best practices
- Latest features
## Migration Guide
If you're upgrading from the previous version...
""",
description="Updated guide with latest information",
tags=["guide", "updated", "v2"],
is_published=True
)
updated_page = client.pages.update(123, update_data)
```
### Deleting Pages
```python
# Delete a page by ID
success = client.pages.delete(123)
if success:
print("Page deleted successfully")
else:
print("Failed to delete page")
```
**⚠️ Warning:** Page deletion is permanent and cannot be undone!
---
## Advanced Features
### Working with Page Metadata
```python
# Get a page
page = client.pages.get(123)
# Access metadata
print(f"Word count: {page.word_count}")
print(f"Reading time: {page.reading_time} minutes")
print(f"Author: {page.author_name}")
print(f"Created: {page.created_at}")
print(f"Last updated: {page.updated_at}")
# Check tags
if page.has_tag("tutorial"):
print("This is a tutorial page")
# Extract headings
headings = page.extract_headings()
print("Page structure:")
for heading in headings:
print(f"- {heading}")
```
### Batch Operations
#### Creating Multiple Pages
```python
from wikijs.models import PageCreate
# Prepare multiple pages
pages_to_create = [
PageCreate(
title=f"Chapter {i}",
path=f"guide/chapter-{i}",
content=f"# Chapter {i}\n\nContent for chapter {i}",
tags=["guide", f"chapter-{i}"]
)
for i in range(1, 6)
]
# Create them one by one
created_pages = []
for page_data in pages_to_create:
try:
created_page = client.pages.create(page_data)
created_pages.append(created_page)
print(f"Created: {created_page.title}")
except Exception as e:
print(f"Failed to create page: {e}")
print(f"Successfully created {len(created_pages)} pages")
```
#### Bulk Updates
```python
from wikijs.models import PageUpdate
# Get pages to update
tutorial_pages = client.pages.get_by_tags(["tutorial"])
# Update all tutorial pages
update_data = PageUpdate(
tags=["tutorial", "updated-2024"]
)
updated_count = 0
for page in tutorial_pages:
try:
client.pages.update(page.id, update_data)
updated_count += 1
except Exception as e:
print(f"Failed to update page {page.id}: {e}")
print(f"Updated {updated_count} tutorial pages")
```
### Content Migration
```python
def migrate_content_format(page):
"""Convert old format to new format."""
old_content = page.content
# Example: Convert old-style headers
new_content = old_content.replace("==", "##")
new_content = new_content.replace("===", "###")
return new_content
# Get pages to migrate
pages_to_migrate = client.pages.list(search="old-format")
for page in pages_to_migrate:
try:
new_content = migrate_content_format(page)
update_data = PageUpdate(
content=new_content,
tags=page.tags + ["migrated"]
)
client.pages.update(page.id, update_data)
print(f"Migrated: {page.title}")
except Exception as e:
print(f"Failed to migrate {page.title}: {e}")
```
### Template System
```python
from wikijs.models import PageCreate
def create_from_template(title, path, template_data):
"""Create a page from a template."""
# Define templates
templates = {
"meeting_notes": """# {meeting_title}
**Date:** {date}
**Attendees:** {attendees}
## Agenda
{agenda}
## Discussion Points
{discussion}
## Action Items
{actions}
## Next Meeting
{next_meeting}
""",
"project_doc": """# {project_name}
## Overview
{overview}
## Requirements
{requirements}
## Timeline
{timeline}
## Resources
{resources}
## Status
- [ ] Planning
- [ ] Development
- [ ] Testing
- [ ] Deployment
"""
}
template = templates.get(template_data["template_type"])
if not template:
raise ValueError(f"Unknown template: {template_data['template_type']}")
# Format template
content = template.format(**template_data)
# Create page
page_data = PageCreate(
title=title,
path=path,
content=content,
tags=template_data.get("tags", [])
)
return client.pages.create(page_data)
# Use template
meeting_page = create_from_template(
title="Weekly Team Meeting - Jan 15",
path="meetings/2024-01-15-weekly",
template_data={
"template_type": "meeting_notes",
"meeting_title": "Weekly Team Meeting",
"date": "January 15, 2024",
"attendees": "Alice, Bob, Charlie",
"agenda": "- Project updates\n- Q1 planning\n- Process improvements",
"discussion": "TBD",
"actions": "TBD",
"next_meeting": "January 22, 2024",
"tags": ["meeting", "weekly", "team"]
}
)
```
---
## Best Practices
### Error Handling
```python
from wikijs.exceptions import (
APIError,
AuthenticationError,
ValidationError,
ConnectionError,
TimeoutError
)
def safe_page_operation(operation_func):
"""Wrapper for safe page operations with proper error handling."""
try:
return operation_func()
except AuthenticationError:
print("❌ Authentication failed. Check your API key.")
return None
except ValidationError as e:
print(f"❌ Invalid input: {e}")
return None
except ConnectionError:
print("❌ Cannot connect to Wiki.js. Check your URL and network.")
return None
except TimeoutError:
print("❌ Request timed out. Try again later.")
return None
except APIError as e:
print(f"❌ API error: {e}")
return None
# Usage
result = safe_page_operation(lambda: client.pages.get(123))
if result:
print(f"✅ Got page: {result.title}")
```
### Resource Management
```python
# Always use context managers for automatic cleanup
with WikiJSClient("https://wiki.example.com", auth="api-key") as client:
# Do your work here
pages = client.pages.list()
# Connection automatically closed when exiting the block
# Or manually manage resources
client = WikiJSClient("https://wiki.example.com", auth="api-key")
try:
pages = client.pages.list()
finally:
client.close() # Always close when done
```
### Configuration Management
```python
import os
from wikijs import WikiJSClient
# Use environment variables for configuration
def create_client():
"""Create a properly configured client from environment variables."""
base_url = os.getenv("WIKIJS_URL")
api_key = os.getenv("WIKIJS_API_KEY")
if not base_url or not api_key:
raise ValueError("WIKIJS_URL and WIKIJS_API_KEY environment variables are required")
return WikiJSClient(
base_url=base_url,
auth=api_key,
timeout=int(os.getenv("WIKIJS_TIMEOUT", "30")),
verify_ssl=os.getenv("WIKIJS_VERIFY_SSL", "true").lower() == "true"
)
# Usage
client = create_client()
```
### Logging
```python
import logging
from wikijs import WikiJSClient
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def create_page_with_logging(client, page_data):
"""Create a page with proper logging."""
logger.info(f"Creating page: {page_data.title}")
try:
created_page = client.pages.create(page_data)
logger.info(f"Successfully created page with ID: {created_page.id}")
return created_page
except Exception as e:
logger.error(f"Failed to create page '{page_data.title}': {e}")
raise
# Usage
with WikiJSClient("https://wiki.example.com", auth="api-key") as client:
page_data = PageCreate(
title="Logged Page",
path="logged-page",
content="This creation is logged."
)
create_page_with_logging(client, page_data)
```
### Performance Optimization
```python
# Efficient pagination
def get_all_pages_efficiently(client, batch_size=100):
"""Get all pages with efficient pagination."""
all_pages = []
offset = 0
while True:
# Get a batch
batch = client.pages.list(limit=batch_size, offset=offset)
if not batch:
break # No more pages
all_pages.extend(batch)
offset += batch_size
# Optional: Add a small delay to be nice to the server
# time.sleep(0.1)
return all_pages
# Use server-side filtering
def get_recent_tutorials(client, days=30):
"""Get recent tutorial pages efficiently."""
from datetime import datetime, timedelta
# Let the server do the filtering
pages = client.pages.get_by_tags(["tutorial"])
# Only filter by date client-side if necessary
cutoff_date = datetime.now() - timedelta(days=days)
recent_pages = [
page for page in pages
if page.updated_at > cutoff_date
]
return recent_pages
```
---
## Troubleshooting
### Common Issues
#### Authentication Problems
```python
# Test your authentication
try:
client = WikiJSClient("https://wiki.example.com", auth="your-api-key")
if client.test_connection():
print("✅ Authentication successful")
else:
print("❌ Authentication failed")
except AuthenticationError as e:
print(f"❌ Authentication error: {e}")
print("💡 Check your API key and permissions")
```
#### Connection Issues
```python
# Test connection with detailed error info
try:
client = WikiJSClient("https://wiki.example.com", auth="api-key")
client.test_connection()
except ConnectionError as e:
print(f"❌ Connection failed: {e}")
print("💡 Possible solutions:")
print(" - Check if the URL is correct")
print(" - Verify the server is running")
print(" - Check your network connection")
print(" - Try with verify_ssl=False if using self-signed certificates")
```
#### SSL Certificate Issues
```python
# For development or self-signed certificates
client = WikiJSClient(
base_url="https://wiki.example.com",
auth="api-key",
verify_ssl=False # Only for development!
)
```
#### Timeout Issues
```python
# Increase timeout for slow connections
client = WikiJSClient(
base_url="https://wiki.example.com",
auth="api-key",
timeout=60 # 60 seconds
)
```
### Debugging
#### Enable Debug Logging
```python
import logging
# Enable debug logging for the wikijs library
logging.getLogger('wikijs').setLevel(logging.DEBUG)
logging.getLogger('urllib3').setLevel(logging.DEBUG)
# Enable debug logging for requests
logging.basicConfig(level=logging.DEBUG)
```
#### Inspect Raw Responses
```python
# You can inspect the raw HTTP responses for debugging
import requests
# Make a manual request to see the raw response
response = requests.get(
"https://wiki.example.com/graphql",
headers={"Authorization": "Bearer your-api-key"},
json={"query": "{ pages { id title } }"}
)
print(f"Status: {response.status_code}")
print(f"Headers: {response.headers}")
print(f"Content: {response.text}")
```
### Getting Help
If you encounter issues:
1. **Check the logs** - Enable debug logging to see what's happening
2. **Verify your setup** - Ensure URL, credentials, and network connectivity
3. **Check the Wiki.js server** - Look at server logs for errors
4. **Test with curl** - Verify the API works outside of Python
5. **Create an issue** - Report bugs on the GitHub repository
#### Testing with curl
```bash
# Test your Wiki.js GraphQL endpoint
curl -X POST https://wiki.example.com/graphql \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{"query": "{ pages { id title } }"}'
```
---
## Next Steps
- Explore the [API Reference](api_reference.md) for detailed information
- Check out the [Examples](../examples/) directory for more code samples
- Read the [Contributing Guide](CONTRIBUTING.md) to help improve the SDK
- Visit the [Wiki.js documentation](https://docs.js.wiki/) to learn more about the platform