wp_python 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- src/wordpress_api/__init__.py +31 -0
- src/wordpress_api/auth.py +174 -0
- src/wordpress_api/client.py +293 -0
- src/wordpress_api/endpoints/__init__.py +43 -0
- src/wordpress_api/endpoints/application_passwords.py +235 -0
- src/wordpress_api/endpoints/autosaves.py +106 -0
- src/wordpress_api/endpoints/base.py +117 -0
- src/wordpress_api/endpoints/blocks.py +107 -0
- src/wordpress_api/endpoints/categories.py +91 -0
- src/wordpress_api/endpoints/comments.py +127 -0
- src/wordpress_api/endpoints/media.py +164 -0
- src/wordpress_api/endpoints/menus.py +120 -0
- src/wordpress_api/endpoints/pages.py +109 -0
- src/wordpress_api/endpoints/plugins.py +89 -0
- src/wordpress_api/endpoints/post_types.py +61 -0
- src/wordpress_api/endpoints/posts.py +131 -0
- src/wordpress_api/endpoints/revisions.py +121 -0
- src/wordpress_api/endpoints/search.py +81 -0
- src/wordpress_api/endpoints/settings.py +55 -0
- src/wordpress_api/endpoints/statuses.py +56 -0
- src/wordpress_api/endpoints/tags.py +79 -0
- src/wordpress_api/endpoints/taxonomies.py +41 -0
- src/wordpress_api/endpoints/themes.py +51 -0
- src/wordpress_api/endpoints/users.py +129 -0
- src/wordpress_api/exceptions.py +79 -0
- src/wordpress_api/models/__init__.py +49 -0
- src/wordpress_api/models/base.py +65 -0
- src/wordpress_api/models/category.py +41 -0
- src/wordpress_api/models/comment.py +75 -0
- src/wordpress_api/models/media.py +108 -0
- src/wordpress_api/models/menu.py +83 -0
- src/wordpress_api/models/page.py +80 -0
- src/wordpress_api/models/plugin.py +36 -0
- src/wordpress_api/models/post.py +112 -0
- src/wordpress_api/models/settings.py +32 -0
- src/wordpress_api/models/tag.py +38 -0
- src/wordpress_api/models/taxonomy.py +49 -0
- src/wordpress_api/models/theme.py +50 -0
- src/wordpress_api/models/user.py +82 -0
- wp_python-0.1.0.dist-info/METADATA +12 -0
- wp_python-0.1.0.dist-info/RECORD +42 -0
- wp_python-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WordPress REST API Python Client
|
|
3
|
+
|
|
4
|
+
A full-featured Python 3.12 interface to the WordPress REST API.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .client import WordPressClient
|
|
8
|
+
from .auth import ApplicationPasswordAuth, BasicAuth, JWTAuth, OAuth2Auth
|
|
9
|
+
from .exceptions import (
|
|
10
|
+
WordPressError,
|
|
11
|
+
AuthenticationError,
|
|
12
|
+
NotFoundError,
|
|
13
|
+
ValidationError,
|
|
14
|
+
RateLimitError,
|
|
15
|
+
ServerError,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__version__ = "1.0.0"
|
|
19
|
+
__all__ = [
|
|
20
|
+
"WordPressClient",
|
|
21
|
+
"ApplicationPasswordAuth",
|
|
22
|
+
"BasicAuth",
|
|
23
|
+
"JWTAuth",
|
|
24
|
+
"OAuth2Auth",
|
|
25
|
+
"WordPressError",
|
|
26
|
+
"AuthenticationError",
|
|
27
|
+
"NotFoundError",
|
|
28
|
+
"ValidationError",
|
|
29
|
+
"RateLimitError",
|
|
30
|
+
"ServerError",
|
|
31
|
+
]
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Authentication handlers for WordPress REST API."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AuthHandler(ABC):
|
|
11
|
+
"""Abstract base class for authentication handlers."""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def get_headers(self) -> dict[str, str]:
|
|
15
|
+
"""Return headers required for authentication."""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def authenticate(self, request: httpx.Request) -> httpx.Request:
|
|
20
|
+
"""Authenticate the request."""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BasicAuth(AuthHandler):
|
|
25
|
+
"""HTTP Basic Authentication.
|
|
26
|
+
|
|
27
|
+
Note: Basic auth should only be used over HTTPS.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, username: str, password: str) -> None:
|
|
31
|
+
self.username = username
|
|
32
|
+
self.password = password
|
|
33
|
+
self._credentials = base64.b64encode(
|
|
34
|
+
f"{username}:{password}".encode()
|
|
35
|
+
).decode()
|
|
36
|
+
|
|
37
|
+
def get_headers(self) -> dict[str, str]:
|
|
38
|
+
return {"Authorization": f"Basic {self._credentials}"}
|
|
39
|
+
|
|
40
|
+
def authenticate(self, request: httpx.Request) -> httpx.Request:
|
|
41
|
+
request.headers.update(self.get_headers())
|
|
42
|
+
return request
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ApplicationPasswordAuth(AuthHandler):
|
|
46
|
+
"""WordPress Application Passwords authentication.
|
|
47
|
+
|
|
48
|
+
Application Passwords were introduced in WordPress 5.6 and provide
|
|
49
|
+
a secure way to authenticate REST API requests without exposing
|
|
50
|
+
the user's main password.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, username: str, application_password: str) -> None:
|
|
54
|
+
self.username = username
|
|
55
|
+
self.application_password = application_password.replace(" ", "")
|
|
56
|
+
self._credentials = base64.b64encode(
|
|
57
|
+
f"{username}:{self.application_password}".encode()
|
|
58
|
+
).decode()
|
|
59
|
+
|
|
60
|
+
def get_headers(self) -> dict[str, str]:
|
|
61
|
+
return {"Authorization": f"Basic {self._credentials}"}
|
|
62
|
+
|
|
63
|
+
def authenticate(self, request: httpx.Request) -> httpx.Request:
|
|
64
|
+
request.headers.update(self.get_headers())
|
|
65
|
+
return request
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class JWTAuth(AuthHandler):
|
|
69
|
+
"""JWT (JSON Web Token) Authentication.
|
|
70
|
+
|
|
71
|
+
Requires a JWT authentication plugin on the WordPress site.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(self, token: str | None = None) -> None:
|
|
75
|
+
self.token = token
|
|
76
|
+
self._base_url: str | None = None
|
|
77
|
+
|
|
78
|
+
def set_base_url(self, url: str) -> None:
|
|
79
|
+
"""Set the base URL for token requests."""
|
|
80
|
+
self._base_url = url
|
|
81
|
+
|
|
82
|
+
def get_headers(self) -> dict[str, str]:
|
|
83
|
+
if not self.token:
|
|
84
|
+
return {}
|
|
85
|
+
return {"Authorization": f"Bearer {self.token}"}
|
|
86
|
+
|
|
87
|
+
def authenticate(self, request: httpx.Request) -> httpx.Request:
|
|
88
|
+
if self.token:
|
|
89
|
+
request.headers.update(self.get_headers())
|
|
90
|
+
return request
|
|
91
|
+
|
|
92
|
+
def obtain_token(
|
|
93
|
+
self,
|
|
94
|
+
client: httpx.Client,
|
|
95
|
+
username: str,
|
|
96
|
+
password: str,
|
|
97
|
+
endpoint: str = "/wp-json/jwt-auth/v1/token",
|
|
98
|
+
) -> str:
|
|
99
|
+
"""Obtain a JWT token from the WordPress site."""
|
|
100
|
+
response = client.post(
|
|
101
|
+
endpoint,
|
|
102
|
+
json={"username": username, "password": password},
|
|
103
|
+
)
|
|
104
|
+
response.raise_for_status()
|
|
105
|
+
data = response.json()
|
|
106
|
+
self.token = data.get("token", data.get("data", {}).get("token"))
|
|
107
|
+
return self.token
|
|
108
|
+
|
|
109
|
+
def validate_token(
|
|
110
|
+
self,
|
|
111
|
+
client: httpx.Client,
|
|
112
|
+
endpoint: str = "/wp-json/jwt-auth/v1/token/validate",
|
|
113
|
+
) -> bool:
|
|
114
|
+
"""Validate the current JWT token."""
|
|
115
|
+
if not self.token:
|
|
116
|
+
return False
|
|
117
|
+
response = client.post(endpoint, headers=self.get_headers())
|
|
118
|
+
return response.status_code == 200
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class OAuth2Auth(AuthHandler):
|
|
122
|
+
"""OAuth 2.0 Authentication.
|
|
123
|
+
|
|
124
|
+
Requires OAuth 2.0 plugin on the WordPress site.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
def __init__(
|
|
128
|
+
self,
|
|
129
|
+
access_token: str | None = None,
|
|
130
|
+
refresh_token: str | None = None,
|
|
131
|
+
client_id: str | None = None,
|
|
132
|
+
client_secret: str | None = None,
|
|
133
|
+
) -> None:
|
|
134
|
+
self.access_token = access_token
|
|
135
|
+
self.refresh_token = refresh_token
|
|
136
|
+
self.client_id = client_id
|
|
137
|
+
self.client_secret = client_secret
|
|
138
|
+
|
|
139
|
+
def get_headers(self) -> dict[str, str]:
|
|
140
|
+
if not self.access_token:
|
|
141
|
+
return {}
|
|
142
|
+
return {"Authorization": f"Bearer {self.access_token}"}
|
|
143
|
+
|
|
144
|
+
def authenticate(self, request: httpx.Request) -> httpx.Request:
|
|
145
|
+
if self.access_token:
|
|
146
|
+
request.headers.update(self.get_headers())
|
|
147
|
+
return request
|
|
148
|
+
|
|
149
|
+
def refresh_access_token(
|
|
150
|
+
self,
|
|
151
|
+
client: httpx.Client,
|
|
152
|
+
token_endpoint: str,
|
|
153
|
+
) -> str:
|
|
154
|
+
"""Refresh the access token using the refresh token."""
|
|
155
|
+
if not self.refresh_token or not self.client_id:
|
|
156
|
+
raise ValueError("Refresh token and client ID required")
|
|
157
|
+
|
|
158
|
+
data: dict[str, Any] = {
|
|
159
|
+
"grant_type": "refresh_token",
|
|
160
|
+
"refresh_token": self.refresh_token,
|
|
161
|
+
"client_id": self.client_id,
|
|
162
|
+
}
|
|
163
|
+
if self.client_secret:
|
|
164
|
+
data["client_secret"] = self.client_secret
|
|
165
|
+
|
|
166
|
+
response = client.post(token_endpoint, data=data)
|
|
167
|
+
response.raise_for_status()
|
|
168
|
+
token_data = response.json()
|
|
169
|
+
|
|
170
|
+
self.access_token = token_data["access_token"]
|
|
171
|
+
if "refresh_token" in token_data:
|
|
172
|
+
self.refresh_token = token_data["refresh_token"]
|
|
173
|
+
|
|
174
|
+
return self.access_token
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"""Main WordPress REST API Client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from urllib.parse import urljoin # noqa: F401
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
from .auth import ApplicationPasswordAuth, AuthHandler, BasicAuth, JWTAuth # noqa: F401
|
|
11
|
+
from .endpoints.application_passwords import ApplicationPasswordsEndpoint
|
|
12
|
+
from .endpoints.autosaves import AutosavesEndpoint
|
|
13
|
+
from .endpoints.blocks import BlocksEndpoint
|
|
14
|
+
from .endpoints.categories import CategoriesEndpoint
|
|
15
|
+
from .endpoints.comments import CommentsEndpoint
|
|
16
|
+
from .endpoints.media import MediaEndpoint
|
|
17
|
+
from .endpoints.menus import MenusEndpoint
|
|
18
|
+
from .endpoints.pages import PagesEndpoint
|
|
19
|
+
from .endpoints.plugins import PluginsEndpoint
|
|
20
|
+
from .endpoints.post_types import PostTypesEndpoint
|
|
21
|
+
from .endpoints.posts import PostsEndpoint
|
|
22
|
+
from .endpoints.revisions import RevisionsEndpoint
|
|
23
|
+
from .endpoints.search import SearchEndpoint
|
|
24
|
+
from .endpoints.settings import SettingsEndpoint
|
|
25
|
+
from .endpoints.statuses import StatusesEndpoint
|
|
26
|
+
from .endpoints.tags import TagsEndpoint
|
|
27
|
+
from .endpoints.taxonomies import TaxonomiesEndpoint
|
|
28
|
+
from .endpoints.themes import ThemesEndpoint
|
|
29
|
+
from .endpoints.users import UsersEndpoint
|
|
30
|
+
from .exceptions import WordPressError, raise_for_status # noqa: F401
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class WordPressClient:
|
|
34
|
+
"""WordPress REST API Client.
|
|
35
|
+
|
|
36
|
+
A full-featured Python interface to the WordPress REST API.
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
```python
|
|
40
|
+
from wordpress_api import WordPressClient, ApplicationPasswordAuth
|
|
41
|
+
|
|
42
|
+
auth = ApplicationPasswordAuth("username", "xxxx xxxx xxxx xxxx xxxx xxxx")
|
|
43
|
+
client = WordPressClient("https://example.com", auth=auth)
|
|
44
|
+
|
|
45
|
+
# List posts
|
|
46
|
+
posts = client.posts.list()
|
|
47
|
+
|
|
48
|
+
# Create a new post
|
|
49
|
+
new_post = client.posts.create({
|
|
50
|
+
"title": "Hello World",
|
|
51
|
+
"content": "This is my first post!",
|
|
52
|
+
"status": "publish"
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
# Get current user
|
|
56
|
+
me = client.users.me()
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
base_url: The WordPress site URL (e.g., "https://example.com").
|
|
61
|
+
auth: Authentication handler (ApplicationPasswordAuth, BasicAuth, or JWTAuth).
|
|
62
|
+
timeout: Request timeout in seconds (default 30).
|
|
63
|
+
verify_ssl: Whether to verify SSL certificates (default True).
|
|
64
|
+
user_agent: Custom User-Agent string.
|
|
65
|
+
api_prefix: REST API prefix (default "/wp-json").
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
base_url: str,
|
|
71
|
+
auth: AuthHandler | None = None,
|
|
72
|
+
timeout: float = 30.0,
|
|
73
|
+
verify_ssl: bool = True,
|
|
74
|
+
user_agent: str | None = None,
|
|
75
|
+
api_prefix: str = "/wp-json",
|
|
76
|
+
) -> None:
|
|
77
|
+
self.base_url = base_url.rstrip("/")
|
|
78
|
+
self.api_prefix = api_prefix
|
|
79
|
+
self.auth = auth
|
|
80
|
+
self.timeout = timeout
|
|
81
|
+
self.verify_ssl = verify_ssl
|
|
82
|
+
|
|
83
|
+
headers = {
|
|
84
|
+
"Accept": "application/json",
|
|
85
|
+
"Content-Type": "application/json",
|
|
86
|
+
}
|
|
87
|
+
if user_agent:
|
|
88
|
+
headers["User-Agent"] = user_agent
|
|
89
|
+
else:
|
|
90
|
+
headers["User-Agent"] = "WordPress-Python-Client/1.0"
|
|
91
|
+
|
|
92
|
+
if auth:
|
|
93
|
+
headers.update(auth.get_headers())
|
|
94
|
+
|
|
95
|
+
self._client = httpx.Client(
|
|
96
|
+
base_url=f"{self.base_url}{self.api_prefix}",
|
|
97
|
+
headers=headers,
|
|
98
|
+
timeout=timeout,
|
|
99
|
+
verify=verify_ssl,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
self._last_response: httpx.Response | None = None
|
|
103
|
+
self._total_pages: int | None = None
|
|
104
|
+
self._total_items: int | None = None
|
|
105
|
+
|
|
106
|
+
self._init_endpoints()
|
|
107
|
+
|
|
108
|
+
def _init_endpoints(self) -> None:
|
|
109
|
+
"""Initialize all API endpoints."""
|
|
110
|
+
self.posts = PostsEndpoint(self)
|
|
111
|
+
self.pages = PagesEndpoint(self)
|
|
112
|
+
self.media = MediaEndpoint(self)
|
|
113
|
+
self.users = UsersEndpoint(self)
|
|
114
|
+
self.comments = CommentsEndpoint(self)
|
|
115
|
+
self.categories = CategoriesEndpoint(self)
|
|
116
|
+
self.tags = TagsEndpoint(self)
|
|
117
|
+
self.taxonomies = TaxonomiesEndpoint(self)
|
|
118
|
+
self.settings = SettingsEndpoint(self)
|
|
119
|
+
self.plugins = PluginsEndpoint(self)
|
|
120
|
+
self.themes = ThemesEndpoint(self)
|
|
121
|
+
self.menus = MenusEndpoint(self)
|
|
122
|
+
self.search = SearchEndpoint(self)
|
|
123
|
+
self.blocks = BlocksEndpoint(self)
|
|
124
|
+
self.revisions = RevisionsEndpoint(self)
|
|
125
|
+
self.autosaves = AutosavesEndpoint(self)
|
|
126
|
+
self.post_types = PostTypesEndpoint(self)
|
|
127
|
+
self.statuses = StatusesEndpoint(self)
|
|
128
|
+
self.application_passwords = ApplicationPasswordsEndpoint(self)
|
|
129
|
+
|
|
130
|
+
def _request(
|
|
131
|
+
self,
|
|
132
|
+
method: str,
|
|
133
|
+
path: str,
|
|
134
|
+
params: dict[str, Any] | None = None,
|
|
135
|
+
json: dict[str, Any] | None = None,
|
|
136
|
+
files: dict[str, Any] | None = None,
|
|
137
|
+
content: bytes | None = None,
|
|
138
|
+
headers: dict[str, str] | None = None,
|
|
139
|
+
) -> Any:
|
|
140
|
+
"""Make an HTTP request to the WordPress REST API."""
|
|
141
|
+
if params:
|
|
142
|
+
params = {k: v for k, v in params.items() if v is not None}
|
|
143
|
+
|
|
144
|
+
request_headers = {}
|
|
145
|
+
if headers:
|
|
146
|
+
request_headers.update(headers)
|
|
147
|
+
|
|
148
|
+
if content is not None:
|
|
149
|
+
response = self._client.request(
|
|
150
|
+
method,
|
|
151
|
+
path,
|
|
152
|
+
params=params,
|
|
153
|
+
content=content,
|
|
154
|
+
headers=request_headers,
|
|
155
|
+
)
|
|
156
|
+
elif files:
|
|
157
|
+
response = self._client.request(
|
|
158
|
+
method,
|
|
159
|
+
path,
|
|
160
|
+
params=params,
|
|
161
|
+
files=files,
|
|
162
|
+
headers=request_headers,
|
|
163
|
+
)
|
|
164
|
+
else:
|
|
165
|
+
response = self._client.request(
|
|
166
|
+
method,
|
|
167
|
+
path,
|
|
168
|
+
params=params,
|
|
169
|
+
json=json,
|
|
170
|
+
headers=request_headers,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
self._last_response = response
|
|
174
|
+
self._total_pages = int(response.headers.get("X-WP-TotalPages", 0))
|
|
175
|
+
self._total_items = int(response.headers.get("X-WP-Total", 0))
|
|
176
|
+
|
|
177
|
+
if response.status_code >= 400:
|
|
178
|
+
try:
|
|
179
|
+
error_data = response.json()
|
|
180
|
+
except Exception:
|
|
181
|
+
error_data = {"message": response.text}
|
|
182
|
+
raise_for_status(response.status_code, error_data)
|
|
183
|
+
|
|
184
|
+
if response.status_code == 204:
|
|
185
|
+
return {}
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
return response.json()
|
|
189
|
+
except Exception:
|
|
190
|
+
return {"raw": response.text}
|
|
191
|
+
|
|
192
|
+
def discover(self) -> dict[str, Any]:
|
|
193
|
+
"""Discover the WordPress REST API.
|
|
194
|
+
|
|
195
|
+
Returns information about available routes and authentication.
|
|
196
|
+
"""
|
|
197
|
+
return self._request("GET", "/")
|
|
198
|
+
|
|
199
|
+
def get_namespaces(self) -> list[str]:
|
|
200
|
+
"""Get available API namespaces."""
|
|
201
|
+
discovery = self.discover()
|
|
202
|
+
return discovery.get("namespaces", [])
|
|
203
|
+
|
|
204
|
+
def get_routes(self) -> dict[str, Any]:
|
|
205
|
+
"""Get all available API routes."""
|
|
206
|
+
discovery = self.discover()
|
|
207
|
+
return discovery.get("routes", {})
|
|
208
|
+
|
|
209
|
+
def get_authentication_status(self) -> dict[str, Any]:
|
|
210
|
+
"""Check if the current authentication is valid."""
|
|
211
|
+
discovery = self.discover()
|
|
212
|
+
return discovery.get("authentication", {})
|
|
213
|
+
|
|
214
|
+
@property
|
|
215
|
+
def total_pages(self) -> int | None:
|
|
216
|
+
"""Total pages from the last list request."""
|
|
217
|
+
return self._total_pages
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def total_items(self) -> int | None:
|
|
221
|
+
"""Total items from the last list request."""
|
|
222
|
+
return self._total_items
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def last_response(self) -> httpx.Response | None:
|
|
226
|
+
"""The last HTTP response received."""
|
|
227
|
+
return self._last_response
|
|
228
|
+
|
|
229
|
+
def close(self) -> None:
|
|
230
|
+
"""Close the HTTP client."""
|
|
231
|
+
self._client.close()
|
|
232
|
+
|
|
233
|
+
def __enter__(self) -> "WordPressClient":
|
|
234
|
+
return self
|
|
235
|
+
|
|
236
|
+
def __exit__(self, *args: Any) -> None:
|
|
237
|
+
self.close()
|
|
238
|
+
|
|
239
|
+
def custom_post_type(self, post_type: str) -> "CustomPostTypeEndpoint":
|
|
240
|
+
"""Access a custom post type endpoint.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
post_type: The custom post type slug (e.g., "products", "portfolio").
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
A CustomPostTypeEndpoint instance for the specified post type.
|
|
247
|
+
"""
|
|
248
|
+
return CustomPostTypeEndpoint(self, post_type)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class CustomPostTypeEndpoint:
|
|
252
|
+
"""Endpoint for custom post types."""
|
|
253
|
+
|
|
254
|
+
def __init__(self, client: WordPressClient, post_type: str) -> None:
|
|
255
|
+
self._client = client
|
|
256
|
+
self.post_type = post_type
|
|
257
|
+
self._path = f"/wp/v2/{post_type}"
|
|
258
|
+
|
|
259
|
+
def _get(self, path: str, params: dict[str, Any] | None = None) -> Any:
|
|
260
|
+
return self._client._request("GET", path, params=params)
|
|
261
|
+
|
|
262
|
+
def _post(self, path: str, data: dict[str, Any] | None = None) -> Any:
|
|
263
|
+
return self._client._request("POST", path, json=data)
|
|
264
|
+
|
|
265
|
+
def _delete(self, path: str, params: dict[str, Any] | None = None) -> Any:
|
|
266
|
+
return self._client._request("DELETE", path, params=params)
|
|
267
|
+
|
|
268
|
+
def list(
|
|
269
|
+
self,
|
|
270
|
+
page: int = 1,
|
|
271
|
+
per_page: int = 10,
|
|
272
|
+
**kwargs: Any,
|
|
273
|
+
) -> list[dict[str, Any]]:
|
|
274
|
+
"""List items of this custom post type."""
|
|
275
|
+
params = {"page": page, "per_page": per_page, **kwargs}
|
|
276
|
+
return self._get(self._path, params=params)
|
|
277
|
+
|
|
278
|
+
def get(self, id: int, **kwargs: Any) -> dict[str, Any]:
|
|
279
|
+
"""Get a single item by ID."""
|
|
280
|
+
return self._get(f"{self._path}/{id}", params=kwargs)
|
|
281
|
+
|
|
282
|
+
def create(self, data: dict[str, Any]) -> dict[str, Any]:
|
|
283
|
+
"""Create a new item."""
|
|
284
|
+
return self._post(self._path, data=data)
|
|
285
|
+
|
|
286
|
+
def update(self, id: int, data: dict[str, Any]) -> dict[str, Any]:
|
|
287
|
+
"""Update an existing item."""
|
|
288
|
+
return self._post(f"{self._path}/{id}", data=data)
|
|
289
|
+
|
|
290
|
+
def delete(self, id: int, force: bool = False, **kwargs: Any) -> dict[str, Any]:
|
|
291
|
+
"""Delete an item."""
|
|
292
|
+
params = {"force": force, **kwargs}
|
|
293
|
+
return self._delete(f"{self._path}/{id}", params=params)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""WordPress REST API Endpoints."""
|
|
2
|
+
|
|
3
|
+
from .posts import PostsEndpoint
|
|
4
|
+
from .pages import PagesEndpoint
|
|
5
|
+
from .media import MediaEndpoint
|
|
6
|
+
from .users import UsersEndpoint
|
|
7
|
+
from .comments import CommentsEndpoint
|
|
8
|
+
from .categories import CategoriesEndpoint
|
|
9
|
+
from .tags import TagsEndpoint
|
|
10
|
+
from .taxonomies import TaxonomiesEndpoint
|
|
11
|
+
from .settings import SettingsEndpoint
|
|
12
|
+
from .plugins import PluginsEndpoint
|
|
13
|
+
from .themes import ThemesEndpoint
|
|
14
|
+
from .menus import MenusEndpoint
|
|
15
|
+
from .search import SearchEndpoint
|
|
16
|
+
from .blocks import BlocksEndpoint
|
|
17
|
+
from .revisions import RevisionsEndpoint
|
|
18
|
+
from .autosaves import AutosavesEndpoint
|
|
19
|
+
from .post_types import PostTypesEndpoint
|
|
20
|
+
from .statuses import StatusesEndpoint
|
|
21
|
+
from .application_passwords import ApplicationPasswordsEndpoint
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"PostsEndpoint",
|
|
25
|
+
"PagesEndpoint",
|
|
26
|
+
"MediaEndpoint",
|
|
27
|
+
"UsersEndpoint",
|
|
28
|
+
"CommentsEndpoint",
|
|
29
|
+
"CategoriesEndpoint",
|
|
30
|
+
"TagsEndpoint",
|
|
31
|
+
"TaxonomiesEndpoint",
|
|
32
|
+
"SettingsEndpoint",
|
|
33
|
+
"PluginsEndpoint",
|
|
34
|
+
"ThemesEndpoint",
|
|
35
|
+
"MenusEndpoint",
|
|
36
|
+
"SearchEndpoint",
|
|
37
|
+
"BlocksEndpoint",
|
|
38
|
+
"RevisionsEndpoint",
|
|
39
|
+
"AutosavesEndpoint",
|
|
40
|
+
"PostTypesEndpoint",
|
|
41
|
+
"StatusesEndpoint",
|
|
42
|
+
"ApplicationPasswordsEndpoint",
|
|
43
|
+
]
|