workos 2.5.1 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +13 -11
  3. data/lib/workos/audit_log_export.rb +55 -0
  4. data/lib/workos/audit_logs.rb +107 -0
  5. data/lib/workos/client.rb +2 -0
  6. data/lib/workos/errors.rb +15 -1
  7. data/lib/workos/organizations.rb +4 -1
  8. data/lib/workos/types/audit_log_export_struct.rb +17 -0
  9. data/lib/workos/types.rb +1 -0
  10. data/lib/workos/version.rb +1 -1
  11. data/lib/workos/webhooks.rb +49 -2
  12. data/lib/workos.rb +2 -0
  13. data/sorbet/rbi/sorbet-typed/lib/rainbow/all/rainbow.rbi +5 -5
  14. data/spec/lib/workos/audit_logs_spec.rb +149 -0
  15. data/spec/lib/workos/organizations_spec.rb +73 -9
  16. data/spec/lib/workos/webhooks_spec.rb +130 -84
  17. data/spec/support/fixtures/vcr_cassettes/audit_logs/create_event.yml +59 -0
  18. data/spec/support/fixtures/vcr_cassettes/audit_logs/create_event_custom_idempotency_key.yml +60 -0
  19. data/spec/support/fixtures/vcr_cassettes/audit_logs/create_event_invalid.yml +59 -0
  20. data/spec/support/fixtures/vcr_cassettes/audit_logs/create_export.yml +76 -0
  21. data/spec/support/fixtures/vcr_cassettes/audit_logs/create_export_with_filters.yml +78 -0
  22. data/spec/support/fixtures/vcr_cassettes/audit_logs/get_export.yml +73 -0
  23. data/spec/support/fixtures/vcr_cassettes/organization/create_with_duplicate_idempotency_key_and_different_payload.yml +155 -0
  24. data/spec/support/fixtures/vcr_cassettes/organization/create_with_duplicate_idempotency_key_and_payload.yml +154 -0
  25. data/spec/support/fixtures/vcr_cassettes/organization/create_with_idempotency_key.yml +79 -0
  26. data/spec/support/webhook_payload.txt +1 -1
  27. metadata +26 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: efe8fb9e00262c08d11cebcc39e65ad5728771bb1f132ea88302c78205134a3b
4
- data.tar.gz: 1532164858cc02a9e46d9cb8ac0c10b0e5401812da204840cd3760342462bfb7
3
+ metadata.gz: 0dc7e2ff5b758d6a8f65679a0947d94c6278551d68285ee366e062821e4c1544
4
+ data.tar.gz: 15d849b4fcf07f8c7741e36abbc4a6cec29d496e43dd5b5f428a150fb02a8ca0
5
5
  SHA512:
6
- metadata.gz: bd4c7ada118af7b5ee60d28db25844264bda9a8249b0b3d0a33c11696c77f57d020cfe6e7410d4661bb7c3832e0ec83af2acc0468b4dc4b21e633a5af12bec16
7
- data.tar.gz: 8ecebf2811768a093ae87518124b4e1ff8bb3caefbc3ed1ff36ea0a81243030968bf204d535c63422e43718d811f13c682ad058c33e132977b283509457e30f3
6
+ metadata.gz: c8d8cdd3e0c5414e116f183af145206505d710c16a8372226b10c45595750c779a4c639bbe62c8410b79f130de689fa48376eeba76b5fc7fbfb3a0c87b6977d0
7
+ data.tar.gz: a0451a8ee3c4ffb30271dc7687817454b3e9ebd95db08cdbb7ba7b53aacb460b9bef54ad864261c29363910a1cbd9b6d8e8a057518ade44fdf8c82c83bb31c1f
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- workos (2.5.1)
4
+ workos (2.6.0)
5
5
  sorbet-runtime (~> 0.5)
6
6
 
7
7
  GEM
@@ -58,17 +58,19 @@ GEM
58
58
  simplecov_json_formatter (~> 0.1)
59
59
  simplecov-html (0.12.3)
60
60
  simplecov_json_formatter (0.1.2)
61
- sorbet (0.5.6388)
62
- sorbet-static (= 0.5.6388)
61
+ sorbet (0.5.10346)
62
+ sorbet-static (= 0.5.10346)
63
63
  sorbet-runtime (0.5.10323)
64
- sorbet-static (0.5.6388-universal-darwin-14)
65
- sorbet-static (0.5.6388-universal-darwin-15)
66
- sorbet-static (0.5.6388-universal-darwin-16)
67
- sorbet-static (0.5.6388-universal-darwin-17)
68
- sorbet-static (0.5.6388-universal-darwin-18)
69
- sorbet-static (0.5.6388-universal-darwin-19)
70
- sorbet-static (0.5.6388-universal-darwin-20)
71
- sorbet-static (0.5.6388-x86_64-linux)
64
+ sorbet-static (0.5.10346-universal-darwin-14)
65
+ sorbet-static (0.5.10346-universal-darwin-15)
66
+ sorbet-static (0.5.10346-universal-darwin-16)
67
+ sorbet-static (0.5.10346-universal-darwin-17)
68
+ sorbet-static (0.5.10346-universal-darwin-18)
69
+ sorbet-static (0.5.10346-universal-darwin-19)
70
+ sorbet-static (0.5.10346-universal-darwin-20)
71
+ sorbet-static (0.5.10346-universal-darwin-21)
72
+ sorbet-static (0.5.10346-universal-darwin-22)
73
+ sorbet-static (0.5.10346-x86_64-linux)
72
74
  unicode-display_width (1.7.0)
73
75
  vcr (5.0.0)
74
76
  webmock (3.12.2)
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module WorkOS
5
+ # The AuditLogExport class represents the WorkOS entity created when exporting Audit Log Events.
6
+ class AuditLogExport
7
+ include HashProvider
8
+ extend T::Sig
9
+
10
+ attr_accessor :object, :id, :state, :url, :created_at, :updated_at
11
+
12
+ sig { params(json: String).void }
13
+ def initialize(json)
14
+ raw = parse_json(json)
15
+
16
+ @object = T.let(raw.object, String)
17
+ @id = T.let(raw.id, String)
18
+ @state = T.let(raw.state, String)
19
+ @url = raw.url
20
+ @created_at = T.let(raw.created_at, String)
21
+ @updated_at = T.let(raw.updated_at, String)
22
+ end
23
+
24
+ def to_json(*)
25
+ {
26
+ object: object,
27
+ id: id,
28
+ state: state,
29
+ url: url,
30
+ created_at: created_at,
31
+ updated_at: updated_at,
32
+ }
33
+ end
34
+
35
+ private
36
+
37
+ sig do
38
+ params(
39
+ json_string: String,
40
+ ).returns(WorkOS::Types::AuditLogExportStruct)
41
+ end
42
+ def parse_json(json_string)
43
+ hash = JSON.parse(json_string, symbolize_names: true)
44
+
45
+ WorkOS::Types::AuditLogExportStruct.new(
46
+ object: hash[:object],
47
+ id: hash[:id],
48
+ state: hash[:state],
49
+ url: hash[:url],
50
+ created_at: hash[:created_at],
51
+ updated_at: hash[:updated_at],
52
+ )
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ require 'net/http'
5
+ require 'uri'
6
+
7
+ module WorkOS
8
+ # The Audit Logs module provides convenience methods for working with the
9
+ # WorkOS Audit Logs platform. You'll need a valid API key.
10
+ module AuditLogs
11
+ class << self
12
+ extend T::Sig
13
+ include Client
14
+
15
+ # Create an Audit Log Event.
16
+ #
17
+ # @param [String] organization An Organization ID
18
+ # @param [Hash] event An Audit Log Event
19
+ # @param [String] idempotency_key An idempotency key
20
+ #
21
+ # @return [nil]
22
+ sig do
23
+ params(
24
+ organization: String,
25
+ event: Hash,
26
+ idempotency_key: T.nilable(String),
27
+ ).void
28
+ end
29
+ def create_event(organization:, event:, idempotency_key: nil)
30
+ request = post_request(
31
+ path: '/audit_logs/events',
32
+ auth: true,
33
+ idempotency_key: idempotency_key,
34
+ body: {
35
+ organization_id: organization,
36
+ event: event,
37
+ },
38
+ )
39
+
40
+ execute_request(request: request)
41
+ end
42
+
43
+ # Create an Export of Audit Log Events.
44
+ #
45
+ # @param [String] organization An Organization ID
46
+ # @param [String] range_start ISO-8601 datetime
47
+ # @param [String] range_end ISO-8601 datetime
48
+ # @param [Array<String>] actions A list of actions to filter by
49
+ # @param [Array<String>] actors A list of actor names to filter by
50
+ # @param [Array<String>] targets A list of target types to filter by
51
+ #
52
+ # @return [WorkOS::AuditLogExport]
53
+ sig do
54
+ params(
55
+ organization: String,
56
+ range_start: String,
57
+ range_end: String,
58
+ actions: T.nilable(T::Array[String]),
59
+ actors: T.nilable(T::Array[String]),
60
+ targets: T.nilable(T::Array[String]),
61
+ ).returns(WorkOS::AuditLogExport)
62
+ end
63
+ def create_export(organization:, range_start:, range_end:, actions: nil, actors: nil, targets: nil)
64
+ body = {
65
+ organization_id: organization,
66
+ range_start: range_start,
67
+ range_end: range_end,
68
+ }
69
+
70
+ body['actions'] = actions unless actions.nil?
71
+ body['actors'] = actors unless actors.nil?
72
+ body['targets'] = targets unless targets.nil?
73
+
74
+ request = post_request(
75
+ path: '/audit_logs/exports',
76
+ auth: true,
77
+ body: body,
78
+ )
79
+
80
+ response = execute_request(request: request)
81
+
82
+ WorkOS::AuditLogExport.new(response.body)
83
+ end
84
+
85
+ # Retrieves an Export of Audit Log Events
86
+ #
87
+ # @param [String] id An Audit Log Export ID
88
+ #
89
+ # @return [WorkOS::AuditLogExport]
90
+ sig do
91
+ params(
92
+ id: String,
93
+ ).returns(WorkOS::AuditLogExport)
94
+ end
95
+ def get_export(id:)
96
+ request = get_request(
97
+ auth: true,
98
+ path: "/audit_logs/exports/#{id}",
99
+ )
100
+
101
+ response = execute_request(request: request)
102
+
103
+ WorkOS::AuditLogExport.new(response.body)
104
+ end
105
+ end
106
+ end
107
+ end
data/lib/workos/client.rb CHANGED
@@ -138,6 +138,8 @@ module WorkOS
138
138
  message: json['message'],
139
139
  http_status: http_status,
140
140
  request_id: response['x-request-id'],
141
+ code: json['code'],
142
+ errors: json['errors'],
141
143
  )
142
144
  when 401
143
145
  raise AuthenticationError.new(
data/lib/workos/errors.rb CHANGED
@@ -9,7 +9,10 @@ module WorkOS
9
9
 
10
10
  attr_reader :http_status
11
11
  attr_reader :request_id
12
+ attr_reader :code
13
+ attr_reader :errors
12
14
 
15
+ # rubocop:disable Metrics/ParameterLists
13
16
  sig do
14
17
  params(
15
18
  message: T.nilable(String),
@@ -18,16 +21,27 @@ module WorkOS
18
21
  http_status: T.nilable(Integer),
19
22
  request_id: T.nilable(String),
20
23
  code: T.nilable(String),
24
+ errors: T.nilable(T::Array[T::Hash[T.untyped, T.untyped]]),
21
25
  ).void
22
26
  end
23
- def initialize(message: nil, error: nil, error_description: nil, http_status: nil, request_id: nil, code: nil)
27
+ def initialize(
28
+ message: nil,
29
+ error: nil,
30
+ error_description: nil,
31
+ http_status: nil,
32
+ request_id: nil,
33
+ code: nil,
34
+ errors: nil
35
+ )
24
36
  @message = message
25
37
  @error = error
26
38
  @error_description = error_description
27
39
  @http_status = http_status
28
40
  @request_id = request_id
29
41
  @code = code
42
+ @errors = errors
30
43
  end
44
+ # rubocop:enable Metrics/ParameterLists
31
45
 
32
46
  sig { returns(String) }
33
47
  def to_s
@@ -82,14 +82,16 @@ module WorkOS
82
82
  # @param [String] name A unique, descriptive name for the organization
83
83
  # @param [Boolean, nil] allow_profiles_outside_organization Whether Connections
84
84
  # within the Organization allow profiles that are outside of the Organization's configured User Email Domains.
85
+ # @param [String] idempotency_key An idempotency key
85
86
  sig do
86
87
  params(
87
88
  domains: T::Array[String],
88
89
  name: String,
89
90
  allow_profiles_outside_organization: T.nilable(T::Boolean),
91
+ idempotency_key: T.nilable(String),
90
92
  ).returns(WorkOS::Organization)
91
93
  end
92
- def create_organization(domains:, name:, allow_profiles_outside_organization: nil)
94
+ def create_organization(domains:, name:, allow_profiles_outside_organization: nil, idempotency_key: nil)
93
95
  request = post_request(
94
96
  auth: true,
95
97
  body: {
@@ -98,6 +100,7 @@ module WorkOS
98
100
  allow_profiles_outside_organization: allow_profiles_outside_organization,
99
101
  },
100
102
  path: '/organizations',
103
+ idempotency_key: idempotency_key,
101
104
  )
102
105
 
103
106
  response = execute_request(request: request)
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+ # typed: strict
3
+
4
+ module WorkOS
5
+ module Types
6
+ # This AuditLogExportStruct acts as a typed interface
7
+ # for the AuditLogExport class
8
+ class AuditLogExportStruct < T::Struct
9
+ const :object, String
10
+ const :id, String
11
+ const :state, String
12
+ const :url, T.nilable(String)
13
+ const :created_at, String
14
+ const :updated_at, String
15
+ end
16
+ end
17
+ end
data/lib/workos/types.rb CHANGED
@@ -5,6 +5,7 @@ module WorkOS
5
5
  # WorkOS believes strongly in typed languages,
6
6
  # so we're using Sorbet throughout this Ruby gem.
7
7
  module Types
8
+ require_relative 'types/audit_log_export_struct'
8
9
  require_relative 'types/connection_struct'
9
10
  require_relative 'types/directory_struct'
10
11
  require_relative 'types/directory_group_struct'
@@ -2,5 +2,5 @@
2
2
  # typed: strong
3
3
 
4
4
  module WorkOS
5
- VERSION = '2.5.1'
5
+ VERSION = '2.6.0'
6
6
  end
@@ -55,8 +55,25 @@ module WorkOS
55
55
  WorkOS::Webhook.new(payload)
56
56
  end
57
57
 
58
- private
59
-
58
+ # Verifies WorkOS-Signature header from request
59
+ # rubocop:disable Layout/LineLength
60
+ #
61
+ # @param [String] payload The payload from the webhook sent by WorkOS. This is the RAW_POST_DATA of the request.
62
+ # @param [String] sig_header The signature from the webhook sent by WorkOS.
63
+ # @param [String] secret The webhook secret from the WorkOS dashboard.
64
+ # @param [Integer] tolerance The time tolerance in seconds for the webhook.
65
+ #
66
+ # @example
67
+ # WorkOS::Webhooks.verify_header(
68
+ # payload: "{"id": "wh_123","data":{"id":"directory_user_01FAEAJCR3ZBZ30D8BD1924TVG","state":"active","emails":[{"type":"work","value":"blair@foo-corp.com","primary":true}],"idp_id":"00u1e8mutl6wlH3lL4x7","object":"directory_user","username":"blair@foo-corp.com","last_name":"Lunchford","first_name":"Blair","directory_id":"directory_01F9M7F68PZP8QXP8G7X5QRHS7","raw_attributes":{"name":{"givenName":"Blair","familyName":"Lunchford","middleName":"Elizabeth","honorificPrefix":"Ms."},"title":"Developer Success Engineer","active":true,"emails":[{"type":"work","value":"blair@foo-corp.com","primary":true}],"groups":[],"locale":"en-US","schemas":["urn:ietf:params:scim:schemas:core:2.0:User","urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"],"userName":"blair@foo-corp.com","addresses":[{"region":"CA","primary":true,"locality":"San Francisco","postalCode":"94016"}],"externalId":"00u1e8mutl6wlH3lL4x7","displayName":"Blair Lunchford","urn:ietf:params:scim:schemas:extension:enterprise:2.0:User":{"manager":{"value":"2","displayName":"Kate Chapman"},"division":"Engineering","department":"Customer Success"}}},"event":"dsync.user.created"}",
69
+ # sig_header: 't=1626125972272, v1=80f7ab7efadc306eb5797c588cee9410da9be4416782b497bf1e1bf4175fb928',
70
+ # secret: 'LJlTiC19GmCKWs8AE0IaOQcos',
71
+ # )
72
+ #
73
+ # => true
74
+ #
75
+ # @return Boolean
76
+ # rubocop:enable Layout/LineLength
60
77
  sig do
61
78
  params(
62
79
  payload: String,
@@ -105,6 +122,18 @@ module WorkOS
105
122
  end
106
123
  # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
107
124
 
125
+ # Extracts timestamp and signature hash from WorkOS-Signature header
126
+ #
127
+ # @param [String] sig_header The signature from the webhook sent by WorkOS.
128
+ #
129
+ # @example
130
+ # WorkOS::Webhooks.get_timestamp_and_signature_hash(
131
+ # sig_header: 't=1626125972272, v1=80f7ab7efadc306eb5797c588cee9410da9be4416782b497bf1e1bf4175fb928',
132
+ # )
133
+ #
134
+ # => ['1626125972272', '80f7ab7efadc306eb5797c588cee9410da9be4416782b497bf1e1bf4175fb928']
135
+ #
136
+ # @return Array
108
137
  sig do
109
138
  params(
110
139
  sig_header: String,
@@ -127,6 +156,24 @@ module WorkOS
127
156
  [timestamp, signature_hash]
128
157
  end
129
158
 
159
+ # Computes expected signature
160
+ # rubocop:disable Layout/LineLength
161
+ #
162
+ # @param [String] timestamp The timestamp from the webhook signature.
163
+ # @param [String] payload The payload from the webhook sent by WorkOS. This is the RAW_POST_DATA of the request.
164
+ # @param [String] secret The webhook secret from the WorkOS dashboard.
165
+ #
166
+ # @example
167
+ # WorkOS::Webhooks.compute_signature(
168
+ # timestamp: '1626125972272',
169
+ # payload: "{"id": "wh_123","data":{"id":"directory_user_01FAEAJCR3ZBZ30D8BD1924TVG","state":"active","emails":[{"type":"work","value":"blair@foo-corp.com","primary":true}],"idp_id":"00u1e8mutl6wlH3lL4x7","object":"directory_user","username":"blair@foo-corp.com","last_name":"Lunchford","first_name":"Blair","directory_id":"directory_01F9M7F68PZP8QXP8G7X5QRHS7","raw_attributes":{"name":{"givenName":"Blair","familyName":"Lunchford","middleName":"Elizabeth","honorificPrefix":"Ms."},"title":"Developer Success Engineer","active":true,"emails":[{"type":"work","value":"blair@foo-corp.com","primary":true}],"groups":[],"locale":"en-US","schemas":["urn:ietf:params:scim:schemas:core:2.0:User","urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"],"userName":"blair@foo-corp.com","addresses":[{"region":"CA","primary":true,"locality":"San Francisco","postalCode":"94016"}],"externalId":"00u1e8mutl6wlH3lL4x7","displayName":"Blair Lunchford","urn:ietf:params:scim:schemas:extension:enterprise:2.0:User":{"manager":{"value":"2","displayName":"Kate Chapman"},"division":"Engineering","department":"Customer Success"}}},"event":"dsync.user.created"}",
170
+ # secret: 'LJlTiC19GmCKWs8AE0IaOQcos',
171
+ # )
172
+ #
173
+ # => '80f7ab7efadc306eb5797c588cee9410da9be4416782b497bf1e1bf4175fb928'
174
+ #
175
+ # @return String
176
+ # rubocop:enable Layout/LineLength
130
177
  sig do
131
178
  params(
132
179
  timestamp: String,
data/lib/workos.rb CHANGED
@@ -39,6 +39,8 @@ module WorkOS
39
39
  autoload :Types, 'workos/types'
40
40
  autoload :Client, 'workos/client'
41
41
  autoload :Configuration, 'workos/configuration'
42
+ autoload :AuditLogExport, 'workos/audit_log_export'
43
+ autoload :AuditLogs, 'workos/audit_logs'
42
44
  autoload :AuditTrail, 'workos/audit_trail'
43
45
  autoload :Connection, 'workos/connection'
44
46
  autoload :DirectorySync, 'workos/directory_sync'
@@ -30,7 +30,7 @@ module Rainbow
30
30
  sig { returns(Integer) }
31
31
  attr_reader :num
32
32
 
33
- sig { params(ground: Symbol, num: Integer).returns(Indexed) }
33
+ sig { params(ground: Symbol, num: Integer).void }
34
34
  def initialize(ground, num); end
35
35
 
36
36
  sig { returns(T::Array[Integer]) }
@@ -46,7 +46,7 @@ module Rainbow
46
46
  sig { returns(String) }
47
47
  def self.valid_names; end
48
48
 
49
- sig { params(ground: Symbol, name: Symbol).returns(Named) }
49
+ sig { params(ground: Symbol, name: Symbol).void }
50
50
  def initialize(ground, name); end
51
51
  end
52
52
 
@@ -57,7 +57,7 @@ module Rainbow
57
57
  sig { params(value: Numeric).returns(Integer) }
58
58
  def to_ansi_domain(value); end
59
59
 
60
- sig { params(ground: Symbol, values: Integer).returns(RGB) }
60
+ sig { params(ground: Symbol, values: Integer).void }
61
61
  def initialize(ground, *values); end
62
62
 
63
63
  sig { returns(T::Array[Integer]) }
@@ -73,7 +73,7 @@ module Rainbow
73
73
  sig { returns(String) }
74
74
  def self.valid_names; end
75
75
 
76
- sig { params(ground: Symbol, name: Symbol).returns(X11Named) }
76
+ sig { params(ground: Symbol, name: Symbol).void }
77
77
  def initialize(ground, name); end
78
78
  end
79
79
  end
@@ -260,7 +260,7 @@ module Rainbow
260
260
  sig { returns(T::Boolean) }
261
261
  attr_accessor :enabled
262
262
 
263
- sig { params(enabled: T::Boolean).returns(Wrapper) }
263
+ sig { params(enabled: T::Boolean).void }
264
264
  def initialize(enabled = true); end
265
265
 
266
266
  sig { params(string: String).returns(T.any(Rainbow::Presenter, Rainbow::NullPresenter)) }
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+ # typed: false
3
+
4
+ describe WorkOS::AuditLogs do
5
+ it_behaves_like 'client'
6
+
7
+ before do
8
+ WorkOS.configure do |config|
9
+ config.key = 'example_api_key'
10
+ end
11
+ end
12
+
13
+ describe '.create_event' do
14
+ context 'with valid event payload' do
15
+ let(:valid_event) do
16
+ {
17
+ action: 'user.signed_in',
18
+ occurred_at: '2022-08-22T15:04:19.704Z',
19
+ actor: {
20
+ id: 'user_123',
21
+ type: 'user',
22
+ name: 'User',
23
+ metadata: {
24
+ foo: 'bar',
25
+ },
26
+ },
27
+ targets: [{
28
+ id: 'team_123',
29
+ type: 'team',
30
+ name: 'Team',
31
+ metadata: {
32
+ foo: 'bar',
33
+ },
34
+ }],
35
+ context: {
36
+ location: '1.1.1.1',
37
+ user_agent: 'Mozilla',
38
+ },
39
+ }
40
+ end
41
+
42
+ context 'with idempotency key' do
43
+ it 'creates an event' do
44
+ VCR.use_cassette 'audit_logs/create_event_custom_idempotency_key', match_requests_on: %i[path body] do
45
+ response = described_class.create_event(
46
+ organization: 'org_123',
47
+ event: valid_event,
48
+ idempotency_key: 'idempotency_key',
49
+ )
50
+
51
+ expect(response).to eq T::Private::Types::Void::VOID
52
+ end
53
+ end
54
+ end
55
+
56
+ context 'without idempotency key' do
57
+ it 'creates an event' do
58
+ VCR.use_cassette 'audit_logs/create_event', match_requests_on: %i[path body] do
59
+ response = described_class.create_event(
60
+ organization: 'org_123',
61
+ event: valid_event,
62
+ )
63
+
64
+ expect(response).to eq T::Private::Types::Void::VOID
65
+ end
66
+ end
67
+ end
68
+
69
+ context 'with invalid event' do
70
+ it 'returns error' do
71
+ VCR.use_cassette 'audit_logs/create_event_invalid', match_requests_on: %i[path body] do
72
+ described_class.create_event(
73
+ organization: 'org_123',
74
+ event: valid_event,
75
+ )
76
+ rescue WorkOS::InvalidRequestError => e
77
+ expect(
78
+ e.message,
79
+ ).to eq 'Status 400, Invalid Audit Log event - request ID: 1cf9b8e7-5910-4a6d-a333-46bcf841422e'
80
+ expect(e.code).to eq 'invalid_audit_log'
81
+ expect(e.errors.count).to eq 1
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ describe '.create_export' do
89
+ context 'without filters applied' do
90
+ it 'creates an event' do
91
+ VCR.use_cassette 'audit_logs/create_export', match_requests_on: %i[path body] do
92
+ audit_log_export = described_class.create_export(
93
+ organization: 'org_123',
94
+ range_start: '2022-06-22T15:04:19.704Z',
95
+ range_end: '2022-08-22T15:04:19.704Z',
96
+ )
97
+
98
+ expect(audit_log_export).to have_attributes(
99
+ object: 'audit_log_export',
100
+ id: 'audit_log_export_123',
101
+ state: 'pending',
102
+ url: nil,
103
+ created_at: '2022-08-22T15:04:19.704Z',
104
+ updated_at: '2022-08-22T15:04:19.704Z',
105
+ )
106
+ end
107
+ end
108
+ end
109
+
110
+ context 'with filters applied' do
111
+ it 'creates an export' do
112
+ VCR.use_cassette 'audit_logs/create_export_with_filters', match_requests_on: %i[path body] do
113
+ audit_log_export = described_class.create_export(
114
+ organization: 'org_123',
115
+ range_start: '2022-06-22T15:04:19.704Z',
116
+ range_end: '2022-08-22T15:04:19.704Z',
117
+ actions: ['user.signed_in'],
118
+ actors: ['Jon Smith'],
119
+ targets: %w[user team],
120
+ )
121
+
122
+ expect(audit_log_export.object).to eq 'audit_log_export'
123
+ expect(audit_log_export.id).to eq 'audit_log_export_123'
124
+ expect(audit_log_export.state).to eq 'pending'
125
+ expect(audit_log_export.url).to eq nil
126
+ expect(audit_log_export.created_at).to eq '2022-08-22T15:04:19.704Z'
127
+ expect(audit_log_export.updated_at).to eq '2022-08-22T15:04:19.704Z'
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ describe '.get_export' do
134
+ it 'returns an export' do
135
+ VCR.use_cassette 'audit_logs/get_export', match_requests_on: %i[path] do
136
+ audit_log_export = described_class.get_export(
137
+ id: 'audit_log_export_123',
138
+ )
139
+
140
+ expect(audit_log_export.object).to eq 'audit_log_export'
141
+ expect(audit_log_export.id).to eq 'audit_log_export_123'
142
+ expect(audit_log_export.state).to eq 'ready'
143
+ expect(audit_log_export.url).to eq 'https://audit-logs.com/download.csv'
144
+ expect(audit_log_export.created_at).to eq '2022-08-22T15:04:19.704Z'
145
+ expect(audit_log_export.updated_at).to eq '2022-08-22T15:04:19.704Z'
146
+ end
147
+ end
148
+ end
149
+ end