workos 5.36.0__tar.gz → 5.38.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.
- {workos-5.36.0 → workos-5.38.0}/PKG-INFO +1 -1
- workos-5.38.0/tests/test_pipes.py +167 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_user_management.py +34 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_vault.py +28 -0
- {workos-5.36.0 → workos-5.38.0}/workos/__about__.py +1 -1
- {workos-5.36.0 → workos-5.38.0}/workos/_base_client.py +5 -0
- {workos-5.36.0 → workos-5.38.0}/workos/async_client.py +7 -0
- {workos-5.36.0 → workos-5.38.0}/workos/client.py +7 -0
- workos-5.38.0/workos/pipes.py +93 -0
- workos-5.38.0/workos/types/pipes/__init__.py +6 -0
- workos-5.38.0/workos/types/pipes/pipes.py +34 -0
- {workos-5.36.0 → workos-5.38.0}/workos/user_management.py +86 -0
- {workos-5.36.0 → workos-5.38.0}/workos/vault.py +29 -0
- {workos-5.36.0 → workos-5.38.0}/workos.egg-info/PKG-INFO +1 -1
- {workos-5.36.0 → workos-5.38.0}/workos.egg-info/SOURCES.txt +4 -0
- {workos-5.36.0 → workos-5.38.0}/LICENSE +0 -0
- {workos-5.36.0 → workos-5.38.0}/README.md +0 -0
- {workos-5.36.0 → workos-5.38.0}/setup.cfg +0 -0
- {workos-5.36.0 → workos-5.38.0}/setup.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_api_keys.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_async_http_client.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_audit_logs.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_client.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_directory_sync.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_events.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_fga.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_mfa.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_organization_domains.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_organizations.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_passwordless.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_portal.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_session.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_sso.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_sync_http_client.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_user_management_list_sessions.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_user_management_revoke_session.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_webhooks.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/tests/test_widgets.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/_client_configuration.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/api_keys.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/audit_logs.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/directory_sync.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/events.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/exceptions.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/fga.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/mfa.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/organization_domains.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/organizations.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/passwordless.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/portal.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/py.typed +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/session.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/sso.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/api_keys/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/api_keys/api_keys.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/audit_logs/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/audit_logs/audit_log_event.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/audit_logs/audit_log_event_actor.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/audit_logs/audit_log_event_context.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/audit_logs/audit_log_event_target.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/audit_logs/audit_log_export.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/audit_logs/audit_log_metadata.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/directory_sync/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/directory_sync/directory.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/directory_sync/directory_group.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/directory_sync/directory_state.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/directory_sync/directory_type.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/directory_sync/directory_user.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/directory_sync/list_filters.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/authentication_payload.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/connection_payload_with_legacy_fields.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/directory_group_membership_payload.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/directory_group_with_previous_attributes.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/directory_payload.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/directory_payload_with_legacy_fields.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/directory_user_with_previous_attributes.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/event.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/event_model.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/event_type.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/list_filters.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/organization_domain_verification_failed_payload.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/previous_attributes.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/events/session_payload.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/feature_flags/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/feature_flags/feature_flag.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/feature_flags/list_filters.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/fga/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/fga/authorization_resource_types.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/fga/authorization_resources.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/fga/check.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/fga/list_filters.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/fga/warnings.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/fga/warrant.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/list_resource.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/metadata.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/mfa/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/mfa/authentication_challenge.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/mfa/authentication_challenge_verification_response.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/mfa/authentication_factor.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/mfa/authentication_factor_totp_and_challenge_response.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/mfa/enroll_authentication_factor_type.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/organization_domains/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/organization_domains/organization_domain.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/organizations/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/organizations/domain_data_input.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/organizations/list_filters.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/organizations/organization.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/organizations/organization_common.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/passwordless/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/passwordless/passwordless_session.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/passwordless/passwordless_session_type.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/portal/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/portal/portal_link.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/portal/portal_link_intent.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/portal/portal_link_intent_options.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/roles/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/roles/role.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/sso/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/sso/connection.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/sso/connection_domain.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/sso/profile.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/sso/sso_provider_type.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/authenticate_with_common.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/authentication_response.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/email_verification.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/impersonator.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/invitation.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/list_filters.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/magic_auth.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/oauth_tokens.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/organization_membership.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/password_hash_type.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/password_reset.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/screen_hint.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/session.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/user.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/user_management/user_management_provider_type.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/vault/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/vault/key.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/vault/object.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/webhooks/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/webhooks/webhook.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/webhooks/webhook_model.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/webhooks/webhook_payload.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/widgets/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/widgets/widget_scope.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/widgets/widget_token_response.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/types/workos_model.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/typing/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/typing/literals.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/typing/sync_or_async.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/typing/untyped_literal.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/typing/webhooks.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/utils/__init__.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/utils/_base_http_client.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/utils/crypto_provider.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/utils/http_client.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/utils/pagination_order.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/utils/request_helper.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/webhooks.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos/widgets.py +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos.egg-info/dependency_links.txt +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos.egg-info/not-zip-safe +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos.egg-info/requires.txt +0 -0
- {workos-5.36.0 → workos-5.38.0}/workos.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from tests.utils.syncify import syncify
|
|
4
|
+
from workos.pipes import AsyncPipes, Pipes
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.sync_and_async(Pipes, AsyncPipes)
|
|
8
|
+
class TestPipes:
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def mock_access_token(self):
|
|
11
|
+
return {
|
|
12
|
+
"object": "access_token",
|
|
13
|
+
"access_token": "test_access_token_123",
|
|
14
|
+
"expires_at": "2026-01-09T12:00:00.000Z",
|
|
15
|
+
"scopes": ["read:users", "write:users"],
|
|
16
|
+
"missing_scopes": [],
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
def test_get_access_token_success_with_expiry(
|
|
20
|
+
self,
|
|
21
|
+
module_instance,
|
|
22
|
+
mock_access_token,
|
|
23
|
+
capture_and_mock_http_client_request,
|
|
24
|
+
):
|
|
25
|
+
response_body = {
|
|
26
|
+
"active": True,
|
|
27
|
+
"access_token": mock_access_token,
|
|
28
|
+
}
|
|
29
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
30
|
+
module_instance._http_client, response_body, 200
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
result = syncify(
|
|
34
|
+
module_instance.get_access_token(
|
|
35
|
+
provider="test-provider",
|
|
36
|
+
user_id="user_123",
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
assert request_kwargs["url"].endswith("data-integrations/test-provider/token")
|
|
41
|
+
assert request_kwargs["method"] == "post"
|
|
42
|
+
assert request_kwargs["json"]["user_id"] == "user_123"
|
|
43
|
+
assert result.active is True
|
|
44
|
+
assert result.access_token.access_token == mock_access_token["access_token"]
|
|
45
|
+
assert result.access_token.scopes == mock_access_token["scopes"]
|
|
46
|
+
|
|
47
|
+
def test_get_access_token_success_without_expiry(
|
|
48
|
+
self,
|
|
49
|
+
module_instance,
|
|
50
|
+
capture_and_mock_http_client_request,
|
|
51
|
+
):
|
|
52
|
+
response_body = {
|
|
53
|
+
"active": True,
|
|
54
|
+
"access_token": {
|
|
55
|
+
"object": "access_token",
|
|
56
|
+
"access_token": "test_token",
|
|
57
|
+
"expires_at": None,
|
|
58
|
+
"scopes": ["read"],
|
|
59
|
+
"missing_scopes": [],
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
capture_and_mock_http_client_request(
|
|
63
|
+
module_instance._http_client, response_body, 200
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
result = syncify(
|
|
67
|
+
module_instance.get_access_token(
|
|
68
|
+
provider="test-provider",
|
|
69
|
+
user_id="user_123",
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
assert result.active is True
|
|
74
|
+
assert result.access_token.expires_at is None
|
|
75
|
+
|
|
76
|
+
def test_get_access_token_with_organization_id(
|
|
77
|
+
self,
|
|
78
|
+
module_instance,
|
|
79
|
+
mock_access_token,
|
|
80
|
+
capture_and_mock_http_client_request,
|
|
81
|
+
):
|
|
82
|
+
response_body = {
|
|
83
|
+
"active": True,
|
|
84
|
+
"access_token": mock_access_token,
|
|
85
|
+
}
|
|
86
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
87
|
+
module_instance._http_client, response_body, 200
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
syncify(
|
|
91
|
+
module_instance.get_access_token(
|
|
92
|
+
provider="test-provider",
|
|
93
|
+
user_id="user_123",
|
|
94
|
+
organization_id="org_456",
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
assert request_kwargs["json"]["organization_id"] == "org_456"
|
|
99
|
+
|
|
100
|
+
def test_get_access_token_without_organization_id(
|
|
101
|
+
self,
|
|
102
|
+
module_instance,
|
|
103
|
+
mock_access_token,
|
|
104
|
+
capture_and_mock_http_client_request,
|
|
105
|
+
):
|
|
106
|
+
response_body = {
|
|
107
|
+
"active": True,
|
|
108
|
+
"access_token": mock_access_token,
|
|
109
|
+
}
|
|
110
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
111
|
+
module_instance._http_client, response_body, 200
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
syncify(
|
|
115
|
+
module_instance.get_access_token(
|
|
116
|
+
provider="test-provider",
|
|
117
|
+
user_id="user_123",
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
assert "organization_id" not in request_kwargs["json"]
|
|
122
|
+
|
|
123
|
+
def test_get_access_token_not_installed(
|
|
124
|
+
self,
|
|
125
|
+
module_instance,
|
|
126
|
+
capture_and_mock_http_client_request,
|
|
127
|
+
):
|
|
128
|
+
response_body = {
|
|
129
|
+
"active": False,
|
|
130
|
+
"error": "not_installed",
|
|
131
|
+
}
|
|
132
|
+
capture_and_mock_http_client_request(
|
|
133
|
+
module_instance._http_client, response_body, 200
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
result = syncify(
|
|
137
|
+
module_instance.get_access_token(
|
|
138
|
+
provider="test-provider",
|
|
139
|
+
user_id="user_123",
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
assert result.active is False
|
|
144
|
+
assert result.error == "not_installed"
|
|
145
|
+
|
|
146
|
+
def test_get_access_token_needs_reauthorization(
|
|
147
|
+
self,
|
|
148
|
+
module_instance,
|
|
149
|
+
capture_and_mock_http_client_request,
|
|
150
|
+
):
|
|
151
|
+
response_body = {
|
|
152
|
+
"active": False,
|
|
153
|
+
"error": "needs_reauthorization",
|
|
154
|
+
}
|
|
155
|
+
capture_and_mock_http_client_request(
|
|
156
|
+
module_instance._http_client, response_body, 200
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
result = syncify(
|
|
160
|
+
module_instance.get_access_token(
|
|
161
|
+
provider="test-provider",
|
|
162
|
+
user_id="user_123",
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
assert result.active is False
|
|
167
|
+
assert result.error == "needs_reauthorization"
|
|
@@ -6,6 +6,7 @@ import pytest
|
|
|
6
6
|
|
|
7
7
|
from tests.utils.fixtures.mock_auth_factor_totp import MockAuthenticationFactorTotp
|
|
8
8
|
from tests.utils.fixtures.mock_email_verification import MockEmailVerification
|
|
9
|
+
from tests.utils.fixtures.mock_feature_flag import MockFeatureFlag
|
|
9
10
|
from tests.utils.fixtures.mock_invitation import MockInvitation
|
|
10
11
|
from tests.utils.fixtures.mock_magic_auth import MockMagicAuth
|
|
11
12
|
from tests.utils.fixtures.mock_organization_membership import MockOrganizationMembership
|
|
@@ -146,6 +147,14 @@ class UserManagementFixtures:
|
|
|
146
147
|
invitations_list = [MockInvitation(id=str(i)).dict() for i in range(40)]
|
|
147
148
|
return list_response_of(data=invitations_list)
|
|
148
149
|
|
|
150
|
+
@pytest.fixture
|
|
151
|
+
def mock_feature_flags(self):
|
|
152
|
+
return {
|
|
153
|
+
"data": [MockFeatureFlag(id=f"flag_{str(i)}").dict() for i in range(2)],
|
|
154
|
+
"object": "list",
|
|
155
|
+
"list_metadata": {"before": None, "after": None},
|
|
156
|
+
}
|
|
157
|
+
|
|
149
158
|
|
|
150
159
|
class TestUserManagementBase(UserManagementFixtures):
|
|
151
160
|
@pytest.fixture(autouse=True)
|
|
@@ -1250,3 +1259,28 @@ class TestUserManagement(UserManagementFixtures):
|
|
|
1250
1259
|
|
|
1251
1260
|
with pytest.raises(Exception):
|
|
1252
1261
|
syncify(self.user_management.resend_invitation("invitation_accepted"))
|
|
1262
|
+
|
|
1263
|
+
def test_list_feature_flags(
|
|
1264
|
+
self, mock_feature_flags, capture_and_mock_http_client_request
|
|
1265
|
+
):
|
|
1266
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
1267
|
+
self.http_client, mock_feature_flags, 200
|
|
1268
|
+
)
|
|
1269
|
+
|
|
1270
|
+
feature_flags_response = syncify(
|
|
1271
|
+
self.user_management.list_feature_flags(
|
|
1272
|
+
user_id="user_01H7ZGXFP5C6BBQY6Z7277ZCT0"
|
|
1273
|
+
)
|
|
1274
|
+
)
|
|
1275
|
+
|
|
1276
|
+
def to_dict(x):
|
|
1277
|
+
return x.dict()
|
|
1278
|
+
|
|
1279
|
+
assert request_kwargs["method"] == "get"
|
|
1280
|
+
assert request_kwargs["url"].endswith(
|
|
1281
|
+
"/user_management/users/user_01H7ZGXFP5C6BBQY6Z7277ZCT0/feature-flags"
|
|
1282
|
+
)
|
|
1283
|
+
assert (
|
|
1284
|
+
list(map(to_dict, feature_flags_response.data))
|
|
1285
|
+
== mock_feature_flags["data"]
|
|
1286
|
+
)
|
|
@@ -107,6 +107,34 @@ class TestVault:
|
|
|
107
107
|
):
|
|
108
108
|
self.vault.read_object(object_id=None)
|
|
109
109
|
|
|
110
|
+
def test_read_object_by_name_success(
|
|
111
|
+
self, mock_vault_object, capture_and_mock_http_client_request
|
|
112
|
+
):
|
|
113
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
114
|
+
self.http_client, mock_vault_object, 200
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
vault_object = self.vault.read_object_by_name(name="test-secret")
|
|
118
|
+
|
|
119
|
+
assert request_kwargs["method"] == "get"
|
|
120
|
+
assert request_kwargs["url"].endswith("/vault/v1/kv/name/test-secret")
|
|
121
|
+
assert vault_object.id == "vault_01234567890abcdef"
|
|
122
|
+
assert vault_object.name == "test-secret"
|
|
123
|
+
assert vault_object.value == "secret-value"
|
|
124
|
+
assert vault_object.metadata.environment_id == "env_01234567890abcdef"
|
|
125
|
+
|
|
126
|
+
def test_read_object_by_name_missing_name(self):
|
|
127
|
+
with pytest.raises(
|
|
128
|
+
ValueError, match="Incomplete arguments: 'name' is a required argument"
|
|
129
|
+
):
|
|
130
|
+
self.vault.read_object_by_name(name="")
|
|
131
|
+
|
|
132
|
+
def test_read_object_by_name_none_name(self):
|
|
133
|
+
with pytest.raises(
|
|
134
|
+
ValueError, match="Incomplete arguments: 'name' is a required argument"
|
|
135
|
+
):
|
|
136
|
+
self.vault.read_object_by_name(name=None)
|
|
137
|
+
|
|
110
138
|
def test_list_objects_default_params(
|
|
111
139
|
self, mock_vault_objects_list, capture_and_mock_http_client_request
|
|
112
140
|
):
|
|
@@ -10,6 +10,7 @@ from workos.events import EventsModule
|
|
|
10
10
|
from workos.fga import FGAModule
|
|
11
11
|
from workos.mfa import MFAModule
|
|
12
12
|
from workos.organization_domains import OrganizationDomainsModule
|
|
13
|
+
from workos.pipes import PipesModule
|
|
13
14
|
from workos.organizations import OrganizationsModule
|
|
14
15
|
from workos.passwordless import PasswordlessModule
|
|
15
16
|
from workos.portal import PortalModule
|
|
@@ -101,6 +102,10 @@ class BaseClient(ClientConfiguration):
|
|
|
101
102
|
@abstractmethod
|
|
102
103
|
def passwordless(self) -> PasswordlessModule: ...
|
|
103
104
|
|
|
105
|
+
@property
|
|
106
|
+
@abstractmethod
|
|
107
|
+
def pipes(self) -> PipesModule: ...
|
|
108
|
+
|
|
104
109
|
@property
|
|
105
110
|
@abstractmethod
|
|
106
111
|
def portal(self) -> PortalModule: ...
|
|
@@ -10,6 +10,7 @@ from workos.mfa import MFAModule
|
|
|
10
10
|
from workos.organizations import AsyncOrganizations
|
|
11
11
|
from workos.organization_domains import AsyncOrganizationDomains
|
|
12
12
|
from workos.passwordless import PasswordlessModule
|
|
13
|
+
from workos.pipes import AsyncPipes
|
|
13
14
|
from workos.portal import PortalModule
|
|
14
15
|
from workos.sso import AsyncSSO
|
|
15
16
|
from workos.user_management import AsyncUserManagement
|
|
@@ -102,6 +103,12 @@ class AsyncClient(BaseClient):
|
|
|
102
103
|
"Passwordless APIs are not yet supported in the async client."
|
|
103
104
|
)
|
|
104
105
|
|
|
106
|
+
@property
|
|
107
|
+
def pipes(self) -> AsyncPipes:
|
|
108
|
+
if not getattr(self, "_pipes", None):
|
|
109
|
+
self._pipes = AsyncPipes(self._http_client)
|
|
110
|
+
return self._pipes
|
|
111
|
+
|
|
105
112
|
@property
|
|
106
113
|
def portal(self) -> PortalModule:
|
|
107
114
|
raise NotImplementedError(
|
|
@@ -8,6 +8,7 @@ from workos.fga import FGA
|
|
|
8
8
|
from workos.organizations import Organizations
|
|
9
9
|
from workos.organization_domains import OrganizationDomains
|
|
10
10
|
from workos.passwordless import Passwordless
|
|
11
|
+
from workos.pipes import Pipes
|
|
11
12
|
from workos.portal import Portal
|
|
12
13
|
from workos.sso import SSO
|
|
13
14
|
from workos.webhooks import Webhooks
|
|
@@ -102,6 +103,12 @@ class SyncClient(BaseClient):
|
|
|
102
103
|
self._passwordless = Passwordless(self._http_client)
|
|
103
104
|
return self._passwordless
|
|
104
105
|
|
|
106
|
+
@property
|
|
107
|
+
def pipes(self) -> Pipes:
|
|
108
|
+
if not getattr(self, "_pipes", None):
|
|
109
|
+
self._pipes = Pipes(self._http_client)
|
|
110
|
+
return self._pipes
|
|
111
|
+
|
|
105
112
|
@property
|
|
106
113
|
def portal(self) -> Portal:
|
|
107
114
|
if not getattr(self, "_portal", None):
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from typing import Dict, Optional, Protocol
|
|
2
|
+
|
|
3
|
+
from workos.types.pipes import (
|
|
4
|
+
GetAccessTokenFailureResponse,
|
|
5
|
+
GetAccessTokenResponse,
|
|
6
|
+
GetAccessTokenSuccessResponse,
|
|
7
|
+
)
|
|
8
|
+
from workos.typing.sync_or_async import SyncOrAsync
|
|
9
|
+
from workos.utils.http_client import AsyncHTTPClient, SyncHTTPClient
|
|
10
|
+
from workos.utils.request_helper import REQUEST_METHOD_POST
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PipesModule(Protocol):
|
|
14
|
+
"""Protocol defining the Pipes module interface."""
|
|
15
|
+
|
|
16
|
+
def get_access_token(
|
|
17
|
+
self,
|
|
18
|
+
*,
|
|
19
|
+
provider: str,
|
|
20
|
+
user_id: str,
|
|
21
|
+
organization_id: Optional[str] = None,
|
|
22
|
+
) -> SyncOrAsync[GetAccessTokenResponse]:
|
|
23
|
+
"""Retrieve an access token for a third-party provider.
|
|
24
|
+
|
|
25
|
+
Kwargs:
|
|
26
|
+
provider (str): The third-party provider identifier
|
|
27
|
+
user_id (str): The WorkOS user ID
|
|
28
|
+
organization_id (str, optional): The WorkOS organization ID
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
GetAccessTokenResponse: Success response with token or failure response with error
|
|
32
|
+
"""
|
|
33
|
+
...
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Pipes(PipesModule):
|
|
37
|
+
"""Sync implementation of the Pipes module."""
|
|
38
|
+
|
|
39
|
+
_http_client: SyncHTTPClient
|
|
40
|
+
|
|
41
|
+
def __init__(self, http_client: SyncHTTPClient):
|
|
42
|
+
self._http_client = http_client
|
|
43
|
+
|
|
44
|
+
def get_access_token(
|
|
45
|
+
self,
|
|
46
|
+
*,
|
|
47
|
+
provider: str,
|
|
48
|
+
user_id: str,
|
|
49
|
+
organization_id: Optional[str] = None,
|
|
50
|
+
) -> GetAccessTokenResponse:
|
|
51
|
+
json_data: Dict[str, str] = {"user_id": user_id}
|
|
52
|
+
if organization_id is not None:
|
|
53
|
+
json_data["organization_id"] = organization_id
|
|
54
|
+
|
|
55
|
+
response = self._http_client.request(
|
|
56
|
+
f"data-integrations/{provider}/token",
|
|
57
|
+
method=REQUEST_METHOD_POST,
|
|
58
|
+
json=json_data,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if response.get("active") is True:
|
|
62
|
+
return GetAccessTokenSuccessResponse.model_validate(response)
|
|
63
|
+
return GetAccessTokenFailureResponse.model_validate(response)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class AsyncPipes(PipesModule):
|
|
67
|
+
"""Async implementation of the Pipes module."""
|
|
68
|
+
|
|
69
|
+
_http_client: AsyncHTTPClient
|
|
70
|
+
|
|
71
|
+
def __init__(self, http_client: AsyncHTTPClient):
|
|
72
|
+
self._http_client = http_client
|
|
73
|
+
|
|
74
|
+
async def get_access_token(
|
|
75
|
+
self,
|
|
76
|
+
*,
|
|
77
|
+
provider: str,
|
|
78
|
+
user_id: str,
|
|
79
|
+
organization_id: Optional[str] = None,
|
|
80
|
+
) -> GetAccessTokenResponse:
|
|
81
|
+
json_data: Dict[str, str] = {"user_id": user_id}
|
|
82
|
+
if organization_id is not None:
|
|
83
|
+
json_data["organization_id"] = organization_id
|
|
84
|
+
|
|
85
|
+
response = await self._http_client.request(
|
|
86
|
+
f"data-integrations/{provider}/token",
|
|
87
|
+
method=REQUEST_METHOD_POST,
|
|
88
|
+
json=json_data,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if response.get("active") is True:
|
|
92
|
+
return GetAccessTokenSuccessResponse.model_validate(response)
|
|
93
|
+
return GetAccessTokenFailureResponse.model_validate(response)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Literal, Optional, Sequence, Union
|
|
3
|
+
|
|
4
|
+
from workos.types.workos_model import WorkOSModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AccessToken(WorkOSModel):
|
|
8
|
+
"""Represents an OAuth access token for a third-party provider."""
|
|
9
|
+
|
|
10
|
+
object: Literal["access_token"]
|
|
11
|
+
access_token: str
|
|
12
|
+
expires_at: Optional[datetime] = None
|
|
13
|
+
scopes: Sequence[str]
|
|
14
|
+
missing_scopes: Sequence[str]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class GetAccessTokenSuccessResponse(WorkOSModel):
|
|
18
|
+
"""Successful response containing the access token."""
|
|
19
|
+
|
|
20
|
+
active: Literal[True]
|
|
21
|
+
access_token: AccessToken
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class GetAccessTokenFailureResponse(WorkOSModel):
|
|
25
|
+
"""Failed response indicating why the token couldn't be retrieved."""
|
|
26
|
+
|
|
27
|
+
active: Literal[False]
|
|
28
|
+
error: Literal["not_installed", "needs_reauthorization"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
GetAccessTokenResponse = Union[
|
|
32
|
+
GetAccessTokenSuccessResponse,
|
|
33
|
+
GetAccessTokenFailureResponse,
|
|
34
|
+
]
|
|
@@ -2,6 +2,8 @@ from typing import Awaitable, Optional, Protocol, Sequence, Type, Union, cast
|
|
|
2
2
|
from urllib.parse import urlencode
|
|
3
3
|
from workos._client_configuration import ClientConfiguration
|
|
4
4
|
from workos.session import AsyncSession, Session
|
|
5
|
+
from workos.types.feature_flags import FeatureFlag
|
|
6
|
+
from workos.types.feature_flags.list_filters import FeatureFlagListFilters
|
|
5
7
|
from workos.types.list_resource import (
|
|
6
8
|
ListArgs,
|
|
7
9
|
ListMetadata,
|
|
@@ -97,6 +99,7 @@ INVITATION_REVOKE_PATH = "user_management/invitations/{0}/revoke"
|
|
|
97
99
|
INVITATION_RESEND_PATH = "user_management/invitations/{0}/resend"
|
|
98
100
|
PASSWORD_RESET_PATH = "user_management/password_reset"
|
|
99
101
|
PASSWORD_RESET_DETAIL_PATH = "user_management/password_reset/{0}"
|
|
102
|
+
USER_FEATURE_FLAGS_PATH = "user_management/users/{0}/feature-flags"
|
|
100
103
|
|
|
101
104
|
|
|
102
105
|
UsersListResource = WorkOSListResource[User, UsersListFilters, ListMetadata]
|
|
@@ -113,6 +116,10 @@ InvitationsListResource = WorkOSListResource[
|
|
|
113
116
|
Invitation, InvitationsListFilters, ListMetadata
|
|
114
117
|
]
|
|
115
118
|
|
|
119
|
+
FeatureFlagsListResource = WorkOSListResource[
|
|
120
|
+
FeatureFlag, FeatureFlagListFilters, ListMetadata
|
|
121
|
+
]
|
|
122
|
+
|
|
116
123
|
from workos.types.user_management.list_filters import SessionsListFilters
|
|
117
124
|
|
|
118
125
|
SessionsListResource = WorkOSListResource[
|
|
@@ -908,6 +915,29 @@ class UserManagementModule(Protocol):
|
|
|
908
915
|
"""
|
|
909
916
|
...
|
|
910
917
|
|
|
918
|
+
def list_feature_flags(
|
|
919
|
+
self,
|
|
920
|
+
user_id: str,
|
|
921
|
+
*,
|
|
922
|
+
limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
|
|
923
|
+
before: Optional[str] = None,
|
|
924
|
+
after: Optional[str] = None,
|
|
925
|
+
order: PaginationOrder = "desc",
|
|
926
|
+
) -> SyncOrAsync[FeatureFlagsListResource]:
|
|
927
|
+
"""Retrieve a list of feature flags for a user
|
|
928
|
+
|
|
929
|
+
Args:
|
|
930
|
+
user_id (str): User's unique identifier
|
|
931
|
+
limit (int): Maximum number of records to return. (Optional)
|
|
932
|
+
before (str): Pagination cursor to receive records before a provided Feature Flag ID. (Optional)
|
|
933
|
+
after (str): Pagination cursor to receive records after a provided Feature Flag ID. (Optional)
|
|
934
|
+
order (Literal["asc","desc"]): Sort records in either ascending or descending (default) order by created_at timestamp. (Optional)
|
|
935
|
+
|
|
936
|
+
Returns:
|
|
937
|
+
FeatureFlagsListResource: Feature flags list response from WorkOS.
|
|
938
|
+
"""
|
|
939
|
+
...
|
|
940
|
+
|
|
911
941
|
|
|
912
942
|
class UserManagement(UserManagementModule):
|
|
913
943
|
_http_client: SyncHTTPClient
|
|
@@ -1603,6 +1633,34 @@ class UserManagement(UserManagementModule):
|
|
|
1603
1633
|
|
|
1604
1634
|
return Invitation.model_validate(response)
|
|
1605
1635
|
|
|
1636
|
+
def list_feature_flags(
|
|
1637
|
+
self,
|
|
1638
|
+
user_id: str,
|
|
1639
|
+
*,
|
|
1640
|
+
limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
|
|
1641
|
+
before: Optional[str] = None,
|
|
1642
|
+
after: Optional[str] = None,
|
|
1643
|
+
order: PaginationOrder = "desc",
|
|
1644
|
+
) -> FeatureFlagsListResource:
|
|
1645
|
+
list_params: FeatureFlagListFilters = {
|
|
1646
|
+
"limit": limit,
|
|
1647
|
+
"before": before,
|
|
1648
|
+
"after": after,
|
|
1649
|
+
"order": order,
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
response = self._http_client.request(
|
|
1653
|
+
USER_FEATURE_FLAGS_PATH.format(user_id),
|
|
1654
|
+
method=REQUEST_METHOD_GET,
|
|
1655
|
+
params=list_params,
|
|
1656
|
+
)
|
|
1657
|
+
|
|
1658
|
+
return FeatureFlagsListResource(
|
|
1659
|
+
list_method=self.list_feature_flags,
|
|
1660
|
+
list_args=list_params,
|
|
1661
|
+
**ListPage[FeatureFlag](**response).model_dump(),
|
|
1662
|
+
)
|
|
1663
|
+
|
|
1606
1664
|
|
|
1607
1665
|
class AsyncUserManagement(UserManagementModule):
|
|
1608
1666
|
_http_client: AsyncHTTPClient
|
|
@@ -2312,3 +2370,31 @@ class AsyncUserManagement(UserManagementModule):
|
|
|
2312
2370
|
)
|
|
2313
2371
|
|
|
2314
2372
|
return Invitation.model_validate(response)
|
|
2373
|
+
|
|
2374
|
+
async def list_feature_flags(
|
|
2375
|
+
self,
|
|
2376
|
+
user_id: str,
|
|
2377
|
+
*,
|
|
2378
|
+
limit: int = DEFAULT_LIST_RESPONSE_LIMIT,
|
|
2379
|
+
before: Optional[str] = None,
|
|
2380
|
+
after: Optional[str] = None,
|
|
2381
|
+
order: PaginationOrder = "desc",
|
|
2382
|
+
) -> FeatureFlagsListResource:
|
|
2383
|
+
list_params: FeatureFlagListFilters = {
|
|
2384
|
+
"limit": limit,
|
|
2385
|
+
"before": before,
|
|
2386
|
+
"after": after,
|
|
2387
|
+
"order": order,
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
response = await self._http_client.request(
|
|
2391
|
+
USER_FEATURE_FLAGS_PATH.format(user_id),
|
|
2392
|
+
method=REQUEST_METHOD_GET,
|
|
2393
|
+
params=list_params,
|
|
2394
|
+
)
|
|
2395
|
+
|
|
2396
|
+
return FeatureFlagsListResource(
|
|
2397
|
+
list_method=self.list_feature_flags,
|
|
2398
|
+
list_args=list_params,
|
|
2399
|
+
**ListPage[FeatureFlag](**response).model_dump(),
|
|
2400
|
+
)
|
|
@@ -37,6 +37,17 @@ class VaultModule(Protocol):
|
|
|
37
37
|
"""
|
|
38
38
|
...
|
|
39
39
|
|
|
40
|
+
def read_object_by_name(self, *, name: str) -> VaultObject:
|
|
41
|
+
"""
|
|
42
|
+
Get a Vault object by name with the value decrypted.
|
|
43
|
+
|
|
44
|
+
Kwargs:
|
|
45
|
+
name (str): The unique name of the object.
|
|
46
|
+
Returns:
|
|
47
|
+
VaultObject: A vault object with metadata, name and decrypted value.
|
|
48
|
+
"""
|
|
49
|
+
...
|
|
50
|
+
|
|
40
51
|
def list_objects(
|
|
41
52
|
self,
|
|
42
53
|
*,
|
|
@@ -230,6 +241,24 @@ class Vault(VaultModule):
|
|
|
230
241
|
|
|
231
242
|
return VaultObject.model_validate(response)
|
|
232
243
|
|
|
244
|
+
def read_object_by_name(
|
|
245
|
+
self,
|
|
246
|
+
*,
|
|
247
|
+
name: str,
|
|
248
|
+
) -> VaultObject:
|
|
249
|
+
if not name:
|
|
250
|
+
raise ValueError("Incomplete arguments: 'name' is a required argument")
|
|
251
|
+
|
|
252
|
+
response = self._http_client.request(
|
|
253
|
+
RequestHelper.build_parameterized_url(
|
|
254
|
+
"vault/v1/kv/name/{name}",
|
|
255
|
+
name=name,
|
|
256
|
+
),
|
|
257
|
+
method=REQUEST_METHOD_GET,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
return VaultObject.model_validate(response)
|
|
261
|
+
|
|
233
262
|
def list_objects(
|
|
234
263
|
self,
|
|
235
264
|
*,
|
|
@@ -12,6 +12,7 @@ tests/test_mfa.py
|
|
|
12
12
|
tests/test_organization_domains.py
|
|
13
13
|
tests/test_organizations.py
|
|
14
14
|
tests/test_passwordless.py
|
|
15
|
+
tests/test_pipes.py
|
|
15
16
|
tests/test_portal.py
|
|
16
17
|
tests/test_session.py
|
|
17
18
|
tests/test_sso.py
|
|
@@ -38,6 +39,7 @@ workos/mfa.py
|
|
|
38
39
|
workos/organization_domains.py
|
|
39
40
|
workos/organizations.py
|
|
40
41
|
workos/passwordless.py
|
|
42
|
+
workos/pipes.py
|
|
41
43
|
workos/portal.py
|
|
42
44
|
workos/py.typed
|
|
43
45
|
workos/session.py
|
|
@@ -113,6 +115,8 @@ workos/types/organizations/organization_common.py
|
|
|
113
115
|
workos/types/passwordless/__init__.py
|
|
114
116
|
workos/types/passwordless/passwordless_session.py
|
|
115
117
|
workos/types/passwordless/passwordless_session_type.py
|
|
118
|
+
workos/types/pipes/__init__.py
|
|
119
|
+
workos/types/pipes/pipes.py
|
|
116
120
|
workos/types/portal/__init__.py
|
|
117
121
|
workos/types/portal/portal_link.py
|
|
118
122
|
workos/types/portal/portal_link_intent.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|