workosv2 2.15.0
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.
- checksums.yaml +7 -0
- data/.github/CODEOWNERS +5 -0
- data/.github/pull_request_template.md +11 -0
- data/.github/renovate.json +5 -0
- data/.gitignore +49 -0
- data/.rspec +1 -0
- data/.rubocop.yml +24 -0
- data/.ruby-version +1 -0
- data/.semaphore/rubygems.yml +24 -0
- data/.semaphore/semaphore.yml +51 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +126 -0
- data/Gemfile.lock.old +127 -0
- data/LICENSE +21 -0
- data/README.md +53 -0
- data/bin/build +3 -0
- data/bin/console +3 -0
- data/bin/docs +5 -0
- data/bin/publish +3 -0
- data/bin/tapioca +29 -0
- data/codecov.yml +12 -0
- data/docs/WorkOS/APIError.html +160 -0
- data/docs/WorkOS/AuditLog.html +235 -0
- data/docs/WorkOS/AuditTrail.html +235 -0
- data/docs/WorkOS/AuthenticationError.html +160 -0
- data/docs/WorkOS/Base.html +287 -0
- data/docs/WorkOS/Client.html +504 -0
- data/docs/WorkOS/InvalidRequestError.html +160 -0
- data/docs/WorkOS/Profile.html +788 -0
- data/docs/WorkOS/RequestError.html +135 -0
- data/docs/WorkOS/SSO.html +691 -0
- data/docs/WorkOS/Types/ProfileStruct.html +135 -0
- data/docs/WorkOS/Types/Provider.html +135 -0
- data/docs/WorkOS/Types.html +128 -0
- data/docs/WorkOS/WorkOSError.html +447 -0
- data/docs/WorkOS.html +324 -0
- data/docs/class_list.html +51 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +496 -0
- data/docs/file.README.html +252 -0
- data/docs/file_list.html +56 -0
- data/docs/frames.html +17 -0
- data/docs/index.html +250 -0
- data/docs/js/app.js +314 -0
- data/docs/js/full_list.js +216 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +267 -0
- data/docs/top-level-namespace.html +110 -0
- data/lib/workosv2/audit_log_export.rb +55 -0
- data/lib/workosv2/audit_logs.rb +114 -0
- data/lib/workosv2/audit_trail.rb +111 -0
- data/lib/workosv2/challenge.rb +55 -0
- data/lib/workosv2/client.rb +186 -0
- data/lib/workosv2/configuration.rb +17 -0
- data/lib/workosv2/connection.rb +66 -0
- data/lib/workosv2/deprecated_hash_wrapper.rb +76 -0
- data/lib/workosv2/directory.rb +65 -0
- data/lib/workosv2/directory_group.rb +68 -0
- data/lib/workosv2/directory_sync.rb +218 -0
- data/lib/workosv2/directory_user.rb +97 -0
- data/lib/workosv2/errors.rb +81 -0
- data/lib/workosv2/event.rb +51 -0
- data/lib/workosv2/events.rb +52 -0
- data/lib/workosv2/factor.rb +54 -0
- data/lib/workosv2/hash_provider.rb +19 -0
- data/lib/workosv2/mfa.rb +178 -0
- data/lib/workosv2/organization.rb +57 -0
- data/lib/workosv2/organizations.rb +188 -0
- data/lib/workosv2/passwordless.rb +85 -0
- data/lib/workosv2/portal.rb +66 -0
- data/lib/workosv2/profile.rb +76 -0
- data/lib/workosv2/profile_and_token.rb +29 -0
- data/lib/workosv2/sso.rb +297 -0
- data/lib/workosv2/types/audit_log_export_struct.rb +17 -0
- data/lib/workosv2/types/challenge_struct.rb +18 -0
- data/lib/workosv2/types/connection_struct.rb +20 -0
- data/lib/workosv2/types/directory_group_struct.rb +19 -0
- data/lib/workosv2/types/directory_struct.rb +19 -0
- data/lib/workosv2/types/directory_user_struct.rb +26 -0
- data/lib/workosv2/types/event_struct.rb +15 -0
- data/lib/workosv2/types/factor_struct.rb +18 -0
- data/lib/workosv2/types/intent_enum.rb +17 -0
- data/lib/workosv2/types/list_struct.rb +13 -0
- data/lib/workosv2/types/organization_struct.rb +17 -0
- data/lib/workosv2/types/passwordless_session_struct.rb +17 -0
- data/lib/workosv2/types/profile_struct.rb +21 -0
- data/lib/workosv2/types/provider_enum.rb +15 -0
- data/lib/workosv2/types/verify_challenge_struct.rb +13 -0
- data/lib/workosv2/types/webhook_struct.rb +15 -0
- data/lib/workosv2/types.rb +25 -0
- data/lib/workosv2/verify_challenge.rb +39 -0
- data/lib/workosv2/version.rb +6 -0
- data/lib/workosv2/webhook.rb +51 -0
- data/lib/workosv2/webhooks.rb +217 -0
- data/lib/workosv2.rb +79 -0
- data/sorbet/config +2 -0
- data/sorbet/rbi/gems/addressable@2.8.0.rbi +290 -0
- data/sorbet/rbi/gems/ast@2.4.2.rbi +54 -0
- data/sorbet/rbi/gems/codecov@0.2.12.rbi +55 -0
- data/sorbet/rbi/gems/coderay@1.1.3.rbi +8 -0
- data/sorbet/rbi/gems/crack@0.4.5.rbi +57 -0
- data/sorbet/rbi/gems/diff-lcs@1.4.4.rbi +185 -0
- data/sorbet/rbi/gems/docile@1.3.5.rbi +54 -0
- data/sorbet/rbi/gems/hashdiff@1.0.1.rbi +82 -0
- data/sorbet/rbi/gems/json@2.5.1.rbi +109 -0
- data/sorbet/rbi/gems/method_source@1.0.0.rbi +8 -0
- data/sorbet/rbi/gems/parallel@1.20.1.rbi +113 -0
- data/sorbet/rbi/gems/parser@3.0.1.0.rbi +1187 -0
- data/sorbet/rbi/gems/pry@0.14.2.rbi +8 -0
- data/sorbet/rbi/gems/public_suffix@4.0.6.rbi +146 -0
- data/sorbet/rbi/gems/rainbow@3.0.0.rbi +153 -0
- data/sorbet/rbi/gems/rake@13.0.3.rbi +807 -0
- data/sorbet/rbi/gems/rbi@0.0.16.rbi +2118 -0
- data/sorbet/rbi/gems/regexp_parser@2.1.1.rbi +1117 -0
- data/sorbet/rbi/gems/rexml@3.2.5.rbi +709 -0
- data/sorbet/rbi/gems/rspec-core@3.9.3.rbi +2467 -0
- data/sorbet/rbi/gems/rspec-expectations@3.9.4.rbi +1569 -0
- data/sorbet/rbi/gems/rspec-mocks@3.9.1.rbi +1493 -0
- data/sorbet/rbi/gems/rspec-support@3.9.4.rbi +511 -0
- data/sorbet/rbi/gems/rspec@3.9.0.rbi +38 -0
- data/sorbet/rbi/gems/rubocop-ast@1.4.1.rbi +1881 -0
- data/sorbet/rbi/gems/rubocop@0.93.1.rbi +11497 -0
- data/sorbet/rbi/gems/ruby-progressbar@1.11.0.rbi +405 -0
- data/sorbet/rbi/gems/simplecov-html@0.12.3.rbi +89 -0
- data/sorbet/rbi/gems/simplecov@0.21.2.rbi +577 -0
- data/sorbet/rbi/gems/simplecov_json_formatter@0.1.2.rbi +8 -0
- data/sorbet/rbi/gems/spoom@1.1.15.rbi +1549 -0
- data/sorbet/rbi/gems/tapioca@0.7.3.rbi +1718 -0
- data/sorbet/rbi/gems/thor@1.2.1.rbi +844 -0
- data/sorbet/rbi/gems/unicode-display_width@1.7.0.rbi +22 -0
- data/sorbet/rbi/gems/unparser@0.6.2.rbi +8 -0
- data/sorbet/rbi/gems/vcr@5.0.0.rbi +699 -0
- data/sorbet/rbi/gems/webmock@3.12.2.rbi +662 -0
- data/sorbet/rbi/gems/yard-sorbet@0.8.0.rbi +268 -0
- data/sorbet/rbi/gems/yard@0.9.26.rbi +4048 -0
- data/sorbet/tapioca/config.yml +13 -0
- data/sorbet/tapioca/require.rb +4 -0
- data/spec/lib/workos/audit_logs_spec.rb +151 -0
- data/spec/lib/workos/audit_trail_spec.rb +146 -0
- data/spec/lib/workos/configuration_spec.rb +61 -0
- data/spec/lib/workos/directory_sync_spec.rb +492 -0
- data/spec/lib/workos/directory_user_spec.rb +36 -0
- data/spec/lib/workos/event_spec.rb +88 -0
- data/spec/lib/workos/mfa_spec.rb +281 -0
- data/spec/lib/workos/organizations_spec.rb +257 -0
- data/spec/lib/workos/passwordless_spec.rb +77 -0
- data/spec/lib/workos/portal_spec.rb +87 -0
- data/spec/lib/workos/sso_spec.rb +650 -0
- data/spec/lib/workos/webhooks_spec.rb +236 -0
- data/spec/spec_helper.rb +56 -0
- data/spec/support/fixtures/vcr_cassettes/audit_logs/create_event.yml +59 -0
- data/spec/support/fixtures/vcr_cassettes/audit_logs/create_event_custom_idempotency_key.yml +60 -0
- data/spec/support/fixtures/vcr_cassettes/audit_logs/create_event_invalid.yml +59 -0
- data/spec/support/fixtures/vcr_cassettes/audit_logs/create_export.yml +76 -0
- data/spec/support/fixtures/vcr_cassettes/audit_logs/create_export_with_filters.yml +77 -0
- data/spec/support/fixtures/vcr_cassettes/audit_logs/get_export.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/audit_trail/create_event.yml +65 -0
- data/spec/support/fixtures/vcr_cassettes/audit_trail/create_event_custom_idempotency_key.yml +67 -0
- data/spec/support/fixtures/vcr_cassettes/audit_trail/create_event_invalid.yml +68 -0
- data/spec/support/fixtures/vcr_cassettes/audit_trail/create_events_duplicate_idempotency_key_and_payload.yml +131 -0
- data/spec/support/fixtures/vcr_cassettes/audit_trail/create_events_duplicate_idempotency_key_different_payload.yml +134 -0
- data/spec/support/fixtures/vcr_cassettes/audit_trail/get_events.yml +61 -0
- data/spec/support/fixtures/vcr_cassettes/base/execute_request_unauthenticated.yml +66 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/delete_directory.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_directory_with_invalid_id.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_directory_with_valid_id.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_group.yml +80 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_group_with_invalid_id.yml +62 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_user.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/get_user_with_invalid_id.yml +62 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_after.yml +87 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_before.yml +89 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_domain.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_limit.yml +85 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_no_options.yml +93 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_directories/with_search.yml +85 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_after.yml +90 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_before.yml +90 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_directory.yml +90 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_limit.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_no_options.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_groups/with_user.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_after.yml +186 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_before.yml +88 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_directory.yml +194 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_group.yml +186 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_limit.yml +189 -0
- data/spec/support/fixtures/vcr_cassettes/directory_sync/list_users/with_no_options.yml +74 -0
- data/spec/support/fixtures/vcr_cassettes/events/list_events_with_after.yml +80 -0
- data/spec/support/fixtures/vcr_cassettes/events/list_events_with_event.yml +80 -0
- data/spec/support/fixtures/vcr_cassettes/events/list_events_with_no_options.yml +80 -0
- data/spec/support/fixtures/vcr_cassettes/events/list_events_with_range.yml +80 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_generic_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_sms_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/challenge_factor_totp_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/delete_factor.yml +80 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_generic_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_sms_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/enroll_factor_totp_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/get_factor_invalid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/get_factor_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/verify_challenge_generic_expired.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/verify_challenge_generic_invalid.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/verify_challenge_generic_valid.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/mfa/verify_challenge_generic_valid_is_false.yml +82 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create_invalid.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create_with_duplicate_idempotency_key_and_different_payload.yml +155 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create_with_duplicate_idempotency_key_and_payload.yml +154 -0
- data/spec/support/fixtures/vcr_cassettes/organization/create_with_idempotency_key.yml +79 -0
- data/spec/support/fixtures/vcr_cassettes/organization/delete.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/organization/delete_invalid.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/organization/get.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/organization/get_invalid.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/organization/list.yml +87 -0
- data/spec/support/fixtures/vcr_cassettes/organization/update.yml +84 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/create_session.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/create_session_invalid.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/send_session.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/passwordless/send_session_invalid.yml +73 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link_audit_logs.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link_dsync.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link_invalid.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/portal/generate_link_sso.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/sso/delete_connection_with_invalid_id.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/sso/delete_connection_with_valid_id.yml +70 -0
- data/spec/support/fixtures/vcr_cassettes/sso/get_connection_with_invalid_id.yml +72 -0
- data/spec/support/fixtures/vcr_cassettes/sso/get_connection_with_valid_id.yml +86 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_after.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_before.yml +86 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_connection_type.yml +90 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_domain.yml +86 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_limit.yml +83 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_no_options.yml +89 -0
- data/spec/support/fixtures/vcr_cassettes/sso/list_connections/with_organization_id.yml +86 -0
- data/spec/support/fixtures/vcr_cassettes/sso/profile.yml +74 -0
- data/spec/support/profile.txt +1 -0
- data/spec/support/shared_examples/client_spec.rb +30 -0
- data/spec/support/webhook_payload.txt +1 -0
- data/workosv2.gemspec +38 -0
- metadata +531 -0
@@ -0,0 +1,281 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
describe WorkOSV2::MFA do
|
5
|
+
it_behaves_like 'client'
|
6
|
+
|
7
|
+
describe '.enroll_factor' do
|
8
|
+
context 'with valid generic argument' do
|
9
|
+
it 'returns a valid factor object' do
|
10
|
+
VCR.use_cassette 'mfa/enroll_factor_generic_valid' do
|
11
|
+
factor = described_class.enroll_factor(
|
12
|
+
type: 'generic_otp',
|
13
|
+
)
|
14
|
+
expect(factor.type == 'generic_otp')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with valid totp arguments' do
|
20
|
+
it 'returns a valid factor object' do
|
21
|
+
VCR.use_cassette 'mfa/enroll_factor_totp_valid' do
|
22
|
+
factor = described_class.enroll_factor(
|
23
|
+
type: 'totp',
|
24
|
+
totp_issuer: 'WorkOSV2',
|
25
|
+
totp_user: 'some_user',
|
26
|
+
)
|
27
|
+
expect(factor.totp.instance_of?(Hash))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'with valid sms arguments' do
|
33
|
+
it 'returns a valid factor object' do
|
34
|
+
VCR.use_cassette 'mfa/enroll_factor_sms_valid' do
|
35
|
+
factor = described_class.enroll_factor(
|
36
|
+
type: 'sms',
|
37
|
+
phone_number: '55555555555',
|
38
|
+
)
|
39
|
+
expect(factor.sms.instance_of?(Hash))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when type is not sms or totp' do
|
45
|
+
it 'returns an error' do
|
46
|
+
expect do
|
47
|
+
described_class.enroll_factor(
|
48
|
+
type: 'invalid',
|
49
|
+
phone_number: '+15005550006',
|
50
|
+
)
|
51
|
+
end.to raise_error(
|
52
|
+
ArgumentError,
|
53
|
+
"Type argument must be either 'sms' or 'totp'",
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when type is totp but missing arguments' do
|
59
|
+
it 'returns an error' do
|
60
|
+
expect do
|
61
|
+
described_class.enroll_factor(
|
62
|
+
type: 'totp',
|
63
|
+
totp_issuer: 'WorkOSV2',
|
64
|
+
)
|
65
|
+
end.to raise_error(
|
66
|
+
ArgumentError,
|
67
|
+
'Incomplete arguments. Need to specify both totp_issuer and totp_user when type is totp',
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
context 'when type is sms and phone number is nil' do
|
72
|
+
it 'returns an error' do
|
73
|
+
expect do
|
74
|
+
described_class.enroll_factor(
|
75
|
+
type: 'sms',
|
76
|
+
)
|
77
|
+
end.to raise_error(
|
78
|
+
ArgumentError,
|
79
|
+
'Incomplete arguments. Need to specify phone_number when type is sms',
|
80
|
+
)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '.challenge_factor' do
|
86
|
+
context 'challenge with totp' do
|
87
|
+
it 'returns challenge factor object for totp' do
|
88
|
+
VCR.use_cassette 'mfa/challenge_factor_totp_valid' do
|
89
|
+
challenge_factor = described_class.challenge_factor(
|
90
|
+
authentication_factor_id: 'auth_factor_01FZ4TS0MWPZR7GATS7KCXANQZ',
|
91
|
+
)
|
92
|
+
expect(challenge_factor.authentication_factor_id.class.instance_of?(String))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'challenge with sms' do
|
98
|
+
it 'returns a challenge factor object for sms' do
|
99
|
+
VCR.use_cassette 'mfa/challenge_factor_sms_valid' do
|
100
|
+
challenge_factor = described_class.challenge_factor(
|
101
|
+
authentication_factor_id: 'auth_factor_01FZ4TS14D1PHFNZ9GF6YD8M1F',
|
102
|
+
sms_template: 'Your code is {{code}}',
|
103
|
+
)
|
104
|
+
expect(challenge_factor.authentication_factor_id.instance_of?(String))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'challenge with generic' do
|
110
|
+
it 'returns a valid challenge factor object for generic otp' do
|
111
|
+
VCR.use_cassette 'mfa/challenge_factor_generic_valid' do
|
112
|
+
challenge_factor = described_class.challenge_factor(
|
113
|
+
authentication_factor_id: 'auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M',
|
114
|
+
)
|
115
|
+
expect(challenge_factor.code.instance_of?(String))
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'challenge with totp mssing authentication_factor_id' do
|
121
|
+
it 'returns argument error' do
|
122
|
+
expect do
|
123
|
+
described_class.challenge_factor
|
124
|
+
end.to raise_error(
|
125
|
+
ArgumentError,
|
126
|
+
"Incomplete arguments: 'authentication_factor_id' is a required argument",
|
127
|
+
)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe '.verify_factor' do
|
133
|
+
it 'throws a warning' do
|
134
|
+
expect do
|
135
|
+
VCR.use_cassette 'mfa/verify_challenge_generic_valid' do
|
136
|
+
described_class.verify_factor(
|
137
|
+
authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
|
138
|
+
code: '897792',
|
139
|
+
)
|
140
|
+
end
|
141
|
+
end.to output("[DEPRECATION] `verify_factor` is deprecated. Please use `verify_challenge` instead.\n").to_stderr
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'calls verify_challenge' do
|
145
|
+
VCR.use_cassette 'mfa/verify_challenge_generic_valid' do
|
146
|
+
verify_factor = described_class.verify_factor(
|
147
|
+
authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
|
148
|
+
code: '897792',
|
149
|
+
)
|
150
|
+
expect(verify_factor.valid == 'true')
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe '.verify_challenge' do
|
156
|
+
context 'with generic otp' do
|
157
|
+
context 'and the challenge has not been verified' do
|
158
|
+
it 'returns true if the code is correct' do
|
159
|
+
VCR.use_cassette 'mfa/verify_challenge_generic_valid' do
|
160
|
+
verify_challenge = described_class.verify_challenge(
|
161
|
+
authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
|
162
|
+
code: '897792',
|
163
|
+
)
|
164
|
+
expect(verify_challenge.valid == 'true')
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'returns false if the code is incorrect' do
|
169
|
+
VCR.use_cassette 'mfa/verify_challenge_generic_valid_is_false' do
|
170
|
+
verify_challenge = described_class.verify_challenge(
|
171
|
+
authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
|
172
|
+
code: '897792',
|
173
|
+
)
|
174
|
+
expect(verify_challenge.valid == 'false')
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'and the challenge has already been verified' do
|
180
|
+
it 'returns an error' do
|
181
|
+
VCR.use_cassette 'mfa/verify_challenge_generic_invalid' do
|
182
|
+
expect do
|
183
|
+
described_class.verify_challenge(
|
184
|
+
authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
|
185
|
+
code: '897792',
|
186
|
+
)
|
187
|
+
end.to raise_error(WorkOSV2::InvalidRequestError)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'and the challenge has expired' do
|
193
|
+
it 'returns an error' do
|
194
|
+
VCR.use_cassette 'mfa/verify_challenge_generic_expired' do
|
195
|
+
expect do
|
196
|
+
described_class.verify_challenge(
|
197
|
+
authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
|
198
|
+
code: '897792',
|
199
|
+
)
|
200
|
+
end.to raise_error(WorkOSV2::InvalidRequestError)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'with missing code argument' do
|
206
|
+
it 'returns an argument error' do
|
207
|
+
expect do
|
208
|
+
described_class.verify_challenge(
|
209
|
+
authentication_challenge_id: 'auth_challenge_01FZ4YVRBMXP5ZM0A7BP4AJ12J',
|
210
|
+
)
|
211
|
+
end.to raise_error(
|
212
|
+
ArgumentError,
|
213
|
+
"Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments",
|
214
|
+
)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
context 'with missing authentication_challenge_id argument' do
|
219
|
+
it 'returns an error' do
|
220
|
+
expect do
|
221
|
+
described_class.verify_challenge(
|
222
|
+
code: '897792',
|
223
|
+
)
|
224
|
+
end.to raise_error(
|
225
|
+
ArgumentError,
|
226
|
+
"Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments",
|
227
|
+
)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context 'with missing code and authentication_challenge_id arguments' do
|
232
|
+
it 'returns an argument error' do
|
233
|
+
expect do
|
234
|
+
described_class.verify_challenge
|
235
|
+
end.to raise_error(
|
236
|
+
ArgumentError,
|
237
|
+
"Incomplete arguments: 'authentication_challenge_id' and 'code' are required arguments",
|
238
|
+
)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe '.get_factor' do
|
245
|
+
context 'with a valid id' do
|
246
|
+
it 'returns a factor' do
|
247
|
+
VCR.use_cassette 'mfa/get_factor_valid' do
|
248
|
+
factor = described_class.get_factor(
|
249
|
+
id: 'auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M',
|
250
|
+
)
|
251
|
+
expect(factor.id.instance_of?(String))
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
context 'with an invalid id' do
|
257
|
+
it 'returns an error' do
|
258
|
+
VCR.use_cassette 'mfa/get_factor_invalid' do
|
259
|
+
expect do
|
260
|
+
described_class.get_factor(
|
261
|
+
id: 'auth_factor_invalid',
|
262
|
+
)
|
263
|
+
end.to raise_error(WorkOSV2::APIError)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
describe '.delete_factor' do
|
270
|
+
context 'deletes facotr' do
|
271
|
+
it 'uses delete_factor to delete factor' do
|
272
|
+
VCR.use_cassette 'mfa/delete_factor' do
|
273
|
+
response = described_class.delete_factor(
|
274
|
+
id: 'auth_factor_01FZ4WMXXA09XF6NK1XMKNWB3M',
|
275
|
+
)
|
276
|
+
expect(response).to be(true)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
@@ -0,0 +1,257 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
describe WorkOSV2::Organizations do
|
5
|
+
it_behaves_like 'client'
|
6
|
+
|
7
|
+
describe '.create_organization' do
|
8
|
+
context 'with valid payload' do
|
9
|
+
context 'with no idempotency key' do
|
10
|
+
it 'creates an organization' do
|
11
|
+
VCR.use_cassette 'organization/create' do
|
12
|
+
organization = described_class.create_organization(
|
13
|
+
domains: ['example.io'],
|
14
|
+
name: 'Test Organization',
|
15
|
+
)
|
16
|
+
|
17
|
+
expect(organization.id).to eq('org_01FCPEJXEZR4DSBA625YMGQT9N')
|
18
|
+
expect(organization.name).to eq('Test Organization')
|
19
|
+
expect(organization.domains.first[:domain]).to eq('example.io')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with idempotency key' do
|
25
|
+
context 'when idempotency key is used once' do
|
26
|
+
it 'creates an organization' do
|
27
|
+
VCR.use_cassette 'organization/create_with_idempotency_key' do
|
28
|
+
organization = described_class.create_organization(
|
29
|
+
domains: ['example.io'],
|
30
|
+
name: 'Test Organization',
|
31
|
+
idempotency_key: 'key',
|
32
|
+
)
|
33
|
+
|
34
|
+
expect(organization.name).to eq('Test Organization')
|
35
|
+
expect(organization.domains.first[:domain]).to eq('example.io')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when idempotency key is used more than once' do
|
41
|
+
context 'with duplicate event payloads' do
|
42
|
+
it 'returns the already created organization' do
|
43
|
+
VCR.use_cassette 'organization/create_with_duplicate_idempotency_key_and_payload' do
|
44
|
+
organization1 = described_class.create_organization(
|
45
|
+
domains: ['example.com'],
|
46
|
+
name: 'Test Organization',
|
47
|
+
idempotency_key: 'foo',
|
48
|
+
)
|
49
|
+
|
50
|
+
organization2 = described_class.create_organization(
|
51
|
+
domains: ['example.com'],
|
52
|
+
name: 'Test Organization',
|
53
|
+
idempotency_key: 'foo',
|
54
|
+
)
|
55
|
+
|
56
|
+
expect(organization1.id).to eq(organization2.id)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'with different event payloads' do
|
62
|
+
it 'raises an error' do
|
63
|
+
VCR.use_cassette 'organization/create_with_duplicate_idempotency_key_and_different_payload' do
|
64
|
+
described_class.create_organization(
|
65
|
+
domains: ['example.me'],
|
66
|
+
name: 'Test Organization',
|
67
|
+
idempotency_key: 'bar',
|
68
|
+
)
|
69
|
+
|
70
|
+
expect do
|
71
|
+
described_class.create_organization(
|
72
|
+
domains: ['example.me'],
|
73
|
+
name: 'Organization Test',
|
74
|
+
idempotency_key: 'bar',
|
75
|
+
)
|
76
|
+
end.to raise_error(
|
77
|
+
WorkOSV2::InvalidRequestError,
|
78
|
+
/Status 400, Another idempotency key \(bar\) with different request parameters was found. Please use a different idempotency key./,
|
79
|
+
)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'with an invalid payload' do
|
88
|
+
it 'returns an error' do
|
89
|
+
VCR.use_cassette 'organization/create_invalid' do
|
90
|
+
expect do
|
91
|
+
described_class.create_organization(
|
92
|
+
domains: ['example.com'],
|
93
|
+
name: 'Test Organization 2',
|
94
|
+
)
|
95
|
+
end.to raise_error(
|
96
|
+
WorkOSV2::APIError,
|
97
|
+
/An Organization with the domain example.com already exists/,
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '.list_organizations' do
|
105
|
+
context 'with no options' do
|
106
|
+
it 'returns organizations and metadata' do
|
107
|
+
expected_metadata = {
|
108
|
+
'after' => nil,
|
109
|
+
'before' => 'before-id',
|
110
|
+
}
|
111
|
+
|
112
|
+
VCR.use_cassette 'organization/list' do
|
113
|
+
organizations = described_class.list_organizations
|
114
|
+
|
115
|
+
expect(organizations.data.size).to eq(6)
|
116
|
+
expect(organizations.list_metadata).to eq(expected_metadata)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'with the before option' do
|
122
|
+
it 'forms the proper request to the API' do
|
123
|
+
request_args = [
|
124
|
+
'/organizations?before=before-id',
|
125
|
+
'Content-Type' => 'application/json'
|
126
|
+
]
|
127
|
+
|
128
|
+
expected_request = Net::HTTP::Get.new(*request_args)
|
129
|
+
|
130
|
+
expect(Net::HTTP::Get).to receive(:new).with(*request_args).
|
131
|
+
and_return(expected_request)
|
132
|
+
|
133
|
+
VCR.use_cassette 'organization/list', match_requests_on: [:path] do
|
134
|
+
organizations = described_class.list_organizations(
|
135
|
+
before: 'before-id',
|
136
|
+
)
|
137
|
+
|
138
|
+
expect(organizations.data.size).to eq(6)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'with the after option' do
|
144
|
+
it 'forms the proper request to the API' do
|
145
|
+
request_args = [
|
146
|
+
'/organizations?after=after-id',
|
147
|
+
'Content-Type' => 'application/json'
|
148
|
+
]
|
149
|
+
|
150
|
+
expected_request = Net::HTTP::Get.new(*request_args)
|
151
|
+
|
152
|
+
expect(Net::HTTP::Get).to receive(:new).with(*request_args).
|
153
|
+
and_return(expected_request)
|
154
|
+
|
155
|
+
VCR.use_cassette 'organization/list', match_requests_on: [:path] do
|
156
|
+
organizations = described_class.list_organizations(after: 'after-id')
|
157
|
+
|
158
|
+
expect(organizations.data.size).to eq(6)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'with the limit option' do
|
164
|
+
it 'forms the proper request to the API' do
|
165
|
+
request_args = [
|
166
|
+
'/organizations?limit=10',
|
167
|
+
'Content-Type' => 'application/json'
|
168
|
+
]
|
169
|
+
|
170
|
+
expected_request = Net::HTTP::Get.new(*request_args)
|
171
|
+
|
172
|
+
expect(Net::HTTP::Get).to receive(:new).with(*request_args).
|
173
|
+
and_return(expected_request)
|
174
|
+
|
175
|
+
VCR.use_cassette 'organization/list', match_requests_on: [:path] do
|
176
|
+
organizations = described_class.list_organizations(limit: 10)
|
177
|
+
|
178
|
+
expect(organizations.data.size).to eq(6)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe '.get_organization' do
|
185
|
+
context 'with a valid id' do
|
186
|
+
it 'gets the organization details' do
|
187
|
+
VCR.use_cassette('organization/get') do
|
188
|
+
organization = described_class.get_organization(
|
189
|
+
id: 'org_01F9293WD2PDEEV4Y625XPZVG7',
|
190
|
+
)
|
191
|
+
|
192
|
+
expect(organization.id).to eq('org_01F9293WD2PDEEV4Y625XPZVG7')
|
193
|
+
expect(organization.name).to eq('Foo Corp')
|
194
|
+
expect(organization.domains.first[:domain]).to eq('foo-corp.com')
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context 'with an invalid id' do
|
200
|
+
it 'raises an error' do
|
201
|
+
VCR.use_cassette('organization/get_invalid') do
|
202
|
+
expect do
|
203
|
+
described_class.get_organization(id: 'invalid')
|
204
|
+
end.to raise_error(
|
205
|
+
WorkOSV2::APIError,
|
206
|
+
'Status 404, Not Found - request ID: ',
|
207
|
+
)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe '.update_organization' do
|
214
|
+
context 'with valid payload' do
|
215
|
+
it 'creates an organization' do
|
216
|
+
VCR.use_cassette 'organization/update' do
|
217
|
+
organization = described_class.update_organization(
|
218
|
+
organization: 'org_01F6Q6TFP7RD2PF6J03ANNWDKV',
|
219
|
+
domains: ['example.me'],
|
220
|
+
name: 'Test Organization',
|
221
|
+
)
|
222
|
+
|
223
|
+
expect(organization.id).to eq('org_01F6Q6TFP7RD2PF6J03ANNWDKV')
|
224
|
+
expect(organization.name).to eq('Test Organization')
|
225
|
+
expect(organization.domains.first[:domain]).to eq('example.me')
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
describe '.delete_organization' do
|
232
|
+
context 'with a valid id' do
|
233
|
+
it 'returns true' do
|
234
|
+
VCR.use_cassette('organization/delete') do
|
235
|
+
response = described_class.delete_organization(
|
236
|
+
id: 'org_01F4A8TD0B4N1Y9SJ8SH635HDB',
|
237
|
+
)
|
238
|
+
|
239
|
+
expect(response).to be(true)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
context 'with an invalid id' do
|
245
|
+
it 'returns false' do
|
246
|
+
VCR.use_cassette('organization/delete_invalid') do
|
247
|
+
expect do
|
248
|
+
described_class.delete_organization(id: 'invalid')
|
249
|
+
end.to raise_error(
|
250
|
+
WorkOSV2::APIError,
|
251
|
+
'Status 404, Not Found - request ID: ',
|
252
|
+
)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
describe WorkOSV2::Passwordless do
|
5
|
+
it_behaves_like 'client'
|
6
|
+
|
7
|
+
describe '.create_session' do
|
8
|
+
context 'with valid options payload' do
|
9
|
+
let(:valid_options) do
|
10
|
+
{
|
11
|
+
email: 'demo@workos-okta.com',
|
12
|
+
type: 'MagicLink',
|
13
|
+
redirect_uri: 'foo.com/auth/callback',
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'creates a session' do
|
18
|
+
VCR.use_cassette('passwordless/create_session') do
|
19
|
+
response = described_class.create_session(valid_options)
|
20
|
+
|
21
|
+
expect(response.email).to eq 'demo@workos-okta.com'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with invalid event payload' do
|
27
|
+
let(:invalid_options) do
|
28
|
+
{}
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'raises an error' do
|
32
|
+
VCR.use_cassette('passwordless/create_session_invalid') do
|
33
|
+
expect do
|
34
|
+
described_class.create_session(invalid_options)
|
35
|
+
end.to raise_error(
|
36
|
+
WorkOSV2::InvalidRequestError,
|
37
|
+
/Status 422, Validation failed \(email: email must be a string; type: type must be a valid enum value\)/,
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '.send_session' do
|
45
|
+
context 'with valid session id' do
|
46
|
+
let(:valid_options) do
|
47
|
+
{
|
48
|
+
email: 'demo@workos-okta.com',
|
49
|
+
type: 'MagicLink',
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'send a session' do
|
54
|
+
VCR.use_cassette('passwordless/send_session') do
|
55
|
+
response = described_class.send_session(
|
56
|
+
'passwordless_session_01EJC0F4KH42T11Y2DHPEB09BM',
|
57
|
+
)
|
58
|
+
|
59
|
+
expect(response['success']).to eq true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'with invalid session id' do
|
65
|
+
it 'raises an error' do
|
66
|
+
VCR.use_cassette('passwordless/send_session_invalid') do
|
67
|
+
expect do
|
68
|
+
described_class.send_session('session_123')
|
69
|
+
end.to raise_error(
|
70
|
+
WorkOSV2::InvalidRequestError,
|
71
|
+
/Status 422, The passwordless session 'session_123' has expired or is invalid./,
|
72
|
+
)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: false
|
3
|
+
|
4
|
+
describe WorkOSV2::Portal do
|
5
|
+
it_behaves_like 'client'
|
6
|
+
|
7
|
+
describe '.generate_link' do
|
8
|
+
let(:organization) { 'org_01EHQMYV6MBK39QC5PZXHY59C3' }
|
9
|
+
|
10
|
+
describe 'with a valid organization' do
|
11
|
+
context 'with the sso intent' do
|
12
|
+
it 'returns an Admin Portal link' do
|
13
|
+
VCR.use_cassette 'portal/generate_link_sso' do
|
14
|
+
portal_link = described_class.generate_link(
|
15
|
+
intent: 'sso',
|
16
|
+
organization: organization,
|
17
|
+
)
|
18
|
+
|
19
|
+
expect(portal_link).to eq(
|
20
|
+
'https://id.workos.com/portal/launch?secret=secret',
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'with the dsync intent' do
|
27
|
+
it 'returns an Admin Portal link' do
|
28
|
+
VCR.use_cassette 'portal/generate_link_dsync' do
|
29
|
+
portal_link = described_class.generate_link(
|
30
|
+
intent: 'dsync',
|
31
|
+
organization: organization,
|
32
|
+
)
|
33
|
+
|
34
|
+
expect(portal_link).to eq(
|
35
|
+
'https://id.workos.com/portal/launch?secret=secret',
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'with the audit_logs intent' do
|
42
|
+
it 'returns an Admin Portal link' do
|
43
|
+
VCR.use_cassette 'portal/generate_link_audit_logs', match_requests_on: %i[path body] do
|
44
|
+
portal_link = described_class.generate_link(
|
45
|
+
intent: 'audit_logs',
|
46
|
+
organization: organization,
|
47
|
+
)
|
48
|
+
|
49
|
+
expect(portal_link).to eq(
|
50
|
+
'https://id.workos.com/portal/launch?secret=secret',
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'with an invalid organization' do
|
58
|
+
it 'raises an error' do
|
59
|
+
VCR.use_cassette 'portal/generate_link_invalid' do
|
60
|
+
expect do
|
61
|
+
described_class.generate_link(
|
62
|
+
intent: 'sso',
|
63
|
+
organization: 'bogus-id',
|
64
|
+
)
|
65
|
+
end.to raise_error(
|
66
|
+
WorkOSV2::InvalidRequestError,
|
67
|
+
/Could not find an organization with the id, bogus-id/,
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'with an invalid intent' do
|
74
|
+
it 'raises an error' do
|
75
|
+
expect do
|
76
|
+
described_class.generate_link(
|
77
|
+
intent: 'bogus-intent',
|
78
|
+
organization: organization,
|
79
|
+
)
|
80
|
+
end.to raise_error(
|
81
|
+
ArgumentError,
|
82
|
+
/bogus-intent is not a valid value/,
|
83
|
+
)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|