workos 7.1.3 → 8.0.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.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +46 -0
  3. data/.github/workflows/release-please.yml +1 -2
  4. data/.gitignore +2 -0
  5. data/.last-synced-sha +1 -1
  6. data/.oagen-manifest.json +61 -40
  7. data/.release-please-manifest.json +1 -1
  8. data/.yardopts +6 -0
  9. data/CHANGELOG.md +25 -3
  10. data/Gemfile +6 -0
  11. data/Gemfile.lock +33 -2
  12. data/lib/workos/api_keys/api_key.rb +1 -1
  13. data/lib/workos/api_keys/api_key_created_data.rb +1 -1
  14. data/lib/workos/api_keys/organization_api_key.rb +43 -0
  15. data/lib/workos/{types/events_order.rb → api_keys/organization_api_key_owner.rb} +1 -3
  16. data/lib/workos/api_keys/organization_api_key_with_value.rb +46 -0
  17. data/lib/workos/{types/audit_logs_order.rb → api_keys/organization_api_key_with_value_owner.rb} +1 -3
  18. data/lib/workos/api_keys.rb +46 -46
  19. data/lib/workos/audit_logs.rb +4 -4
  20. data/lib/workos/authorization/user_organization_membership_base_list_data.rb +5 -2
  21. data/lib/workos/authorization/{role_assignment.rb → user_role_assignment.rb} +5 -2
  22. data/lib/workos/authorization/{role_assignment_resource.rb → user_role_assignment_resource.rb} +1 -1
  23. data/lib/workos/authorization.rb +122 -22
  24. data/lib/workos/base_client.rb +1 -14
  25. data/lib/workos/client.rb +4 -4
  26. data/lib/workos/connect.rb +2 -2
  27. data/lib/workos/directory_sync/directory_user.rb +3 -0
  28. data/lib/workos/directory_sync/directory_user_with_groups.rb +4 -1
  29. data/lib/workos/directory_sync/dsync_user_updated_data.rb +3 -0
  30. data/lib/workos/directory_sync.rb +6 -6
  31. data/lib/workos/events.rb +2 -2
  32. data/lib/workos/feature_flags.rb +6 -6
  33. data/lib/workos/groups.rb +4 -4
  34. data/lib/workos/multi_factor_auth.rb +2 -2
  35. data/lib/workos/organizations.rb +2 -2
  36. data/lib/workos/sso/profile.rb +3 -0
  37. data/lib/workos/sso.rb +2 -2
  38. data/lib/workos/types/event_context_actor_source.rb +2 -1
  39. data/lib/workos/types/{applications_order.rb → pagination_order.rb} +1 -1
  40. data/lib/workos/types/{vault_byok_key_verification_completed_data_key_provider.rb → vault_byok_key_provider.rb} +1 -1
  41. data/lib/workos/user_management/create_user_api_key.rb +25 -0
  42. data/lib/workos/user_management/organization_membership.rb +5 -2
  43. data/lib/workos/user_management/user_api_key.rb +43 -0
  44. data/lib/workos/user_management/user_api_key_created_data_owner.rb +25 -0
  45. data/lib/workos/{api_keys/api_key_with_value_owner.rb → user_management/user_api_key_owner.rb} +1 -1
  46. data/lib/workos/{types/webhooks_order.rb → user_management/user_api_key_revoked_data_owner.rb} +1 -3
  47. data/lib/workos/{api_keys/api_key_with_value.rb → user_management/user_api_key_with_value.rb} +2 -2
  48. data/lib/workos/{types/groups_order.rb → user_management/user_api_key_with_value_owner.rb} +1 -3
  49. data/lib/workos/user_management/user_organization_membership.rb +5 -2
  50. data/lib/workos/user_management.rb +107 -10
  51. data/lib/workos/user_management_organization_membership_groups.rb +2 -2
  52. data/lib/workos/vault/vault_byok_key_deleted.rb +34 -0
  53. data/lib/workos/vault/vault_byok_key_deleted_data.rb +22 -0
  54. data/lib/workos/version.rb +1 -1
  55. data/lib/workos/webhooks.rb +2 -2
  56. data/rbi/workos/api_key.rbi +2 -2
  57. data/rbi/workos/api_key_created_data.rbi +2 -2
  58. data/rbi/workos/api_key_revoked_data.rbi +2 -2
  59. data/rbi/workos/api_keys.rbi +17 -17
  60. data/rbi/workos/authorization.rbi +27 -1
  61. data/rbi/workos/client.rbi +3 -3
  62. data/rbi/workos/create_user_api_key.rbi +36 -0
  63. data/rbi/workos/directory_user.rbi +6 -0
  64. data/rbi/workos/directory_user_with_groups.rbi +6 -0
  65. data/rbi/workos/dsync_user_updated_data.rbi +6 -0
  66. data/rbi/workos/organization_api_key.rbi +72 -0
  67. data/rbi/workos/{api_key_with_value_owner.rbi → organization_api_key_owner.rbi} +1 -1
  68. data/rbi/workos/organization_api_key_with_value.rbi +78 -0
  69. data/rbi/workos/organization_api_key_with_value_owner.rbi +30 -0
  70. data/rbi/workos/organization_membership.rbi +6 -0
  71. data/rbi/workos/profile.rbi +6 -0
  72. data/rbi/workos/user_api_key.rbi +72 -0
  73. data/rbi/workos/user_api_key_created_data_owner.rbi +36 -0
  74. data/rbi/workos/user_api_key_owner.rbi +36 -0
  75. data/rbi/workos/user_api_key_revoked_data_owner.rbi +36 -0
  76. data/rbi/workos/{api_key_with_value.rbi → user_api_key_with_value.rbi} +3 -3
  77. data/rbi/workos/user_api_key_with_value_owner.rbi +36 -0
  78. data/rbi/workos/user_management.rbi +31 -0
  79. data/rbi/workos/user_organization_membership.rbi +6 -0
  80. data/rbi/workos/user_organization_membership_base_list_data.rbi +6 -0
  81. data/rbi/workos/{role_assignment.rbi → user_role_assignment.rbi} +9 -3
  82. data/rbi/workos/{role_assignment_resource.rbi → user_role_assignment_resource.rbi} +1 -1
  83. data/rbi/workos/vault_byok_key_deleted.rbi +54 -0
  84. data/rbi/workos/vault_byok_key_deleted_data.rbi +30 -0
  85. data/script/docs +16 -0
  86. data/script/docs-serve +12 -0
  87. data/script/llms-txt +37 -0
  88. data/test/workos/test_api_keys.rb +17 -17
  89. data/test/workos/test_authorization.rb +16 -0
  90. data/test/workos/test_base_client.rb +0 -163
  91. data/test/workos/test_model_round_trip.rb +278 -83
  92. data/test/workos/test_user_management.rb +25 -1
  93. metadata +39 -33
  94. data/lib/workos/types/authorization_order.rb +0 -9
  95. data/lib/workos/types/connections_order.rb +0 -9
  96. data/lib/workos/types/directories_order.rb +0 -9
  97. data/lib/workos/types/directory_groups_order.rb +0 -9
  98. data/lib/workos/types/directory_users_order.rb +0 -9
  99. data/lib/workos/types/feature_flags_order.rb +0 -9
  100. data/lib/workos/types/organizations_api_keys_order.rb +0 -9
  101. data/lib/workos/types/organizations_feature_flags_order.rb +0 -9
  102. data/lib/workos/types/organizations_order.rb +0 -9
  103. data/lib/workos/types/permissions_order.rb +0 -9
  104. data/lib/workos/types/user_management_invitations_order.rb +0 -9
  105. data/lib/workos/types/user_management_multi_factor_authentication_order.rb +0 -9
  106. data/lib/workos/types/user_management_organization_membership_groups_order.rb +0 -9
  107. data/lib/workos/types/user_management_organization_membership_order.rb +0 -9
  108. data/lib/workos/types/user_management_users_authorized_applications_order.rb +0 -9
  109. data/lib/workos/types/user_management_users_feature_flags_order.rb +0 -9
  110. data/lib/workos/types/user_management_users_order.rb +0 -9
@@ -11,20 +11,6 @@ class ApiKeysTest < Minitest::Test
11
11
  @client = WorkOS::Client.new(api_key: "sk_test_123")
12
12
  end
13
13
 
14
- def test_create_validation_returns_expected_result
15
- stub_request(:post, %r{\Ahttps://api\.workos\.com/api_keys/validations(\?|\z)})
16
- .to_return(body: "{}", status: 200)
17
- result = @client.api_keys.create_validation(value: "stub")
18
- refute_nil result
19
- end
20
-
21
- def test_delete_api_key_returns_expected_result
22
- stub_request(:delete, %r{\Ahttps://api\.workos\.com/api_keys/stub(\?|\z)})
23
- .to_return(body: "{}", status: 200)
24
- result = @client.api_keys.delete_api_key(id: "stub")
25
- assert_nil result
26
- end
27
-
28
14
  def test_list_organization_api_keys_returns_expected_result
29
15
  stub_request(:get, %r{\Ahttps://api\.workos\.com/organizations/stub/api_keys(\?|\z)})
30
16
  .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)
@@ -39,12 +25,26 @@ class ApiKeysTest < Minitest::Test
39
25
  refute_nil result
40
26
  end
41
27
 
28
+ def test_create_validation_returns_expected_result
29
+ stub_request(:post, %r{\Ahttps://api\.workos\.com/api_keys/validations(\?|\z)})
30
+ .to_return(body: "{}", status: 200)
31
+ result = @client.api_keys.create_validation(value: "stub")
32
+ refute_nil result
33
+ end
34
+
35
+ def test_delete_api_key_returns_expected_result
36
+ stub_request(:delete, %r{\Ahttps://api\.workos\.com/api_keys/stub(\?|\z)})
37
+ .to_return(body: "{}", status: 200)
38
+ result = @client.api_keys.delete_api_key(id: "stub")
39
+ assert_nil result
40
+ end
41
+
42
42
  # Parameterized authentication error tests (one per endpoint).
43
43
  [
44
- {name: :create_validation, verb: :post, url: %r{\Ahttps://api\.workos\.com/api_keys/validations(\?|\z)}, args: {value: "stub"}},
45
- {name: :delete_api_key, verb: :delete, url: %r{\Ahttps://api\.workos\.com/api_keys/stub(\?|\z)}, args: {id: "stub"}},
46
44
  {name: :list_organization_api_keys, verb: :get, url: %r{\Ahttps://api\.workos\.com/organizations/stub/api_keys(\?|\z)}, args: {organization_id: "stub"}},
47
- {name: :create_organization_api_key, verb: :post, url: %r{\Ahttps://api\.workos\.com/organizations/stub/api_keys(\?|\z)}, args: {organization_id: "stub", name: "stub"}}
45
+ {name: :create_organization_api_key, verb: :post, url: %r{\Ahttps://api\.workos\.com/organizations/stub/api_keys(\?|\z)}, args: {organization_id: "stub", name: "stub"}},
46
+ {name: :create_validation, verb: :post, url: %r{\Ahttps://api\.workos\.com/api_keys/validations(\?|\z)}, args: {value: "stub"}},
47
+ {name: :delete_api_key, verb: :delete, url: %r{\Ahttps://api\.workos\.com/api_keys/stub(\?|\z)}, args: {id: "stub"}}
48
48
  ].each do |spec|
49
49
  define_method("test_#{spec[:name]}_raises_authentication_error_on_401") do
50
50
  stub_request(spec[:verb], spec[:url])
@@ -192,6 +192,13 @@ class AuthorizationTest < Minitest::Test
192
192
  assert_kind_of WorkOS::Types::ListStruct, result
193
193
  end
194
194
 
195
+ def test_list_role_assignments_for_resource_by_external_id_returns_expected_result
196
+ stub_request(:get, %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub/role_assignments(\?|\z)})
197
+ .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)
198
+ result = @client.authorization.list_role_assignments_for_resource_by_external_id(organization_id: "stub", resource_type_slug: "stub", external_id: "stub")
199
+ assert_kind_of WorkOS::Types::ListStruct, result
200
+ end
201
+
195
202
  def test_list_resources_returns_expected_result
196
203
  stub_request(:get, %r{\Ahttps://api\.workos\.com/authorization/resources(\?|\z)})
197
204
  .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)
@@ -259,6 +266,13 @@ class AuthorizationTest < Minitest::Test
259
266
  assert_kind_of WorkOS::Types::ListStruct, result
260
267
  end
261
268
 
269
+ def test_list_role_assignments_for_resource_returns_expected_result
270
+ stub_request(:get, %r{\Ahttps://api\.workos\.com/authorization/resources/stub/role_assignments(\?|\z)})
271
+ .to_return(body: '{"data": [], "list_metadata": {}}', status: 200)
272
+ result = @client.authorization.list_role_assignments_for_resource(resource_id: "stub")
273
+ assert_kind_of WorkOS::Types::ListStruct, result
274
+ end
275
+
262
276
  def test_list_environment_roles_returns_expected_result
263
277
  stub_request(:get, %r{\Ahttps://api\.workos\.com/authorization/roles(\?|\z)})
264
278
  .to_return(body: "{}", status: 200)
@@ -358,12 +372,14 @@ class AuthorizationTest < Minitest::Test
358
372
  {name: :update_resource_by_external_id, verb: :patch, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub(\?|\z)}, args: {organization_id: "stub", resource_type_slug: "stub", external_id: "stub", parent_resource: WorkOS::Authorization::ParentResourceById.new(parent_resource_id: "stub")}},
359
373
  {name: :delete_resource_by_external_id, verb: :delete, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub(\?|\z)}, args: {organization_id: "stub", resource_type_slug: "stub", external_id: "stub"}},
360
374
  {name: :list_memberships_for_resource_by_external_id, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub/organization_memberships(\?|\z)}, args: {organization_id: "stub", resource_type_slug: "stub", external_id: "stub", permission_slug: "stub"}},
375
+ {name: :list_role_assignments_for_resource_by_external_id, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/organizations/stub/resources/stub/stub/role_assignments(\?|\z)}, args: {organization_id: "stub", resource_type_slug: "stub", external_id: "stub"}},
361
376
  {name: :list_resources, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/resources(\?|\z)}, args: {parent: WorkOS::Authorization::ParentById.new(parent_resource_id: "stub")}},
362
377
  {name: :create_resource, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/resources(\?|\z)}, args: {external_id: "stub", name: "stub", resource_type_slug: "stub", organization_id: "stub", parent_resource: WorkOS::Authorization::ParentResourceById.new(parent_resource_id: "stub")}},
363
378
  {name: :get_resource, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub(\?|\z)}, args: {resource_id: "stub"}},
364
379
  {name: :update_resource, verb: :patch, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub(\?|\z)}, args: {resource_id: "stub", parent_resource: WorkOS::Authorization::ParentResourceById.new(parent_resource_id: "stub")}},
365
380
  {name: :delete_resource, verb: :delete, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub(\?|\z)}, args: {resource_id: "stub"}},
366
381
  {name: :list_memberships_for_resource, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub/organization_memberships(\?|\z)}, args: {resource_id: "stub", permission_slug: "stub"}},
382
+ {name: :list_role_assignments_for_resource, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/resources/stub/role_assignments(\?|\z)}, args: {resource_id: "stub"}},
367
383
  {name: :list_environment_roles, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/roles(\?|\z)}},
368
384
  {name: :create_environment_role, verb: :post, url: %r{\Ahttps://api\.workos\.com/authorization/roles(\?|\z)}, args: {slug: "stub", name: "stub"}},
369
385
  {name: :get_environment_role, verb: :get, url: %r{\Ahttps://api\.workos\.com/authorization/roles/stub(\?|\z)}, args: {slug: "stub"}},
@@ -82,36 +82,10 @@ class BaseClientTest < Minitest::Test
82
82
  end
83
83
  end
84
84
 
85
- # A pooled connection that either returns a canned response or raises when
86
- # driven, so execute_request can be exercised without real TCP+TLS.
87
- class StubConnection < FakeConnection
88
- attr_accessor :read_timeout, :open_timeout
89
-
90
- def initialize(response: nil, error: nil, **kwargs)
91
- super(**kwargs)
92
- @response = response
93
- @error = error
94
- end
95
-
96
- def request(_request)
97
- raise @error if @error
98
-
99
- @response
100
- end
101
- end
102
-
103
85
  def setup
104
86
  @client = WorkOS::BaseClient.new(api_key: "sk_test_123", max_retries: 1)
105
87
  end
106
88
 
107
- def teardown
108
- super
109
- # Close any open connections and clear the fiber-local cache to avoid
110
- # leaking pooled connections between tests.
111
- @client.shutdown
112
- Fiber[:workos_connections] = nil
113
- end
114
-
115
89
  def test_request_dispatches_known_methods
116
90
  client = RecordingClient.new(api_key: "sk_test_123")
117
91
 
@@ -196,141 +170,4 @@ class BaseClientTest < Minitest::Test
196
170
  assert evict.finished
197
171
  refute keep.finished
198
172
  end
199
-
200
- # An exception execute_request's rescue clause doesn't list still leaves the
201
- # socket mid-stream, so the connection must not survive in the pool for the
202
- # next request on this thread to pick up.
203
- def test_unlisted_request_error_evicts_the_pooled_connection
204
- conn = StubConnection.new(error: Net::HTTPBadResponse.new("wrong version"))
205
- cache = @client.send(:thread_connections)
206
- cache["https:api.workos.com:443:30"] = conn
207
-
208
- assert_raises(Net::HTTPBadResponse) do
209
- @client.execute_request(request: Net::HTTP::Get.new("/things"))
210
- end
211
-
212
- refute cache.key?("https:api.workos.com:443:30"),
213
- "a connection whose request did not complete must not stay pooled"
214
- assert conn.finished
215
- end
216
-
217
- def test_completed_request_keeps_the_connection_pooled
218
- conn = StubConnection.new(response: Net::HTTPOK.new("1.1", "200", "OK"))
219
- cache = @client.send(:thread_connections)
220
- cache["https:api.workos.com:443:30"] = conn
221
-
222
- @client.execute_request(request: Net::HTTP::Get.new("/things"))
223
-
224
- assert cache.key?("https:api.workos.com:443:30")
225
- refute conn.finished
226
- end
227
-
228
- # Raised asynchronously into the worker thread to stand in for a caller-side
229
- # abort: Timeout.timeout, Thread#kill, a signal handler unwinding the stack.
230
- # It descends from Exception rather than StandardError so that nothing in
231
- # execute_request can catch it — the `ensure` is the only cleanup that runs.
232
- class AbortSignal < Exception; end # standard:disable Lint/InheritException
233
-
234
- # End-to-end regression test over a real keep-alive socket, for the failure
235
- # the eviction actually prevents: request one is abandoned mid-flight, the
236
- # server then writes response one onto that socket, and request two on the
237
- # same thread reads those stale bytes as if they were its own response.
238
- #
239
- # Before the fix, request two sees marker "one" (or a mangled response) off
240
- # the pooled socket. After it, the abandoned socket is closed and the server
241
- # accepts a fresh connection for request two.
242
- def test_aborted_request_does_not_leak_its_response_to_the_next_request
243
- WebMock.disable!
244
- server = TCPServer.new("127.0.0.1", 0)
245
- port = server.addr[1]
246
- client = WorkOS::BaseClient.new(
247
- api_key: "sk_test_123",
248
- base_url: "http://127.0.0.1:#{port}",
249
- timeout: 5,
250
- max_retries: 0
251
- )
252
-
253
- request_one_read = Queue.new
254
- release_response_one = Queue.new
255
- response_one_written = Queue.new
256
- aborted = Queue.new
257
- connections = Queue.new
258
-
259
- server_thread = Thread.new do
260
- # Connection one: read the request, then hold the response back until
261
- # the client has been aborted and has unwound.
262
- first = server.accept
263
- connections << first
264
- read_http_request(first)
265
- request_one_read << true
266
- release_response_one.pop
267
- begin
268
- write_http_response(first, {marker: "one"})
269
- rescue Errno::EPIPE, Errno::ECONNRESET, IOError
270
- # Expected once the fix closes the abandoned socket.
271
- end
272
- response_one_written << true
273
-
274
- # Connection two: a fresh accept, which only completes because the
275
- # client did not reuse the socket above.
276
- second = server.accept
277
- connections << second
278
- read_http_request(second)
279
- write_http_response(second, {marker: "two"})
280
- end
281
-
282
- worker = Thread.new do
283
- begin
284
- client.execute_request(request: Net::HTTP::Get.new("/one"))
285
- rescue AbortSignal
286
- # The caller unwinds but the thread survives and goes on to serve more
287
- # work, the way a Puma or Solid Queue worker does. execute_request's
288
- # `ensure` has already run by the time this body executes.
289
- aborted << true
290
- response_one_written.pop
291
- end
292
- response = client.execute_request(request: Net::HTTP::Get.new("/two"))
293
- JSON.parse(response.body)["marker"]
294
- end
295
-
296
- marker = Timeout.timeout(15) do
297
- request_one_read.pop
298
- worker.raise(AbortSignal)
299
- aborted.pop
300
- release_response_one << true
301
- worker.value
302
- end
303
-
304
- assert_equal "two", marker,
305
- "request two read the abandoned socket's response instead of its own"
306
- ensure
307
- server_thread&.kill
308
- worker&.kill
309
- until connections.nil? || connections.empty?
310
- socket = connections.pop
311
- socket.close unless socket.closed?
312
- end
313
- server&.close
314
- WebMock.enable!
315
- end
316
-
317
- def read_http_request(socket)
318
- socket.gets # request line
319
- loop do
320
- line = socket.gets
321
- break if line.nil? || line == "\r\n"
322
- end
323
- end
324
-
325
- def write_http_response(socket, body)
326
- payload = JSON.generate(body)
327
- socket.write(
328
- "HTTP/1.1 200 OK\r\n" \
329
- "Content-Type: application/json\r\n" \
330
- "Content-Length: #{payload.bytesize}\r\n" \
331
- "Connection: keep-alive\r\n" \
332
- "\r\n#{payload}"
333
- )
334
- socket.flush
335
- end
336
173
  end