valentina-python-client 2.5.0__tar.gz → 2.7.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.
Potentially problematic release.
This version of valentina-python-client might be problematic. Click here for more details.
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/PKG-INFO +1 -1
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/pyproject.toml +7 -6
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/__init__.py +5 -5
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_codegen.py +5 -5
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/__init__.py +2 -2
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/client.py +12 -13
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/registry.py +12 -13
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/__init__.py +2 -2
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/base.py +10 -3
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/developers.py +3 -1
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/global_admin.py +3 -1
- valentina_python_client-2.7.0/src/vclient/_sync/services/identity.py +78 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/user_lookup.py +13 -1
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/users.py +53 -10
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/testing/_client.py +6 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/client.py +12 -13
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/constants.py +5 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/endpoints.py +4 -1
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/exceptions.py +25 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/__init__.py +10 -2
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/developers.py +4 -1
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/global_admin.py +3 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/shared.py +12 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/users.py +61 -20
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/registry.py +12 -13
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/__init__.py +2 -2
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/base.py +15 -4
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/developers.py +3 -1
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/global_admin.py +3 -1
- valentina_python_client-2.7.0/src/vclient/services/identity.py +81 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/user_lookup.py +13 -1
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/users.py +59 -10
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/testing/__init__.py +4 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/testing/_client.py +6 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/testing/_factories.py +14 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/testing/_router.py +3 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/testing/_routes.py +5 -2
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/validate_constants.py +39 -11
- valentina_python_client-2.5.0/src/vclient/_sync/services/user_self_registration.py +0 -71
- valentina_python_client-2.5.0/src/vclient/services/user_self_registration.py +0 -74
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/LICENSE +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/README.md +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/_audit_params.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/campaign_book_chapters.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/campaign_books.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/campaigns.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/character_autogen.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/character_blueprint.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/character_traits.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/characters.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/companies.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/dicerolls.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/dictionary.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/options.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/services/system.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/_sync/testing/__init__.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/config.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/audit_logs.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/books.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/campaigns.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/chapters.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/character_autogen.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/character_blueprint.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/character_trait.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/characters.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/companies.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/diceroll.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/dictionary.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/full_sheet.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/pagination.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/system.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/models/user_lookup.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/py.typed +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/_audit_params.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/campaign_book_chapters.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/campaign_books.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/campaigns.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/character_autogen.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/character_blueprint.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/character_traits.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/characters.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/companies.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/dicerolls.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/dictionary.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/options.py +0 -0
- {valentina_python_client-2.5.0 → valentina_python_client-2.7.0}/src/vclient/services/system.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.7.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.7.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.5.0 → valentina_python_client-2.7.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.5.0 → valentina_python_client-2.7.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
|
|
@@ -195,7 +195,7 @@ def sync_user_lookup_service() -> "SyncUserLookupService":
|
|
|
195
195
|
"""Create a SyncUserLookupService using the default client.
|
|
196
196
|
|
|
197
197
|
Discover which companies a person has a user record in by searching
|
|
198
|
-
via email, Discord ID, Google ID, or
|
|
198
|
+
via email, Discord ID, Google ID, GitHub ID, or Apple ID.
|
|
199
199
|
|
|
200
200
|
Returns:
|
|
201
201
|
SyncUserLookupService: A service instance for cross-company user lookup.
|
|
@@ -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.5.0 → valentina_python_client-2.7.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())
|
|
@@ -10,7 +10,7 @@ class SyncUserLookupService(SyncBaseService):
|
|
|
10
10
|
"""Service for looking up users across companies.
|
|
11
11
|
|
|
12
12
|
Discover which companies a person has a user record in by searching
|
|
13
|
-
via email, Discord ID, Google ID, or
|
|
13
|
+
via email, Discord ID, Google ID, GitHub ID, or Apple ID.
|
|
14
14
|
|
|
15
15
|
Example:
|
|
16
16
|
>>> async with SyncVClient() as client:
|
|
@@ -66,3 +66,15 @@ class SyncUserLookupService(SyncBaseService):
|
|
|
66
66
|
"""
|
|
67
67
|
response = self._get(Endpoints.USERS_LOOKUP, params={"github_id": github_id})
|
|
68
68
|
return [UserLookupResult.model_validate(item) for item in response.json()]
|
|
69
|
+
|
|
70
|
+
def by_apple_id(self, apple_id: str) -> list[UserLookupResult]:
|
|
71
|
+
"""Look up a user by Apple profile ID.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
apple_id: Apple profile ID to search for.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
List of companies where a matching user was found. Empty list if no matches.
|
|
78
|
+
"""
|
|
79
|
+
response = self._get(Endpoints.USERS_LOOKUP, params={"apple_id": apple_id})
|
|
80
|
+
return [UserLookupResult.model_validate(item) for item in response.json()]
|
{valentina_python_client-2.5.0 → valentina_python_client-2.7.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,41 @@ 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
|
+
|
|
175
211
|
def get_page(
|
|
176
212
|
self,
|
|
177
213
|
*,
|
|
@@ -275,20 +311,22 @@ class SyncUsersService(SyncBaseService):
|
|
|
275
311
|
def create(self, request: UserCreate | None = None, **kwargs) -> User:
|
|
276
312
|
"""Create a new user within a company.
|
|
277
313
|
|
|
278
|
-
The user is automatically added to the company's user list.
|
|
279
|
-
is optional and is not used for authentication but is included for Discord bot
|
|
280
|
-
integration.
|
|
314
|
+
The user is automatically added to the company's user list.
|
|
281
315
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
316
|
+
Provider identities (Discord, Google, GitHub, Apple) cannot be set here;
|
|
317
|
+
any provider-profile values passed are silently ignored. Resolve them
|
|
318
|
+
through the verified identity endpoints instead
|
|
319
|
+
(:meth:`SyncIdentityService.identify` or :meth:`SyncUsersService.link_identity`).
|
|
320
|
+
|
|
321
|
+
The initial ``role`` cannot be ``UNAPPROVED`` or ``DEACTIVATED`` (not a
|
|
322
|
+
creation path); either will surface as ``ValidationError``.
|
|
285
323
|
|
|
286
324
|
Args:
|
|
287
325
|
request: A UserCreate model, OR pass fields as keyword arguments.
|
|
288
326
|
**kwargs: Fields for UserCreate if request is not provided.
|
|
289
327
|
Accepts: username (str, required), email (str, required),
|
|
290
328
|
role (UserRole, required),
|
|
291
|
-
|
|
329
|
+
name_first (str | None), name_last (str | None).
|
|
292
330
|
|
|
293
331
|
Returns:
|
|
294
332
|
The newly created User object.
|
|
@@ -322,9 +360,14 @@ class SyncUsersService(SyncBaseService):
|
|
|
322
360
|
user_id: The ID of the user to update.
|
|
323
361
|
request: A UserUpdate model, OR pass fields as keyword arguments.
|
|
324
362
|
**kwargs: Fields for UserUpdate if request is not provided.
|
|
325
|
-
Accepts:
|
|
363
|
+
Accepts: username (str | None), email (str | None),
|
|
326
364
|
role (UserRole | None),
|
|
327
|
-
|
|
365
|
+
name_first (str | None), name_last (str | None).
|
|
366
|
+
|
|
367
|
+
Provider identities (Discord, Google, GitHub, Apple) cannot be changed
|
|
368
|
+
here; any provider-profile values passed are silently ignored. Resolve
|
|
369
|
+
them through the verified identity endpoints instead
|
|
370
|
+
(:meth:`SyncIdentityService.identify` or :meth:`SyncUsersService.link_identity`).
|
|
328
371
|
|
|
329
372
|
Returns:
|
|
330
373
|
The updated User object.
|
{valentina_python_client-2.5.0 → valentina_python_client-2.7.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)
|