valentina-python-client 2.2.0__tar.gz → 2.3.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.2.0 → valentina_python_client-2.3.0}/PKG-INFO +1 -1
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/pyproject.toml +1 -1
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/__init__.py +1 -1
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/registry.py +1 -1
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/global_admin.py +182 -21
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/constants.py +1 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/endpoints.py +2 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/__init__.py +6 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/companies.py +4 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/users.py +35 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/registry.py +1 -1
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/global_admin.py +198 -19
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/testing/__init__.py +2 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/testing/_factories.py +8 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/testing/_router.py +3 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/testing/_routes.py +8 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/validate_constants.py +1 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/LICENSE +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/README.md +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_codegen.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/__init__.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/client.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/__init__.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/_audit_params.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/base.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/campaign_book_chapters.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/campaign_books.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/campaigns.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/character_autogen.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/character_blueprint.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/character_traits.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/characters.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/companies.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/developers.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/dicerolls.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/dictionary.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/options.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/system.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/user_lookup.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/user_self_registration.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/users.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/testing/__init__.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/testing/_client.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/client.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/config.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/exceptions.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/audit_logs.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/books.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/campaigns.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/chapters.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/character_autogen.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/character_blueprint.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/character_trait.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/characters.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/developers.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/diceroll.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/dictionary.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/full_sheet.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/global_admin.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/pagination.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/shared.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/system.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/user_lookup.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/py.typed +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/__init__.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/_audit_params.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/base.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/campaign_book_chapters.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/campaign_books.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/campaigns.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/character_autogen.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/character_blueprint.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/character_traits.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/characters.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/companies.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/developers.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/dicerolls.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/dictionary.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/options.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/system.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/user_lookup.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/user_self_registration.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/users.py +0 -0
- {valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/testing/_client.py +0 -0
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/registry.py
RENAMED
|
@@ -160,7 +160,7 @@ def sync_global_admin_service() -> "SyncGlobalAdminService":
|
|
|
160
160
|
Example:
|
|
161
161
|
```python
|
|
162
162
|
admins = sync_global_admin_service()
|
|
163
|
-
developers = await admins.
|
|
163
|
+
developers = await admins.list_all_developers()
|
|
164
164
|
```
|
|
165
165
|
"""
|
|
166
166
|
from vclient._sync.services.global_admin import SyncGlobalAdminService
|
|
@@ -15,9 +15,13 @@ from vclient.constants import (
|
|
|
15
15
|
AuditLogInclude,
|
|
16
16
|
AuditOperation,
|
|
17
17
|
LogLevel,
|
|
18
|
+
UserRole,
|
|
18
19
|
)
|
|
19
20
|
from vclient.endpoints import Endpoints
|
|
20
21
|
from vclient.models import (
|
|
22
|
+
AdminUser,
|
|
23
|
+
AdminUserCreate,
|
|
24
|
+
AdminUserUpdate,
|
|
21
25
|
AuditLog,
|
|
22
26
|
AuditLogDetail,
|
|
23
27
|
Developer,
|
|
@@ -57,16 +61,16 @@ def _filename_from_content_disposition(header: str | None, *, fallback: str) ->
|
|
|
57
61
|
class SyncGlobalAdminService(SyncBaseService):
|
|
58
62
|
"""Service for global admin operations in the Valentina API.
|
|
59
63
|
|
|
60
|
-
Provides
|
|
61
|
-
|
|
64
|
+
Provides cross-company management of developer accounts and their API keys,
|
|
65
|
+
plus server log access. Requires global admin privileges.
|
|
62
66
|
|
|
63
67
|
Example:
|
|
64
68
|
>>> async with SyncVClient() as client:
|
|
65
|
-
... developers = await client.global_admin.
|
|
66
|
-
... developer = await client.global_admin.
|
|
69
|
+
... developers = await client.global_admin.list_all_developers()
|
|
70
|
+
... developer = await client.global_admin.get_developer("developer_id")
|
|
67
71
|
"""
|
|
68
72
|
|
|
69
|
-
def
|
|
73
|
+
def get_developer_page(
|
|
70
74
|
self,
|
|
71
75
|
*,
|
|
72
76
|
limit: int = DEFAULT_PAGE_LIMIT,
|
|
@@ -90,11 +94,11 @@ class SyncGlobalAdminService(SyncBaseService):
|
|
|
90
94
|
Endpoints.ADMIN_DEVELOPERS, Developer, limit=limit, offset=offset, params=params
|
|
91
95
|
)
|
|
92
96
|
|
|
93
|
-
def
|
|
97
|
+
def list_all_developers(self, *, is_global_admin: bool | None = None) -> list[Developer]:
|
|
94
98
|
"""Retrieve all developer accounts.
|
|
95
99
|
|
|
96
|
-
Automatically paginates through all results. Use `
|
|
97
|
-
or `
|
|
100
|
+
Automatically paginates through all results. Use `get_developer_page()` for paginated
|
|
101
|
+
access or `iter_all_developers()` for memory-efficient streaming of large datasets.
|
|
98
102
|
|
|
99
103
|
Args:
|
|
100
104
|
is_global_admin: Optional filter by global admin status.
|
|
@@ -102,9 +106,11 @@ class SyncGlobalAdminService(SyncBaseService):
|
|
|
102
106
|
Returns:
|
|
103
107
|
A list of all Developer objects.
|
|
104
108
|
"""
|
|
105
|
-
return [
|
|
109
|
+
return [
|
|
110
|
+
developer for developer in self.iter_all_developers(is_global_admin=is_global_admin)
|
|
111
|
+
]
|
|
106
112
|
|
|
107
|
-
def
|
|
113
|
+
def iter_all_developers(
|
|
108
114
|
self, *, limit: int = 100, is_global_admin: bool | None = None
|
|
109
115
|
) -> Iterator[Developer]:
|
|
110
116
|
"""Iterate through all developer accounts.
|
|
@@ -120,18 +126,14 @@ class SyncGlobalAdminService(SyncBaseService):
|
|
|
120
126
|
Individual Developer objects.
|
|
121
127
|
|
|
122
128
|
Example:
|
|
123
|
-
>>> async for developer in client.global_admin.
|
|
129
|
+
>>> async for developer in client.global_admin.iter_all_developers():
|
|
124
130
|
... print(developer.username)
|
|
125
131
|
"""
|
|
126
|
-
params =
|
|
127
|
-
|
|
128
|
-
params["is_global_admin"] = is_global_admin
|
|
129
|
-
for item in self._iter_all_pages(
|
|
130
|
-
Endpoints.ADMIN_DEVELOPERS, limit=limit, params=params or None
|
|
131
|
-
):
|
|
132
|
+
params = self._build_params(is_global_admin=is_global_admin)
|
|
133
|
+
for item in self._iter_all_pages(Endpoints.ADMIN_DEVELOPERS, limit=limit, params=params):
|
|
132
134
|
yield Developer.model_validate(item)
|
|
133
135
|
|
|
134
|
-
def
|
|
136
|
+
def get_developer(self, developer_id: str) -> Developer:
|
|
135
137
|
"""Retrieve detailed information about a specific developer.
|
|
136
138
|
|
|
137
139
|
Args:
|
|
@@ -147,7 +149,7 @@ class SyncGlobalAdminService(SyncBaseService):
|
|
|
147
149
|
response = self._get(Endpoints.ADMIN_DEVELOPER.format(developer_id=developer_id))
|
|
148
150
|
return Developer.model_validate(response.json())
|
|
149
151
|
|
|
150
|
-
def
|
|
152
|
+
def create_developer(self, request: DeveloperCreate | None = None, **kwargs) -> Developer:
|
|
151
153
|
"""Create a new developer account.
|
|
152
154
|
|
|
153
155
|
This creates the account but does not create an API key or grant access to any
|
|
@@ -174,7 +176,7 @@ class SyncGlobalAdminService(SyncBaseService):
|
|
|
174
176
|
)
|
|
175
177
|
return Developer.model_validate(response.json())
|
|
176
178
|
|
|
177
|
-
def
|
|
179
|
+
def update_developer(
|
|
178
180
|
self, developer_id: str, request: DeveloperUpdate | None = None, **kwargs
|
|
179
181
|
) -> Developer:
|
|
180
182
|
"""Modify a developer account's properties.
|
|
@@ -204,7 +206,7 @@ class SyncGlobalAdminService(SyncBaseService):
|
|
|
204
206
|
)
|
|
205
207
|
return Developer.model_validate(response.json())
|
|
206
208
|
|
|
207
|
-
def
|
|
209
|
+
def delete_developer(self, developer_id: str) -> None:
|
|
208
210
|
"""Remove a developer account from the system.
|
|
209
211
|
|
|
210
212
|
The developer's API key will be invalidated immediately.
|
|
@@ -218,6 +220,165 @@ class SyncGlobalAdminService(SyncBaseService):
|
|
|
218
220
|
"""
|
|
219
221
|
self._delete(Endpoints.ADMIN_DEVELOPER.format(developer_id=developer_id))
|
|
220
222
|
|
|
223
|
+
def get_user_page(
|
|
224
|
+
self,
|
|
225
|
+
*,
|
|
226
|
+
company_id: str | None = None,
|
|
227
|
+
role: UserRole | None = None,
|
|
228
|
+
email: str | None = None,
|
|
229
|
+
is_archived: bool | None = None,
|
|
230
|
+
limit: int = DEFAULT_PAGE_LIMIT,
|
|
231
|
+
offset: int = 0,
|
|
232
|
+
) -> PaginatedResponse[AdminUser]:
|
|
233
|
+
"""Retrieve a paginated page of users across all companies.
|
|
234
|
+
|
|
235
|
+
Authenticates with the global-admin API key only; no On-Behalf-Of header
|
|
236
|
+
is sent. Archived (soft-deleted) users are included when
|
|
237
|
+
``is_archived=True``.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
company_id: Filter to a single company.
|
|
241
|
+
role: Filter by user role.
|
|
242
|
+
email: Filter by exact email match.
|
|
243
|
+
is_archived: Filter by archived state.
|
|
244
|
+
limit: Maximum number of items to return (0-100, default 10).
|
|
245
|
+
offset: Number of items to skip from the beginning (default 0).
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
A PaginatedResponse containing AdminUser objects and pagination metadata.
|
|
249
|
+
"""
|
|
250
|
+
params = self._build_params(
|
|
251
|
+
company_id=company_id, role=role, email=email, is_archived=is_archived
|
|
252
|
+
)
|
|
253
|
+
return self._get_paginated_as(
|
|
254
|
+
Endpoints.ADMIN_USERS, AdminUser, limit=limit, offset=offset, params=params
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
def list_all_users(
|
|
258
|
+
self,
|
|
259
|
+
*,
|
|
260
|
+
company_id: str | None = None,
|
|
261
|
+
role: UserRole | None = None,
|
|
262
|
+
email: str | None = None,
|
|
263
|
+
is_archived: bool | None = None,
|
|
264
|
+
) -> list[AdminUser]:
|
|
265
|
+
"""Retrieve all users across all companies.
|
|
266
|
+
|
|
267
|
+
Automatically paginates through all results. Use ``get_user_page()`` for
|
|
268
|
+
paginated access or ``iter_all_users()`` for memory-efficient streaming.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
company_id: Filter to a single company.
|
|
272
|
+
role: Filter by user role.
|
|
273
|
+
email: Filter by exact email match.
|
|
274
|
+
is_archived: Filter by archived state.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
A list of all matching AdminUser objects.
|
|
278
|
+
"""
|
|
279
|
+
return [
|
|
280
|
+
user
|
|
281
|
+
for user in self.iter_all_users(
|
|
282
|
+
company_id=company_id, role=role, email=email, is_archived=is_archived
|
|
283
|
+
)
|
|
284
|
+
]
|
|
285
|
+
|
|
286
|
+
def iter_all_users(
|
|
287
|
+
self,
|
|
288
|
+
*,
|
|
289
|
+
limit: int = 100,
|
|
290
|
+
company_id: str | None = None,
|
|
291
|
+
role: UserRole | None = None,
|
|
292
|
+
email: str | None = None,
|
|
293
|
+
is_archived: bool | None = None,
|
|
294
|
+
) -> Iterator[AdminUser]:
|
|
295
|
+
"""Iterate through all users across all companies.
|
|
296
|
+
|
|
297
|
+
Yields individual users, automatically fetching subsequent pages until all
|
|
298
|
+
matching users have been retrieved.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
limit: Items per page (default 100 for efficiency).
|
|
302
|
+
company_id: Filter to a single company.
|
|
303
|
+
role: Filter by user role.
|
|
304
|
+
email: Filter by exact email match.
|
|
305
|
+
is_archived: Filter by archived state.
|
|
306
|
+
|
|
307
|
+
Yields:
|
|
308
|
+
Individual AdminUser objects.
|
|
309
|
+
"""
|
|
310
|
+
params = self._build_params(
|
|
311
|
+
company_id=company_id, role=role, email=email, is_archived=is_archived
|
|
312
|
+
)
|
|
313
|
+
for item in self._iter_all_pages(Endpoints.ADMIN_USERS, limit=limit, params=params):
|
|
314
|
+
yield AdminUser.model_validate(item)
|
|
315
|
+
|
|
316
|
+
def get_user(self, user_id: str) -> AdminUser:
|
|
317
|
+
"""Retrieve a single user by ID, including archived users.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
user_id: The ID of the user to retrieve.
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
The AdminUser object, with ``is_archived`` reflecting soft-delete state.
|
|
324
|
+
"""
|
|
325
|
+
response = self._get(Endpoints.ADMIN_USER.format(user_id=user_id))
|
|
326
|
+
return AdminUser.model_validate(response.json())
|
|
327
|
+
|
|
328
|
+
def create_user(self, request: AdminUserCreate | None = None, **kwargs) -> AdminUser:
|
|
329
|
+
"""Create a user in a target company.
|
|
330
|
+
|
|
331
|
+
The role assignment matrix enforced on company-scoped endpoints does not
|
|
332
|
+
apply here, but the server still rejects ``UNAPPROVED``/``DEACTIVATED`` on
|
|
333
|
+
create.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
request: An AdminUserCreate model, OR pass fields as keyword arguments.
|
|
337
|
+
**kwargs: Fields for AdminUserCreate if request is not provided.
|
|
338
|
+
Requires: company_id, username, email, role.
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
The newly created AdminUser object.
|
|
342
|
+
"""
|
|
343
|
+
body = request if request is not None else self._validate_request(AdminUserCreate, **kwargs)
|
|
344
|
+
response = self._post(
|
|
345
|
+
Endpoints.ADMIN_USERS,
|
|
346
|
+
json=body.model_dump(exclude_none=True, exclude_unset=True, mode="json"),
|
|
347
|
+
)
|
|
348
|
+
return AdminUser.model_validate(response.json())
|
|
349
|
+
|
|
350
|
+
def update_user(
|
|
351
|
+
self, user_id: str, request: AdminUserUpdate | None = None, **kwargs
|
|
352
|
+
) -> AdminUser:
|
|
353
|
+
"""Update any user by ID, bypassing the company-scoped role hierarchy.
|
|
354
|
+
|
|
355
|
+
Set ``is_archived=False`` to restore a soft-deleted user.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
user_id: The ID of the user to update.
|
|
359
|
+
request: An AdminUserUpdate model, OR pass fields as keyword arguments.
|
|
360
|
+
**kwargs: Fields for AdminUserUpdate if request is not provided.
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
The updated AdminUser object.
|
|
364
|
+
"""
|
|
365
|
+
body = request if request is not None else self._validate_request(AdminUserUpdate, **kwargs)
|
|
366
|
+
response = self._patch(
|
|
367
|
+
Endpoints.ADMIN_USER.format(user_id=user_id),
|
|
368
|
+
json=body.model_dump(exclude_none=True, exclude_unset=True, mode="json"),
|
|
369
|
+
)
|
|
370
|
+
return AdminUser.model_validate(response.json())
|
|
371
|
+
|
|
372
|
+
def delete_user(self, user_id: str) -> None:
|
|
373
|
+
"""Soft-delete a user by ID.
|
|
374
|
+
|
|
375
|
+
The deletion is reversible via ``update_user(user_id, is_archived=False)``.
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
user_id: The ID of the user to soft-delete.
|
|
379
|
+
"""
|
|
380
|
+
self._delete(Endpoints.ADMIN_USER.format(user_id=user_id))
|
|
381
|
+
|
|
221
382
|
def create_api_key(self, developer_id: str) -> DeveloperWithApiKey:
|
|
222
383
|
"""Generate a new API key for a developer.
|
|
223
384
|
|
|
@@ -84,6 +84,7 @@ HunterCreed = Literal["ENTREPRENEURIAL", "FAITHFUL", "INQUISITIVE", "MARTIAL", "
|
|
|
84
84
|
HunterEdgeType = Literal["ASSETS", "APTITUDES", "ENDOWMENTS"]
|
|
85
85
|
LogLevel = Literal["TRACE", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
86
86
|
ManageCampaignPermission = Literal["UNRESTRICTED", "STORYTELLER"]
|
|
87
|
+
ManageNPCPermission = Literal["UNRESTRICTED", "STORYTELLER"]
|
|
87
88
|
PermissionLevel = Literal["USER", "ADMIN", "OWNER", "REVOKE"]
|
|
88
89
|
RecoupXPPermission = Literal["UNRESTRICTED", "DENIED", "WITHIN_SESSION"]
|
|
89
90
|
RollResultType = Literal["SUCCESS", "FAILURE", "BOTCH", "CRITICAL", "OTHER"]
|
|
@@ -29,6 +29,8 @@ class Endpoints:
|
|
|
29
29
|
ADMIN_DEVELOPER_AUDIT_LOGS = f"{ADMIN_DEVELOPER}/audit-logs"
|
|
30
30
|
ADMIN_LOGS = f"{_BASE}/admin/logs"
|
|
31
31
|
ADMIN_LOGS_DOWNLOAD = f"{ADMIN_LOGS}/download"
|
|
32
|
+
ADMIN_USERS = f"{_BASE}/admin/users"
|
|
33
|
+
ADMIN_USER = f"{_BASE}/admin/users/{{user_id}}"
|
|
32
34
|
|
|
33
35
|
# Developer endpoints (self-service)
|
|
34
36
|
DEVELOPER_ME = f"{_BASE}/developers/me"
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/__init__.py
RENAMED
|
@@ -127,6 +127,9 @@ from .user_lookup import (
|
|
|
127
127
|
UserLookupResult,
|
|
128
128
|
)
|
|
129
129
|
from .users import (
|
|
130
|
+
AdminUser,
|
|
131
|
+
AdminUserCreate,
|
|
132
|
+
AdminUserUpdate,
|
|
130
133
|
CampaignExperience,
|
|
131
134
|
DiscordProfile,
|
|
132
135
|
DiscordProfileUpdate,
|
|
@@ -146,6 +149,9 @@ from .users import (
|
|
|
146
149
|
)
|
|
147
150
|
|
|
148
151
|
__all__ = [
|
|
152
|
+
"AdminUser",
|
|
153
|
+
"AdminUserCreate",
|
|
154
|
+
"AdminUserUpdate",
|
|
149
155
|
"Asset",
|
|
150
156
|
"AuditLog",
|
|
151
157
|
"AuditLogDetail",
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/companies.py
RENAMED
|
@@ -9,6 +9,7 @@ from vclient.constants import (
|
|
|
9
9
|
FreeTraitChangesPermission,
|
|
10
10
|
GrantXPPermission,
|
|
11
11
|
ManageCampaignPermission,
|
|
12
|
+
ManageNPCPermission,
|
|
12
13
|
PermissionLevel,
|
|
13
14
|
RecoupXPPermission,
|
|
14
15
|
)
|
|
@@ -27,6 +28,7 @@ class CompanySettings(BaseModel):
|
|
|
27
28
|
character_autogen_num_choices: int
|
|
28
29
|
character_autogen_starting_points: int
|
|
29
30
|
permission_manage_campaign: ManageCampaignPermission
|
|
31
|
+
permission_manage_npc: ManageNPCPermission
|
|
30
32
|
permission_grant_xp: GrantXPPermission
|
|
31
33
|
permission_free_trait_changes: FreeTraitChangesPermission
|
|
32
34
|
permission_recoup_xp: RecoupXPPermission
|
|
@@ -42,6 +44,7 @@ class CompanySettingsCreate(BaseModel):
|
|
|
42
44
|
character_autogen_num_choices: int | None = None
|
|
43
45
|
character_autogen_starting_points: int | None = None
|
|
44
46
|
permission_manage_campaign: ManageCampaignPermission | None = None
|
|
47
|
+
permission_manage_npc: ManageNPCPermission | None = None
|
|
45
48
|
permission_grant_xp: GrantXPPermission | None = None
|
|
46
49
|
permission_free_trait_changes: FreeTraitChangesPermission | None = None
|
|
47
50
|
permission_recoup_xp: RecoupXPPermission | None = None
|
|
@@ -57,6 +60,7 @@ class CompanySettingsUpdate(BaseModel):
|
|
|
57
60
|
character_autogen_num_choices: int | None = None
|
|
58
61
|
character_autogen_starting_points: int | None = None
|
|
59
62
|
permission_manage_campaign: ManageCampaignPermission | None = None
|
|
63
|
+
permission_manage_npc: ManageNPCPermission | None = None
|
|
60
64
|
permission_grant_xp: GrantXPPermission | None = None
|
|
61
65
|
permission_free_trait_changes: FreeTraitChangesPermission | None = None
|
|
62
66
|
permission_recoup_xp: RecoupXPPermission | None = None
|
|
@@ -122,6 +122,17 @@ class UserDetail(User):
|
|
|
122
122
|
)
|
|
123
123
|
|
|
124
124
|
|
|
125
|
+
class AdminUser(User):
|
|
126
|
+
"""Response model for a user returned by the global-admin user endpoints.
|
|
127
|
+
|
|
128
|
+
Extends the tenant-scoped ``User`` with ``is_archived``, which is always
|
|
129
|
+
present on the admin endpoints so callers can identify soft-deleted users
|
|
130
|
+
directly from the response body.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
is_archived: bool
|
|
134
|
+
|
|
135
|
+
|
|
125
136
|
# -----------------------------------------------------------------------------
|
|
126
137
|
# User Request Models
|
|
127
138
|
# -----------------------------------------------------------------------------
|
|
@@ -194,6 +205,27 @@ class UserUpdate(BaseModel):
|
|
|
194
205
|
github_profile: GitHubProfile | None = None
|
|
195
206
|
|
|
196
207
|
|
|
208
|
+
class AdminUserCreate(UserCreate):
|
|
209
|
+
"""Request body for creating a user as a global admin.
|
|
210
|
+
|
|
211
|
+
Extends the tenant-scoped ``UserCreate`` with an explicit ``company_id`` for
|
|
212
|
+
the target company. The server rejects ``UNAPPROVED``/``DEACTIVATED`` roles
|
|
213
|
+
on create, so no client-side role restriction is applied here.
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
company_id: str
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class AdminUserUpdate(UserUpdate):
|
|
220
|
+
"""Request body for updating any user as a global admin.
|
|
221
|
+
|
|
222
|
+
Extends the tenant-scoped ``UserUpdate`` with ``is_archived``. Set it to
|
|
223
|
+
``False`` to restore a soft-deleted user.
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
is_archived: bool | None = None
|
|
227
|
+
|
|
228
|
+
|
|
197
229
|
class UserApproveDTO(BaseModel):
|
|
198
230
|
"""Approve an unapproved user and assign a role."""
|
|
199
231
|
|
|
@@ -262,6 +294,9 @@ class _ExperienceAddRemove(BaseModel):
|
|
|
262
294
|
|
|
263
295
|
|
|
264
296
|
__all__ = [
|
|
297
|
+
"AdminUser",
|
|
298
|
+
"AdminUserCreate",
|
|
299
|
+
"AdminUserUpdate",
|
|
265
300
|
"CampaignExperience",
|
|
266
301
|
"DiscordProfile",
|
|
267
302
|
"GitHubProfile",
|
|
@@ -160,7 +160,7 @@ def global_admin_service() -> "GlobalAdminService":
|
|
|
160
160
|
Example:
|
|
161
161
|
```python
|
|
162
162
|
admins = global_admin_service()
|
|
163
|
-
developers = await admins.
|
|
163
|
+
developers = await admins.list_all_developers()
|
|
164
164
|
```
|
|
165
165
|
"""
|
|
166
166
|
from vclient.services.global_admin import GlobalAdminService
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/global_admin.py
RENAMED
|
@@ -13,9 +13,13 @@ from vclient.constants import (
|
|
|
13
13
|
AuditLogInclude,
|
|
14
14
|
AuditOperation,
|
|
15
15
|
LogLevel,
|
|
16
|
+
UserRole,
|
|
16
17
|
)
|
|
17
18
|
from vclient.endpoints import Endpoints
|
|
18
19
|
from vclient.models import (
|
|
20
|
+
AdminUser,
|
|
21
|
+
AdminUserCreate,
|
|
22
|
+
AdminUserUpdate,
|
|
19
23
|
AuditLog,
|
|
20
24
|
AuditLogDetail,
|
|
21
25
|
Developer,
|
|
@@ -56,16 +60,16 @@ def _filename_from_content_disposition(header: str | None, *, fallback: str) ->
|
|
|
56
60
|
class GlobalAdminService(BaseService):
|
|
57
61
|
"""Service for global admin operations in the Valentina API.
|
|
58
62
|
|
|
59
|
-
Provides
|
|
60
|
-
|
|
63
|
+
Provides cross-company management of developer accounts and their API keys,
|
|
64
|
+
plus server log access. Requires global admin privileges.
|
|
61
65
|
|
|
62
66
|
Example:
|
|
63
67
|
>>> async with VClient() as client:
|
|
64
|
-
... developers = await client.global_admin.
|
|
65
|
-
... developer = await client.global_admin.
|
|
68
|
+
... developers = await client.global_admin.list_all_developers()
|
|
69
|
+
... developer = await client.global_admin.get_developer("developer_id")
|
|
66
70
|
"""
|
|
67
71
|
|
|
68
|
-
async def
|
|
72
|
+
async def get_developer_page(
|
|
69
73
|
self,
|
|
70
74
|
*,
|
|
71
75
|
limit: int = DEFAULT_PAGE_LIMIT,
|
|
@@ -93,11 +97,11 @@ class GlobalAdminService(BaseService):
|
|
|
93
97
|
params=params,
|
|
94
98
|
)
|
|
95
99
|
|
|
96
|
-
async def
|
|
100
|
+
async def list_all_developers(self, *, is_global_admin: bool | None = None) -> list[Developer]:
|
|
97
101
|
"""Retrieve all developer accounts.
|
|
98
102
|
|
|
99
|
-
Automatically paginates through all results. Use `
|
|
100
|
-
or `
|
|
103
|
+
Automatically paginates through all results. Use `get_developer_page()` for paginated
|
|
104
|
+
access or `iter_all_developers()` for memory-efficient streaming of large datasets.
|
|
101
105
|
|
|
102
106
|
Args:
|
|
103
107
|
is_global_admin: Optional filter by global admin status.
|
|
@@ -105,9 +109,12 @@ class GlobalAdminService(BaseService):
|
|
|
105
109
|
Returns:
|
|
106
110
|
A list of all Developer objects.
|
|
107
111
|
"""
|
|
108
|
-
return [
|
|
112
|
+
return [
|
|
113
|
+
developer
|
|
114
|
+
async for developer in self.iter_all_developers(is_global_admin=is_global_admin)
|
|
115
|
+
]
|
|
109
116
|
|
|
110
|
-
async def
|
|
117
|
+
async def iter_all_developers(
|
|
111
118
|
self,
|
|
112
119
|
*,
|
|
113
120
|
limit: int = 100,
|
|
@@ -126,21 +133,19 @@ class GlobalAdminService(BaseService):
|
|
|
126
133
|
Individual Developer objects.
|
|
127
134
|
|
|
128
135
|
Example:
|
|
129
|
-
>>> async for developer in client.global_admin.
|
|
136
|
+
>>> async for developer in client.global_admin.iter_all_developers():
|
|
130
137
|
... print(developer.username)
|
|
131
138
|
"""
|
|
132
|
-
params =
|
|
133
|
-
if is_global_admin is not None:
|
|
134
|
-
params["is_global_admin"] = is_global_admin
|
|
139
|
+
params = self._build_params(is_global_admin=is_global_admin)
|
|
135
140
|
|
|
136
141
|
async for item in self._iter_all_pages(
|
|
137
142
|
Endpoints.ADMIN_DEVELOPERS,
|
|
138
143
|
limit=limit,
|
|
139
|
-
params=params
|
|
144
|
+
params=params,
|
|
140
145
|
):
|
|
141
146
|
yield Developer.model_validate(item)
|
|
142
147
|
|
|
143
|
-
async def
|
|
148
|
+
async def get_developer(self, developer_id: str) -> Developer:
|
|
144
149
|
"""Retrieve detailed information about a specific developer.
|
|
145
150
|
|
|
146
151
|
Args:
|
|
@@ -156,7 +161,7 @@ class GlobalAdminService(BaseService):
|
|
|
156
161
|
response = await self._get(Endpoints.ADMIN_DEVELOPER.format(developer_id=developer_id))
|
|
157
162
|
return Developer.model_validate(response.json())
|
|
158
163
|
|
|
159
|
-
async def
|
|
164
|
+
async def create_developer(
|
|
160
165
|
self,
|
|
161
166
|
request: DeveloperCreate | None = None,
|
|
162
167
|
**kwargs,
|
|
@@ -187,7 +192,7 @@ class GlobalAdminService(BaseService):
|
|
|
187
192
|
)
|
|
188
193
|
return Developer.model_validate(response.json())
|
|
189
194
|
|
|
190
|
-
async def
|
|
195
|
+
async def update_developer(
|
|
191
196
|
self,
|
|
192
197
|
developer_id: str,
|
|
193
198
|
request: DeveloperUpdate | None = None,
|
|
@@ -220,7 +225,7 @@ class GlobalAdminService(BaseService):
|
|
|
220
225
|
)
|
|
221
226
|
return Developer.model_validate(response.json())
|
|
222
227
|
|
|
223
|
-
async def
|
|
228
|
+
async def delete_developer(self, developer_id: str) -> None:
|
|
224
229
|
"""Remove a developer account from the system.
|
|
225
230
|
|
|
226
231
|
The developer's API key will be invalidated immediately.
|
|
@@ -234,6 +239,180 @@ class GlobalAdminService(BaseService):
|
|
|
234
239
|
"""
|
|
235
240
|
await self._delete(Endpoints.ADMIN_DEVELOPER.format(developer_id=developer_id))
|
|
236
241
|
|
|
242
|
+
async def get_user_page(
|
|
243
|
+
self,
|
|
244
|
+
*,
|
|
245
|
+
company_id: str | None = None,
|
|
246
|
+
role: UserRole | None = None,
|
|
247
|
+
email: str | None = None,
|
|
248
|
+
is_archived: bool | None = None,
|
|
249
|
+
limit: int = DEFAULT_PAGE_LIMIT,
|
|
250
|
+
offset: int = 0,
|
|
251
|
+
) -> PaginatedResponse[AdminUser]:
|
|
252
|
+
"""Retrieve a paginated page of users across all companies.
|
|
253
|
+
|
|
254
|
+
Authenticates with the global-admin API key only; no On-Behalf-Of header
|
|
255
|
+
is sent. Archived (soft-deleted) users are included when
|
|
256
|
+
``is_archived=True``.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
company_id: Filter to a single company.
|
|
260
|
+
role: Filter by user role.
|
|
261
|
+
email: Filter by exact email match.
|
|
262
|
+
is_archived: Filter by archived state.
|
|
263
|
+
limit: Maximum number of items to return (0-100, default 10).
|
|
264
|
+
offset: Number of items to skip from the beginning (default 0).
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
A PaginatedResponse containing AdminUser objects and pagination metadata.
|
|
268
|
+
"""
|
|
269
|
+
params = self._build_params(
|
|
270
|
+
company_id=company_id, role=role, email=email, is_archived=is_archived
|
|
271
|
+
)
|
|
272
|
+
return await self._get_paginated_as(
|
|
273
|
+
Endpoints.ADMIN_USERS,
|
|
274
|
+
AdminUser,
|
|
275
|
+
limit=limit,
|
|
276
|
+
offset=offset,
|
|
277
|
+
params=params,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
async def list_all_users(
|
|
281
|
+
self,
|
|
282
|
+
*,
|
|
283
|
+
company_id: str | None = None,
|
|
284
|
+
role: UserRole | None = None,
|
|
285
|
+
email: str | None = None,
|
|
286
|
+
is_archived: bool | None = None,
|
|
287
|
+
) -> list[AdminUser]:
|
|
288
|
+
"""Retrieve all users across all companies.
|
|
289
|
+
|
|
290
|
+
Automatically paginates through all results. Use ``get_user_page()`` for
|
|
291
|
+
paginated access or ``iter_all_users()`` for memory-efficient streaming.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
company_id: Filter to a single company.
|
|
295
|
+
role: Filter by user role.
|
|
296
|
+
email: Filter by exact email match.
|
|
297
|
+
is_archived: Filter by archived state.
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
A list of all matching AdminUser objects.
|
|
301
|
+
"""
|
|
302
|
+
return [
|
|
303
|
+
user
|
|
304
|
+
async for user in self.iter_all_users(
|
|
305
|
+
company_id=company_id, role=role, email=email, is_archived=is_archived
|
|
306
|
+
)
|
|
307
|
+
]
|
|
308
|
+
|
|
309
|
+
async def iter_all_users(
|
|
310
|
+
self,
|
|
311
|
+
*,
|
|
312
|
+
limit: int = 100,
|
|
313
|
+
company_id: str | None = None,
|
|
314
|
+
role: UserRole | None = None,
|
|
315
|
+
email: str | None = None,
|
|
316
|
+
is_archived: bool | None = None,
|
|
317
|
+
) -> AsyncIterator[AdminUser]:
|
|
318
|
+
"""Iterate through all users across all companies.
|
|
319
|
+
|
|
320
|
+
Yields individual users, automatically fetching subsequent pages until all
|
|
321
|
+
matching users have been retrieved.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
limit: Items per page (default 100 for efficiency).
|
|
325
|
+
company_id: Filter to a single company.
|
|
326
|
+
role: Filter by user role.
|
|
327
|
+
email: Filter by exact email match.
|
|
328
|
+
is_archived: Filter by archived state.
|
|
329
|
+
|
|
330
|
+
Yields:
|
|
331
|
+
Individual AdminUser objects.
|
|
332
|
+
"""
|
|
333
|
+
params = self._build_params(
|
|
334
|
+
company_id=company_id, role=role, email=email, is_archived=is_archived
|
|
335
|
+
)
|
|
336
|
+
async for item in self._iter_all_pages(
|
|
337
|
+
Endpoints.ADMIN_USERS,
|
|
338
|
+
limit=limit,
|
|
339
|
+
params=params,
|
|
340
|
+
):
|
|
341
|
+
yield AdminUser.model_validate(item)
|
|
342
|
+
|
|
343
|
+
async def get_user(self, user_id: str) -> AdminUser:
|
|
344
|
+
"""Retrieve a single user by ID, including archived users.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
user_id: The ID of the user to retrieve.
|
|
348
|
+
|
|
349
|
+
Returns:
|
|
350
|
+
The AdminUser object, with ``is_archived`` reflecting soft-delete state.
|
|
351
|
+
"""
|
|
352
|
+
response = await self._get(Endpoints.ADMIN_USER.format(user_id=user_id))
|
|
353
|
+
return AdminUser.model_validate(response.json())
|
|
354
|
+
|
|
355
|
+
async def create_user(
|
|
356
|
+
self,
|
|
357
|
+
request: AdminUserCreate | None = None,
|
|
358
|
+
**kwargs,
|
|
359
|
+
) -> AdminUser:
|
|
360
|
+
"""Create a user in a target company.
|
|
361
|
+
|
|
362
|
+
The role assignment matrix enforced on company-scoped endpoints does not
|
|
363
|
+
apply here, but the server still rejects ``UNAPPROVED``/``DEACTIVATED`` on
|
|
364
|
+
create.
|
|
365
|
+
|
|
366
|
+
Args:
|
|
367
|
+
request: An AdminUserCreate model, OR pass fields as keyword arguments.
|
|
368
|
+
**kwargs: Fields for AdminUserCreate if request is not provided.
|
|
369
|
+
Requires: company_id, username, email, role.
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
The newly created AdminUser object.
|
|
373
|
+
"""
|
|
374
|
+
body = request if request is not None else self._validate_request(AdminUserCreate, **kwargs)
|
|
375
|
+
response = await self._post(
|
|
376
|
+
Endpoints.ADMIN_USERS,
|
|
377
|
+
json=body.model_dump(exclude_none=True, exclude_unset=True, mode="json"),
|
|
378
|
+
)
|
|
379
|
+
return AdminUser.model_validate(response.json())
|
|
380
|
+
|
|
381
|
+
async def update_user(
|
|
382
|
+
self,
|
|
383
|
+
user_id: str,
|
|
384
|
+
request: AdminUserUpdate | None = None,
|
|
385
|
+
**kwargs,
|
|
386
|
+
) -> AdminUser:
|
|
387
|
+
"""Update any user by ID, bypassing the company-scoped role hierarchy.
|
|
388
|
+
|
|
389
|
+
Set ``is_archived=False`` to restore a soft-deleted user.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
user_id: The ID of the user to update.
|
|
393
|
+
request: An AdminUserUpdate model, OR pass fields as keyword arguments.
|
|
394
|
+
**kwargs: Fields for AdminUserUpdate if request is not provided.
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
The updated AdminUser object.
|
|
398
|
+
"""
|
|
399
|
+
body = request if request is not None else self._validate_request(AdminUserUpdate, **kwargs)
|
|
400
|
+
response = await self._patch(
|
|
401
|
+
Endpoints.ADMIN_USER.format(user_id=user_id),
|
|
402
|
+
json=body.model_dump(exclude_none=True, exclude_unset=True, mode="json"),
|
|
403
|
+
)
|
|
404
|
+
return AdminUser.model_validate(response.json())
|
|
405
|
+
|
|
406
|
+
async def delete_user(self, user_id: str) -> None:
|
|
407
|
+
"""Soft-delete a user by ID.
|
|
408
|
+
|
|
409
|
+
The deletion is reversible via ``update_user(user_id, is_archived=False)``.
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
user_id: The ID of the user to soft-delete.
|
|
413
|
+
"""
|
|
414
|
+
await self._delete(Endpoints.ADMIN_USER.format(user_id=user_id))
|
|
415
|
+
|
|
237
416
|
async def create_api_key(self, developer_id: str) -> DeveloperWithApiKey:
|
|
238
417
|
"""Generate a new API key for a developer.
|
|
239
418
|
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/testing/__init__.py
RENAMED
|
@@ -15,6 +15,7 @@ except ImportError as e:
|
|
|
15
15
|
from vclient._sync.testing import SyncFakeVClient
|
|
16
16
|
from vclient.testing._client import FakeVClient
|
|
17
17
|
from vclient.testing._factories import (
|
|
18
|
+
AdminUserFactory,
|
|
18
19
|
AssetFactory,
|
|
19
20
|
AuditLogDetailFactory,
|
|
20
21
|
AuditLogFactory,
|
|
@@ -68,6 +69,7 @@ from vclient.testing._factories import (
|
|
|
68
69
|
from vclient.testing._routes import Routes, RouteSpec
|
|
69
70
|
|
|
70
71
|
__all__ = [
|
|
72
|
+
"AdminUserFactory",
|
|
71
73
|
"AssetFactory",
|
|
72
74
|
"AuditLogDetailFactory",
|
|
73
75
|
"AuditLogFactory",
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/testing/_factories.py
RENAMED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from polyfactory.factories.pydantic_factory import ModelFactory
|
|
4
4
|
|
|
5
5
|
from vclient.models import (
|
|
6
|
+
AdminUser,
|
|
6
7
|
Asset,
|
|
7
8
|
AuditLog,
|
|
8
9
|
AuditLogDetail,
|
|
@@ -272,6 +273,12 @@ class TraitFactory(ModelFactory[Trait]):
|
|
|
272
273
|
__use_defaults__ = True
|
|
273
274
|
|
|
274
275
|
|
|
276
|
+
class AdminUserFactory(ModelFactory[AdminUser]):
|
|
277
|
+
__model__ = AdminUser
|
|
278
|
+
__use_defaults__ = True
|
|
279
|
+
role = "PLAYER"
|
|
280
|
+
|
|
281
|
+
|
|
275
282
|
class UserFactory(ModelFactory[User]):
|
|
276
283
|
__model__ = User
|
|
277
284
|
__use_defaults__ = True
|
|
@@ -306,6 +313,7 @@ class WerewolfTribeFactory(ModelFactory[WerewolfTribe]):
|
|
|
306
313
|
|
|
307
314
|
|
|
308
315
|
__all__ = [
|
|
316
|
+
"AdminUserFactory",
|
|
309
317
|
"AssetFactory",
|
|
310
318
|
"AuditLogDetailFactory",
|
|
311
319
|
"AuditLogFactory",
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/testing/_router.py
RENAMED
|
@@ -14,6 +14,7 @@ if TYPE_CHECKING:
|
|
|
14
14
|
|
|
15
15
|
from vclient.constants import REQUEST_ID_HEADER
|
|
16
16
|
from vclient.models import (
|
|
17
|
+
AdminUser,
|
|
17
18
|
Asset,
|
|
18
19
|
AuditLog,
|
|
19
20
|
AuditLogDetail,
|
|
@@ -59,6 +60,7 @@ from vclient.models import (
|
|
|
59
60
|
from vclient.models.character_autogen import ChargenSessionResponse
|
|
60
61
|
from vclient.models.users import CampaignExperience
|
|
61
62
|
from vclient.testing._factories import (
|
|
63
|
+
AdminUserFactory,
|
|
62
64
|
AssetFactory,
|
|
63
65
|
AuditLogDetailFactory,
|
|
64
66
|
AuditLogFactory,
|
|
@@ -106,6 +108,7 @@ from vclient.testing._factories import (
|
|
|
106
108
|
from vclient.testing._routes import BYTES, LIST, NO_CONTENT, PAGINATED, RAW_JSON, Routes, RouteSpec
|
|
107
109
|
|
|
108
110
|
_FACTORY_MAP: dict[type, type[ModelFactory]] = {
|
|
111
|
+
AdminUser: AdminUserFactory,
|
|
109
112
|
AuditLog: AuditLogFactory,
|
|
110
113
|
AuditLogDetail: AuditLogDetailFactory,
|
|
111
114
|
Asset: AssetFactory,
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/testing/_routes.py
RENAMED
|
@@ -6,6 +6,7 @@ from typing import NamedTuple
|
|
|
6
6
|
|
|
7
7
|
from vclient.endpoints import Endpoints
|
|
8
8
|
from vclient.models import (
|
|
9
|
+
AdminUser,
|
|
9
10
|
Asset,
|
|
10
11
|
AuditLog,
|
|
11
12
|
BulkAssignTraitResponse,
|
|
@@ -112,6 +113,13 @@ class Routes:
|
|
|
112
113
|
ADMIN_LOGS_TAIL = RouteSpec("GET", Endpoints.ADMIN_LOGS, LIST, ServerLogEntry)
|
|
113
114
|
ADMIN_LOGS_DOWNLOAD = RouteSpec("GET", Endpoints.ADMIN_LOGS_DOWNLOAD, BYTES, None)
|
|
114
115
|
|
|
116
|
+
# Admin users (cross-company, global-admin only)
|
|
117
|
+
ADMIN_USERS_LIST = RouteSpec("GET", Endpoints.ADMIN_USERS, PAGINATED, AdminUser)
|
|
118
|
+
ADMIN_USERS_GET = RouteSpec("GET", Endpoints.ADMIN_USER, SINGLE, AdminUser)
|
|
119
|
+
ADMIN_USERS_CREATE = RouteSpec("POST", Endpoints.ADMIN_USERS, SINGLE, AdminUser)
|
|
120
|
+
ADMIN_USERS_UPDATE = RouteSpec("PATCH", Endpoints.ADMIN_USER, SINGLE, AdminUser)
|
|
121
|
+
ADMIN_USERS_DELETE = RouteSpec("DELETE", Endpoints.ADMIN_USER, NO_CONTENT, None)
|
|
122
|
+
|
|
115
123
|
# Developer self-service
|
|
116
124
|
DEVELOPERS_ME_GET = RouteSpec("GET", Endpoints.DEVELOPER_ME, SINGLE, MeDeveloper)
|
|
117
125
|
DEVELOPERS_ME_UPDATE = RouteSpec("PATCH", Endpoints.DEVELOPER_ME, SINGLE, MeDeveloper)
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/validate_constants.py
RENAMED
|
@@ -77,6 +77,7 @@ CONSTANT_MAP: dict[str, ConstantMapping] = {
|
|
|
77
77
|
"HunterCreed": ConstantMapping("characters", "HunterCreed"),
|
|
78
78
|
"HunterEdgeType": ConstantMapping("characters", "HunterEdgeType"),
|
|
79
79
|
"ManageCampaignPermission": ConstantMapping("companies", "PermissionManageCampaign"),
|
|
80
|
+
"ManageNPCPermission": ConstantMapping("companies", "PermissionManageNPC"),
|
|
80
81
|
"PermissionLevel": ConstantMapping("companies", "CompanyPermission"),
|
|
81
82
|
"RecoupXPPermission": ConstantMapping("companies", "PermissionsRecoupXP"),
|
|
82
83
|
"RollResultType": ConstantMapping("gameplay", "RollResultType"),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/base.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
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/system.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/services/users.py
RENAMED
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/_sync/testing/_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/audit_logs.py
RENAMED
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/campaigns.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/chapters.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/characters.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/developers.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/diceroll.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/dictionary.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/full_sheet.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/global_admin.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/pagination.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/shared.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/system.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/models/user_lookup.py
RENAMED
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/campaigns.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/characters.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/companies.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/developers.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/dicerolls.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/dictionary.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/options.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/system.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/user_lookup.py
RENAMED
|
File without changes
|
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/services/users.py
RENAMED
|
File without changes
|
{valentina_python_client-2.2.0 → valentina_python_client-2.3.0}/src/vclient/testing/_client.py
RENAMED
|
File without changes
|