568 lines
18 KiB
Python
568 lines
18 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Wiki.js Python SDK Experimentation Script
|
||
|
||
This interactive script lets you experiment with the SDK features in a safe,
|
||
mocked environment. Perfect for learning how the SDK works without needing
|
||
a real Wiki.js instance.
|
||
|
||
Usage:
|
||
python experiment.py
|
||
|
||
The script will guide you through different SDK features and let you
|
||
try them out interactively.
|
||
"""
|
||
|
||
import json
|
||
import sys
|
||
from datetime import datetime
|
||
from unittest.mock import Mock, patch
|
||
|
||
# Import SDK components
|
||
from wikijs import WikiJSClient
|
||
from wikijs.models import PageCreate, PageUpdate, Page
|
||
from wikijs.auth import APIKeyAuth, JWTAuth, NoAuth
|
||
from wikijs.exceptions import APIError, ValidationError, NotFoundError
|
||
|
||
|
||
class Colors:
|
||
"""ANSI color codes for pretty output."""
|
||
HEADER = '\033[95m'
|
||
BLUE = '\033[94m'
|
||
CYAN = '\033[96m'
|
||
GREEN = '\033[92m'
|
||
YELLOW = '\033[93m'
|
||
RED = '\033[91m'
|
||
BOLD = '\033[1m'
|
||
UNDERLINE = '\033[4m'
|
||
END = '\033[0m'
|
||
|
||
|
||
def print_header(text):
|
||
"""Print a colored header."""
|
||
print(f"\n{Colors.HEADER}{Colors.BOLD}{'='*60}{Colors.END}")
|
||
print(f"{Colors.HEADER}{Colors.BOLD}{text.center(60)}{Colors.END}")
|
||
print(f"{Colors.HEADER}{Colors.BOLD}{'='*60}{Colors.END}\n")
|
||
|
||
|
||
def print_success(text):
|
||
"""Print success message."""
|
||
print(f"{Colors.GREEN}✅ {text}{Colors.END}")
|
||
|
||
|
||
def print_info(text):
|
||
"""Print info message."""
|
||
print(f"{Colors.CYAN}ℹ️ {text}{Colors.END}")
|
||
|
||
|
||
def print_warning(text):
|
||
"""Print warning message."""
|
||
print(f"{Colors.YELLOW}⚠️ {text}{Colors.END}")
|
||
|
||
|
||
def print_error(text):
|
||
"""Print error message."""
|
||
print(f"{Colors.RED}❌ {text}{Colors.END}")
|
||
|
||
|
||
def print_code(code):
|
||
"""Print code snippet."""
|
||
print(f"{Colors.BLUE}{code}{Colors.END}")
|
||
|
||
|
||
def wait_for_enter(prompt="Press Enter to continue..."):
|
||
"""Wait for user input."""
|
||
input(f"\n{Colors.YELLOW}{prompt}{Colors.END}")
|
||
|
||
|
||
def setup_mock_session():
|
||
"""Set up a mock session for API calls."""
|
||
mock_session = Mock()
|
||
|
||
# Sample pages data
|
||
sample_pages = [
|
||
{
|
||
"id": 1,
|
||
"title": "Welcome to Wiki.js",
|
||
"path": "home",
|
||
"content": "# Welcome!\n\nThis is your wiki home page.",
|
||
"created_at": "2023-01-01T00:00:00Z",
|
||
"updated_at": "2023-01-01T12:00:00Z",
|
||
"is_published": True,
|
||
"tags": ["welcome", "home"]
|
||
},
|
||
{
|
||
"id": 2,
|
||
"title": "Getting Started Guide",
|
||
"path": "getting-started",
|
||
"content": "# Getting Started\n\nLearn how to use this wiki effectively.",
|
||
"created_at": "2023-01-02T00:00:00Z",
|
||
"updated_at": "2023-01-02T10:00:00Z",
|
||
"is_published": True,
|
||
"tags": ["guide", "tutorial"]
|
||
},
|
||
{
|
||
"id": 3,
|
||
"title": "API Documentation",
|
||
"path": "api-docs",
|
||
"content": "# API Documentation\n\nComplete API reference.",
|
||
"created_at": "2023-01-03T00:00:00Z",
|
||
"updated_at": "2023-01-03T14:00:00Z",
|
||
"is_published": False,
|
||
"tags": ["api", "documentation"]
|
||
}
|
||
]
|
||
|
||
def mock_request(method, url, **kwargs):
|
||
"""Mock HTTP request handler."""
|
||
response = Mock()
|
||
response.ok = True
|
||
response.status_code = 200
|
||
|
||
# Simulate different API endpoints
|
||
if "pages" in url and method.upper() == "GET":
|
||
if url.endswith("/pages"):
|
||
# List pages
|
||
response.json.return_value = {"data": {"pages": sample_pages}}
|
||
else:
|
||
# Get specific page
|
||
page_id = int(url.split("/")[-1]) if url.split("/")[-1].isdigit() else 1
|
||
page = next((p for p in sample_pages if p["id"] == page_id), sample_pages[0])
|
||
response.json.return_value = page
|
||
|
||
elif "pages" in url and method.upper() == "POST":
|
||
# Create page
|
||
new_page = {
|
||
"id": len(sample_pages) + 1,
|
||
"title": kwargs.get("json", {}).get("title", "New Page"),
|
||
"path": kwargs.get("json", {}).get("path", "new-page"),
|
||
"content": kwargs.get("json", {}).get("content", ""),
|
||
"created_at": datetime.now().isoformat() + "Z",
|
||
"updated_at": datetime.now().isoformat() + "Z",
|
||
"is_published": kwargs.get("json", {}).get("is_published", True),
|
||
"tags": kwargs.get("json", {}).get("tags", [])
|
||
}
|
||
sample_pages.append(new_page)
|
||
response.json.return_value = new_page
|
||
response.status_code = 201
|
||
|
||
elif "pages" in url and method.upper() == "PUT":
|
||
# Update page
|
||
page_id = int(url.split("/")[-1]) if url.split("/")[-1].isdigit() else 1
|
||
page = next((p for p in sample_pages if p["id"] == page_id), sample_pages[0])
|
||
|
||
# Update fields from request
|
||
update_data = kwargs.get("json", {})
|
||
for key, value in update_data.items():
|
||
if key in page:
|
||
page[key] = value
|
||
page["updated_at"] = datetime.now().isoformat() + "Z"
|
||
|
||
response.json.return_value = page
|
||
|
||
elif "pages" in url and method.upper() == "DELETE":
|
||
# Delete page
|
||
page_id = int(url.split("/")[-1]) if url.split("/")[-1].isdigit() else 1
|
||
sample_pages[:] = [p for p in sample_pages if p["id"] != page_id]
|
||
response.json.return_value = {"success": True}
|
||
response.status_code = 204
|
||
|
||
else:
|
||
# Default response
|
||
response.json.return_value = {"message": "Success"}
|
||
|
||
return response
|
||
|
||
mock_session.request.side_effect = mock_request
|
||
return mock_session
|
||
|
||
|
||
def experiment_client_setup():
|
||
"""Experiment with client setup."""
|
||
print_header("🔧 CLIENT SETUP EXPERIMENT")
|
||
|
||
print_info("Let's create different types of Wiki.js clients!")
|
||
|
||
print("\n1. Creating a client with API key authentication:")
|
||
print_code("client = WikiJSClient('https://wiki.example.com', auth='your-api-key')")
|
||
|
||
try:
|
||
client = WikiJSClient('https://wiki.example.com', auth='demo-api-key-12345')
|
||
print_success(f"Client created! Base URL: {client.base_url}")
|
||
print_info(f"Auth type: {type(client._auth_handler).__name__}")
|
||
except Exception as e:
|
||
print_error(f"Error creating client: {e}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n2. Creating a client with JWT authentication:")
|
||
print_code("jwt_token = 'eyJ0eXAiOiJKV1Q...'")
|
||
print_code("jwt_auth = JWTAuth(jwt_token)")
|
||
print_code("client = WikiJSClient('https://wiki.example.com', auth=jwt_auth)")
|
||
|
||
try:
|
||
jwt_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
|
||
jwt_auth = JWTAuth(jwt_token)
|
||
jwt_client = WikiJSClient('https://wiki.example.com', auth=jwt_auth)
|
||
print_success("JWT client created successfully!")
|
||
print_info(f"Token preview: {jwt_auth.token_preview}")
|
||
except Exception as e:
|
||
print_error(f"Error creating JWT client: {e}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n3. URL normalization demo:")
|
||
test_urls = [
|
||
"wiki.example.com",
|
||
"https://wiki.example.com/",
|
||
"http://localhost:3000///",
|
||
"wiki.company.internal:8080"
|
||
]
|
||
|
||
for url in test_urls:
|
||
try:
|
||
client = WikiJSClient(url, auth='test-key')
|
||
print_success(f"'{url}' → '{client.base_url}'")
|
||
except Exception as e:
|
||
print_error(f"'{url}' → Error: {e}")
|
||
|
||
return client
|
||
|
||
|
||
def experiment_data_models():
|
||
"""Experiment with data models."""
|
||
print_header("📋 DATA MODELS EXPERIMENT")
|
||
|
||
print_info("Let's create and manipulate Wiki.js data models!")
|
||
|
||
print("\n1. Creating a new page:")
|
||
print_code("""
|
||
page_data = PageCreate(
|
||
title="My Awesome Page",
|
||
path="awesome-page",
|
||
content="# Welcome\\n\\nThis is **awesome** content!",
|
||
tags=["awesome", "demo"],
|
||
is_published=True
|
||
)""")
|
||
|
||
try:
|
||
page_data = PageCreate(
|
||
title="My Awesome Page",
|
||
path="awesome-page",
|
||
content="# Welcome\n\nThis is **awesome** content!",
|
||
tags=["awesome", "demo"],
|
||
is_published=True
|
||
)
|
||
print_success("PageCreate model created!")
|
||
print_info(f"Title: {page_data.title}")
|
||
print_info(f"Path: {page_data.path}")
|
||
print_info(f"Tags: {page_data.tags}")
|
||
except Exception as e:
|
||
print_error(f"Error creating page model: {e}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n2. Model serialization:")
|
||
print_code("page_dict = page_data.to_dict()")
|
||
print_code("page_json = page_data.to_json()")
|
||
|
||
try:
|
||
page_dict = page_data.to_dict()
|
||
page_json = page_data.to_json()
|
||
|
||
print_success("Serialization successful!")
|
||
print_info("Dictionary format:")
|
||
print(json.dumps(page_dict, indent=2))
|
||
print_info("\nJSON format:")
|
||
print(page_json)
|
||
except Exception as e:
|
||
print_error(f"Serialization error: {e}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n3. Creating update data:")
|
||
print_code("""
|
||
update_data = PageUpdate(
|
||
title="Updated Awesome Page",
|
||
content="# Updated Content\\n\\nThis content has been updated!",
|
||
tags=["awesome", "demo", "updated"]
|
||
)""")
|
||
|
||
try:
|
||
update_data = PageUpdate(
|
||
title="Updated Awesome Page",
|
||
content="# Updated Content\n\nThis content has been updated!",
|
||
tags=["awesome", "demo", "updated"]
|
||
)
|
||
print_success("PageUpdate model created!")
|
||
print_info(f"New title: {update_data.title}")
|
||
print_info(f"New tags: {update_data.tags}")
|
||
except Exception as e:
|
||
print_error(f"Error creating update model: {e}")
|
||
|
||
return page_data, update_data
|
||
|
||
|
||
@patch('wikijs.client.requests.Session')
|
||
def experiment_api_operations(mock_session_class, client, page_data, update_data):
|
||
"""Experiment with API operations."""
|
||
print_header("🌐 API OPERATIONS EXPERIMENT")
|
||
|
||
# Set up mock session
|
||
mock_session = setup_mock_session()
|
||
mock_session_class.return_value = mock_session
|
||
|
||
print_info("Let's try different API operations with mocked responses!")
|
||
|
||
print("\n1. Listing all pages:")
|
||
print_code("pages = client.pages.list()")
|
||
|
||
try:
|
||
pages = client.pages.list()
|
||
print_success(f"Found {len(pages)} pages!")
|
||
for i, page in enumerate(pages[:3], 1):
|
||
print_info(f"{i}. {page.title} ({page.path}) - {len(page.tags)} tags")
|
||
except Exception as e:
|
||
print_error(f"Error listing pages: {e}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n2. Getting a specific page:")
|
||
print_code("page = client.pages.get(1)")
|
||
|
||
try:
|
||
page = client.pages.get(1)
|
||
print_success("Page retrieved!")
|
||
print_info(f"Title: {page.title}")
|
||
print_info(f"Path: {page.path}")
|
||
print_info(f"Published: {page.is_published}")
|
||
print_info(f"Content preview: {page.content[:50]}...")
|
||
except Exception as e:
|
||
print_error(f"Error getting page: {e}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n3. Creating a new page:")
|
||
print_code("new_page = client.pages.create(page_data)")
|
||
|
||
try:
|
||
new_page = client.pages.create(page_data)
|
||
print_success("Page created!")
|
||
print_info(f"New page ID: {new_page.id}")
|
||
print_info(f"Title: {new_page.title}")
|
||
print_info(f"Created at: {new_page.created_at}")
|
||
except Exception as e:
|
||
print_error(f"Error creating page: {e}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n4. Updating a page:")
|
||
print_code("updated_page = client.pages.update(1, update_data)")
|
||
|
||
try:
|
||
updated_page = client.pages.update(1, update_data)
|
||
print_success("Page updated!")
|
||
print_info(f"Updated title: {updated_page.title}")
|
||
print_info(f"Updated at: {updated_page.updated_at}")
|
||
except Exception as e:
|
||
print_error(f"Error updating page: {e}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n5. Searching pages:")
|
||
print_code("search_results = client.pages.search('guide')")
|
||
|
||
try:
|
||
search_results = client.pages.search('guide')
|
||
print_success(f"Found {len(search_results)} matching pages!")
|
||
for result in search_results:
|
||
print_info(f"• {result.title} - {result.path}")
|
||
except Exception as e:
|
||
print_error(f"Error searching pages: {e}")
|
||
|
||
|
||
def experiment_error_handling():
|
||
"""Experiment with error handling."""
|
||
print_header("⚠️ ERROR HANDLING EXPERIMENT")
|
||
|
||
print_info("Let's see how the SDK handles different types of errors!")
|
||
|
||
print("\n1. Validation errors:")
|
||
print_code("""
|
||
try:
|
||
invalid_page = PageCreate(title="", path="", content="")
|
||
except ValidationError as e:
|
||
print(f"Validation error: {e}")
|
||
""")
|
||
|
||
try:
|
||
invalid_page = PageCreate(title="", path="", content="")
|
||
print_warning("Expected validation error, but none occurred!")
|
||
except ValidationError as e:
|
||
print_success(f"Caught validation error: {e}")
|
||
except Exception as e:
|
||
print_error(f"Unexpected error: {e}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n2. Authentication errors:")
|
||
print_code("""
|
||
try:
|
||
bad_auth = APIKeyAuth("")
|
||
except ValidationError as e:
|
||
print(f"Auth error: {e}")
|
||
""")
|
||
|
||
try:
|
||
bad_auth = APIKeyAuth("")
|
||
print_warning("Expected authentication error, but none occurred!")
|
||
except ValidationError as e:
|
||
print_success(f"Caught auth error: {e}")
|
||
except Exception as e:
|
||
print_error(f"Unexpected error: {e}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n3. URL validation errors:")
|
||
print_code("""
|
||
try:
|
||
from wikijs.utils.helpers import normalize_url
|
||
normalize_url("")
|
||
except ValidationError as e:
|
||
print(f"URL error: {e}")
|
||
""")
|
||
|
||
try:
|
||
from wikijs.utils.helpers import normalize_url
|
||
normalize_url("")
|
||
print_warning("Expected URL validation error!")
|
||
except ValidationError as e:
|
||
print_success(f"Caught URL error: {e}")
|
||
except Exception as e:
|
||
print_error(f"Unexpected error: {e}")
|
||
|
||
|
||
def experiment_utilities():
|
||
"""Experiment with utility functions."""
|
||
print_header("🛠️ UTILITIES EXPERIMENT")
|
||
|
||
print_info("Let's try out the SDK's utility functions!")
|
||
|
||
from wikijs.utils.helpers import (
|
||
normalize_url, sanitize_path, chunk_list,
|
||
safe_get, build_api_url
|
||
)
|
||
|
||
print("\n1. URL normalization:")
|
||
test_urls = [
|
||
"wiki.example.com",
|
||
"https://wiki.example.com/",
|
||
"localhost:3000",
|
||
"wiki.company.internal:8080/"
|
||
]
|
||
|
||
for url in test_urls:
|
||
try:
|
||
normalized = normalize_url(url)
|
||
print_success(f"'{url}' → '{normalized}'")
|
||
except Exception as e:
|
||
print_error(f"'{url}' → Error: {e}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n2. Path sanitization:")
|
||
test_paths = [
|
||
"hello world",
|
||
"/my/wiki/page/",
|
||
"special-chars!@#$",
|
||
" multiple spaces "
|
||
]
|
||
|
||
for path in test_paths:
|
||
try:
|
||
sanitized = sanitize_path(path)
|
||
print_success(f"'{path}' → '{sanitized}'")
|
||
except Exception as e:
|
||
print_error(f"'{path}' → Error: {e}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n3. List chunking:")
|
||
test_list = list(range(1, 13)) # [1, 2, 3, ..., 12]
|
||
chunk_sizes = [3, 4, 5]
|
||
|
||
for size in chunk_sizes:
|
||
chunks = chunk_list(test_list, size)
|
||
print_success(f"Chunks of {size}: {chunks}")
|
||
|
||
wait_for_enter()
|
||
|
||
print("\n4. Safe dictionary access:")
|
||
test_data = {
|
||
"user": {
|
||
"profile": {
|
||
"name": "John Doe",
|
||
"email": "john@example.com"
|
||
}
|
||
},
|
||
"settings": {
|
||
"theme": "dark",
|
||
"notifications": True
|
||
}
|
||
}
|
||
|
||
test_keys = [
|
||
"user.profile.name",
|
||
"user.profile.email",
|
||
"settings.theme",
|
||
"user.missing.key",
|
||
"nonexistent"
|
||
]
|
||
|
||
for key in test_keys:
|
||
value = safe_get(test_data, key, "NOT_FOUND")
|
||
print_success(f"'{key}' → {value}")
|
||
|
||
|
||
def main():
|
||
"""Main experimentation function."""
|
||
print_header("🧪 WIKI.JS SDK EXPERIMENTATION LAB")
|
||
|
||
print(f"{Colors.CYAN}Welcome to the Wiki.js Python SDK Experiment Lab!{Colors.END}")
|
||
print(f"{Colors.CYAN}Here you can safely try out all the SDK features with mocked data.{Colors.END}")
|
||
|
||
wait_for_enter("Ready to start experimenting?")
|
||
|
||
# Experiment with different features
|
||
client = experiment_client_setup()
|
||
page_data, update_data = experiment_data_models()
|
||
experiment_api_operations(client, page_data, update_data)
|
||
experiment_error_handling()
|
||
experiment_utilities()
|
||
|
||
# Final summary
|
||
print_header("🎉 EXPERIMENT COMPLETE")
|
||
print_success("Congratulations! You've experimented with:")
|
||
print_info("✨ Client setup and authentication")
|
||
print_info("✨ Data models and serialization")
|
||
print_info("✨ API operations (mocked)")
|
||
print_info("✨ Error handling")
|
||
print_info("✨ Utility functions")
|
||
|
||
print(f"\n{Colors.YELLOW}💡 Next steps:{Colors.END}")
|
||
print(f"{Colors.CYAN}1. Check out the examples/ directory for real-world usage{Colors.END}")
|
||
print(f"{Colors.CYAN}2. Read the docs/user_guide.md for detailed documentation{Colors.END}")
|
||
print(f"{Colors.CYAN}3. Try connecting to a real Wiki.js instance{Colors.END}")
|
||
|
||
print(f"\n{Colors.GREEN}Happy coding with Wiki.js SDK! 🚀{Colors.END}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
try:
|
||
main()
|
||
except KeyboardInterrupt:
|
||
print(f"\n\n{Colors.YELLOW}Experiment interrupted. Goodbye! 👋{Colors.END}")
|
||
sys.exit(0)
|
||
except Exception as e:
|
||
print_error(f"Unexpected error: {e}")
|
||
sys.exit(1) |