valentina-python-client 2.0.0__tar.gz → 2.2.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-2.0.0 → valentina_python_client-2.2.0}/PKG-INFO +4 -4
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/pyproject.toml +13 -13
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/__init__.py +1 -1
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/base.py +9 -2
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/dicerolls.py +55 -7
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/global_admin.py +80 -1
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/constants.py +7 -1
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/endpoints.py +2 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/__init__.py +4 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/characters.py +6 -2
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/global_admin.py +33 -1
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/base.py +3 -1
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/dicerolls.py +49 -5
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/global_admin.py +79 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/testing/__init__.py +2 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/testing/_factories.py +7 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/testing/_router.py +12 -2
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/testing/_routes.py +6 -2
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/LICENSE +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/README.md +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_codegen.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/__init__.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/client.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/registry.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/__init__.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/_audit_params.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/campaign_book_chapters.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/campaign_books.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/campaigns.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/character_autogen.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/character_blueprint.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/character_traits.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/characters.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/companies.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/developers.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/dictionary.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/options.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/system.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/user_lookup.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/user_self_registration.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/users.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/testing/__init__.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/testing/_client.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/client.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/config.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/exceptions.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/audit_logs.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/books.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/campaigns.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/chapters.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/character_autogen.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/character_blueprint.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/character_trait.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/companies.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/developers.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/diceroll.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/dictionary.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/full_sheet.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/pagination.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/shared.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/system.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/user_lookup.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/users.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/py.typed +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/registry.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/__init__.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/_audit_params.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/campaign_book_chapters.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/campaign_books.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/campaigns.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/character_autogen.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/character_blueprint.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/character_traits.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/characters.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/companies.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/developers.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/dictionary.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/options.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/system.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/user_lookup.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/user_self_registration.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/users.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/testing/_client.py +0 -0
- {valentina_python_client-2.0.0 → valentina_python_client-2.2.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: 2.
|
|
3
|
+
Version: 2.2.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>
|
|
@@ -15,11 +15,11 @@ License: MIT License
|
|
|
15
15
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
-
Requires-Dist: anyio>=4.13.0
|
|
18
|
+
Requires-Dist: anyio>=4.13.0,<5.0.0
|
|
19
19
|
Requires-Dist: httpx>=0.28.1
|
|
20
20
|
Requires-Dist: loguru>=0.7.3
|
|
21
|
-
Requires-Dist: polyfactory>=3.3.0
|
|
22
|
-
Requires-Dist: pydantic[email]>=2.13.
|
|
21
|
+
Requires-Dist: polyfactory>=3.3.0,<4.0.0
|
|
22
|
+
Requires-Dist: pydantic[email]>=2.13.4,<3.0.0
|
|
23
23
|
Requires-Dist: polyfactory>=3.3.0 ; extra == 'testing'
|
|
24
24
|
Requires-Python: >=3.13
|
|
25
25
|
Project-URL: Homepage, https://docs.valentina-noir.com/python-api-client/
|
|
@@ -5,18 +5,18 @@
|
|
|
5
5
|
"Programming Language :: Python :: 3.14",
|
|
6
6
|
]
|
|
7
7
|
dependencies = [
|
|
8
|
-
"anyio>=4.13.0",
|
|
8
|
+
"anyio>=4.13.0, <5.0.0",
|
|
9
9
|
"httpx>=0.28.1",
|
|
10
10
|
"loguru>=0.7.3",
|
|
11
|
-
"polyfactory>=3.3.0",
|
|
12
|
-
"pydantic[email]>=2.13.
|
|
11
|
+
"polyfactory>=3.3.0, <4.0.0",
|
|
12
|
+
"pydantic[email]>=2.13.4, <3.0.0",
|
|
13
13
|
]
|
|
14
14
|
description = "Async Python client library for the Valentina Noir API"
|
|
15
15
|
license = { file = "LICENSE" }
|
|
16
16
|
name = "valentina-python-client"
|
|
17
17
|
readme = "README.md"
|
|
18
18
|
requires-python = ">=3.13"
|
|
19
|
-
version = "2.
|
|
19
|
+
version = "2.2.0"
|
|
20
20
|
|
|
21
21
|
[project.optional-dependencies]
|
|
22
22
|
testing = ["polyfactory>=3.3.0"]
|
|
@@ -27,35 +27,35 @@
|
|
|
27
27
|
|
|
28
28
|
[build-system]
|
|
29
29
|
build-backend = "uv_build"
|
|
30
|
-
requires = ["uv_build>=0.
|
|
30
|
+
requires = ["uv_build>=0.11.15,<0.12.0"]
|
|
31
31
|
|
|
32
32
|
[tool.uv.build-backend]
|
|
33
33
|
module-name = "vclient"
|
|
34
34
|
|
|
35
35
|
[dependency-groups]
|
|
36
36
|
dev = [
|
|
37
|
-
"commitizen>=4.
|
|
38
|
-
"coverage>=7.
|
|
37
|
+
"commitizen>=4.16.2",
|
|
38
|
+
"coverage>=7.14.0",
|
|
39
39
|
"duty>=1.9.0",
|
|
40
|
-
"prek>=0.
|
|
40
|
+
"prek>=0.4.1",
|
|
41
41
|
"pytest-anyio>=0.0.0",
|
|
42
42
|
"pytest-clarity>=1.0.1",
|
|
43
43
|
"pytest-cov>=7.1.0",
|
|
44
|
-
"pytest-devtools>=1.
|
|
44
|
+
"pytest-devtools>=1.2.0",
|
|
45
45
|
"pytest-mock>=3.15.1",
|
|
46
46
|
"pytest-repeat>=0.9.4",
|
|
47
47
|
"pytest-sugar>=1.1.1",
|
|
48
48
|
"pytest-xdist>=3.8.0",
|
|
49
49
|
"pytest>=9.0.3",
|
|
50
50
|
"respx>=0.23.1",
|
|
51
|
-
"ruff>=0.15.
|
|
51
|
+
"ruff>=0.15.14",
|
|
52
52
|
"shellcheck-py>=0.11.0.1",
|
|
53
|
-
"ty>=0.0.
|
|
54
|
-
"typos>=1.
|
|
53
|
+
"ty>=0.0.39",
|
|
54
|
+
"typos>=1.46.2",
|
|
55
55
|
"vulture>=2.16",
|
|
56
56
|
"yamllint>=1.38.0",
|
|
57
57
|
]
|
|
58
|
-
docs = ["zensical>=0.0.
|
|
58
|
+
docs = ["zensical>=0.0.43"]
|
|
59
59
|
|
|
60
60
|
[tool.commitizen]
|
|
61
61
|
bump_message = "bump(release): v$current_version → v$new_version"
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/base.py
RENAMED
|
@@ -381,17 +381,24 @@ class SyncBaseService:
|
|
|
381
381
|
return None
|
|
382
382
|
return SyncBaseService._parse_rate_limit_header_value(rate_limit_header, "r")
|
|
383
383
|
|
|
384
|
-
def _get(
|
|
384
|
+
def _get(
|
|
385
|
+
self,
|
|
386
|
+
path: str,
|
|
387
|
+
*,
|
|
388
|
+
params: dict[str, Any] | None = None,
|
|
389
|
+
headers: dict[str, str] | None = None,
|
|
390
|
+
) -> httpx.Response:
|
|
385
391
|
"""Make a GET request.
|
|
386
392
|
|
|
387
393
|
Args:
|
|
388
394
|
path: API endpoint path.
|
|
389
395
|
params: Query parameters.
|
|
396
|
+
headers: Additional headers (e.g. an Accept override for binary downloads).
|
|
390
397
|
|
|
391
398
|
Returns:
|
|
392
399
|
The HTTP response.
|
|
393
400
|
"""
|
|
394
|
-
return self._request("GET", path, params=params)
|
|
401
|
+
return self._request("GET", path, params=params, headers=headers)
|
|
395
402
|
|
|
396
403
|
def _merge_on_behalf_of_header(self, headers: dict[str, str] | None) -> dict[str, str] | None:
|
|
397
404
|
"""Merge the On-Behalf-Of header into headers when _on_behalf_of is set.
|
|
@@ -5,7 +5,7 @@ from collections.abc import Iterator
|
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from vclient._sync.services.base import SyncBaseService
|
|
8
|
-
from vclient.constants import DEFAULT_PAGE_LIMIT
|
|
8
|
+
from vclient.constants import DEFAULT_PAGE_LIMIT, CharacterType
|
|
9
9
|
from vclient.endpoints import Endpoints
|
|
10
10
|
from vclient.models import Diceroll, DicerollCreate, PaginatedResponse, _DicerollQuickrollCreate
|
|
11
11
|
|
|
@@ -40,15 +40,32 @@ class SyncDicerollService(SyncBaseService):
|
|
|
40
40
|
userid: str | None = None,
|
|
41
41
|
characterid: str | None = None,
|
|
42
42
|
campaignid: str | None = None,
|
|
43
|
+
character_type: CharacterType | None = None,
|
|
43
44
|
) -> PaginatedResponse[Diceroll]:
|
|
44
|
-
"""Retrieve a paginated page of dice rolls.
|
|
45
|
+
"""Retrieve a paginated page of dice rolls.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
limit: Maximum number of items to return (0-100, default 10).
|
|
49
|
+
offset: Number of items to skip from the beginning (default 0).
|
|
50
|
+
userid: Filter by user ID.
|
|
51
|
+
characterid: Filter by character ID.
|
|
52
|
+
campaignid: Filter by campaign ID.
|
|
53
|
+
character_type: Filter by the associated character's type. Rolls with
|
|
54
|
+
no character are excluded when this filter is set.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
A PaginatedResponse containing Diceroll objects and pagination metadata.
|
|
58
|
+
"""
|
|
45
59
|
return self._get_paginated_as(
|
|
46
60
|
self._format_endpoint(Endpoints.DICEROLLS),
|
|
47
61
|
Diceroll,
|
|
48
62
|
limit=limit,
|
|
49
63
|
offset=offset,
|
|
50
64
|
params=self._build_params(
|
|
51
|
-
userid=userid,
|
|
65
|
+
userid=userid,
|
|
66
|
+
characterid=characterid,
|
|
67
|
+
campaignid=campaignid,
|
|
68
|
+
character_type=character_type,
|
|
52
69
|
),
|
|
53
70
|
)
|
|
54
71
|
|
|
@@ -58,12 +75,27 @@ class SyncDicerollService(SyncBaseService):
|
|
|
58
75
|
userid: str | None = None,
|
|
59
76
|
characterid: str | None = None,
|
|
60
77
|
campaignid: str | None = None,
|
|
78
|
+
character_type: CharacterType | None = None,
|
|
61
79
|
) -> list[Diceroll]:
|
|
62
|
-
"""Retrieve all dice rolls.
|
|
80
|
+
"""Retrieve all dice rolls.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
userid: Filter by user ID.
|
|
84
|
+
characterid: Filter by character ID.
|
|
85
|
+
campaignid: Filter by campaign ID.
|
|
86
|
+
character_type: Filter by the associated character's type. Rolls with
|
|
87
|
+
no character are excluded when this filter is set.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
A list of all Diceroll objects.
|
|
91
|
+
"""
|
|
63
92
|
return [
|
|
64
93
|
diceroll
|
|
65
94
|
for diceroll in self.iter_all(
|
|
66
|
-
userid=userid,
|
|
95
|
+
userid=userid,
|
|
96
|
+
characterid=characterid,
|
|
97
|
+
campaignid=campaignid,
|
|
98
|
+
character_type=character_type,
|
|
67
99
|
)
|
|
68
100
|
]
|
|
69
101
|
|
|
@@ -73,14 +105,30 @@ class SyncDicerollService(SyncBaseService):
|
|
|
73
105
|
userid: str | None = None,
|
|
74
106
|
characterid: str | None = None,
|
|
75
107
|
campaignid: str | None = None,
|
|
108
|
+
character_type: CharacterType | None = None,
|
|
76
109
|
limit: int = 100,
|
|
77
110
|
) -> Iterator[Diceroll]:
|
|
78
|
-
"""Iterate through all dice rolls.
|
|
111
|
+
"""Iterate through all dice rolls.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
userid: Filter by user ID.
|
|
115
|
+
characterid: Filter by character ID.
|
|
116
|
+
campaignid: Filter by campaign ID.
|
|
117
|
+
character_type: Filter by the associated character's type. Rolls with
|
|
118
|
+
no character are excluded when this filter is set.
|
|
119
|
+
limit: Items per page (default 100 for efficiency).
|
|
120
|
+
|
|
121
|
+
Yields:
|
|
122
|
+
Individual Diceroll objects.
|
|
123
|
+
"""
|
|
79
124
|
for item in self._iter_all_pages(
|
|
80
125
|
self._format_endpoint(Endpoints.DICEROLLS),
|
|
81
126
|
limit=limit,
|
|
82
127
|
params=self._build_params(
|
|
83
|
-
userid=userid,
|
|
128
|
+
userid=userid,
|
|
129
|
+
characterid=characterid,
|
|
130
|
+
campaignid=campaignid,
|
|
131
|
+
character_type=character_type,
|
|
84
132
|
),
|
|
85
133
|
):
|
|
86
134
|
yield Diceroll.model_validate(item)
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
# AUTO-GENERATED — do not edit. Run 'uv run duty generate_sync' to regenerate.
|
|
2
2
|
"""Service for interacting with the Global Admin API."""
|
|
3
3
|
|
|
4
|
+
import re
|
|
4
5
|
from collections.abc import Iterator, Sequence
|
|
5
6
|
from datetime import datetime
|
|
6
7
|
|
|
7
8
|
from vclient._sync.services.base import SyncBaseService
|
|
8
|
-
from vclient.constants import
|
|
9
|
+
from vclient.constants import (
|
|
10
|
+
DEFAULT_LOG_TAIL_LIMIT,
|
|
11
|
+
DEFAULT_PAGE_LIMIT,
|
|
12
|
+
MAX_LOG_TAIL_LIMIT,
|
|
13
|
+
MIN_LOG_TAIL_LIMIT,
|
|
14
|
+
AuditEntityType,
|
|
15
|
+
AuditLogInclude,
|
|
16
|
+
AuditOperation,
|
|
17
|
+
LogLevel,
|
|
18
|
+
)
|
|
9
19
|
from vclient.endpoints import Endpoints
|
|
10
20
|
from vclient.models import (
|
|
11
21
|
AuditLog,
|
|
@@ -15,9 +25,34 @@ from vclient.models import (
|
|
|
15
25
|
DeveloperUpdate,
|
|
16
26
|
DeveloperWithApiKey,
|
|
17
27
|
PaginatedResponse,
|
|
28
|
+
ServerLogArchive,
|
|
29
|
+
ServerLogEntry,
|
|
18
30
|
)
|
|
19
31
|
from vclient.services._audit_params import _build_audit_params
|
|
20
32
|
|
|
33
|
+
_CONTENT_DISPOSITION_FILENAME = re.compile('filename=(?:"([^"]+)"|([^;]+))', re.IGNORECASE)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _filename_from_content_disposition(header: str | None, *, fallback: str) -> str:
|
|
37
|
+
"""Extract the attachment filename from a Content-Disposition header.
|
|
38
|
+
|
|
39
|
+
Return ``fallback`` when the header is absent or contains no filename so callers
|
|
40
|
+
always get a usable name for the downloaded archive.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
header: The raw Content-Disposition header value, or None.
|
|
44
|
+
fallback: Filename to return when none can be parsed.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
The parsed filename, or the fallback.
|
|
48
|
+
"""
|
|
49
|
+
if not header:
|
|
50
|
+
return fallback
|
|
51
|
+
match = _CONTENT_DISPOSITION_FILENAME.search(header)
|
|
52
|
+
if not match:
|
|
53
|
+
return fallback
|
|
54
|
+
return (match.group(1) or match.group(2)).strip()
|
|
55
|
+
|
|
21
56
|
|
|
22
57
|
class SyncGlobalAdminService(SyncBaseService):
|
|
23
58
|
"""Service for global admin operations in the Valentina API.
|
|
@@ -404,3 +439,47 @@ class SyncGlobalAdminService(SyncBaseService):
|
|
|
404
439
|
params=params,
|
|
405
440
|
):
|
|
406
441
|
yield model.model_validate(item)
|
|
442
|
+
|
|
443
|
+
def tail_logs(
|
|
444
|
+
self, *, level: LogLevel | None = None, limit: int = DEFAULT_LOG_TAIL_LIMIT
|
|
445
|
+
) -> list[ServerLogEntry]:
|
|
446
|
+
"""Tail the most recent server log entries, newest first.
|
|
447
|
+
|
|
448
|
+
Inspect on-disk server logs without shelling into the host. Requires global
|
|
449
|
+
admin privileges and that file logging is enabled on the server.
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
level: Minimum log level to include. Defaults to the server's configured
|
|
453
|
+
level when omitted.
|
|
454
|
+
limit: Maximum number of entries to return. Clamped to 1-500 (default 100).
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
A list of ServerLogEntry objects, newest first.
|
|
458
|
+
|
|
459
|
+
Raises:
|
|
460
|
+
AuthorizationError: If you don't have global admin privileges.
|
|
461
|
+
ConflictError: If file logging is not enabled on the server.
|
|
462
|
+
"""
|
|
463
|
+
clamped_limit = min(max(limit, MIN_LOG_TAIL_LIMIT), MAX_LOG_TAIL_LIMIT)
|
|
464
|
+
params = self._build_params(level=level, limit=clamped_limit)
|
|
465
|
+
response = self._get(Endpoints.ADMIN_LOGS, params=params)
|
|
466
|
+
return [ServerLogEntry.model_validate(item) for item in response.json()]
|
|
467
|
+
|
|
468
|
+
def download_logs(self) -> ServerLogArchive:
|
|
469
|
+
"""Download a zip archive of the server log files.
|
|
470
|
+
|
|
471
|
+
Stream the active log file plus rotated backups as a single zip. Requires
|
|
472
|
+
global admin privileges and that file logging is enabled on the server.
|
|
473
|
+
|
|
474
|
+
Returns:
|
|
475
|
+
A ServerLogArchive with the server-provided filename and raw zip bytes.
|
|
476
|
+
|
|
477
|
+
Raises:
|
|
478
|
+
AuthorizationError: If you don't have global admin privileges.
|
|
479
|
+
ConflictError: If file logging is not enabled or no log files exist.
|
|
480
|
+
"""
|
|
481
|
+
response = self._get(Endpoints.ADMIN_LOGS_DOWNLOAD, headers={"Accept": "application/zip"})
|
|
482
|
+
filename = _filename_from_content_disposition(
|
|
483
|
+
response.headers.get("Content-Disposition"), fallback="vapi-logs.zip"
|
|
484
|
+
)
|
|
485
|
+
return ServerLogArchive(filename=filename, content=response.content)
|
|
@@ -21,6 +21,11 @@ IDEMPOTENT_HTTP_METHODS: frozenset[str] = frozenset({"GET", "PUT", "DELETE"})
|
|
|
21
21
|
DEFAULT_PAGE_LIMIT = 10
|
|
22
22
|
MAX_PAGE_LIMIT = 100
|
|
23
23
|
|
|
24
|
+
# Server log tail defaults
|
|
25
|
+
DEFAULT_LOG_TAIL_LIMIT = 100
|
|
26
|
+
MIN_LOG_TAIL_LIMIT = 1
|
|
27
|
+
MAX_LOG_TAIL_LIMIT = 500
|
|
28
|
+
|
|
24
29
|
# HTTP Status Code Ranges (5xx Server Errors)
|
|
25
30
|
HTTP_500_INTERNAL_SERVER_ERROR = 500
|
|
26
31
|
HTTP_600_UPPER_BOUND = 600
|
|
@@ -70,13 +75,14 @@ CharacterInventoryType = Literal[
|
|
|
70
75
|
"WEAPON",
|
|
71
76
|
]
|
|
72
77
|
CharacterStatus = Literal["ALIVE", "DEAD"]
|
|
73
|
-
CharacterType = Literal["PLAYER", "NPC", "STORYTELLER"
|
|
78
|
+
CharacterType = Literal["PLAYER", "NPC", "STORYTELLER"]
|
|
74
79
|
DiceSize = Literal[4, 6, 8, 10, 20, 100]
|
|
75
80
|
FreeTraitChangesPermission = Literal["UNRESTRICTED", "WITHIN_24_HOURS", "STORYTELLER"]
|
|
76
81
|
GameVersion = Literal["V4", "V5"]
|
|
77
82
|
GrantXPPermission = Literal["UNRESTRICTED", "PLAYER", "STORYTELLER"]
|
|
78
83
|
HunterCreed = Literal["ENTREPRENEURIAL", "FAITHFUL", "INQUISITIVE", "MARTIAL", "UNDERGROUND"]
|
|
79
84
|
HunterEdgeType = Literal["ASSETS", "APTITUDES", "ENDOWMENTS"]
|
|
85
|
+
LogLevel = Literal["TRACE", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
80
86
|
ManageCampaignPermission = Literal["UNRESTRICTED", "STORYTELLER"]
|
|
81
87
|
PermissionLevel = Literal["USER", "ADMIN", "OWNER", "REVOKE"]
|
|
82
88
|
RecoupXPPermission = Literal["UNRESTRICTED", "DENIED", "WITHIN_SESSION"]
|
|
@@ -27,6 +27,8 @@ class Endpoints:
|
|
|
27
27
|
ADMIN_DEVELOPER = f"{_BASE}/admin/developers/{{developer_id}}"
|
|
28
28
|
ADMIN_DEVELOPER_NEW_KEY = f"{_BASE}/admin/developers/{{developer_id}}/new-key"
|
|
29
29
|
ADMIN_DEVELOPER_AUDIT_LOGS = f"{ADMIN_DEVELOPER}/audit-logs"
|
|
30
|
+
ADMIN_LOGS = f"{_BASE}/admin/logs"
|
|
31
|
+
ADMIN_LOGS_DOWNLOAD = f"{ADMIN_LOGS}/download"
|
|
30
32
|
|
|
31
33
|
# Developer endpoints (self-service)
|
|
32
34
|
DEVELOPER_ME = f"{_BASE}/developers/me"
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/__init__.py
RENAMED
|
@@ -107,6 +107,8 @@ from .global_admin import (
|
|
|
107
107
|
DeveloperCreate,
|
|
108
108
|
DeveloperUpdate,
|
|
109
109
|
DeveloperWithApiKey,
|
|
110
|
+
ServerLogArchive,
|
|
111
|
+
ServerLogEntry,
|
|
110
112
|
)
|
|
111
113
|
from .pagination import PaginatedResponse
|
|
112
114
|
from .shared import (
|
|
@@ -222,6 +224,8 @@ __all__ = [
|
|
|
222
224
|
"QuickrollCreate",
|
|
223
225
|
"QuickrollUpdate",
|
|
224
226
|
"RollStatistics",
|
|
227
|
+
"ServerLogArchive",
|
|
228
|
+
"ServerLogEntry",
|
|
225
229
|
"SheetSection",
|
|
226
230
|
"SystemHealth",
|
|
227
231
|
"Trait",
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/characters.py
RENAMED
|
@@ -190,8 +190,12 @@ class Character(BaseModel):
|
|
|
190
190
|
concept_name: str | None = Field(default=None, description="Name of the character concept.")
|
|
191
191
|
|
|
192
192
|
# Relationships
|
|
193
|
-
user_creator_id: str = Field(
|
|
194
|
-
|
|
193
|
+
user_creator_id: str | None = Field(
|
|
194
|
+
default=None, description="ID of the user who created the character."
|
|
195
|
+
)
|
|
196
|
+
user_player_id: str | None = Field(
|
|
197
|
+
default=None, description="ID of the user who plays the character."
|
|
198
|
+
)
|
|
195
199
|
company_id: str = Field(..., description="ID of the company.")
|
|
196
200
|
campaign_id: str = Field(..., description="ID of the campaign.")
|
|
197
201
|
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/global_admin.py
RENAMED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"""Pydantic models for Global Admin API responses."""
|
|
2
2
|
|
|
3
|
+
from dataclasses import dataclass
|
|
3
4
|
from datetime import datetime
|
|
5
|
+
from typing import Any
|
|
4
6
|
|
|
5
|
-
from pydantic import BaseModel
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
6
8
|
|
|
7
9
|
from vclient.constants import PermissionLevel
|
|
8
10
|
|
|
@@ -70,10 +72,40 @@ class DeveloperUpdate(BaseModel):
|
|
|
70
72
|
is_global_admin: bool | None = None
|
|
71
73
|
|
|
72
74
|
|
|
75
|
+
class ServerLogEntry(BaseModel):
|
|
76
|
+
"""A single parsed server log entry from the admin logs tail endpoint.
|
|
77
|
+
|
|
78
|
+
Every field is nullable because individual log lines may omit values or fail
|
|
79
|
+
to parse as structured JSON (in which case ``raw`` holds the original line).
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
timestamp: str | None = None
|
|
83
|
+
level: str | None = None
|
|
84
|
+
name: str | None = None
|
|
85
|
+
message: str | None = None
|
|
86
|
+
exception: str | None = None
|
|
87
|
+
extra: dict[str, Any] = Field(default_factory=dict)
|
|
88
|
+
raw: str | None = None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True)
|
|
92
|
+
class ServerLogArchive:
|
|
93
|
+
"""A downloaded server-log zip archive.
|
|
94
|
+
|
|
95
|
+
Pairs the server-provided ``Content-Disposition`` filename with the raw zip
|
|
96
|
+
bytes so callers can write the archive straight to disk.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
filename: str
|
|
100
|
+
content: bytes
|
|
101
|
+
|
|
102
|
+
|
|
73
103
|
__all__ = [
|
|
74
104
|
"Developer",
|
|
75
105
|
"DeveloperCompanyPermission",
|
|
76
106
|
"DeveloperCreate",
|
|
77
107
|
"DeveloperUpdate",
|
|
78
108
|
"DeveloperWithApiKey",
|
|
109
|
+
"ServerLogArchive",
|
|
110
|
+
"ServerLogEntry",
|
|
79
111
|
]
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/base.py
RENAMED
|
@@ -430,17 +430,19 @@ class BaseService:
|
|
|
430
430
|
path: str,
|
|
431
431
|
*,
|
|
432
432
|
params: dict[str, Any] | None = None,
|
|
433
|
+
headers: dict[str, str] | None = None,
|
|
433
434
|
) -> httpx.Response:
|
|
434
435
|
"""Make a GET request.
|
|
435
436
|
|
|
436
437
|
Args:
|
|
437
438
|
path: API endpoint path.
|
|
438
439
|
params: Query parameters.
|
|
440
|
+
headers: Additional headers (e.g. an Accept override for binary downloads).
|
|
439
441
|
|
|
440
442
|
Returns:
|
|
441
443
|
The HTTP response.
|
|
442
444
|
"""
|
|
443
|
-
return await self._request("GET", path, params=params)
|
|
445
|
+
return await self._request("GET", path, params=params, headers=headers)
|
|
444
446
|
|
|
445
447
|
def _merge_on_behalf_of_header(
|
|
446
448
|
self,
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/dicerolls.py
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from collections.abc import AsyncIterator
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
|
-
from vclient.constants import DEFAULT_PAGE_LIMIT
|
|
6
|
+
from vclient.constants import DEFAULT_PAGE_LIMIT, CharacterType
|
|
7
7
|
from vclient.endpoints import Endpoints
|
|
8
8
|
from vclient.models import (
|
|
9
9
|
Diceroll,
|
|
@@ -48,8 +48,22 @@ class DicerollService(BaseService):
|
|
|
48
48
|
userid: str | None = None,
|
|
49
49
|
characterid: str | None = None,
|
|
50
50
|
campaignid: str | None = None,
|
|
51
|
+
character_type: CharacterType | None = None,
|
|
51
52
|
) -> PaginatedResponse[Diceroll]:
|
|
52
|
-
"""Retrieve a paginated page of dice rolls.
|
|
53
|
+
"""Retrieve a paginated page of dice rolls.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
limit: Maximum number of items to return (0-100, default 10).
|
|
57
|
+
offset: Number of items to skip from the beginning (default 0).
|
|
58
|
+
userid: Filter by user ID.
|
|
59
|
+
characterid: Filter by character ID.
|
|
60
|
+
campaignid: Filter by campaign ID.
|
|
61
|
+
character_type: Filter by the associated character's type. Rolls with
|
|
62
|
+
no character are excluded when this filter is set.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
A PaginatedResponse containing Diceroll objects and pagination metadata.
|
|
66
|
+
"""
|
|
53
67
|
return await self._get_paginated_as(
|
|
54
68
|
self._format_endpoint(Endpoints.DICEROLLS),
|
|
55
69
|
Diceroll,
|
|
@@ -59,6 +73,7 @@ class DicerollService(BaseService):
|
|
|
59
73
|
userid=userid,
|
|
60
74
|
characterid=characterid,
|
|
61
75
|
campaignid=campaignid,
|
|
76
|
+
character_type=character_type,
|
|
62
77
|
),
|
|
63
78
|
)
|
|
64
79
|
|
|
@@ -68,12 +83,27 @@ class DicerollService(BaseService):
|
|
|
68
83
|
userid: str | None = None,
|
|
69
84
|
characterid: str | None = None,
|
|
70
85
|
campaignid: str | None = None,
|
|
86
|
+
character_type: CharacterType | None = None,
|
|
71
87
|
) -> list[Diceroll]:
|
|
72
|
-
"""Retrieve all dice rolls.
|
|
88
|
+
"""Retrieve all dice rolls.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
userid: Filter by user ID.
|
|
92
|
+
characterid: Filter by character ID.
|
|
93
|
+
campaignid: Filter by campaign ID.
|
|
94
|
+
character_type: Filter by the associated character's type. Rolls with
|
|
95
|
+
no character are excluded when this filter is set.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
A list of all Diceroll objects.
|
|
99
|
+
"""
|
|
73
100
|
return [
|
|
74
101
|
diceroll
|
|
75
102
|
async for diceroll in self.iter_all(
|
|
76
|
-
userid=userid,
|
|
103
|
+
userid=userid,
|
|
104
|
+
characterid=characterid,
|
|
105
|
+
campaignid=campaignid,
|
|
106
|
+
character_type=character_type,
|
|
77
107
|
)
|
|
78
108
|
]
|
|
79
109
|
|
|
@@ -83,9 +113,22 @@ class DicerollService(BaseService):
|
|
|
83
113
|
userid: str | None = None,
|
|
84
114
|
characterid: str | None = None,
|
|
85
115
|
campaignid: str | None = None,
|
|
116
|
+
character_type: CharacterType | None = None,
|
|
86
117
|
limit: int = 100,
|
|
87
118
|
) -> AsyncIterator[Diceroll]:
|
|
88
|
-
"""Iterate through all dice rolls.
|
|
119
|
+
"""Iterate through all dice rolls.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
userid: Filter by user ID.
|
|
123
|
+
characterid: Filter by character ID.
|
|
124
|
+
campaignid: Filter by campaign ID.
|
|
125
|
+
character_type: Filter by the associated character's type. Rolls with
|
|
126
|
+
no character are excluded when this filter is set.
|
|
127
|
+
limit: Items per page (default 100 for efficiency).
|
|
128
|
+
|
|
129
|
+
Yields:
|
|
130
|
+
Individual Diceroll objects.
|
|
131
|
+
"""
|
|
89
132
|
async for item in self._iter_all_pages(
|
|
90
133
|
self._format_endpoint(Endpoints.DICEROLLS),
|
|
91
134
|
limit=limit,
|
|
@@ -93,6 +136,7 @@ class DicerollService(BaseService):
|
|
|
93
136
|
userid=userid,
|
|
94
137
|
characterid=characterid,
|
|
95
138
|
campaignid=campaignid,
|
|
139
|
+
character_type=character_type,
|
|
96
140
|
),
|
|
97
141
|
):
|
|
98
142
|
yield Diceroll.model_validate(item)
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/global_admin.py
RENAMED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
"""Service for interacting with the Global Admin API."""
|
|
2
2
|
|
|
3
|
+
import re
|
|
3
4
|
from collections.abc import AsyncIterator, Sequence
|
|
4
5
|
from datetime import datetime
|
|
5
6
|
|
|
6
7
|
from vclient.constants import (
|
|
8
|
+
DEFAULT_LOG_TAIL_LIMIT,
|
|
7
9
|
DEFAULT_PAGE_LIMIT,
|
|
10
|
+
MAX_LOG_TAIL_LIMIT,
|
|
11
|
+
MIN_LOG_TAIL_LIMIT,
|
|
8
12
|
AuditEntityType,
|
|
9
13
|
AuditLogInclude,
|
|
10
14
|
AuditOperation,
|
|
15
|
+
LogLevel,
|
|
11
16
|
)
|
|
12
17
|
from vclient.endpoints import Endpoints
|
|
13
18
|
from vclient.models import (
|
|
@@ -18,10 +23,35 @@ from vclient.models import (
|
|
|
18
23
|
DeveloperUpdate,
|
|
19
24
|
DeveloperWithApiKey,
|
|
20
25
|
PaginatedResponse,
|
|
26
|
+
ServerLogArchive,
|
|
27
|
+
ServerLogEntry,
|
|
21
28
|
)
|
|
22
29
|
from vclient.services._audit_params import _build_audit_params
|
|
23
30
|
from vclient.services.base import BaseService
|
|
24
31
|
|
|
32
|
+
_CONTENT_DISPOSITION_FILENAME = re.compile(r'filename=(?:"([^"]+)"|([^;]+))', re.IGNORECASE)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _filename_from_content_disposition(header: str | None, *, fallback: str) -> str:
|
|
36
|
+
"""Extract the attachment filename from a Content-Disposition header.
|
|
37
|
+
|
|
38
|
+
Return ``fallback`` when the header is absent or contains no filename so callers
|
|
39
|
+
always get a usable name for the downloaded archive.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
header: The raw Content-Disposition header value, or None.
|
|
43
|
+
fallback: Filename to return when none can be parsed.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
The parsed filename, or the fallback.
|
|
47
|
+
"""
|
|
48
|
+
if not header:
|
|
49
|
+
return fallback
|
|
50
|
+
match = _CONTENT_DISPOSITION_FILENAME.search(header)
|
|
51
|
+
if not match:
|
|
52
|
+
return fallback
|
|
53
|
+
return (match.group(1) or match.group(2)).strip()
|
|
54
|
+
|
|
25
55
|
|
|
26
56
|
class GlobalAdminService(BaseService):
|
|
27
57
|
"""Service for global admin operations in the Valentina API.
|
|
@@ -427,3 +457,52 @@ class GlobalAdminService(BaseService):
|
|
|
427
457
|
params=params,
|
|
428
458
|
):
|
|
429
459
|
yield model.model_validate(item)
|
|
460
|
+
|
|
461
|
+
async def tail_logs(
|
|
462
|
+
self,
|
|
463
|
+
*,
|
|
464
|
+
level: LogLevel | None = None,
|
|
465
|
+
limit: int = DEFAULT_LOG_TAIL_LIMIT,
|
|
466
|
+
) -> list[ServerLogEntry]:
|
|
467
|
+
"""Tail the most recent server log entries, newest first.
|
|
468
|
+
|
|
469
|
+
Inspect on-disk server logs without shelling into the host. Requires global
|
|
470
|
+
admin privileges and that file logging is enabled on the server.
|
|
471
|
+
|
|
472
|
+
Args:
|
|
473
|
+
level: Minimum log level to include. Defaults to the server's configured
|
|
474
|
+
level when omitted.
|
|
475
|
+
limit: Maximum number of entries to return. Clamped to 1-500 (default 100).
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
A list of ServerLogEntry objects, newest first.
|
|
479
|
+
|
|
480
|
+
Raises:
|
|
481
|
+
AuthorizationError: If you don't have global admin privileges.
|
|
482
|
+
ConflictError: If file logging is not enabled on the server.
|
|
483
|
+
"""
|
|
484
|
+
clamped_limit = min(max(limit, MIN_LOG_TAIL_LIMIT), MAX_LOG_TAIL_LIMIT)
|
|
485
|
+
params = self._build_params(level=level, limit=clamped_limit)
|
|
486
|
+
response = await self._get(Endpoints.ADMIN_LOGS, params=params)
|
|
487
|
+
return [ServerLogEntry.model_validate(item) for item in response.json()]
|
|
488
|
+
|
|
489
|
+
async def download_logs(self) -> ServerLogArchive:
|
|
490
|
+
"""Download a zip archive of the server log files.
|
|
491
|
+
|
|
492
|
+
Stream the active log file plus rotated backups as a single zip. Requires
|
|
493
|
+
global admin privileges and that file logging is enabled on the server.
|
|
494
|
+
|
|
495
|
+
Returns:
|
|
496
|
+
A ServerLogArchive with the server-provided filename and raw zip bytes.
|
|
497
|
+
|
|
498
|
+
Raises:
|
|
499
|
+
AuthorizationError: If you don't have global admin privileges.
|
|
500
|
+
ConflictError: If file logging is not enabled or no log files exist.
|
|
501
|
+
"""
|
|
502
|
+
response = await self._get(
|
|
503
|
+
Endpoints.ADMIN_LOGS_DOWNLOAD, headers={"Accept": "application/zip"}
|
|
504
|
+
)
|
|
505
|
+
filename = _filename_from_content_disposition(
|
|
506
|
+
response.headers.get("Content-Disposition"), fallback="vapi-logs.zip"
|
|
507
|
+
)
|
|
508
|
+
return ServerLogArchive(filename=filename, content=response.content)
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/testing/__init__.py
RENAMED
|
@@ -52,6 +52,7 @@ from vclient.testing._factories import (
|
|
|
52
52
|
NoteFactory,
|
|
53
53
|
QuickrollFactory,
|
|
54
54
|
RollStatisticsFactory,
|
|
55
|
+
ServerLogEntryFactory,
|
|
55
56
|
SheetSectionFactory,
|
|
56
57
|
SystemHealthFactory,
|
|
57
58
|
TraitCategoryFactory,
|
|
@@ -107,6 +108,7 @@ __all__ = [
|
|
|
107
108
|
"RollStatisticsFactory",
|
|
108
109
|
"RouteSpec",
|
|
109
110
|
"Routes",
|
|
111
|
+
"ServerLogEntryFactory",
|
|
110
112
|
"SheetSectionFactory",
|
|
111
113
|
"SyncFakeVClient",
|
|
112
114
|
"SystemHealthFactory",
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/testing/_factories.py
RENAMED
|
@@ -38,6 +38,7 @@ from vclient.models import (
|
|
|
38
38
|
Note,
|
|
39
39
|
Quickroll,
|
|
40
40
|
RollStatistics,
|
|
41
|
+
ServerLogEntry,
|
|
41
42
|
SheetSection,
|
|
42
43
|
SystemHealth,
|
|
43
44
|
Trait,
|
|
@@ -241,6 +242,11 @@ class RollStatisticsFactory(ModelFactory[RollStatistics]):
|
|
|
241
242
|
__use_defaults__ = True
|
|
242
243
|
|
|
243
244
|
|
|
245
|
+
class ServerLogEntryFactory(ModelFactory[ServerLogEntry]):
|
|
246
|
+
__model__ = ServerLogEntry
|
|
247
|
+
__use_defaults__ = True
|
|
248
|
+
|
|
249
|
+
|
|
244
250
|
class SheetSectionFactory(ModelFactory[SheetSection]):
|
|
245
251
|
__model__ = SheetSection
|
|
246
252
|
__use_defaults__ = True
|
|
@@ -337,6 +343,7 @@ __all__ = [
|
|
|
337
343
|
"NoteFactory",
|
|
338
344
|
"QuickrollFactory",
|
|
339
345
|
"RollStatisticsFactory",
|
|
346
|
+
"ServerLogEntryFactory",
|
|
340
347
|
"SheetSectionFactory",
|
|
341
348
|
"SystemHealthFactory",
|
|
342
349
|
"TraitCategoryFactory",
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/testing/_router.py
RENAMED
|
@@ -43,6 +43,7 @@ from vclient.models import (
|
|
|
43
43
|
Note,
|
|
44
44
|
Quickroll,
|
|
45
45
|
RollStatistics,
|
|
46
|
+
ServerLogEntry,
|
|
46
47
|
SheetSection,
|
|
47
48
|
SystemHealth,
|
|
48
49
|
Trait,
|
|
@@ -89,6 +90,7 @@ from vclient.testing._factories import (
|
|
|
89
90
|
NoteFactory,
|
|
90
91
|
QuickrollFactory,
|
|
91
92
|
RollStatisticsFactory,
|
|
93
|
+
ServerLogEntryFactory,
|
|
92
94
|
SheetSectionFactory,
|
|
93
95
|
SystemHealthFactory,
|
|
94
96
|
TraitCategoryFactory,
|
|
@@ -101,7 +103,7 @@ from vclient.testing._factories import (
|
|
|
101
103
|
WerewolfAuspiceFactory,
|
|
102
104
|
WerewolfTribeFactory,
|
|
103
105
|
)
|
|
104
|
-
from vclient.testing._routes import LIST, NO_CONTENT, PAGINATED, RAW_JSON, Routes, RouteSpec
|
|
106
|
+
from vclient.testing._routes import BYTES, LIST, NO_CONTENT, PAGINATED, RAW_JSON, Routes, RouteSpec
|
|
105
107
|
|
|
106
108
|
_FACTORY_MAP: dict[type, type[ModelFactory]] = {
|
|
107
109
|
AuditLog: AuditLogFactory,
|
|
@@ -135,6 +137,7 @@ _FACTORY_MAP: dict[type, type[ModelFactory]] = {
|
|
|
135
137
|
Note: NoteFactory,
|
|
136
138
|
Quickroll: QuickrollFactory,
|
|
137
139
|
RollStatistics: RollStatisticsFactory,
|
|
140
|
+
ServerLogEntry: ServerLogEntryFactory,
|
|
138
141
|
SheetSection: SheetSectionFactory,
|
|
139
142
|
SystemHealth: SystemHealthFactory,
|
|
140
143
|
Trait: TraitFactory,
|
|
@@ -208,7 +211,7 @@ class _Route:
|
|
|
208
211
|
|
|
209
212
|
return True
|
|
210
213
|
|
|
211
|
-
def respond(self) -> httpx.Response:
|
|
214
|
+
def respond(self) -> httpx.Response: # noqa: PLR0911
|
|
212
215
|
"""Generate an httpx.Response for this route."""
|
|
213
216
|
if self.override_json is not None:
|
|
214
217
|
return httpx.Response(
|
|
@@ -222,6 +225,13 @@ class _Route:
|
|
|
222
225
|
if self.style == RAW_JSON:
|
|
223
226
|
return httpx.Response(status_code=200, json={})
|
|
224
227
|
|
|
228
|
+
if self.style == BYTES:
|
|
229
|
+
return httpx.Response(
|
|
230
|
+
status_code=200,
|
|
231
|
+
content=b"PK\x03\x04fake-log-archive",
|
|
232
|
+
headers={"Content-Disposition": 'attachment; filename="vapi-logs-fake.zip"'},
|
|
233
|
+
)
|
|
234
|
+
|
|
225
235
|
if self.model_class is None:
|
|
226
236
|
msg = f"model_class required for style {self.style}"
|
|
227
237
|
raise RuntimeError(msg)
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/testing/_routes.py
RENAMED
|
@@ -34,6 +34,7 @@ from vclient.models import (
|
|
|
34
34
|
Note,
|
|
35
35
|
Quickroll,
|
|
36
36
|
RollStatistics,
|
|
37
|
+
ServerLogEntry,
|
|
37
38
|
SheetSection,
|
|
38
39
|
SystemHealth,
|
|
39
40
|
Trait,
|
|
@@ -55,6 +56,7 @@ SINGLE = "single"
|
|
|
55
56
|
NO_CONTENT = "no_content"
|
|
56
57
|
RAW_JSON = "raw_json"
|
|
57
58
|
LIST = "list"
|
|
59
|
+
BYTES = "bytes"
|
|
58
60
|
|
|
59
61
|
|
|
60
62
|
class RouteSpec(NamedTuple):
|
|
@@ -63,9 +65,9 @@ class RouteSpec(NamedTuple):
|
|
|
63
65
|
Attributes:
|
|
64
66
|
method: HTTP method (GET, POST, PATCH, PUT, DELETE).
|
|
65
67
|
pattern: Endpoint URL pattern from the Endpoints class.
|
|
66
|
-
style: Response style
|
|
68
|
+
style: Response style: one of PAGINATED, SINGLE, LIST, NO_CONTENT, RAW_JSON, or BYTES.
|
|
67
69
|
model_class: The Pydantic model class used for auto-generating responses,
|
|
68
|
-
or None for NO_CONTENT and
|
|
70
|
+
or None for NO_CONTENT, RAW_JSON, and BYTES routes.
|
|
69
71
|
"""
|
|
70
72
|
|
|
71
73
|
method: str
|
|
@@ -107,6 +109,8 @@ class Routes:
|
|
|
107
109
|
ADMIN_DEVELOPER_AUDIT_LOGS_LIST = RouteSpec(
|
|
108
110
|
"GET", Endpoints.ADMIN_DEVELOPER_AUDIT_LOGS, PAGINATED, AuditLog
|
|
109
111
|
)
|
|
112
|
+
ADMIN_LOGS_TAIL = RouteSpec("GET", Endpoints.ADMIN_LOGS, LIST, ServerLogEntry)
|
|
113
|
+
ADMIN_LOGS_DOWNLOAD = RouteSpec("GET", Endpoints.ADMIN_LOGS_DOWNLOAD, BYTES, None)
|
|
110
114
|
|
|
111
115
|
# Developer self-service
|
|
112
116
|
DEVELOPERS_ME_GET = RouteSpec("GET", Endpoints.DEVELOPER_ME, SINGLE, MeDeveloper)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/system.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/services/users.py
RENAMED
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/_sync/testing/_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/audit_logs.py
RENAMED
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/campaigns.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/chapters.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/companies.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/developers.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/diceroll.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/dictionary.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/full_sheet.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/pagination.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/shared.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/system.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/models/user_lookup.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/campaigns.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/characters.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/companies.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/developers.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/dictionary.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/options.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/system.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/user_lookup.py
RENAMED
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/services/users.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/testing/_client.py
RENAMED
|
File without changes
|
{valentina_python_client-2.0.0 → valentina_python_client-2.2.0}/src/vclient/validate_constants.py
RENAMED
|
File without changes
|