Fix code formatting and linting issues

- Updated GitHub Actions workflow to use correct flake8 configuration
- Fixed line length issues by using 88 characters as configured
- Removed unused imports and trailing whitespace
- Fixed f-string placeholders and unused variables
- All linting checks now pass with project configuration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-30 20:49:40 -04:00
parent 16bd151337
commit ade9aacf56
33 changed files with 1099 additions and 1096 deletions

View File

@@ -9,13 +9,13 @@ Supported authentication methods:
- No authentication for testing (NoAuth)
"""
from .base import AuthHandler, NoAuth
from .api_key import APIKeyAuth
from .base import AuthHandler, NoAuth
from .jwt import JWTAuth
__all__ = [
"AuthHandler",
"NoAuth",
"NoAuth",
"APIKeyAuth",
"JWTAuth",
]
]

View File

@@ -4,20 +4,20 @@ This module implements API key authentication for Wiki.js instances.
API keys are typically used for server-to-server authentication.
"""
from typing import Dict, Optional
from typing import Dict
from .base import AuthHandler
class APIKeyAuth(AuthHandler):
"""API key authentication handler for Wiki.js.
This handler implements authentication using an API key, which is
included in the Authorization header as a Bearer token.
Args:
api_key: The API key string from Wiki.js admin panel.
Example:
>>> auth = APIKeyAuth("your-api-key-here")
>>> client = WikiJSClient("https://wiki.example.com", auth=auth)
@@ -25,35 +25,35 @@ class APIKeyAuth(AuthHandler):
def __init__(self, api_key: str) -> None:
"""Initialize API key authentication.
Args:
api_key: The API key from Wiki.js admin panel.
Raises:
ValueError: If api_key is empty or None.
"""
if not api_key or not api_key.strip():
raise ValueError("API key cannot be empty")
self._api_key = api_key.strip()
def get_headers(self) -> Dict[str, str]:
"""Get authentication headers with API key.
Returns:
Dict[str, str]: Headers containing the Authorization header.
"""
return {
"Authorization": f"Bearer {self._api_key}",
"Content-Type": "application/json"
"Content-Type": "application/json",
}
def is_valid(self) -> bool:
"""Check if API key is valid.
For API keys, we assume they're valid if they're not empty.
Actual validation happens on the server side.
Returns:
bool: True if API key exists, False otherwise.
"""
@@ -61,29 +61,30 @@ class APIKeyAuth(AuthHandler):
def refresh(self) -> None:
"""Refresh authentication credentials.
API keys don't typically need refreshing, so this is a no-op.
If the API key becomes invalid, a new one must be provided.
"""
# API keys don't refresh - they're static until manually replaced
pass
@property
def api_key(self) -> str:
"""Get the masked API key for logging/debugging.
Returns:
str: Masked API key showing only first 4 and last 4 characters.
"""
if len(self._api_key) <= 8:
return "*" * len(self._api_key)
return f"{self._api_key[:4]}{'*' * (len(self._api_key) - 8)}{self._api_key[-4:]}"
return (
f"{self._api_key[:4]}{'*' * (len(self._api_key) - 8)}{self._api_key[-4:]}"
)
def __repr__(self) -> str:
"""String representation of the auth handler.
Returns:
str: Safe representation with masked API key.
"""
return f"APIKeyAuth(api_key='{self.api_key}')"
return f"APIKeyAuth(api_key='{self.api_key}')"

View File

@@ -5,12 +5,12 @@ providing a consistent interface for different authentication methods.
"""
from abc import ABC, abstractmethod
from typing import Dict, Optional
from typing import Dict
class AuthHandler(ABC):
"""Abstract base class for Wiki.js authentication handlers.
This class defines the interface that all authentication implementations
must follow, ensuring consistent behavior across different auth methods.
"""
@@ -18,56 +18,54 @@ class AuthHandler(ABC):
@abstractmethod
def get_headers(self) -> Dict[str, str]:
"""Get authentication headers for HTTP requests.
Returns:
Dict[str, str]: Dictionary of headers to include in requests.
Raises:
AuthenticationError: If authentication is invalid or expired.
"""
pass
@abstractmethod
def is_valid(self) -> bool:
"""Check if the current authentication is valid.
Returns:
bool: True if authentication is valid, False otherwise.
"""
pass
@abstractmethod
def refresh(self) -> None:
"""Refresh the authentication if possible.
For token-based authentication, this should refresh the token.
For API key authentication, this is typically a no-op.
Raises:
AuthenticationError: If refresh fails.
"""
pass
def validate_credentials(self) -> None:
"""Validate credentials and refresh if necessary.
This is a convenience method that checks validity and refreshes
if needed. Subclasses can override for custom behavior.
Raises:
AuthenticationError: If credentials are invalid or refresh fails.
"""
if not self.is_valid():
self.refresh()
if not self.is_valid():
from ..exceptions import AuthenticationError
raise AuthenticationError("Authentication credentials are invalid")
class NoAuth(AuthHandler):
"""No-authentication handler for testing or public instances.
This handler provides an empty authentication implementation,
useful for testing or when accessing public Wiki.js instances
that don't require authentication.
@@ -75,7 +73,7 @@ class NoAuth(AuthHandler):
def get_headers(self) -> Dict[str, str]:
"""Return empty headers dict.
Returns:
Dict[str, str]: Empty dictionary.
"""
@@ -83,7 +81,7 @@ class NoAuth(AuthHandler):
def is_valid(self) -> bool:
"""Always return True for no-auth.
Returns:
bool: Always True.
"""
@@ -91,7 +89,6 @@ class NoAuth(AuthHandler):
def refresh(self) -> None:
"""No-op for no-auth.
This method does nothing since there's no authentication to refresh.
"""
pass

View File

@@ -5,7 +5,7 @@ JWT tokens are typically used for user-based authentication and have expiration
"""
import time
from datetime import datetime, timedelta
from datetime import timedelta
from typing import Dict, Optional
from .base import AuthHandler
@@ -13,39 +13,39 @@ from .base import AuthHandler
class JWTAuth(AuthHandler):
"""JWT token authentication handler for Wiki.js.
This handler manages JWT tokens with automatic refresh capabilities.
JWT tokens typically expire and need to be refreshed periodically.
Args:
token: The JWT token string.
refresh_token: Optional refresh token for automatic renewal.
expires_at: Optional expiration timestamp (Unix timestamp).
Example:
>>> auth = JWTAuth("eyJ0eXAiOiJKV1QiLCJhbGc...")
>>> client = WikiJSClient("https://wiki.example.com", auth=auth)
"""
def __init__(
self,
token: str,
self,
token: str,
refresh_token: Optional[str] = None,
expires_at: Optional[float] = None
expires_at: Optional[float] = None,
) -> None:
"""Initialize JWT authentication.
Args:
token: The JWT token string.
refresh_token: Optional refresh token for automatic renewal.
expires_at: Optional expiration timestamp (Unix timestamp).
Raises:
ValueError: If token is empty or None.
"""
if not token or not token.strip():
raise ValueError("JWT token cannot be empty")
self._token = token.strip()
self._refresh_token = refresh_token.strip() if refresh_token else None
self._expires_at = expires_at
@@ -53,66 +53,66 @@ class JWTAuth(AuthHandler):
def get_headers(self) -> Dict[str, str]:
"""Get authentication headers with JWT token.
Automatically attempts to refresh the token if it's expired.
Returns:
Dict[str, str]: Headers containing the Authorization header.
Raises:
AuthenticationError: If token is expired and cannot be refreshed.
"""
# Try to refresh if token is near expiration
if not self.is_valid():
self.refresh()
return {
"Authorization": f"Bearer {self._token}",
"Content-Type": "application/json"
"Content-Type": "application/json",
}
def is_valid(self) -> bool:
"""Check if JWT token is valid and not expired.
Returns:
bool: True if token exists and is not expired, False otherwise.
"""
if not self._token or not self._token.strip():
return False
# If no expiration time is set, assume token is valid
if self._expires_at is None:
return True
# Check if token is expired (with buffer for refresh)
current_time = time.time()
return current_time < (self._expires_at - self._refresh_buffer)
def refresh(self) -> None:
"""Refresh the JWT token using the refresh token.
This method attempts to refresh the JWT token using the refresh token.
If no refresh token is available, it raises an AuthenticationError.
Note: This is a placeholder implementation. In a real implementation,
this would make an HTTP request to the Wiki.js token refresh endpoint.
Raises:
AuthenticationError: If refresh token is not available or refresh fails.
"""
from ..exceptions import AuthenticationError
if not self._refresh_token:
raise AuthenticationError(
"JWT token expired and no refresh token available"
)
# TODO: Implement actual token refresh logic
# This would typically involve:
# 1. Making a POST request to /auth/refresh endpoint
# 2. Sending the refresh token
# 3. Updating self._token and self._expires_at with the response
raise AuthenticationError(
"JWT token refresh not yet implemented. "
"Please provide a new token or use API key authentication."
@@ -120,46 +120,46 @@ class JWTAuth(AuthHandler):
def is_expired(self) -> bool:
"""Check if the JWT token is expired.
Returns:
bool: True if token is expired, False otherwise.
"""
if self._expires_at is None:
return False
return time.time() >= self._expires_at
def time_until_expiry(self) -> Optional[timedelta]:
"""Get time until token expires.
Returns:
Optional[timedelta]: Time until expiration, or None if no expiration set.
"""
if self._expires_at is None:
return None
remaining_seconds = self._expires_at - time.time()
return timedelta(seconds=max(0, remaining_seconds))
@property
def token_preview(self) -> str:
"""Get a preview of the JWT token for logging/debugging.
Returns:
str: Masked token showing only first and last few characters.
"""
if not self._token:
return "None"
if len(self._token) <= 20:
return "*" * len(self._token)
return f"{self._token[:10]}...{self._token[-10:]}"
def __repr__(self) -> str:
"""String representation of the auth handler.
Returns:
str: Safe representation with masked token.
"""
return f"JWTAuth(token='{self.token_preview}', expires_at={self._expires_at})"
return f"JWTAuth(token='{self.token_preview}', expires_at={self._expires_at})"