valentina-python-client 2.6.0__tar.gz → 2.8.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.6.0 → valentina_python_client-2.8.0}/PKG-INFO +1 -1
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/pyproject.toml +7 -6
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/__init__.py +5 -5
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_codegen.py +5 -5
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/__init__.py +2 -2
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/client.py +12 -13
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/registry.py +11 -12
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/__init__.py +2 -2
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/base.py +10 -3
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/developers.py +3 -1
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/global_admin.py +3 -1
- valentina_python_client-2.8.0/src/vclient/_sync/services/identity.py +78 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/users.py +83 -17
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/testing/_client.py +6 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/client.py +12 -13
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/constants.py +5 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/endpoints.py +5 -1
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/exceptions.py +25 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/__init__.py +8 -2
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/developers.py +4 -1
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/global_admin.py +3 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/shared.py +12 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/users.py +51 -23
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/registry.py +11 -12
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/__init__.py +2 -2
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/base.py +15 -4
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/developers.py +3 -1
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/global_admin.py +3 -1
- valentina_python_client-2.8.0/src/vclient/services/identity.py +81 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/users.py +94 -17
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/testing/__init__.py +2 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/testing/_client.py +6 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/testing/_factories.py +7 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/testing/_router.py +3 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/testing/_routes.py +6 -2
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/validate_constants.py +39 -11
- valentina_python_client-2.6.0/src/vclient/_sync/services/user_self_registration.py +0 -72
- valentina_python_client-2.6.0/src/vclient/services/user_self_registration.py +0 -75
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/LICENSE +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/README.md +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/_audit_params.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/campaign_book_chapters.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/campaign_books.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/campaigns.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/character_autogen.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/character_blueprint.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/character_traits.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/characters.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/companies.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/dicerolls.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/dictionary.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/options.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/system.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/user_lookup.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/testing/__init__.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/config.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/audit_logs.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/books.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/campaigns.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/chapters.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/character_autogen.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/character_blueprint.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/character_trait.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/characters.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/companies.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/diceroll.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/dictionary.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/full_sheet.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/pagination.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/system.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/models/user_lookup.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/py.typed +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/_audit_params.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/campaign_book_chapters.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/campaign_books.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/campaigns.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/character_autogen.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/character_blueprint.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/character_traits.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/characters.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/companies.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/dicerolls.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/dictionary.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/options.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/system.py +0 -0
- {valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/services/user_lookup.py +0 -0
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
name = "valentina-python-client"
|
|
17
17
|
readme = "README.md"
|
|
18
18
|
requires-python = ">=3.13"
|
|
19
|
-
version = "2.
|
|
19
|
+
version = "2.8.0"
|
|
20
20
|
|
|
21
21
|
[project.optional-dependencies]
|
|
22
22
|
testing = ["polyfactory>=3.3.0"]
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"commitizen>=4.16.3",
|
|
38
38
|
"coverage>=7.14.1",
|
|
39
39
|
"duty>=1.9.0",
|
|
40
|
-
"prek>=0.4.
|
|
40
|
+
"prek>=0.4.4",
|
|
41
41
|
"pytest-anyio>=0.0.0",
|
|
42
42
|
"pytest-clarity>=1.0.1",
|
|
43
43
|
"pytest-cov>=7.1.0",
|
|
@@ -48,14 +48,14 @@
|
|
|
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.16",
|
|
52
52
|
"shellcheck-py>=0.11.0.1",
|
|
53
|
-
"ty>=0.0.
|
|
54
|
-
"typos>=1.47.
|
|
53
|
+
"ty>=0.0.44",
|
|
54
|
+
"typos>=1.47.2",
|
|
55
55
|
"vulture>=2.16",
|
|
56
56
|
"yamllint>=1.38.0",
|
|
57
57
|
]
|
|
58
|
-
docs = ["zensical>=0.0.
|
|
58
|
+
docs = ["zensical>=0.0.44"]
|
|
59
59
|
|
|
60
60
|
[tool.commitizen]
|
|
61
61
|
bump_message = "bump(release): v$current_version → v$new_version"
|
|
@@ -132,6 +132,7 @@
|
|
|
132
132
|
]
|
|
133
133
|
per-file-ignores = { "src/vclient/_sync/**" = [
|
|
134
134
|
"C416", # Unnecessary list comprehension (AST unparser artifact)
|
|
135
|
+
"C901", # Function too complex (noqa stripped by AST codegen)
|
|
135
136
|
"D", # Docstring formatting (AST unparser doesn't preserve style)
|
|
136
137
|
"E501", # Line too long
|
|
137
138
|
"ERA001", # Commented-out code false positives
|
|
@@ -50,10 +50,10 @@ from vclient._sync import ( # noqa: E402
|
|
|
50
50
|
sync_dicerolls_service,
|
|
51
51
|
sync_dictionary_service,
|
|
52
52
|
sync_global_admin_service,
|
|
53
|
+
sync_identity_service,
|
|
53
54
|
sync_options_service,
|
|
54
55
|
sync_system_service,
|
|
55
56
|
sync_user_lookup_service,
|
|
56
|
-
sync_user_self_registration_service,
|
|
57
57
|
sync_users_service,
|
|
58
58
|
)
|
|
59
59
|
from vclient.client import VClient # noqa: E402
|
|
@@ -70,10 +70,10 @@ from vclient.registry import ( # noqa: E402
|
|
|
70
70
|
dicerolls_service,
|
|
71
71
|
dictionary_service,
|
|
72
72
|
global_admin_service,
|
|
73
|
+
identity_service,
|
|
73
74
|
options_service,
|
|
74
75
|
system_service,
|
|
75
76
|
user_lookup_service,
|
|
76
|
-
user_self_registration_service,
|
|
77
77
|
users_service,
|
|
78
78
|
)
|
|
79
79
|
|
|
@@ -92,6 +92,7 @@ __all__ = (
|
|
|
92
92
|
"dicerolls_service",
|
|
93
93
|
"dictionary_service",
|
|
94
94
|
"global_admin_service",
|
|
95
|
+
"identity_service",
|
|
95
96
|
"options_service",
|
|
96
97
|
"sync_books_service",
|
|
97
98
|
"sync_campaigns_service",
|
|
@@ -105,15 +106,14 @@ __all__ = (
|
|
|
105
106
|
"sync_dicerolls_service",
|
|
106
107
|
"sync_dictionary_service",
|
|
107
108
|
"sync_global_admin_service",
|
|
109
|
+
"sync_identity_service",
|
|
108
110
|
"sync_options_service",
|
|
109
111
|
"sync_system_service",
|
|
110
112
|
"sync_user_lookup_service",
|
|
111
|
-
"sync_user_self_registration_service",
|
|
112
113
|
"sync_users_service",
|
|
113
114
|
"system_service",
|
|
114
115
|
"user_lookup_service",
|
|
115
|
-
"user_self_registration_service",
|
|
116
116
|
"users_service",
|
|
117
117
|
)
|
|
118
118
|
|
|
119
|
-
__version__ = "2.
|
|
119
|
+
__version__ = "2.8.0"
|
|
@@ -26,10 +26,10 @@ RENAME_CLASSES: dict[str, str] = {
|
|
|
26
26
|
"DicerollService": "SyncDicerollService",
|
|
27
27
|
"DictionaryService": "SyncDictionaryService",
|
|
28
28
|
"GlobalAdminService": "SyncGlobalAdminService",
|
|
29
|
+
"IdentityService": "SyncIdentityService",
|
|
29
30
|
"OptionsService": "SyncOptionsService",
|
|
30
31
|
"SystemService": "SyncSystemService",
|
|
31
32
|
"UserLookupService": "SyncUserLookupService",
|
|
32
|
-
"UserSelfRegistrationService": "SyncUserSelfRegistrationService",
|
|
33
33
|
"UsersService": "SyncUsersService",
|
|
34
34
|
"VClient": "SyncVClient",
|
|
35
35
|
"FakeVClient": "SyncFakeVClient",
|
|
@@ -39,6 +39,7 @@ FACTORY_RENAMES: dict[str, str] = {
|
|
|
39
39
|
"companies_service": "sync_companies_service",
|
|
40
40
|
"developer_service": "sync_developer_service",
|
|
41
41
|
"global_admin_service": "sync_global_admin_service",
|
|
42
|
+
"identity_service": "sync_identity_service",
|
|
42
43
|
"system_service": "sync_system_service",
|
|
43
44
|
"users_service": "sync_users_service",
|
|
44
45
|
"campaigns_service": "sync_campaigns_service",
|
|
@@ -52,7 +53,6 @@ FACTORY_RENAMES: dict[str, str] = {
|
|
|
52
53
|
"options_service": "sync_options_service",
|
|
53
54
|
"character_autogen_service": "sync_character_autogen_service",
|
|
54
55
|
"user_lookup_service": "sync_user_lookup_service",
|
|
55
|
-
"user_self_registration_service": "sync_user_self_registration_service",
|
|
56
56
|
"configure_default_client": "sync_configure_default_client",
|
|
57
57
|
"clear_default_client": "sync_clear_default_client",
|
|
58
58
|
"default_client": "sync_default_client",
|
|
@@ -78,7 +78,7 @@ IMPORT_REWRITES: dict[str, str] = {
|
|
|
78
78
|
"vclient.services.options": "vclient._sync.services.options",
|
|
79
79
|
"vclient.services.character_autogen": "vclient._sync.services.character_autogen",
|
|
80
80
|
"vclient.services.user_lookup": "vclient._sync.services.user_lookup",
|
|
81
|
-
"vclient.services.
|
|
81
|
+
"vclient.services.identity": "vclient._sync.services.identity",
|
|
82
82
|
"vclient.registry": "vclient._sync.registry",
|
|
83
83
|
"vclient.testing._client": "vclient._sync.testing._client",
|
|
84
84
|
}
|
|
@@ -311,10 +311,10 @@ def _write_sync_init(path: Path) -> None:
|
|
|
311
311
|
" sync_dicerolls_service,",
|
|
312
312
|
" sync_dictionary_service,",
|
|
313
313
|
" sync_global_admin_service,",
|
|
314
|
+
" sync_identity_service,",
|
|
314
315
|
" sync_options_service,",
|
|
315
316
|
" sync_system_service,",
|
|
316
317
|
" sync_user_lookup_service,",
|
|
317
|
-
" sync_user_self_registration_service,",
|
|
318
318
|
" sync_users_service,",
|
|
319
319
|
")",
|
|
320
320
|
"",
|
|
@@ -335,10 +335,10 @@ def _write_sync_init(path: Path) -> None:
|
|
|
335
335
|
' "sync_dicerolls_service",',
|
|
336
336
|
' "sync_dictionary_service",',
|
|
337
337
|
' "sync_global_admin_service",',
|
|
338
|
+
' "sync_identity_service",',
|
|
338
339
|
' "sync_options_service",',
|
|
339
340
|
' "sync_system_service",',
|
|
340
341
|
' "sync_user_lookup_service",',
|
|
341
|
-
' "sync_user_self_registration_service",',
|
|
342
342
|
' "sync_users_service",',
|
|
343
343
|
"]",
|
|
344
344
|
"",
|
{valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/__init__.py
RENAMED
|
@@ -17,10 +17,10 @@ from vclient._sync.registry import (
|
|
|
17
17
|
sync_dicerolls_service,
|
|
18
18
|
sync_dictionary_service,
|
|
19
19
|
sync_global_admin_service,
|
|
20
|
+
sync_identity_service,
|
|
20
21
|
sync_options_service,
|
|
21
22
|
sync_system_service,
|
|
22
23
|
sync_user_lookup_service,
|
|
23
|
-
sync_user_self_registration_service,
|
|
24
24
|
sync_users_service,
|
|
25
25
|
)
|
|
26
26
|
|
|
@@ -41,9 +41,9 @@ __all__ = [
|
|
|
41
41
|
"sync_dicerolls_service",
|
|
42
42
|
"sync_dictionary_service",
|
|
43
43
|
"sync_global_admin_service",
|
|
44
|
+
"sync_identity_service",
|
|
44
45
|
"sync_options_service",
|
|
45
46
|
"sync_system_service",
|
|
46
47
|
"sync_user_lookup_service",
|
|
47
|
-
"sync_user_self_registration_service",
|
|
48
48
|
"sync_users_service",
|
|
49
49
|
]
|
|
@@ -35,10 +35,10 @@ if TYPE_CHECKING:
|
|
|
35
35
|
SyncDicerollService,
|
|
36
36
|
SyncDictionaryService,
|
|
37
37
|
SyncGlobalAdminService,
|
|
38
|
+
SyncIdentityService,
|
|
38
39
|
SyncOptionsService,
|
|
39
40
|
SyncSystemService,
|
|
40
41
|
SyncUserLookupService,
|
|
41
|
-
SyncUserSelfRegistrationService,
|
|
42
42
|
SyncUsersService,
|
|
43
43
|
)
|
|
44
44
|
|
|
@@ -322,31 +322,30 @@ class SyncVClient:
|
|
|
322
322
|
self, company_id=self._resolve_company_id(company_id), on_behalf_of=on_behalf_of
|
|
323
323
|
)
|
|
324
324
|
|
|
325
|
-
def
|
|
326
|
-
|
|
327
|
-
) -> "SyncUserSelfRegistrationService":
|
|
328
|
-
"""Get a SyncUserSelfRegistrationService scoped to a specific company.
|
|
325
|
+
def identity(self, *, company_id: str | None = None) -> "SyncIdentityService":
|
|
326
|
+
"""Get a company-scoped SyncIdentityService.
|
|
329
327
|
|
|
330
|
-
|
|
331
|
-
|
|
328
|
+
Resolves verified provider logins (Apple/Google ID tokens,
|
|
329
|
+
Discord/GitHub access tokens) to canonical users. Does not require
|
|
330
|
+
an acting user, only developer API key authentication.
|
|
332
331
|
|
|
333
332
|
Args:
|
|
334
|
-
company_id: The ID of the company to
|
|
333
|
+
company_id: The ID of the company to resolve identities in. If not
|
|
335
334
|
provided, uses the default_company_id from config.
|
|
336
335
|
|
|
337
336
|
Returns:
|
|
338
|
-
|
|
337
|
+
An SyncIdentityService instance.
|
|
339
338
|
|
|
340
339
|
Raises:
|
|
341
340
|
ValueError: If no company_id provided and no default configured.
|
|
342
341
|
|
|
343
342
|
Example:
|
|
344
|
-
>>>
|
|
345
|
-
>>>
|
|
343
|
+
>>> identity = client.identity()
|
|
344
|
+
>>> result = await identity.identify(provider="google", token="<id-token>")
|
|
346
345
|
"""
|
|
347
|
-
from vclient._sync.services.
|
|
346
|
+
from vclient._sync.services.identity import SyncIdentityService
|
|
348
347
|
|
|
349
|
-
return
|
|
348
|
+
return SyncIdentityService(self, self._resolve_company_id(company_id))
|
|
350
349
|
|
|
351
350
|
def campaigns(
|
|
352
351
|
self, on_behalf_of: str, *, company_id: str | None = None
|
{valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/registry.py
RENAMED
|
@@ -36,10 +36,10 @@ if TYPE_CHECKING:
|
|
|
36
36
|
SyncDicerollService,
|
|
37
37
|
SyncDictionaryService,
|
|
38
38
|
SyncGlobalAdminService,
|
|
39
|
+
SyncIdentityService,
|
|
39
40
|
SyncOptionsService,
|
|
40
41
|
SyncSystemService,
|
|
41
42
|
SyncUserLookupService,
|
|
42
|
-
SyncUserSelfRegistrationService,
|
|
43
43
|
SyncUsersService,
|
|
44
44
|
)
|
|
45
45
|
_default_client: "SyncVClient | None" = None
|
|
@@ -242,20 +242,19 @@ def sync_users_service(on_behalf_of: str, *, company_id: str | None = None) -> "
|
|
|
242
242
|
return sync_default_client().users(on_behalf_of, company_id=company_id)
|
|
243
243
|
|
|
244
244
|
|
|
245
|
-
def
|
|
246
|
-
|
|
247
|
-
) -> "SyncUserSelfRegistrationService":
|
|
248
|
-
"""Create a SyncUserSelfRegistrationService using the default client.
|
|
245
|
+
def sync_identity_service(*, company_id: str | None = None) -> "SyncIdentityService":
|
|
246
|
+
"""Create a company-scoped SyncIdentityService using the default client.
|
|
249
247
|
|
|
250
|
-
|
|
251
|
-
|
|
248
|
+
Resolves verified provider logins (Apple/Google ID tokens, Discord/GitHub
|
|
249
|
+
access tokens) to canonical users. Does not require an acting user, only
|
|
250
|
+
developer API key authentication.
|
|
252
251
|
|
|
253
252
|
Args:
|
|
254
|
-
company_id: The ID of the company to
|
|
253
|
+
company_id: The ID of the company to resolve identities in. If not
|
|
255
254
|
provided, uses the default_company_id from the client config.
|
|
256
255
|
|
|
257
256
|
Returns:
|
|
258
|
-
|
|
257
|
+
SyncIdentityService: A service instance for identity resolution.
|
|
259
258
|
|
|
260
259
|
Raises:
|
|
261
260
|
RuntimeError: If no default client has been configured.
|
|
@@ -263,11 +262,11 @@ def sync_user_self_registration_service(
|
|
|
263
262
|
|
|
264
263
|
Example:
|
|
265
264
|
```python
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
identity = sync_identity_service()
|
|
266
|
+
result = await identity.identify(provider="google", token="<id-token>")
|
|
268
267
|
```
|
|
269
268
|
"""
|
|
270
|
-
return sync_default_client().
|
|
269
|
+
return sync_default_client().identity(company_id=company_id)
|
|
271
270
|
|
|
272
271
|
|
|
273
272
|
def sync_campaigns_service(
|
|
@@ -14,10 +14,10 @@ from .developers import SyncDeveloperService
|
|
|
14
14
|
from .dicerolls import SyncDicerollService
|
|
15
15
|
from .dictionary import SyncDictionaryService
|
|
16
16
|
from .global_admin import SyncGlobalAdminService
|
|
17
|
+
from .identity import SyncIdentityService
|
|
17
18
|
from .options import SyncOptionsService
|
|
18
19
|
from .system import SyncSystemService
|
|
19
20
|
from .user_lookup import SyncUserLookupService
|
|
20
|
-
from .user_self_registration import SyncUserSelfRegistrationService
|
|
21
21
|
from .users import SyncUsersService
|
|
22
22
|
|
|
23
23
|
__all__ = [
|
|
@@ -34,9 +34,9 @@ __all__ = [
|
|
|
34
34
|
"SyncDicerollService",
|
|
35
35
|
"SyncDictionaryService",
|
|
36
36
|
"SyncGlobalAdminService",
|
|
37
|
+
"SyncIdentityService",
|
|
37
38
|
"SyncOptionsService",
|
|
38
39
|
"SyncSystemService",
|
|
39
40
|
"SyncUserLookupService",
|
|
40
|
-
"SyncUserSelfRegistrationService",
|
|
41
41
|
"SyncUsersService",
|
|
42
42
|
]
|
{valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/base.py
RENAMED
|
@@ -31,6 +31,7 @@ from vclient.exceptions import (
|
|
|
31
31
|
RateLimitError,
|
|
32
32
|
RequestValidationError,
|
|
33
33
|
ServerError,
|
|
34
|
+
UnprocessableEntityError,
|
|
34
35
|
ValidationError,
|
|
35
36
|
)
|
|
36
37
|
from vclient.models.pagination import PaginatedResponse
|
|
@@ -278,6 +279,7 @@ class SyncBaseService:
|
|
|
278
279
|
NotFoundError: For 404 responses.
|
|
279
280
|
ValidationError: For 400 responses.
|
|
280
281
|
ConflictError: For 409 responses.
|
|
282
|
+
UnprocessableEntityError: For 422 responses.
|
|
281
283
|
RateLimitError: For 429 responses.
|
|
282
284
|
ServerError: For 5xx responses.
|
|
283
285
|
APIError: For other error responses.
|
|
@@ -287,10 +289,12 @@ class SyncBaseService:
|
|
|
287
289
|
status_code = response.status_code
|
|
288
290
|
try:
|
|
289
291
|
response_data = response.json()
|
|
290
|
-
|
|
291
|
-
|
|
292
|
+
except ValueError:
|
|
293
|
+
response_data = {}
|
|
294
|
+
if not isinstance(response_data, dict):
|
|
292
295
|
response_data = {}
|
|
293
|
-
|
|
296
|
+
detail = response_data.get("detail")
|
|
297
|
+
message = detail if isinstance(detail, str) else response.text or f"HTTP {status_code}"
|
|
294
298
|
self._inject_request_id_fallback(response_data, response)
|
|
295
299
|
error_logger = logger.bind(
|
|
296
300
|
method=method,
|
|
@@ -320,6 +324,9 @@ class SyncBaseService:
|
|
|
320
324
|
if status_code == 409:
|
|
321
325
|
error_logger.warning("Return 409 conflict")
|
|
322
326
|
raise ConflictError(message, status_code, response_data)
|
|
327
|
+
if status_code == 422:
|
|
328
|
+
error_logger.warning("Reject with unprocessable entity error")
|
|
329
|
+
raise UnprocessableEntityError(message, status_code, response_data)
|
|
323
330
|
if HTTP_500_INTERNAL_SERVER_ERROR <= status_code < HTTP_600_UPPER_BOUND:
|
|
324
331
|
raise ServerError(message, status_code, response_data)
|
|
325
332
|
raise APIError(message, status_code, response_data)
|
|
@@ -41,7 +41,9 @@ class SyncDeveloperService(SyncBaseService):
|
|
|
41
41
|
Args:
|
|
42
42
|
request: A MeDeveloperUpdate model, OR pass fields as keyword arguments.
|
|
43
43
|
**kwargs: Fields for MeDeveloperUpdate if request is not provided.
|
|
44
|
-
Accepts: username (str | None), email (str | None)
|
|
44
|
+
Accepts: username (str | None), email (str | None),
|
|
45
|
+
provider_audiences (ProviderAudiences | None): per-provider OIDC
|
|
46
|
+
audience allowlists ({"apple": [...], "google": [...]}).
|
|
45
47
|
|
|
46
48
|
Returns:
|
|
47
49
|
The updated MeDeveloper object.
|
|
@@ -188,7 +188,9 @@ class SyncGlobalAdminService(SyncBaseService):
|
|
|
188
188
|
request: A DeveloperUpdate model, OR pass fields as keyword arguments.
|
|
189
189
|
**kwargs: Fields for DeveloperUpdate if request is not provided.
|
|
190
190
|
Accepts: username (str | None), email (str | None),
|
|
191
|
-
is_global_admin (bool | None)
|
|
191
|
+
is_global_admin (bool | None),
|
|
192
|
+
provider_audiences (ProviderAudiences | None): per-provider OIDC
|
|
193
|
+
audience allowlists ({"apple": [...], "google": [...]}).
|
|
192
194
|
|
|
193
195
|
Returns:
|
|
194
196
|
The updated Developer object.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# AUTO-GENERATED — do not edit. Run 'uv run duty generate_sync' to regenerate.
|
|
2
|
+
"""Service for verified identity resolution."""
|
|
3
|
+
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from vclient._sync.services.base import SyncBaseService
|
|
7
|
+
from vclient.endpoints import Endpoints
|
|
8
|
+
from vclient.models import IdentityResolution, UserIdentifyDTO
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from vclient._sync.client import SyncVClient
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SyncIdentityService(SyncBaseService):
|
|
15
|
+
"""Service for resolving verified provider logins to canonical users.
|
|
16
|
+
|
|
17
|
+
Forward the credential obtained from a provider's own login flow (an OIDC
|
|
18
|
+
ID token for apple/google, an OAuth access token for discord/github). The
|
|
19
|
+
API verifies the credential with the provider and resolves the user in
|
|
20
|
+
order: match by provider ID, auto-link by provider-verified email, or
|
|
21
|
+
create a new UNAPPROVED user.
|
|
22
|
+
|
|
23
|
+
This service does not require an acting user (no On-Behalf-Of header).
|
|
24
|
+
Authentication is via developer API key only.
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
>>> async with SyncVClient() as client:
|
|
28
|
+
... identity = client.identity()
|
|
29
|
+
... result = await identity.identify(provider="google", token="<id-token>")
|
|
30
|
+
... print(result.resolution, result.user.id)
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, client: "SyncVClient", company_id: str) -> None:
|
|
34
|
+
"""Initialize the service scoped to a specific company.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
client: The SyncVClient instance to use for requests.
|
|
38
|
+
company_id: The ID of the company to resolve identities in.
|
|
39
|
+
"""
|
|
40
|
+
super().__init__(client)
|
|
41
|
+
self._company_id = company_id
|
|
42
|
+
|
|
43
|
+
def _format_endpoint(self, endpoint: str, **kwargs: str) -> str:
|
|
44
|
+
"""Format an endpoint with the scoped company_id plus any extra params."""
|
|
45
|
+
return endpoint.format(company_id=self._company_id, **kwargs)
|
|
46
|
+
|
|
47
|
+
def identify(self, request: UserIdentifyDTO | None = None, **kwargs) -> IdentityResolution:
|
|
48
|
+
"""Resolve a verified provider login to a canonical user.
|
|
49
|
+
|
|
50
|
+
The returned ``resolution`` field reports which path the API took:
|
|
51
|
+
``matched`` (an existing user holds this provider identity), ``linked``
|
|
52
|
+
(the identity was auto-linked onto an existing user by provider-verified
|
|
53
|
+
email), or ``created`` (a new UNAPPROVED user was registered).
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
request: A UserIdentifyDTO model, OR pass fields as keyword arguments.
|
|
57
|
+
**kwargs: Fields for UserIdentifyDTO if request is not provided.
|
|
58
|
+
Accepts: provider (IdentityProvider, required), token (str,
|
|
59
|
+
required), username (str | None), email (str | None). The
|
|
60
|
+
username and email apply only when a new user is created;
|
|
61
|
+
email is required there only if the provider supplied none.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
The IdentityResolution with the resolved user.
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
RequestValidationError: If the input parameters fail client-side validation.
|
|
68
|
+
UnprocessableEntityError: If the provider token fails verification
|
|
69
|
+
(code TOKEN_VERIFICATION_FAILED) or creation needs an email the
|
|
70
|
+
provider did not supply (code EMAIL_REQUIRED).
|
|
71
|
+
ServerError: If the provider is unreachable (503, code PROVIDER_UNAVAILABLE).
|
|
72
|
+
"""
|
|
73
|
+
body = request if request is not None else self._validate_request(UserIdentifyDTO, **kwargs)
|
|
74
|
+
response = self._post(
|
|
75
|
+
self._format_endpoint(Endpoints.AUTH_IDENTIFY),
|
|
76
|
+
json=body.model_dump(exclude_none=True, exclude_unset=True, mode="json"),
|
|
77
|
+
)
|
|
78
|
+
return IdentityResolution.model_validate(response.json())
|
{valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/services/users.py
RENAMED
|
@@ -6,7 +6,7 @@ from collections.abc import Iterator, Sequence
|
|
|
6
6
|
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
8
|
from vclient._sync.services.base import SyncBaseService
|
|
9
|
-
from vclient.constants import DEFAULT_PAGE_LIMIT, UserInclude, UserRole
|
|
9
|
+
from vclient.constants import DEFAULT_PAGE_LIMIT, IdentityProvider, UserInclude, UserRole
|
|
10
10
|
from vclient.endpoints import Endpoints
|
|
11
11
|
from vclient.models import (
|
|
12
12
|
Asset,
|
|
@@ -23,6 +23,7 @@ from vclient.models import (
|
|
|
23
23
|
UserApproveDTO,
|
|
24
24
|
UserCreate,
|
|
25
25
|
UserDetail,
|
|
26
|
+
UserIdentityLinkDTO,
|
|
26
27
|
UserMergeDTO,
|
|
27
28
|
UserUpdate,
|
|
28
29
|
_ExperienceAddRemove,
|
|
@@ -172,6 +173,72 @@ class SyncUsersService(SyncBaseService):
|
|
|
172
173
|
)
|
|
173
174
|
return User.model_validate(response.json())
|
|
174
175
|
|
|
176
|
+
def link_identity(self, user_id: str, *, provider: IdentityProvider, token: str) -> User:
|
|
177
|
+
"""Attach an additional verified provider identity to a user.
|
|
178
|
+
|
|
179
|
+
Use for "connect your account" settings flows: a user already
|
|
180
|
+
authenticated via one provider presents a credential from a second
|
|
181
|
+
provider, which the API verifies and links to the same user. Only the
|
|
182
|
+
acting user themselves or a company ADMIN may link identities.
|
|
183
|
+
Re-linking the same identity is idempotent and refreshes the stored
|
|
184
|
+
profile.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
user_id: The ID of the user receiving the identity.
|
|
188
|
+
provider: The identity provider that issued the credential.
|
|
189
|
+
token: The provider credential (OIDC ID token for apple/google,
|
|
190
|
+
OAuth access token for discord/github).
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
The updated User object.
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
RequestValidationError: If the input parameters fail client-side validation.
|
|
197
|
+
AuthorizationError: If the acting user is neither the target user nor an admin.
|
|
198
|
+
ConflictError: If the identity belongs to another user, or the user
|
|
199
|
+
already has a different identity from this provider
|
|
200
|
+
(code IDENTITY_ALREADY_LINKED).
|
|
201
|
+
UnprocessableEntityError: If the provider token fails verification
|
|
202
|
+
(code TOKEN_VERIFICATION_FAILED).
|
|
203
|
+
"""
|
|
204
|
+
body = self._validate_request(UserIdentityLinkDTO, provider=provider, token=token)
|
|
205
|
+
response = self._post(
|
|
206
|
+
self._format_endpoint(Endpoints.USER_IDENTITIES, user_id=user_id),
|
|
207
|
+
json=body.model_dump(exclude_none=True, exclude_unset=True, mode="json"),
|
|
208
|
+
)
|
|
209
|
+
return User.model_validate(response.json())
|
|
210
|
+
|
|
211
|
+
def unlink_identity(self, user_id: str, *, provider: IdentityProvider) -> User:
|
|
212
|
+
"""Disconnect a verified provider identity from a user.
|
|
213
|
+
|
|
214
|
+
The counterpart to link_identity for "disconnect your account" settings
|
|
215
|
+
flows: remove a single OAuth provider identity so it can no longer be
|
|
216
|
+
used to log in. Only the acting user themselves or a company ADMIN may
|
|
217
|
+
unlink identities. The provider's profile field on the returned User is
|
|
218
|
+
cleared to None.
|
|
219
|
+
|
|
220
|
+
The final remaining identity cannot be removed, since that would leave
|
|
221
|
+
the account unable to authenticate. Link another provider first.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
user_id: The ID of the user losing the identity.
|
|
225
|
+
provider: The identity provider to disconnect.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
The updated User object with the provider's profile field cleared.
|
|
229
|
+
|
|
230
|
+
Raises:
|
|
231
|
+
AuthorizationError: If the acting user is neither the target user nor an admin.
|
|
232
|
+
NotFoundError: If the user has no identity from this provider
|
|
233
|
+
(code IDENTITY_NOT_LINKED).
|
|
234
|
+
ConflictError: If the provider is the user's only linked identity,
|
|
235
|
+
which cannot be removed (code LAST_IDENTITY).
|
|
236
|
+
"""
|
|
237
|
+
response = self._delete(
|
|
238
|
+
self._format_endpoint(Endpoints.USER_IDENTITY, user_id=user_id, provider=provider)
|
|
239
|
+
)
|
|
240
|
+
return User.model_validate(response.json())
|
|
241
|
+
|
|
175
242
|
def get_page(
|
|
176
243
|
self,
|
|
177
244
|
*,
|
|
@@ -275,24 +342,22 @@ class SyncUsersService(SyncBaseService):
|
|
|
275
342
|
def create(self, request: UserCreate | None = None, **kwargs) -> User:
|
|
276
343
|
"""Create a new user within a company.
|
|
277
344
|
|
|
278
|
-
The user is automatically added to the company's user list.
|
|
279
|
-
|
|
280
|
-
|
|
345
|
+
The user is automatically added to the company's user list.
|
|
346
|
+
|
|
347
|
+
Provider identities (Discord, Google, GitHub, Apple) cannot be set here;
|
|
348
|
+
any provider-profile values passed are silently ignored. Resolve them
|
|
349
|
+
through the verified identity endpoints instead
|
|
350
|
+
(:meth:`SyncIdentityService.identify` or :meth:`SyncUsersService.link_identity`).
|
|
281
351
|
|
|
282
|
-
The initial ``role`` cannot be ``UNAPPROVED``
|
|
283
|
-
|
|
284
|
-
``ValidationError``.
|
|
352
|
+
The initial ``role`` cannot be ``UNAPPROVED`` or ``DEACTIVATED`` (not a
|
|
353
|
+
creation path); either will surface as ``ValidationError``.
|
|
285
354
|
|
|
286
355
|
Args:
|
|
287
356
|
request: A UserCreate model, OR pass fields as keyword arguments.
|
|
288
357
|
**kwargs: Fields for UserCreate if request is not provided.
|
|
289
358
|
Accepts: username (str, required), email (str, required),
|
|
290
359
|
role (UserRole, required),
|
|
291
|
-
name_first (str | None), name_last (str | None)
|
|
292
|
-
discord_profile (DiscordProfileUpdate | None),
|
|
293
|
-
google_profile (GoogleProfile | None),
|
|
294
|
-
github_profile (GitHubProfile | None),
|
|
295
|
-
apple_profile (AppleProfile | None).
|
|
360
|
+
name_first (str | None), name_last (str | None).
|
|
296
361
|
|
|
297
362
|
Returns:
|
|
298
363
|
The newly created User object.
|
|
@@ -328,11 +393,12 @@ class SyncUsersService(SyncBaseService):
|
|
|
328
393
|
**kwargs: Fields for UserUpdate if request is not provided.
|
|
329
394
|
Accepts: username (str | None), email (str | None),
|
|
330
395
|
role (UserRole | None),
|
|
331
|
-
name_first (str | None), name_last (str | None)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
396
|
+
name_first (str | None), name_last (str | None).
|
|
397
|
+
|
|
398
|
+
Provider identities (Discord, Google, GitHub, Apple) cannot be changed
|
|
399
|
+
here; any provider-profile values passed are silently ignored. Resolve
|
|
400
|
+
them through the verified identity endpoints instead
|
|
401
|
+
(:meth:`SyncIdentityService.identify` or :meth:`SyncUsersService.link_identity`).
|
|
336
402
|
|
|
337
403
|
Returns:
|
|
338
404
|
The updated User object.
|
{valentina_python_client-2.6.0 → valentina_python_client-2.8.0}/src/vclient/_sync/testing/_client.py
RENAMED
|
@@ -164,6 +164,7 @@ class SyncFakeVClient(SyncVClient):
|
|
|
164
164
|
*,
|
|
165
165
|
status_code: int,
|
|
166
166
|
detail: str | None = None,
|
|
167
|
+
code: str | None = None,
|
|
167
168
|
params: dict[str, str] | None = None,
|
|
168
169
|
) -> None:
|
|
169
170
|
"""Override a route to return an error response.
|
|
@@ -172,6 +173,9 @@ class SyncFakeVClient(SyncVClient):
|
|
|
172
173
|
route: A ``RouteSpec`` from the ``Routes`` class identifying the endpoint.
|
|
173
174
|
status_code: The HTTP status code to return.
|
|
174
175
|
detail: Optional error detail message. Defaults to ``"Error {status_code}"``.
|
|
176
|
+
code: Optional machine-readable error code, an RFC 9457 extension
|
|
177
|
+
member exposed as ``APIError.code`` (e.g.
|
|
178
|
+
``"TOKEN_VERIFICATION_FAILED"``). Omitted from the body when not set.
|
|
175
179
|
params: Optional path parameter values to match against. When set,
|
|
176
180
|
the error override only applies to requests whose URL path segments
|
|
177
181
|
match all specified values.
|
|
@@ -182,4 +186,6 @@ class SyncFakeVClient(SyncVClient):
|
|
|
182
186
|
"detail": detail or f"Error {status_code}",
|
|
183
187
|
"request_id": request_id,
|
|
184
188
|
}
|
|
189
|
+
if code is not None:
|
|
190
|
+
body["code"] = code
|
|
185
191
|
self._router.add_route(method, pattern, json=body, status_code=status_code, params=params)
|