valentina-python-client 1.7.4__tar.gz → 1.9.0__tar.gz
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.
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/PKG-INFO +3 -1
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/pyproject.toml +7 -4
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/__init__.py +1 -1
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_codegen.py +29 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/character_traits.py +22 -5
- valentina_python_client-1.9.0/src/vclient/_sync/testing/__init__.py +5 -0
- valentina_python_client-1.9.0/src/vclient/_sync/testing/_client.py +70 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/__init__.py +2 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/character_trait.py +8 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/character_traits.py +7 -3
- valentina_python_client-1.9.0/src/vclient/testing/__init__.py +97 -0
- valentina_python_client-1.9.0/src/vclient/testing/_client.py +74 -0
- valentina_python_client-1.9.0/src/vclient/testing/_factories.py +271 -0
- valentina_python_client-1.9.0/src/vclient/testing/_router.py +446 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/LICENSE +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/README.md +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/__init__.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/client.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/registry.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/__init__.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/base.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/campaign_book_chapters.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/campaign_books.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/campaigns.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/character_autogen.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/character_blueprint.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/characters.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/companies.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/developers.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/dicerolls.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/dictionary.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/global_admin.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/options.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/system.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/_sync/services/users.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/client.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/config.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/constants.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/endpoints.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/exceptions.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/books.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/campaigns.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/chapters.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/character_autogen.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/character_blueprint.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/characters.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/companies.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/developers.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/diceroll.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/dictionary.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/global_admin.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/pagination.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/shared.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/system.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/users.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/py.typed +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/registry.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/__init__.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/base.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/campaign_book_chapters.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/campaign_books.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/campaigns.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/character_autogen.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/character_blueprint.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/characters.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/companies.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/developers.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/dicerolls.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/dictionary.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/global_admin.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/options.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/system.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/services/users.py +0 -0
- {valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/validate_constants.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: valentina-python-client
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9.0
|
|
4
4
|
Summary: Async Python client library for the Valentina Noir API
|
|
5
5
|
Author: Nate Landau
|
|
6
6
|
Author-email: Nate Landau <github@natenate.org>
|
|
@@ -19,9 +19,11 @@ Requires-Dist: anyio>=4.12.1
|
|
|
19
19
|
Requires-Dist: httpx>=0.28.1
|
|
20
20
|
Requires-Dist: loguru>=0.7.3
|
|
21
21
|
Requires-Dist: pydantic[email]>=2.12.5
|
|
22
|
+
Requires-Dist: polyfactory>=2.21.0 ; extra == 'testing'
|
|
22
23
|
Requires-Python: >=3.13
|
|
23
24
|
Project-URL: Homepage, https://docs.valentina-noir.com/python-api-client/
|
|
24
25
|
Project-URL: Repository, https://github.com/natelandau/valentina-python-client
|
|
26
|
+
Provides-Extra: testing
|
|
25
27
|
Description-Content-Type: text/markdown
|
|
26
28
|
|
|
27
29
|
# Valentina Python Client
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
name = "valentina-python-client"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.13"
|
|
13
|
-
version = "1.
|
|
13
|
+
version = "1.9.0"
|
|
14
|
+
|
|
15
|
+
[project.optional-dependencies]
|
|
16
|
+
testing = ["polyfactory>=2.21.0"]
|
|
14
17
|
|
|
15
18
|
[project.urls]
|
|
16
19
|
Homepage = "https://docs.valentina-noir.com/python-api-client/"
|
|
@@ -39,11 +42,11 @@
|
|
|
39
42
|
"pytest-xdist>=3.8.0",
|
|
40
43
|
"pytest>=9.0.2",
|
|
41
44
|
"respx>=0.22.0",
|
|
42
|
-
"ruff>=0.15.
|
|
45
|
+
"ruff>=0.15.5",
|
|
43
46
|
"shellcheck-py>=0.11.0.1",
|
|
44
|
-
"ty>=0.0.
|
|
47
|
+
"ty>=0.0.21",
|
|
45
48
|
"typos>=1.44.0",
|
|
46
|
-
"vulture>=2.
|
|
49
|
+
"vulture>=2.15",
|
|
47
50
|
"yamllint>=1.38.0",
|
|
48
51
|
]
|
|
49
52
|
docs = ["zensical>=0.0.24"]
|
|
@@ -30,6 +30,7 @@ RENAME_CLASSES: dict[str, str] = {
|
|
|
30
30
|
"SystemService": "SyncSystemService",
|
|
31
31
|
"UsersService": "SyncUsersService",
|
|
32
32
|
"VClient": "SyncVClient",
|
|
33
|
+
"FakeVClient": "SyncFakeVClient",
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
FACTORY_RENAMES: dict[str, str] = {
|
|
@@ -73,6 +74,7 @@ IMPORT_REWRITES: dict[str, str] = {
|
|
|
73
74
|
"vclient.services.options": "vclient._sync.services.options",
|
|
74
75
|
"vclient.services.character_autogen": "vclient._sync.services.character_autogen",
|
|
75
76
|
"vclient.registry": "vclient._sync.registry",
|
|
77
|
+
"vclient.testing._client": "vclient._sync.testing._client",
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
# Combined lookup for renaming any identifier (class or factory function)
|
|
@@ -334,6 +336,22 @@ def _write_sync_init(path: Path) -> None:
|
|
|
334
336
|
path.write_text("\n".join(lines))
|
|
335
337
|
|
|
336
338
|
|
|
339
|
+
def _write_sync_testing_init(path: Path) -> None:
|
|
340
|
+
"""Write the _sync/testing/__init__.py that re-exports public names.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
path: Path to the ``_sync/testing/__init__.py`` file to write.
|
|
344
|
+
"""
|
|
345
|
+
lines = [
|
|
346
|
+
HEADER_COMMENT,
|
|
347
|
+
"from vclient._sync.testing._client import SyncFakeVClient",
|
|
348
|
+
"",
|
|
349
|
+
'__all__ = ["SyncFakeVClient"]',
|
|
350
|
+
"",
|
|
351
|
+
]
|
|
352
|
+
path.write_text("\n".join(lines))
|
|
353
|
+
|
|
354
|
+
|
|
337
355
|
def generate_sync(src_dir: Path) -> None:
|
|
338
356
|
"""Transform all async source files and write them into the ``_sync/`` package.
|
|
339
357
|
|
|
@@ -362,6 +380,17 @@ def generate_sync(src_dir: Path) -> None:
|
|
|
362
380
|
# Write the _sync/__init__.py
|
|
363
381
|
_write_sync_init(sync_dir / "__init__.py")
|
|
364
382
|
|
|
383
|
+
# Transform testing module
|
|
384
|
+
testing_src = src_dir / "testing"
|
|
385
|
+
testing_dst = sync_dir / "testing"
|
|
386
|
+
if testing_src.exists():
|
|
387
|
+
testing_dst.mkdir(exist_ok=True)
|
|
388
|
+
client_source = testing_src / "_client.py"
|
|
389
|
+
if client_source.exists():
|
|
390
|
+
output_path = testing_dst / "_client.py"
|
|
391
|
+
output_path.write_text(transform_file(client_source))
|
|
392
|
+
_write_sync_testing_init(testing_dst / "__init__.py")
|
|
393
|
+
|
|
365
394
|
|
|
366
395
|
if __name__ == "__main__":
|
|
367
396
|
project_root = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("src/vclient")
|
|
@@ -8,8 +8,8 @@ from vclient._sync.services.base import SyncBaseService
|
|
|
8
8
|
from vclient.constants import DEFAULT_PAGE_LIMIT, TraitModifyCurrency
|
|
9
9
|
from vclient.endpoints import Endpoints
|
|
10
10
|
from vclient.models import (
|
|
11
|
-
CharacterCreateTraitAssign,
|
|
12
11
|
CharacterTrait,
|
|
12
|
+
CharacterTraitAdd,
|
|
13
13
|
CharacterTraitValueOptionsResponse,
|
|
14
14
|
PaginatedResponse,
|
|
15
15
|
TraitCreate,
|
|
@@ -137,22 +137,37 @@ class SyncCharacterTraitsService(SyncBaseService):
|
|
|
137
137
|
)
|
|
138
138
|
return CharacterTrait.model_validate(response.json())
|
|
139
139
|
|
|
140
|
-
def delete(self, character_trait_id: str) -> None:
|
|
140
|
+
def delete(self, character_trait_id: str, currency: TraitModifyCurrency | None = None) -> None:
|
|
141
141
|
"""Delete a character trait.
|
|
142
142
|
|
|
143
143
|
Args:
|
|
144
144
|
character_trait_id: The ID of the trait to delete.
|
|
145
|
+
currency: The currency to use to recoup the cost of the trait.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
None
|
|
149
|
+
|
|
150
|
+
Raises:
|
|
151
|
+
NotFoundError: If the trait does not exist.
|
|
152
|
+
AuthorizationError: If you don't have access to the character.
|
|
153
|
+
RequestValidationError: If the input parameters fail client-side validation.
|
|
154
|
+
ValidationError: If the request data is invalid.
|
|
145
155
|
"""
|
|
156
|
+
params: dict[str, str | int] = {}
|
|
157
|
+
if currency is not None:
|
|
158
|
+
params["currency"] = currency
|
|
146
159
|
self._delete(
|
|
147
|
-
self._format_endpoint(Endpoints.CHARACTER_TRAIT, character_trait_id=character_trait_id)
|
|
160
|
+
self._format_endpoint(Endpoints.CHARACTER_TRAIT, character_trait_id=character_trait_id),
|
|
161
|
+
params=params or None,
|
|
148
162
|
)
|
|
149
163
|
|
|
150
|
-
def assign(self, trait_id: str, value: int) -> CharacterTrait:
|
|
164
|
+
def assign(self, trait_id: str, value: int, currency: TraitModifyCurrency) -> CharacterTrait:
|
|
151
165
|
"""Assign a trait to a character.
|
|
152
166
|
|
|
153
167
|
Args:
|
|
154
168
|
trait_id: The ID of the trait to assign.
|
|
155
169
|
value: The value of the trait to assign.
|
|
170
|
+
currency: The currency to use to pay for the trait.
|
|
156
171
|
|
|
157
172
|
Returns:
|
|
158
173
|
The newly assigned CharacterTrait object.
|
|
@@ -163,7 +178,9 @@ class SyncCharacterTraitsService(SyncBaseService):
|
|
|
163
178
|
RequestValidationError: If the input parameters fail client-side validation.
|
|
164
179
|
ValidationError: If the request data is invalid.
|
|
165
180
|
"""
|
|
166
|
-
body = self._validate_request(
|
|
181
|
+
body = self._validate_request(
|
|
182
|
+
CharacterTraitAdd, trait_id=trait_id, value=value, currency=currency
|
|
183
|
+
)
|
|
167
184
|
response = self._post(
|
|
168
185
|
self._format_endpoint(Endpoints.CHARACTER_TRAIT_ASSIGN),
|
|
169
186
|
json=body.model_dump(exclude_none=True, exclude_unset=True, mode="json"),
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# AUTO-GENERATED — do not edit. Run 'uv run duty generate_sync' to regenerate.
|
|
2
|
+
"""Fake async API client for testing downstream applications.
|
|
3
|
+
|
|
4
|
+
FakeSyncVClient is a drop-in replacement for SyncVClient that uses httpx.MockTransport
|
|
5
|
+
instead of real HTTP. All real service classes work unmodified.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
|
|
14
|
+
from vclient._sync.client import SyncVClient
|
|
15
|
+
from vclient.testing._router import _FakeRouter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SyncFakeVClient(SyncVClient):
|
|
19
|
+
"""A fake SyncVClient for testing that uses mock HTTP transport.
|
|
20
|
+
|
|
21
|
+
Drop-in replacement for SyncVClient that returns auto-generated responses
|
|
22
|
+
for all endpoints. Registers itself as the default client so factory
|
|
23
|
+
functions like sync_campaigns_service() work without configuration.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
```python
|
|
27
|
+
from vclient.testing import FakeSyncVClient
|
|
28
|
+
from vclient import sync_campaigns_service
|
|
29
|
+
|
|
30
|
+
async with FakeSyncVClient() as client:
|
|
31
|
+
campaigns = sync_campaigns_service("user123")
|
|
32
|
+
result = await campaigns.list_all()
|
|
33
|
+
```
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
*,
|
|
39
|
+
default_company_id: str = "fake-company",
|
|
40
|
+
set_as_default: bool = True,
|
|
41
|
+
**kwargs: Any,
|
|
42
|
+
) -> None:
|
|
43
|
+
self._router = _FakeRouter()
|
|
44
|
+
super().__init__(
|
|
45
|
+
base_url="https://fake.valentina-api.test",
|
|
46
|
+
api_key="fake-api-key",
|
|
47
|
+
default_company_id=default_company_id,
|
|
48
|
+
set_as_default=set_as_default,
|
|
49
|
+
**kwargs,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def _create_http_client(self) -> httpx.Client:
|
|
53
|
+
"""Create an HTTP client backed by the fake router."""
|
|
54
|
+
return httpx.Client(
|
|
55
|
+
transport=httpx.MockTransport(self._router.handle),
|
|
56
|
+
base_url="https://fake.valentina-api.test",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def add_route(
|
|
60
|
+
self, method: str, pattern: str, *, json: dict[str, Any], status_code: int = 200
|
|
61
|
+
) -> None:
|
|
62
|
+
"""Add a custom route override.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
method: HTTP method (GET, POST, PATCH, DELETE, PUT).
|
|
66
|
+
pattern: Endpoint pattern from vclient.endpoints.Endpoints.
|
|
67
|
+
json: The JSON response body to return.
|
|
68
|
+
status_code: HTTP status code to return (default 200).
|
|
69
|
+
"""
|
|
70
|
+
self._router.add_route(method, pattern, json=json, status_code=status_code)
|
{valentina_python_client-1.7.4 → valentina_python_client-1.9.0}/src/vclient/models/__init__.py
RENAMED
|
@@ -36,6 +36,7 @@ from .character_blueprint import (
|
|
|
36
36
|
from .character_trait import (
|
|
37
37
|
CharacterCreateTraitAssign,
|
|
38
38
|
CharacterTrait,
|
|
39
|
+
CharacterTraitAdd,
|
|
39
40
|
CharacterTraitValueOptionsResponse,
|
|
40
41
|
TraitCreate,
|
|
41
42
|
_TraitModify as _TraitModify,
|
|
@@ -138,6 +139,7 @@ __all__ = [
|
|
|
138
139
|
"CharacterHunterEdge",
|
|
139
140
|
"CharacterSpecialty",
|
|
140
141
|
"CharacterTrait",
|
|
142
|
+
"CharacterTraitAdd",
|
|
141
143
|
"CharacterTraitValueOptionsResponse",
|
|
142
144
|
"CharacterUpdate",
|
|
143
145
|
"ChargenSessionFinalizeDTO",
|
|
@@ -25,6 +25,14 @@ class CharacterCreateTraitAssign(BaseModel):
|
|
|
25
25
|
value: int
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
class CharacterTraitAdd(BaseModel):
|
|
29
|
+
"""Request model for adding a character trait to an already created character."""
|
|
30
|
+
|
|
31
|
+
trait_id: str
|
|
32
|
+
value: int
|
|
33
|
+
currency: TraitModifyCurrency
|
|
34
|
+
|
|
35
|
+
|
|
28
36
|
class TraitCreate(BaseModel):
|
|
29
37
|
"""Request model for creating a character trait.
|
|
30
38
|
|
|
@@ -6,8 +6,8 @@ from typing import TYPE_CHECKING
|
|
|
6
6
|
from vclient.constants import DEFAULT_PAGE_LIMIT, TraitModifyCurrency
|
|
7
7
|
from vclient.endpoints import Endpoints
|
|
8
8
|
from vclient.models import (
|
|
9
|
-
CharacterCreateTraitAssign,
|
|
10
9
|
CharacterTrait,
|
|
10
|
+
CharacterTraitAdd,
|
|
11
11
|
CharacterTraitValueOptionsResponse,
|
|
12
12
|
PaginatedResponse,
|
|
13
13
|
TraitCreate,
|
|
@@ -173,12 +173,15 @@ class CharacterTraitsService(BaseService):
|
|
|
173
173
|
params=params or None,
|
|
174
174
|
)
|
|
175
175
|
|
|
176
|
-
async def assign(
|
|
176
|
+
async def assign(
|
|
177
|
+
self, trait_id: str, value: int, currency: TraitModifyCurrency
|
|
178
|
+
) -> CharacterTrait:
|
|
177
179
|
"""Assign a trait to a character.
|
|
178
180
|
|
|
179
181
|
Args:
|
|
180
182
|
trait_id: The ID of the trait to assign.
|
|
181
183
|
value: The value of the trait to assign.
|
|
184
|
+
currency: The currency to use to pay for the trait.
|
|
182
185
|
|
|
183
186
|
Returns:
|
|
184
187
|
The newly assigned CharacterTrait object.
|
|
@@ -190,9 +193,10 @@ class CharacterTraitsService(BaseService):
|
|
|
190
193
|
ValidationError: If the request data is invalid.
|
|
191
194
|
"""
|
|
192
195
|
body = self._validate_request(
|
|
193
|
-
|
|
196
|
+
CharacterTraitAdd,
|
|
194
197
|
trait_id=trait_id,
|
|
195
198
|
value=value,
|
|
199
|
+
currency=currency,
|
|
196
200
|
)
|
|
197
201
|
response = await self._post(
|
|
198
202
|
self._format_endpoint(Endpoints.CHARACTER_TRAIT_ASSIGN),
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Testing utilities for downstream applications using vclient.
|
|
2
|
+
|
|
3
|
+
Requires the 'testing' extra: pip install vclient[testing]
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import polyfactory # noqa: F401
|
|
8
|
+
except ImportError as e:
|
|
9
|
+
msg = (
|
|
10
|
+
"vclient.testing requires the 'testing' extra. "
|
|
11
|
+
"Install it with: pip install vclient[testing]"
|
|
12
|
+
)
|
|
13
|
+
raise ImportError(msg) from e
|
|
14
|
+
|
|
15
|
+
from vclient._sync.testing import SyncFakeVClient
|
|
16
|
+
from vclient.testing._client import FakeVClient
|
|
17
|
+
from vclient.testing._factories import (
|
|
18
|
+
AssetFactory,
|
|
19
|
+
CampaignBookFactory,
|
|
20
|
+
CampaignChapterFactory,
|
|
21
|
+
CampaignExperienceFactory,
|
|
22
|
+
CampaignFactory,
|
|
23
|
+
CharacterConceptFactory,
|
|
24
|
+
CharacterFactory,
|
|
25
|
+
CharacterTraitFactory,
|
|
26
|
+
CharacterTraitValueOptionsResponseFactory,
|
|
27
|
+
ChargenSessionResponseFactory,
|
|
28
|
+
CompanyFactory,
|
|
29
|
+
CompanyPermissionsFactory,
|
|
30
|
+
DeveloperFactory,
|
|
31
|
+
DeveloperWithApiKeyFactory,
|
|
32
|
+
DicerollFactory,
|
|
33
|
+
DictionaryTermFactory,
|
|
34
|
+
EdgeAndPerksFactory,
|
|
35
|
+
HunterEdgeFactory,
|
|
36
|
+
HunterEdgePerkFactory,
|
|
37
|
+
InventoryItemFactory,
|
|
38
|
+
MeDeveloperFactory,
|
|
39
|
+
MeDeveloperWithApiKeyFactory,
|
|
40
|
+
NewCompanyResponseFactory,
|
|
41
|
+
NoteFactory,
|
|
42
|
+
PerkFactory,
|
|
43
|
+
QuickrollFactory,
|
|
44
|
+
RollStatisticsFactory,
|
|
45
|
+
SheetSectionFactory,
|
|
46
|
+
SystemHealthFactory,
|
|
47
|
+
TraitCategoryFactory,
|
|
48
|
+
TraitFactory,
|
|
49
|
+
UserFactory,
|
|
50
|
+
VampireClanFactory,
|
|
51
|
+
WerewolfAuspiceFactory,
|
|
52
|
+
WerewolfGiftFactory,
|
|
53
|
+
WerewolfRiteFactory,
|
|
54
|
+
WerewolfTribeFactory,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
__all__ = [
|
|
58
|
+
"AssetFactory",
|
|
59
|
+
"CampaignBookFactory",
|
|
60
|
+
"CampaignChapterFactory",
|
|
61
|
+
"CampaignExperienceFactory",
|
|
62
|
+
"CampaignFactory",
|
|
63
|
+
"CharacterConceptFactory",
|
|
64
|
+
"CharacterFactory",
|
|
65
|
+
"CharacterTraitFactory",
|
|
66
|
+
"CharacterTraitValueOptionsResponseFactory",
|
|
67
|
+
"ChargenSessionResponseFactory",
|
|
68
|
+
"CompanyFactory",
|
|
69
|
+
"CompanyPermissionsFactory",
|
|
70
|
+
"DeveloperFactory",
|
|
71
|
+
"DeveloperWithApiKeyFactory",
|
|
72
|
+
"DicerollFactory",
|
|
73
|
+
"DictionaryTermFactory",
|
|
74
|
+
"EdgeAndPerksFactory",
|
|
75
|
+
"FakeVClient",
|
|
76
|
+
"HunterEdgeFactory",
|
|
77
|
+
"HunterEdgePerkFactory",
|
|
78
|
+
"InventoryItemFactory",
|
|
79
|
+
"MeDeveloperFactory",
|
|
80
|
+
"MeDeveloperWithApiKeyFactory",
|
|
81
|
+
"NewCompanyResponseFactory",
|
|
82
|
+
"NoteFactory",
|
|
83
|
+
"PerkFactory",
|
|
84
|
+
"QuickrollFactory",
|
|
85
|
+
"RollStatisticsFactory",
|
|
86
|
+
"SheetSectionFactory",
|
|
87
|
+
"SyncFakeVClient",
|
|
88
|
+
"SystemHealthFactory",
|
|
89
|
+
"TraitCategoryFactory",
|
|
90
|
+
"TraitFactory",
|
|
91
|
+
"UserFactory",
|
|
92
|
+
"VampireClanFactory",
|
|
93
|
+
"WerewolfAuspiceFactory",
|
|
94
|
+
"WerewolfGiftFactory",
|
|
95
|
+
"WerewolfRiteFactory",
|
|
96
|
+
"WerewolfTribeFactory",
|
|
97
|
+
]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Fake async API client for testing downstream applications.
|
|
2
|
+
|
|
3
|
+
FakeVClient is a drop-in replacement for VClient that uses httpx.MockTransport
|
|
4
|
+
instead of real HTTP. All real service classes work unmodified.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
|
|
13
|
+
from vclient.client import VClient
|
|
14
|
+
from vclient.testing._router import _FakeRouter
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FakeVClient(VClient):
|
|
18
|
+
"""A fake VClient for testing that uses mock HTTP transport.
|
|
19
|
+
|
|
20
|
+
Drop-in replacement for VClient that returns auto-generated responses
|
|
21
|
+
for all endpoints. Registers itself as the default client so factory
|
|
22
|
+
functions like campaigns_service() work without configuration.
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
```python
|
|
26
|
+
from vclient.testing import FakeVClient
|
|
27
|
+
from vclient import campaigns_service
|
|
28
|
+
|
|
29
|
+
async with FakeVClient() as client:
|
|
30
|
+
campaigns = campaigns_service("user123")
|
|
31
|
+
result = await campaigns.list_all()
|
|
32
|
+
```
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
*,
|
|
38
|
+
default_company_id: str = "fake-company",
|
|
39
|
+
set_as_default: bool = True,
|
|
40
|
+
**kwargs: Any,
|
|
41
|
+
) -> None:
|
|
42
|
+
self._router = _FakeRouter()
|
|
43
|
+
super().__init__(
|
|
44
|
+
base_url="https://fake.valentina-api.test",
|
|
45
|
+
api_key="fake-api-key",
|
|
46
|
+
default_company_id=default_company_id,
|
|
47
|
+
set_as_default=set_as_default,
|
|
48
|
+
**kwargs,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def _create_http_client(self) -> httpx.AsyncClient:
|
|
52
|
+
"""Create an HTTP client backed by the fake router."""
|
|
53
|
+
return httpx.AsyncClient(
|
|
54
|
+
transport=httpx.MockTransport(self._router.handle),
|
|
55
|
+
base_url="https://fake.valentina-api.test",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def add_route(
|
|
59
|
+
self,
|
|
60
|
+
method: str,
|
|
61
|
+
pattern: str,
|
|
62
|
+
*,
|
|
63
|
+
json: dict[str, Any],
|
|
64
|
+
status_code: int = 200,
|
|
65
|
+
) -> None:
|
|
66
|
+
"""Add a custom route override.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
method: HTTP method (GET, POST, PATCH, DELETE, PUT).
|
|
70
|
+
pattern: Endpoint pattern from vclient.endpoints.Endpoints.
|
|
71
|
+
json: The JSON response body to return.
|
|
72
|
+
status_code: HTTP status code to return (default 200).
|
|
73
|
+
"""
|
|
74
|
+
self._router.add_route(method, pattern, json=json, status_code=status_code)
|