fix(ci): fix black/ruff failures, add CoC and PR template
- Fix Python formatting (sync no longer strips blank lines from .py files) - Remove test_community_build.py (tests private sync module) - Fix ruff warnings in test files - Add CODE_OF_CONDUCT.md - Add .github/PULL_REQUEST_TEMPLATE.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,16 +12,19 @@ from typing import Any
|
||||
|
||||
import aiohttp
|
||||
|
||||
|
||||
class ConfigurationError(Exception):
|
||||
"""Raised when site configuration is invalid or incomplete."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class AuthenticationError(Exception):
|
||||
"""Raised when authentication fails (401/403)."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class WordPressClient:
|
||||
"""
|
||||
WordPress REST API client for HTTP communication.
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -154,6 +155,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class CommentsHandler:
|
||||
"""Handle comment-related operations for WordPress"""
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -219,6 +220,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class CouponsHandler:
|
||||
"""Handle coupon-related operations for WooCommerce"""
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -141,6 +142,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class CustomersHandler:
|
||||
"""Handle WooCommerce customer operations"""
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import aiohttp
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -155,6 +156,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class MediaHandler:
|
||||
"""Handle media-related operations for WordPress"""
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -148,6 +149,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class MenusHandler:
|
||||
"""Handle menu-related operations for WordPress"""
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -196,6 +197,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class OrdersHandler:
|
||||
"""Handle WooCommerce order-related operations"""
|
||||
|
||||
|
||||
@@ -7,12 +7,14 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def _count_words(html_content: str) -> int:
|
||||
"""Strip HTML tags and count words."""
|
||||
text = re.sub(r"<[^>]+>", " ", html_content)
|
||||
text = re.sub(r"\s+", " ", text).strip()
|
||||
return len(text.split()) if text else 0
|
||||
|
||||
|
||||
def _strip_html(html_content: str, max_chars: int = 500) -> str:
|
||||
"""Strip HTML tags and return first max_chars characters."""
|
||||
text = re.sub(r"<[^>]+>", " ", html_content)
|
||||
@@ -21,6 +23,7 @@ def _strip_html(html_content: str, max_chars: int = 500) -> str:
|
||||
return text[:max_chars] + "..."
|
||||
return text
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -454,6 +457,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class PostsHandler:
|
||||
"""Handle post-related operations for WordPress"""
|
||||
|
||||
|
||||
@@ -7,12 +7,14 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def _count_words(html_content: str) -> int:
|
||||
"""Strip HTML tags and count words."""
|
||||
text = re.sub(r"<[^>]+>", " ", html_content)
|
||||
text = re.sub(r"\s+", " ", text).strip()
|
||||
return len(text.split()) if text else 0
|
||||
|
||||
|
||||
def _strip_html(html_content: str, max_chars: int = 500) -> str:
|
||||
"""Strip HTML tags and return first max_chars characters."""
|
||||
text = re.sub(r"<[^>]+>", " ", html_content)
|
||||
@@ -21,6 +23,7 @@ def _strip_html(html_content: str, max_chars: int = 500) -> str:
|
||||
return text[:max_chars] + "..."
|
||||
return text
|
||||
|
||||
|
||||
def normalize_id_list(value: Any, field_name: str = "item") -> list[dict[str, Any]]:
|
||||
"""
|
||||
Convert various formats of category/tag IDs to WooCommerce API format.
|
||||
@@ -100,6 +103,7 @@ def normalize_id_list(value: Any, field_name: str = "item") -> list[dict[str, An
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -589,6 +593,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class ProductsHandler:
|
||||
"""Handle WooCommerce product-related operations"""
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -86,6 +87,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class ReportsHandler:
|
||||
"""Handle WooCommerce reporting operations"""
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -163,6 +164,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class SEOHandler:
|
||||
"""Handle SEO-related operations for WordPress (Yoast SEO and RankMath)"""
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -69,6 +70,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class SiteHandler:
|
||||
"""Handle site management operations for WordPress"""
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -269,6 +270,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class TaxonomyHandler:
|
||||
"""Handle taxonomy-related operations for WordPress"""
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -46,6 +47,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class UsersHandler:
|
||||
"""Handle user-related operations for WordPress"""
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any
|
||||
|
||||
from plugins.wordpress.wp_cli import WPCLIManager
|
||||
|
||||
|
||||
def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
"""Return tool specifications for ToolGenerator"""
|
||||
return [
|
||||
@@ -183,6 +184,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class WPCLIHandler:
|
||||
"""Handle WP-CLI operations for WordPress"""
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ from plugins.base import BasePlugin
|
||||
from plugins.wordpress import handlers
|
||||
from plugins.wordpress.client import WordPressClient
|
||||
|
||||
|
||||
class WordPressPlugin(BasePlugin):
|
||||
"""
|
||||
WordPress project plugin - Option B architecture.
|
||||
|
||||
@@ -14,6 +14,7 @@ import aiohttp
|
||||
from plugins.base import BasePlugin
|
||||
from plugins.wordpress.wp_cli import WPCLIManager
|
||||
|
||||
|
||||
class WordPressPlugin(BasePlugin):
|
||||
"""
|
||||
WordPress project plugin.
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
|
||||
class PaginationParams(BaseModel):
|
||||
"""Pagination parameters for list endpoints"""
|
||||
|
||||
@@ -16,6 +17,7 @@ class PaginationParams(BaseModel):
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class StatusFilter(BaseModel):
|
||||
"""Status filter for posts/pages/products"""
|
||||
|
||||
@@ -29,6 +31,7 @@ class StatusFilter(BaseModel):
|
||||
raise ValueError(f"Status must be one of: {', '.join(allowed)}")
|
||||
return v
|
||||
|
||||
|
||||
class ErrorResponse(BaseModel):
|
||||
"""Standard error response"""
|
||||
|
||||
@@ -37,6 +40,7 @@ class ErrorResponse(BaseModel):
|
||||
code: str | None = Field(None, description="Error code")
|
||||
details: dict[str, Any] | None = Field(None, description="Additional error details")
|
||||
|
||||
|
||||
class SuccessResponse(BaseModel):
|
||||
"""Standard success response"""
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ Validation schemas for WordPress media library operations.
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, HttpUrl, field_validator
|
||||
|
||||
|
||||
class MediaBase(BaseModel):
|
||||
"""Base media schema"""
|
||||
|
||||
@@ -16,6 +17,7 @@ class MediaBase(BaseModel):
|
||||
caption: str | None = Field(None, description="Media caption")
|
||||
description: str | None = Field(None, description="Media description")
|
||||
|
||||
|
||||
class MediaUpload(MediaBase):
|
||||
"""Schema for uploading media from URL"""
|
||||
|
||||
@@ -33,12 +35,14 @@ class MediaUpload(MediaBase):
|
||||
raise ValueError("Filename too long (max 255 characters)")
|
||||
return v
|
||||
|
||||
|
||||
class MediaUpdate(MediaBase):
|
||||
"""Schema for updating media metadata"""
|
||||
|
||||
# All fields optional for updates
|
||||
pass
|
||||
|
||||
|
||||
class MediaResponse(BaseModel):
|
||||
"""Schema for media response data"""
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
|
||||
|
||||
|
||||
class OrderLineItem(BaseModel):
|
||||
"""Schema for order line item"""
|
||||
|
||||
@@ -19,6 +20,7 @@ class OrderLineItem(BaseModel):
|
||||
subtotal: str | None = Field(None, description="Line subtotal")
|
||||
total: str | None = Field(None, description="Line total")
|
||||
|
||||
|
||||
class ShippingAddress(BaseModel):
|
||||
"""Schema for shipping address"""
|
||||
|
||||
@@ -34,12 +36,14 @@ class ShippingAddress(BaseModel):
|
||||
postcode: str | None = Field(None, description="Postal code")
|
||||
country: str | None = Field(None, description="Country code (ISO 3166-1 alpha-2)")
|
||||
|
||||
|
||||
class BillingAddress(ShippingAddress):
|
||||
"""Schema for billing address (extends shipping)"""
|
||||
|
||||
email: EmailStr | None = Field(None, description="Email address")
|
||||
phone: str | None = Field(None, description="Phone number")
|
||||
|
||||
|
||||
class OrderBase(BaseModel):
|
||||
"""Base order schema"""
|
||||
|
||||
@@ -76,6 +80,7 @@ class OrderBase(BaseModel):
|
||||
raise ValueError(f"Status must be one of: {', '.join(allowed)}")
|
||||
return v
|
||||
|
||||
|
||||
class OrderCreate(OrderBase):
|
||||
"""Schema for creating a new order"""
|
||||
|
||||
@@ -85,12 +90,14 @@ class OrderCreate(OrderBase):
|
||||
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
|
||||
class OrderUpdate(OrderBase):
|
||||
"""Schema for updating an existing order"""
|
||||
|
||||
# All fields optional for updates
|
||||
pass
|
||||
|
||||
|
||||
class OrderStatusUpdate(BaseModel):
|
||||
"""Schema for updating order status"""
|
||||
|
||||
@@ -112,6 +119,7 @@ class OrderStatusUpdate(BaseModel):
|
||||
raise ValueError(f"Status must be one of: {', '.join(allowed)}")
|
||||
return v
|
||||
|
||||
|
||||
class OrderResponse(BaseModel):
|
||||
"""Schema for order response data"""
|
||||
|
||||
@@ -126,6 +134,7 @@ class OrderResponse(BaseModel):
|
||||
shipping: dict[str, Any]
|
||||
line_items: list[dict[str, Any]]
|
||||
|
||||
|
||||
class CustomerBase(BaseModel):
|
||||
"""Base customer schema"""
|
||||
|
||||
@@ -138,6 +147,7 @@ class CustomerBase(BaseModel):
|
||||
billing: BillingAddress | None = Field(None, description="Billing address")
|
||||
shipping: ShippingAddress | None = Field(None, description="Shipping address")
|
||||
|
||||
|
||||
class CustomerCreate(CustomerBase):
|
||||
"""Schema for creating a new customer"""
|
||||
|
||||
@@ -145,6 +155,7 @@ class CustomerCreate(CustomerBase):
|
||||
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
|
||||
class CustomerUpdate(CustomerBase):
|
||||
"""Schema for updating an existing customer"""
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ Validation schemas for WordPress posts, pages, and custom post types.
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
|
||||
class PostBase(BaseModel):
|
||||
"""Base post schema with common fields"""
|
||||
|
||||
@@ -44,6 +45,7 @@ class PostBase(BaseModel):
|
||||
raise ValueError("Status must be 'open' or 'closed'")
|
||||
return v
|
||||
|
||||
|
||||
class PostCreate(PostBase):
|
||||
"""Schema for creating a new post"""
|
||||
|
||||
@@ -52,12 +54,14 @@ class PostCreate(PostBase):
|
||||
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
||||
|
||||
class PostUpdate(PostBase):
|
||||
"""Schema for updating an existing post"""
|
||||
|
||||
# All fields optional for updates
|
||||
pass
|
||||
|
||||
|
||||
class PostResponse(BaseModel):
|
||||
"""Schema for post response data"""
|
||||
|
||||
@@ -77,6 +81,7 @@ class PostResponse(BaseModel):
|
||||
categories: list[int]
|
||||
tags: list[int]
|
||||
|
||||
|
||||
class PageCreate(PostCreate):
|
||||
"""Schema for creating a new page"""
|
||||
|
||||
@@ -84,6 +89,7 @@ class PageCreate(PostCreate):
|
||||
menu_order: int | None = Field(None, description="Menu order")
|
||||
template: str | None = Field(None, description="Page template")
|
||||
|
||||
|
||||
class PageUpdate(PostUpdate):
|
||||
"""Schema for updating an existing page"""
|
||||
|
||||
@@ -91,11 +97,13 @@ class PageUpdate(PostUpdate):
|
||||
menu_order: int | None = Field(None, description="Menu order")
|
||||
template: str | None = Field(None, description="Page template")
|
||||
|
||||
|
||||
class CustomPostCreate(PostCreate):
|
||||
"""Schema for creating custom post types"""
|
||||
|
||||
post_type: str = Field(..., description="Custom post type")
|
||||
|
||||
|
||||
class CustomPostUpdate(PostUpdate):
|
||||
"""Schema for updating custom post types"""
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
|
||||
class ProductBase(BaseModel):
|
||||
"""Base product schema"""
|
||||
|
||||
@@ -71,18 +72,21 @@ class ProductBase(BaseModel):
|
||||
raise ValueError(f"Stock status must be one of: {', '.join(allowed)}")
|
||||
return v
|
||||
|
||||
|
||||
class ProductCreate(ProductBase):
|
||||
"""Schema for creating a new product"""
|
||||
|
||||
name: str = Field(..., min_length=1, description="Product name (required)")
|
||||
type: str = Field("simple", description="Product type")
|
||||
|
||||
|
||||
class ProductUpdate(ProductBase):
|
||||
"""Schema for updating an existing product"""
|
||||
|
||||
# All fields optional for updates
|
||||
pass
|
||||
|
||||
|
||||
class ProductResponse(BaseModel):
|
||||
"""Schema for product response data"""
|
||||
|
||||
@@ -100,6 +104,7 @@ class ProductResponse(BaseModel):
|
||||
stock_status: str
|
||||
permalink: str
|
||||
|
||||
|
||||
class ProductCategory(BaseModel):
|
||||
"""Schema for product category"""
|
||||
|
||||
@@ -112,6 +117,7 @@ class ProductCategory(BaseModel):
|
||||
display: str | None = Field("default", description="Display type")
|
||||
image: dict[str, Any] | None = Field(None, description="Category image")
|
||||
|
||||
|
||||
class ProductVariation(BaseModel):
|
||||
"""Schema for product variation"""
|
||||
|
||||
@@ -128,6 +134,7 @@ class ProductVariation(BaseModel):
|
||||
attributes: list[dict[str, Any]] | None = Field(None, description="Variation attributes")
|
||||
image: dict[str, Any] | None = Field(None, description="Variation image")
|
||||
|
||||
|
||||
class ProductAttribute(BaseModel):
|
||||
"""Schema for product attribute"""
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ Validation schemas for SEO plugin data (Yoast, RankMath, etc.).
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
|
||||
class SEOData(BaseModel):
|
||||
"""Schema for SEO data (read)"""
|
||||
|
||||
@@ -23,6 +24,7 @@ class SEOData(BaseModel):
|
||||
twitter_image: str | None = Field(None, description="Twitter card image URL")
|
||||
robots: list[str] | None = Field(None, description="Robots meta tags")
|
||||
|
||||
|
||||
class SEOUpdate(BaseModel):
|
||||
"""Schema for SEO data updates"""
|
||||
|
||||
@@ -61,6 +63,7 @@ class SEOUpdate(BaseModel):
|
||||
pass
|
||||
return v
|
||||
|
||||
|
||||
class YoastSEO(SEOData):
|
||||
"""Yoast SEO specific data"""
|
||||
|
||||
@@ -70,6 +73,7 @@ class YoastSEO(SEOData):
|
||||
yoast_wpseo_metadesc: str | None = Field(None, description="Yoast meta description")
|
||||
yoast_wpseo_title: str | None = Field(None, description="Yoast SEO title")
|
||||
|
||||
|
||||
class RankMathSEO(SEOData):
|
||||
"""RankMath SEO specific data"""
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import json
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
|
||||
class WPCLIManager:
|
||||
"""
|
||||
Manages WP-CLI command execution for WordPress containers.
|
||||
|
||||
Reference in New Issue
Block a user