strapi-kit 0.0.1__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.
- strapi_kit/__init__.py +97 -0
- strapi_kit/__version__.py +15 -0
- strapi_kit/_version.py +34 -0
- strapi_kit/auth/__init__.py +7 -0
- strapi_kit/auth/api_token.py +48 -0
- strapi_kit/cache/__init__.py +5 -0
- strapi_kit/cache/schema_cache.py +211 -0
- strapi_kit/client/__init__.py +11 -0
- strapi_kit/client/async_client.py +1032 -0
- strapi_kit/client/base.py +460 -0
- strapi_kit/client/sync_client.py +980 -0
- strapi_kit/config_provider.py +368 -0
- strapi_kit/exceptions/__init__.py +37 -0
- strapi_kit/exceptions/errors.py +205 -0
- strapi_kit/export/__init__.py +10 -0
- strapi_kit/export/exporter.py +384 -0
- strapi_kit/export/importer.py +619 -0
- strapi_kit/export/media_handler.py +322 -0
- strapi_kit/export/relation_resolver.py +172 -0
- strapi_kit/models/__init__.py +104 -0
- strapi_kit/models/bulk.py +69 -0
- strapi_kit/models/config.py +174 -0
- strapi_kit/models/enums.py +97 -0
- strapi_kit/models/export_format.py +166 -0
- strapi_kit/models/import_options.py +142 -0
- strapi_kit/models/request/__init__.py +1 -0
- strapi_kit/models/request/fields.py +65 -0
- strapi_kit/models/request/filters.py +611 -0
- strapi_kit/models/request/pagination.py +168 -0
- strapi_kit/models/request/populate.py +281 -0
- strapi_kit/models/request/query.py +429 -0
- strapi_kit/models/request/sort.py +147 -0
- strapi_kit/models/response/__init__.py +1 -0
- strapi_kit/models/response/base.py +75 -0
- strapi_kit/models/response/component.py +67 -0
- strapi_kit/models/response/media.py +91 -0
- strapi_kit/models/response/meta.py +44 -0
- strapi_kit/models/response/normalized.py +168 -0
- strapi_kit/models/response/relation.py +48 -0
- strapi_kit/models/response/v4.py +70 -0
- strapi_kit/models/response/v5.py +57 -0
- strapi_kit/models/schema.py +93 -0
- strapi_kit/operations/__init__.py +16 -0
- strapi_kit/operations/media.py +226 -0
- strapi_kit/operations/streaming.py +144 -0
- strapi_kit/parsers/__init__.py +5 -0
- strapi_kit/parsers/version_detecting.py +171 -0
- strapi_kit/protocols.py +455 -0
- strapi_kit/utils/__init__.py +15 -0
- strapi_kit/utils/rate_limiter.py +201 -0
- strapi_kit/utils/uid.py +88 -0
- strapi_kit-0.0.1.dist-info/METADATA +1098 -0
- strapi_kit-0.0.1.dist-info/RECORD +55 -0
- strapi_kit-0.0.1.dist-info/WHEEL +4 -0
- strapi_kit-0.0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"""Configuration provider and factory for dependency injection.
|
|
2
|
+
|
|
3
|
+
This module provides a flexible way to create StrapiConfig instances from
|
|
4
|
+
various sources (environment variables, .env files, dictionaries, etc.).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from pydantic import SecretStr, ValidationError
|
|
11
|
+
|
|
12
|
+
from .exceptions import StrapiError
|
|
13
|
+
from .models.config import RetryConfig, StrapiConfig
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ConfigurationError(StrapiError):
|
|
17
|
+
"""Raised when configuration cannot be loaded or is invalid.
|
|
18
|
+
|
|
19
|
+
Inherits from StrapiError for consistent exception handling.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, message: str) -> None:
|
|
23
|
+
"""Initialize ConfigurationError.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
message: Human-readable error message
|
|
27
|
+
"""
|
|
28
|
+
super().__init__(message, details=None)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ConfigFactory:
|
|
32
|
+
"""Factory for creating StrapiConfig instances from various sources.
|
|
33
|
+
|
|
34
|
+
This class provides a flexible dependency injection pattern for configuration,
|
|
35
|
+
allowing configs to be loaded from:
|
|
36
|
+
- Environment variables
|
|
37
|
+
- .env files (with custom paths)
|
|
38
|
+
- Dictionaries
|
|
39
|
+
- Default values
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
>>> # Load from .env file in current directory
|
|
43
|
+
>>> config = ConfigFactory.from_env()
|
|
44
|
+
|
|
45
|
+
>>> # Load from custom .env file
|
|
46
|
+
>>> config = ConfigFactory.from_env_file("/path/to/.env")
|
|
47
|
+
|
|
48
|
+
>>> # Load from dictionary
|
|
49
|
+
>>> config = ConfigFactory.from_dict({
|
|
50
|
+
... "base_url": "http://localhost:1337",
|
|
51
|
+
... "api_token": "secret-token"
|
|
52
|
+
... })
|
|
53
|
+
|
|
54
|
+
>>> # Load with custom search paths
|
|
55
|
+
>>> config = ConfigFactory.from_env(
|
|
56
|
+
... search_paths=[".env", ".env.local", "~/.strapi/.env"]
|
|
57
|
+
... )
|
|
58
|
+
|
|
59
|
+
>>> # Create with explicit values (no .env loading)
|
|
60
|
+
>>> config = ConfigFactory.create(
|
|
61
|
+
... base_url="http://localhost:1337",
|
|
62
|
+
... api_token="secret-token"
|
|
63
|
+
... )
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def from_env(
|
|
68
|
+
*,
|
|
69
|
+
search_paths: list[str | Path] | None = None,
|
|
70
|
+
required: bool = False,
|
|
71
|
+
) -> StrapiConfig:
|
|
72
|
+
"""Load configuration from environment variables and .env files.
|
|
73
|
+
|
|
74
|
+
Searches for .env files in the specified paths and loads the first one found.
|
|
75
|
+
Environment variables always take precedence over .env file values.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
search_paths: List of paths to search for .env files
|
|
79
|
+
(default: [".env", ".env.local", "~/.config/strapi/.env"])
|
|
80
|
+
required: If True, raises ConfigurationError if no .env file is found
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Configured StrapiConfig instance
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
ConfigurationError: If required=True and no .env file found,
|
|
87
|
+
or if configuration values are invalid (wraps ValidationError)
|
|
88
|
+
|
|
89
|
+
Example:
|
|
90
|
+
>>> config = ConfigFactory.from_env(
|
|
91
|
+
... search_paths=[".env", ".env.production"],
|
|
92
|
+
... required=True
|
|
93
|
+
... )
|
|
94
|
+
"""
|
|
95
|
+
if search_paths is None:
|
|
96
|
+
search_paths = [
|
|
97
|
+
".env",
|
|
98
|
+
".env.local",
|
|
99
|
+
Path.home() / ".config" / "strapi" / ".env",
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
# Find first existing .env file
|
|
103
|
+
env_file = None
|
|
104
|
+
for path in search_paths:
|
|
105
|
+
resolved_path = Path(path).expanduser().resolve()
|
|
106
|
+
if resolved_path.exists():
|
|
107
|
+
env_file = resolved_path
|
|
108
|
+
break
|
|
109
|
+
|
|
110
|
+
if required and env_file is None:
|
|
111
|
+
searched = [str(Path(p).expanduser().resolve()) for p in search_paths]
|
|
112
|
+
raise ConfigurationError(f"No .env file found. Searched: {', '.join(searched)}")
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
# Load with custom env_file path
|
|
116
|
+
if env_file:
|
|
117
|
+
return StrapiConfig(_env_file=str(env_file)) # type: ignore[call-arg]
|
|
118
|
+
else:
|
|
119
|
+
# Load from environment variables only
|
|
120
|
+
return StrapiConfig(_env_file=None) # type: ignore[call-arg]
|
|
121
|
+
|
|
122
|
+
except ValidationError as e:
|
|
123
|
+
raise ConfigurationError(f"Invalid configuration: {e.error_count()} errors\n{e}") from e
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def from_env_file(
|
|
127
|
+
env_file: str | Path,
|
|
128
|
+
*,
|
|
129
|
+
required: bool = True,
|
|
130
|
+
) -> StrapiConfig:
|
|
131
|
+
"""Load configuration from a specific .env file.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
env_file: Path to the .env file
|
|
135
|
+
required: If True, raises error if file doesn't exist
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Configured StrapiConfig instance
|
|
139
|
+
|
|
140
|
+
Raises:
|
|
141
|
+
ConfigurationError: If file doesn't exist (when required=True),
|
|
142
|
+
or if configuration values are invalid (wraps ValidationError)
|
|
143
|
+
|
|
144
|
+
Example:
|
|
145
|
+
>>> config = ConfigFactory.from_env_file("/etc/strapi/.env")
|
|
146
|
+
"""
|
|
147
|
+
resolved_path = Path(env_file).expanduser().resolve()
|
|
148
|
+
|
|
149
|
+
if required and not resolved_path.exists():
|
|
150
|
+
raise ConfigurationError(f".env file not found: {resolved_path}")
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
return StrapiConfig( # type: ignore[call-arg]
|
|
154
|
+
_env_file=str(resolved_path) if resolved_path.exists() else None
|
|
155
|
+
)
|
|
156
|
+
except ValidationError as e:
|
|
157
|
+
raise ConfigurationError(
|
|
158
|
+
f"Invalid configuration in {resolved_path}: {e.error_count()} errors\n{e}"
|
|
159
|
+
) from e
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def from_dict(config_dict: dict[str, Any]) -> StrapiConfig:
|
|
163
|
+
"""Create configuration from a dictionary.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
config_dict: Dictionary with configuration values
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Configured StrapiConfig instance
|
|
170
|
+
|
|
171
|
+
Raises:
|
|
172
|
+
ConfigurationError: If configuration values are invalid (wraps ValidationError)
|
|
173
|
+
|
|
174
|
+
Example:
|
|
175
|
+
>>> config = ConfigFactory.from_dict({
|
|
176
|
+
... "base_url": "http://localhost:1337",
|
|
177
|
+
... "api_token": "secret-token",
|
|
178
|
+
... "timeout": 60.0,
|
|
179
|
+
... "retry": {
|
|
180
|
+
... "max_attempts": 5,
|
|
181
|
+
... "initial_wait": 2.0
|
|
182
|
+
... }
|
|
183
|
+
... })
|
|
184
|
+
"""
|
|
185
|
+
try:
|
|
186
|
+
# Disable .env loading when creating from dict
|
|
187
|
+
return StrapiConfig(_env_file=None, **config_dict) # type: ignore[call-arg]
|
|
188
|
+
except ValidationError as e:
|
|
189
|
+
raise ConfigurationError(f"Invalid configuration: {e.error_count()} errors\n{e}") from e
|
|
190
|
+
|
|
191
|
+
@staticmethod
|
|
192
|
+
def create(
|
|
193
|
+
*,
|
|
194
|
+
base_url: str,
|
|
195
|
+
api_token: str,
|
|
196
|
+
api_version: str = "auto",
|
|
197
|
+
timeout: float = 30.0,
|
|
198
|
+
max_connections: int = 10,
|
|
199
|
+
retry: RetryConfig | dict[str, Any] | None = None,
|
|
200
|
+
rate_limit_per_second: float | None = None,
|
|
201
|
+
verify_ssl: bool = True,
|
|
202
|
+
) -> StrapiConfig:
|
|
203
|
+
"""Create configuration with explicit values (no .env loading).
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
base_url: Base URL of Strapi instance
|
|
207
|
+
api_token: API authentication token
|
|
208
|
+
api_version: API version (v4, v5, or auto)
|
|
209
|
+
timeout: Request timeout in seconds
|
|
210
|
+
max_connections: Maximum concurrent connections
|
|
211
|
+
retry: Retry configuration (RetryConfig instance or dict)
|
|
212
|
+
rate_limit_per_second: Maximum requests per second
|
|
213
|
+
verify_ssl: Whether to verify SSL certificates
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
Configured StrapiConfig instance
|
|
217
|
+
|
|
218
|
+
Raises:
|
|
219
|
+
ConfigurationError: If validation fails
|
|
220
|
+
|
|
221
|
+
Example:
|
|
222
|
+
>>> config = ConfigFactory.create(
|
|
223
|
+
... base_url="http://localhost:1337",
|
|
224
|
+
... api_token="secret-token",
|
|
225
|
+
... timeout=60.0
|
|
226
|
+
... )
|
|
227
|
+
"""
|
|
228
|
+
try:
|
|
229
|
+
# Convert retry dict to RetryConfig if needed
|
|
230
|
+
retry_config: RetryConfig
|
|
231
|
+
if retry is None:
|
|
232
|
+
retry_config = RetryConfig()
|
|
233
|
+
elif isinstance(retry, dict):
|
|
234
|
+
retry_config = RetryConfig(**retry)
|
|
235
|
+
else:
|
|
236
|
+
retry_config = retry
|
|
237
|
+
|
|
238
|
+
return StrapiConfig( # type: ignore[call-arg]
|
|
239
|
+
_env_file=None, # Disable .env loading
|
|
240
|
+
base_url=base_url,
|
|
241
|
+
api_token=SecretStr(api_token),
|
|
242
|
+
api_version=api_version, # type: ignore[arg-type]
|
|
243
|
+
timeout=timeout,
|
|
244
|
+
max_connections=max_connections,
|
|
245
|
+
retry=retry_config,
|
|
246
|
+
rate_limit_per_second=rate_limit_per_second,
|
|
247
|
+
verify_ssl=verify_ssl,
|
|
248
|
+
)
|
|
249
|
+
except ValidationError as e:
|
|
250
|
+
raise ConfigurationError(f"Invalid configuration: {e.error_count()} errors\n{e}") from e
|
|
251
|
+
|
|
252
|
+
@staticmethod
|
|
253
|
+
def from_environment_only() -> StrapiConfig:
|
|
254
|
+
"""Load configuration from environment variables only (no .env files).
|
|
255
|
+
|
|
256
|
+
This is useful in containerized environments where configuration
|
|
257
|
+
is injected via environment variables.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Configured StrapiConfig instance
|
|
261
|
+
|
|
262
|
+
Raises:
|
|
263
|
+
ConfigurationError: If validation fails
|
|
264
|
+
|
|
265
|
+
Example:
|
|
266
|
+
>>> # Set env vars first:
|
|
267
|
+
>>> # export STRAPI_BASE_URL=http://localhost:1337
|
|
268
|
+
>>> # export STRAPI_API_TOKEN=secret-token
|
|
269
|
+
>>> config = ConfigFactory.from_environment_only()
|
|
270
|
+
"""
|
|
271
|
+
try:
|
|
272
|
+
return StrapiConfig(_env_file=None) # type: ignore[call-arg]
|
|
273
|
+
except ValidationError as e:
|
|
274
|
+
raise ConfigurationError(
|
|
275
|
+
f"Invalid configuration from environment: {e.error_count()} errors\n{e}"
|
|
276
|
+
) from e
|
|
277
|
+
|
|
278
|
+
@staticmethod
|
|
279
|
+
def merge(
|
|
280
|
+
*configs: StrapiConfig,
|
|
281
|
+
base: StrapiConfig | None = None,
|
|
282
|
+
) -> StrapiConfig:
|
|
283
|
+
"""Merge multiple configurations with later configs overriding earlier ones.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
*configs: Configuration instances to merge
|
|
287
|
+
base: Optional base configuration (merged first)
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Merged StrapiConfig instance
|
|
291
|
+
|
|
292
|
+
Example:
|
|
293
|
+
>>> base_config = ConfigFactory.from_env_file("base.env")
|
|
294
|
+
>>> override_config = ConfigFactory.from_dict({"timeout": 60.0})
|
|
295
|
+
>>> final_config = ConfigFactory.merge(base_config, override_config)
|
|
296
|
+
"""
|
|
297
|
+
if not configs and base is None:
|
|
298
|
+
raise ValueError("At least one config must be provided")
|
|
299
|
+
|
|
300
|
+
all_configs = [base] if base else []
|
|
301
|
+
all_configs.extend(configs)
|
|
302
|
+
|
|
303
|
+
# Start with first config's dict
|
|
304
|
+
merged_dict: dict[str, Any] = all_configs[0].model_dump(exclude_unset=False)
|
|
305
|
+
|
|
306
|
+
# Merge each subsequent config
|
|
307
|
+
for config in all_configs[1:]:
|
|
308
|
+
config_dict = config.model_dump(exclude_unset=True)
|
|
309
|
+
merged_dict.update(config_dict)
|
|
310
|
+
|
|
311
|
+
return ConfigFactory.from_dict(merged_dict)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
# Convenience functions for common patterns
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def load_config(
|
|
318
|
+
env_file: str | Path | None = None,
|
|
319
|
+
*,
|
|
320
|
+
required: bool = False,
|
|
321
|
+
) -> StrapiConfig:
|
|
322
|
+
"""Convenience function to load configuration.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
env_file: Optional path to .env file (searches defaults if None)
|
|
326
|
+
required: If True, raises error if no .env file found
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
Configured StrapiConfig instance
|
|
330
|
+
|
|
331
|
+
Example:
|
|
332
|
+
>>> # Load from default locations
|
|
333
|
+
>>> config = load_config()
|
|
334
|
+
|
|
335
|
+
>>> # Load from specific file
|
|
336
|
+
>>> config = load_config("/path/to/.env")
|
|
337
|
+
|
|
338
|
+
>>> # Require .env file
|
|
339
|
+
>>> config = load_config(required=True)
|
|
340
|
+
"""
|
|
341
|
+
if env_file:
|
|
342
|
+
return ConfigFactory.from_env_file(env_file, required=required)
|
|
343
|
+
return ConfigFactory.from_env(required=required)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def create_config(
|
|
347
|
+
base_url: str,
|
|
348
|
+
api_token: str,
|
|
349
|
+
**kwargs: Any,
|
|
350
|
+
) -> StrapiConfig:
|
|
351
|
+
"""Convenience function to create configuration with explicit values.
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
base_url: Base URL of Strapi instance
|
|
355
|
+
api_token: API authentication token
|
|
356
|
+
**kwargs: Additional configuration options
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Configured StrapiConfig instance
|
|
360
|
+
|
|
361
|
+
Example:
|
|
362
|
+
>>> config = create_config(
|
|
363
|
+
... base_url="http://localhost:1337",
|
|
364
|
+
... api_token="secret-token",
|
|
365
|
+
... timeout=60.0
|
|
366
|
+
... )
|
|
367
|
+
"""
|
|
368
|
+
return ConfigFactory.create(base_url=base_url, api_token=api_token, **kwargs)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Exception classes for strapi-kit."""
|
|
2
|
+
|
|
3
|
+
from .errors import (
|
|
4
|
+
AuthenticationError,
|
|
5
|
+
AuthorizationError,
|
|
6
|
+
ConflictError,
|
|
7
|
+
ConnectionError,
|
|
8
|
+
FormatError,
|
|
9
|
+
ImportExportError,
|
|
10
|
+
MediaError,
|
|
11
|
+
NetworkError,
|
|
12
|
+
NotFoundError,
|
|
13
|
+
RateLimitError,
|
|
14
|
+
RelationError,
|
|
15
|
+
ServerError,
|
|
16
|
+
StrapiError,
|
|
17
|
+
TimeoutError,
|
|
18
|
+
ValidationError,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"StrapiError",
|
|
23
|
+
"AuthenticationError",
|
|
24
|
+
"AuthorizationError",
|
|
25
|
+
"NotFoundError",
|
|
26
|
+
"ValidationError",
|
|
27
|
+
"ConflictError",
|
|
28
|
+
"NetworkError",
|
|
29
|
+
"ConnectionError",
|
|
30
|
+
"TimeoutError",
|
|
31
|
+
"RateLimitError",
|
|
32
|
+
"ServerError",
|
|
33
|
+
"ImportExportError",
|
|
34
|
+
"FormatError",
|
|
35
|
+
"RelationError",
|
|
36
|
+
"MediaError",
|
|
37
|
+
]
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Exception hierarchy for strapi-kit.
|
|
2
|
+
|
|
3
|
+
This module defines all custom exceptions used throughout the package,
|
|
4
|
+
organized in a clear hierarchy for better error handling.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StrapiError(Exception):
|
|
11
|
+
"""Base exception for all strapi-kit errors.
|
|
12
|
+
|
|
13
|
+
All custom exceptions in this package inherit from this class,
|
|
14
|
+
making it easy to catch all package-specific errors.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, message: str, details: dict[str, Any] | None = None) -> None:
|
|
18
|
+
"""Initialize the exception.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
message: Human-readable error message
|
|
22
|
+
details: Optional dictionary with additional error context
|
|
23
|
+
"""
|
|
24
|
+
super().__init__(message)
|
|
25
|
+
self.message = message
|
|
26
|
+
self.details = details or {}
|
|
27
|
+
|
|
28
|
+
def __str__(self) -> str:
|
|
29
|
+
"""Return string representation of the error."""
|
|
30
|
+
if self.details:
|
|
31
|
+
return f"{self.message} (details: {self.details})"
|
|
32
|
+
return self.message
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# HTTP Status Code Related Errors
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class AuthenticationError(StrapiError):
|
|
39
|
+
"""Raised when authentication fails (HTTP 401).
|
|
40
|
+
|
|
41
|
+
This typically means the API token is invalid, expired, or missing.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AuthorizationError(StrapiError):
|
|
48
|
+
"""Raised when authorization fails (HTTP 403).
|
|
49
|
+
|
|
50
|
+
The authentication was successful, but the user doesn't have
|
|
51
|
+
permission to access the requested resource.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class NotFoundError(StrapiError):
|
|
58
|
+
"""Raised when a resource is not found (HTTP 404).
|
|
59
|
+
|
|
60
|
+
This can mean the content type, document ID, or endpoint doesn't exist.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ValidationError(StrapiError):
|
|
67
|
+
"""Raised when request validation fails (HTTP 400).
|
|
68
|
+
|
|
69
|
+
This typically means the request data doesn't match the expected schema
|
|
70
|
+
or contains invalid values.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ConflictError(StrapiError):
|
|
77
|
+
"""Raised when a conflict occurs (HTTP 409).
|
|
78
|
+
|
|
79
|
+
This typically happens when trying to create a resource that already exists
|
|
80
|
+
or when there's a version conflict during updates.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class ServerError(StrapiError):
|
|
87
|
+
"""Raised when the server returns a 5xx error.
|
|
88
|
+
|
|
89
|
+
This indicates an internal server error that is typically temporary
|
|
90
|
+
and may succeed if retried.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
def __init__(
|
|
94
|
+
self, message: str, status_code: int, details: dict[str, Any] | None = None
|
|
95
|
+
) -> None:
|
|
96
|
+
"""Initialize server error with status code.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
message: Human-readable error message
|
|
100
|
+
status_code: HTTP status code (5xx)
|
|
101
|
+
details: Optional dictionary with additional error context
|
|
102
|
+
"""
|
|
103
|
+
super().__init__(message, details)
|
|
104
|
+
self.status_code = status_code
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# Network Related Errors
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class NetworkError(StrapiError):
|
|
111
|
+
"""Base class for network-related errors.
|
|
112
|
+
|
|
113
|
+
This is raised when there's a problem with the network connection
|
|
114
|
+
rather than an HTTP error response.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
pass
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class ConnectionError(NetworkError):
|
|
121
|
+
"""Raised when a connection to the server cannot be established.
|
|
122
|
+
|
|
123
|
+
This typically means the server is unreachable or the URL is incorrect.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class TimeoutError(NetworkError):
|
|
130
|
+
"""Raised when a request times out.
|
|
131
|
+
|
|
132
|
+
The server didn't respond within the configured timeout period.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class RateLimitError(NetworkError):
|
|
139
|
+
"""Raised when rate limit is exceeded (HTTP 429).
|
|
140
|
+
|
|
141
|
+
The client has sent too many requests in a given time period.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def __init__(
|
|
145
|
+
self,
|
|
146
|
+
message: str,
|
|
147
|
+
retry_after: int | None = None,
|
|
148
|
+
details: dict[str, Any] | None = None,
|
|
149
|
+
) -> None:
|
|
150
|
+
"""Initialize rate limit error.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
message: Human-readable error message
|
|
154
|
+
retry_after: Seconds to wait before retrying (from Retry-After header)
|
|
155
|
+
details: Optional dictionary with additional error context
|
|
156
|
+
"""
|
|
157
|
+
super().__init__(message, details)
|
|
158
|
+
self.retry_after = retry_after
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# Import/Export Related Errors
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class ImportExportError(StrapiError):
|
|
165
|
+
"""Base class for import/export related errors.
|
|
166
|
+
|
|
167
|
+
Raised during data export or import operations when something goes wrong.
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class FormatError(ImportExportError):
|
|
174
|
+
"""Raised when data format is invalid or unsupported.
|
|
175
|
+
|
|
176
|
+
This happens when the import data doesn't match the expected format
|
|
177
|
+
or contains malformed JSON/data structures.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
pass
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class RelationError(ImportExportError):
|
|
184
|
+
"""Raised when there's an error resolving or mapping relations.
|
|
185
|
+
|
|
186
|
+
This can happen when:
|
|
187
|
+
- A referenced document doesn't exist
|
|
188
|
+
- Circular relations are detected
|
|
189
|
+
- Relation format is invalid
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class MediaError(ImportExportError):
|
|
196
|
+
"""Raised when there's an error handling media files.
|
|
197
|
+
|
|
198
|
+
This can happen during:
|
|
199
|
+
- Media file download (export)
|
|
200
|
+
- Media file upload (import)
|
|
201
|
+
- Invalid media references
|
|
202
|
+
- File system errors
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
pass
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Export and import functionality for Strapi data.
|
|
2
|
+
|
|
3
|
+
This package provides tools for exporting and importing Strapi content types,
|
|
4
|
+
entities, and media files in a portable format.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from strapi_kit.export.exporter import StrapiExporter
|
|
8
|
+
from strapi_kit.export.importer import StrapiImporter
|
|
9
|
+
|
|
10
|
+
__all__ = ["StrapiExporter", "StrapiImporter"]
|