ready for try
This commit is contained in:
117
docs/CHANGELOG.md
Normal file
117
docs/CHANGELOG.md
Normal 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
342
docs/CONTRIBUTING.md
Normal 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
614
docs/api_reference.md
Normal 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
721
docs/development.md
Normal 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
804
docs/user_guide.md
Normal 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
|
||||
Reference in New Issue
Block a user