reactor_sdk 0.1.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 (78) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +19 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +281 -0
  5. data/lib/reactor_sdk/authentication.rb +137 -0
  6. data/lib/reactor_sdk/client.rb +186 -0
  7. data/lib/reactor_sdk/configuration.rb +102 -0
  8. data/lib/reactor_sdk/connection.rb +342 -0
  9. data/lib/reactor_sdk/endpoints/app_configurations.rb +42 -0
  10. data/lib/reactor_sdk/endpoints/audit_events.rb +64 -0
  11. data/lib/reactor_sdk/endpoints/base_endpoint.rb +207 -0
  12. data/lib/reactor_sdk/endpoints/builds.rb +62 -0
  13. data/lib/reactor_sdk/endpoints/callbacks.rb +38 -0
  14. data/lib/reactor_sdk/endpoints/companies.rb +42 -0
  15. data/lib/reactor_sdk/endpoints/data_elements.rb +251 -0
  16. data/lib/reactor_sdk/endpoints/environments.rb +174 -0
  17. data/lib/reactor_sdk/endpoints/extension_package_usage_authorizations.rb +51 -0
  18. data/lib/reactor_sdk/endpoints/extension_packages.rb +63 -0
  19. data/lib/reactor_sdk/endpoints/extensions.rb +181 -0
  20. data/lib/reactor_sdk/endpoints/hosts.rb +101 -0
  21. data/lib/reactor_sdk/endpoints/libraries.rb +872 -0
  22. data/lib/reactor_sdk/endpoints/notes.rb +11 -0
  23. data/lib/reactor_sdk/endpoints/profiles.rb +14 -0
  24. data/lib/reactor_sdk/endpoints/properties.rb +123 -0
  25. data/lib/reactor_sdk/endpoints/revisions.rb +102 -0
  26. data/lib/reactor_sdk/endpoints/rule_components.rb +218 -0
  27. data/lib/reactor_sdk/endpoints/rules.rb +240 -0
  28. data/lib/reactor_sdk/endpoints/search.rb +23 -0
  29. data/lib/reactor_sdk/endpoints/secrets.rb +76 -0
  30. data/lib/reactor_sdk/error.rb +115 -0
  31. data/lib/reactor_sdk/library_comparison_builder.rb +74 -0
  32. data/lib/reactor_sdk/library_snapshot_builder.rb +66 -0
  33. data/lib/reactor_sdk/paginator.rb +92 -0
  34. data/lib/reactor_sdk/rate_limiter.rb +96 -0
  35. data/lib/reactor_sdk/reference_extractor.rb +34 -0
  36. data/lib/reactor_sdk/resource_metadata.rb +73 -0
  37. data/lib/reactor_sdk/resource_normalizer.rb +90 -0
  38. data/lib/reactor_sdk/resources/app_configuration.rb +20 -0
  39. data/lib/reactor_sdk/resources/audit_event.rb +45 -0
  40. data/lib/reactor_sdk/resources/base_resource.rb +181 -0
  41. data/lib/reactor_sdk/resources/build.rb +64 -0
  42. data/lib/reactor_sdk/resources/callback.rb +16 -0
  43. data/lib/reactor_sdk/resources/company.rb +38 -0
  44. data/lib/reactor_sdk/resources/comprehensive_data_element.rb +28 -0
  45. data/lib/reactor_sdk/resources/comprehensive_extension.rb +30 -0
  46. data/lib/reactor_sdk/resources/comprehensive_resource.rb +31 -0
  47. data/lib/reactor_sdk/resources/comprehensive_rule.rb +26 -0
  48. data/lib/reactor_sdk/resources/comprehensive_upstream_chain.rb +50 -0
  49. data/lib/reactor_sdk/resources/comprehensive_upstream_chain_entry.rb +34 -0
  50. data/lib/reactor_sdk/resources/data_element.rb +108 -0
  51. data/lib/reactor_sdk/resources/environment.rb +45 -0
  52. data/lib/reactor_sdk/resources/extension.rb +66 -0
  53. data/lib/reactor_sdk/resources/extension_package.rb +49 -0
  54. data/lib/reactor_sdk/resources/extension_package_usage_authorization.rb +26 -0
  55. data/lib/reactor_sdk/resources/host.rb +68 -0
  56. data/lib/reactor_sdk/resources/library.rb +67 -0
  57. data/lib/reactor_sdk/resources/library_comparison.rb +72 -0
  58. data/lib/reactor_sdk/resources/library_comparison_entry.rb +144 -0
  59. data/lib/reactor_sdk/resources/library_snapshot.rb +118 -0
  60. data/lib/reactor_sdk/resources/library_snapshot_extension_index.rb +70 -0
  61. data/lib/reactor_sdk/resources/library_snapshot_index.rb +169 -0
  62. data/lib/reactor_sdk/resources/library_with_resources.rb +194 -0
  63. data/lib/reactor_sdk/resources/note.rb +37 -0
  64. data/lib/reactor_sdk/resources/profile.rb +22 -0
  65. data/lib/reactor_sdk/resources/property.rb +44 -0
  66. data/lib/reactor_sdk/resources/revision.rb +156 -0
  67. data/lib/reactor_sdk/resources/rule.rb +44 -0
  68. data/lib/reactor_sdk/resources/rule_component.rb +101 -0
  69. data/lib/reactor_sdk/resources/search_results.rb +28 -0
  70. data/lib/reactor_sdk/resources/secret.rb +17 -0
  71. data/lib/reactor_sdk/resources/upstream_chain.rb +80 -0
  72. data/lib/reactor_sdk/resources/upstream_chain_entry.rb +55 -0
  73. data/lib/reactor_sdk/response_parser.rb +160 -0
  74. data/lib/reactor_sdk/version.rb +5 -0
  75. data/lib/reactor_sdk.rb +79 -0
  76. data/reactor_sdk.gemspec +70 -0
  77. data/sig/reactor_sdk.rbs +346 -0
  78. metadata +293 -0
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # @file configuration.rb
5
+ # @description Holds and validates all configuration for a ReactorSDK::Client.
6
+ #
7
+ # Validated eagerly on initialization — raises immediately if required
8
+ # values are missing rather than failing mid-request when it is harder
9
+ # to diagnose.
10
+ #
11
+ # The ims_token_url parameter exists specifically for testing — it allows
12
+ # specs to point at a WebMock or VCR stub instead of the real Adobe IMS
13
+ # endpoint without any monkey-patching.
14
+ #
15
+ # @domain Infrastructure
16
+ #
17
+
18
+ module ReactorSDK
19
+ class Configuration
20
+ # Default Reactor API base URL
21
+ DEFAULT_BASE_URL = 'https://reactor.adobe.io'
22
+
23
+ # Default HTTP timeout in seconds
24
+ DEFAULT_TIMEOUT = 30
25
+
26
+ # @return [String] Adobe Developer Console client ID
27
+ attr_reader :client_id
28
+
29
+ # @return [String] Adobe Developer Console client secret
30
+ attr_reader :client_secret
31
+
32
+ # @return [String] Adobe IMS organisation ID (format: XXXXX@AdobeOrg)
33
+ attr_reader :org_id
34
+
35
+ # @return [String] Reactor API base URL
36
+ attr_reader :base_url
37
+
38
+ # @return [String] Adobe IMS token endpoint URL — overridable for testing
39
+ attr_reader :ims_token_url
40
+
41
+ # @return [Integer] HTTP timeout in seconds
42
+ attr_reader :timeout
43
+
44
+ # @return [Logger, nil] Optional logger — if provided, HTTP calls are logged
45
+ attr_reader :logger
46
+
47
+ # @return [Boolean] Whether to auto-refresh the token before expiry
48
+ attr_reader :auto_refresh_token
49
+
50
+ ##
51
+ # Initializes and validates SDK configuration.
52
+ #
53
+ # @param client_id [String] Adobe Developer Console client ID
54
+ # @param client_secret [String] Adobe Developer Console client secret
55
+ # @param org_id [String] Adobe IMS organisation ID
56
+ # @param base_url [String] Override Reactor API base URL (optional)
57
+ # @param ims_token_url [String] Override IMS token URL — for testing only (optional)
58
+ # @param timeout [Integer] HTTP timeout in seconds (optional)
59
+ # @param logger [Logger] Custom logger instance (optional)
60
+ # @param auto_refresh_token [Boolean] Auto-refresh token before expiry (optional)
61
+ # @raise [ReactorSDK::ConfigurationError] if any required value is blank
62
+ #
63
+ def initialize(
64
+ client_id:,
65
+ client_secret:,
66
+ org_id:,
67
+ base_url: DEFAULT_BASE_URL,
68
+ ims_token_url: Authentication::IMS_TOKEN_URL,
69
+ timeout: DEFAULT_TIMEOUT,
70
+ logger: nil,
71
+ auto_refresh_token: true
72
+ )
73
+ @client_id = client_id
74
+ @client_secret = client_secret
75
+ @org_id = org_id
76
+ @base_url = base_url
77
+ @ims_token_url = ims_token_url
78
+ @timeout = timeout
79
+ @logger = logger
80
+ @auto_refresh_token = auto_refresh_token
81
+
82
+ validate!
83
+ end
84
+
85
+ private
86
+
87
+ ##
88
+ # Checks that all required fields are present and non-blank.
89
+ #
90
+ # @raise [ReactorSDK::ConfigurationError] if any required field is blank
91
+ #
92
+ def validate!
93
+ %i[client_id client_secret org_id].each do |field|
94
+ value = public_send(field)
95
+ if value.nil? || value.strip.empty?
96
+ raise ConfigurationError,
97
+ "#{field} is required and cannot be blank"
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,342 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # @file connection.rb
5
+ # @description Authenticated Faraday HTTP connection for the Reactor API.
6
+ #
7
+ # Responsibilities:
8
+ # - Injects required Adobe auth headers on every request
9
+ # - Enforces rate limiting via RateLimiter before every request
10
+ # - Retries automatically on 429 and 5xx via faraday-retry middleware
11
+ # - Translates HTTP error status codes into typed ReactorSDK errors
12
+ # - Parses JSON responses into Ruby hashes
13
+ #
14
+ # @domain Infrastructure
15
+ # @depends ReactorSDK::Authentication, ReactorSDK::RateLimiter
16
+ #
17
+
18
+ module ReactorSDK
19
+ class Connection
20
+ # Pins the Reactor API to version 1 on every request.
21
+ ACCEPT_HEADER = 'application/vnd.api+json;revision=1'
22
+
23
+ # Required content type for all write requests
24
+ CONTENT_TYPE = 'application/vnd.api+json'
25
+
26
+ ##
27
+ # @param config [ReactorSDK::Configuration] SDK configuration
28
+ # @param auth [ReactorSDK::Authentication] Token provider
29
+ # @param rate_limiter [ReactorSDK::RateLimiter] Request throttler
30
+ #
31
+ def initialize(config, auth, rate_limiter = RateLimiter.new)
32
+ @config = config
33
+ @auth = auth
34
+ @rate_limiter = rate_limiter
35
+ @http = build_faraday_connection
36
+ end
37
+
38
+ ##
39
+ # Executes an authenticated GET request.
40
+ #
41
+ # @param path [String] Relative path
42
+ # @param params [Hash] Optional query string parameters
43
+ # @return [Hash, nil] Parsed JSON response body
44
+ # @raise [ReactorSDK::Error] on non-2xx after all retries exhausted
45
+ #
46
+ def get(path, params: {})
47
+ @rate_limiter.acquire
48
+ response = @http.get(path, params) { |req| inject_headers(req) }
49
+ handle_response(response)
50
+ end
51
+
52
+ ##
53
+ # Executes an authenticated POST request.
54
+ #
55
+ # @param path [String] Relative API path
56
+ # @param body [Hash] Request body
57
+ # @return [Hash, nil] Parsed JSON response body
58
+ # @raise [ReactorSDK::Error] on non-2xx after all retries exhausted
59
+ #
60
+ def post(path, body)
61
+ @rate_limiter.acquire
62
+ response = @http.post(path, body.to_json) { |req| inject_headers(req) }
63
+ handle_response(response)
64
+ end
65
+
66
+ ##
67
+ # Executes an authenticated multipart POST request.
68
+ #
69
+ # @param path [String] Relative API path
70
+ # @param file_path [String] Path to the file to upload
71
+ # @param field_name [String] Multipart field name
72
+ # @param mime_type [String] MIME type for the uploaded file
73
+ # @return [Hash, nil] Parsed JSON response body
74
+ #
75
+ def post_multipart(path, file_path:, field_name: 'package', mime_type: 'application/octet-stream')
76
+ @rate_limiter.acquire
77
+ response = @http.post(path) do |req|
78
+ inject_headers(req, content_type: nil)
79
+ req.body = {
80
+ field_name => Faraday::Multipart::FilePart.new(file_path, mime_type)
81
+ }
82
+ end
83
+ handle_response(response)
84
+ end
85
+
86
+ ##
87
+ # Executes an authenticated PATCH request.
88
+ #
89
+ # @param path [String] Relative API path
90
+ # @param body [Hash] Partial update body
91
+ # @return [Hash, nil] Parsed JSON response body
92
+ # @raise [ReactorSDK::Error] on non-2xx after all retries exhausted
93
+ #
94
+ def patch(path, body)
95
+ @rate_limiter.acquire
96
+ response = @http.patch(path, body.to_json) { |req| inject_headers(req) }
97
+ handle_response(response)
98
+ end
99
+
100
+ ##
101
+ # Executes an authenticated DELETE request with no body.
102
+ #
103
+ # @param path [String] Relative API path
104
+ # @return [nil]
105
+ # @raise [ReactorSDK::Error] on non-2xx after all retries exhausted
106
+ #
107
+ def delete(path)
108
+ @rate_limiter.acquire
109
+ response = @http.delete(path) { |req| inject_headers(req) }
110
+ handle_response(response)
111
+ end
112
+
113
+ ##
114
+ # Executes an authenticated DELETE request with a JSON body.
115
+ # Used for JSON:API relationship removal.
116
+ #
117
+ # @param path [String] Relative API path
118
+ # @param body [Hash] Relationship payload
119
+ # @return [nil]
120
+ # @raise [ReactorSDK::Error] on non-2xx after all retries exhausted
121
+ #
122
+ def delete_relationship(path, body)
123
+ @rate_limiter.acquire
124
+ response = @http.run_request(:delete, path, body.to_json, {}) do |req|
125
+ inject_headers(req)
126
+ end
127
+ handle_response(response)
128
+ end
129
+
130
+ private
131
+
132
+ ##
133
+ # Builds the Faraday connection with retry middleware.
134
+ #
135
+ # @return [Faraday::Connection]
136
+ #
137
+ def build_faraday_connection
138
+ Faraday.new(url: @config.base_url) do |f|
139
+ f.request :multipart
140
+ f.request :retry, retry_options
141
+ f.response :logger, @config.logger if @config.logger
142
+ f.adapter :net_http
143
+ f.options.timeout = @config.timeout
144
+ f.options.open_timeout = 10
145
+ end
146
+ end
147
+
148
+ ##
149
+ # Retry configuration for transient Adobe API failures.
150
+ #
151
+ # @return [Hash]
152
+ #
153
+ def retry_options
154
+ {
155
+ max: 3,
156
+ interval: 1.0,
157
+ interval_randomness: 0.5,
158
+ backoff_factor: 2,
159
+ retry_statuses: [429, 500, 502, 503, 504]
160
+ }
161
+ end
162
+
163
+ ##
164
+ # Injects required Adobe authentication and versioning headers.
165
+ #
166
+ # @param req [Faraday::Request] Outgoing request
167
+ # @sideeffect Modifies req.headers
168
+ #
169
+ def inject_headers(req, content_type: CONTENT_TYPE)
170
+ req.headers['Authorization'] = "Bearer #{@auth.access_token}"
171
+ req.headers['x-api-key'] = @config.client_id
172
+ req.headers['x-gw-ims-org-id'] = @config.org_id
173
+ req.headers['Accept'] = ACCEPT_HEADER
174
+ req.headers['Content-Type'] = content_type if content_type
175
+ end
176
+
177
+ ##
178
+ # Parses the response and raises typed errors for non-2xx responses.
179
+ #
180
+ # @param response [Faraday::Response] Raw HTTP response
181
+ # @return [Hash, nil] Parsed response body or nil for 204
182
+ # @raise [ReactorSDK::Error] on non-2xx response
183
+ #
184
+ def handle_response(response)
185
+ return nil if response.status == 204
186
+
187
+ body = parse_body(response.body)
188
+ return body if response.status.between?(200, 299)
189
+
190
+ raise_error_for_status(response, body)
191
+ end
192
+
193
+ ##
194
+ # Raises the appropriate typed error for a non-2xx response.
195
+ # Extracts Adobe error detail from the response body when available.
196
+ #
197
+ # @param response [Faraday::Response] Raw HTTP response
198
+ # @param body [Hash, nil] Parsed response body
199
+ # @raise [ReactorSDK::Error]
200
+ #
201
+ def raise_error_for_status(response, body)
202
+ raise_rate_limit_error(response) if response.status == 429
203
+
204
+ raise build_error_for_status(response, body)
205
+ end
206
+
207
+ ##
208
+ # Builds the appropriate typed error for a non-2xx response.
209
+ #
210
+ # @param response [Faraday::Response]
211
+ # @param body [Hash, nil]
212
+ # @return [ReactorSDK::Error]
213
+ #
214
+ def build_error_for_status(response, body)
215
+ adobe_message = extract_adobe_message(body)
216
+
217
+ case response.status
218
+ when 400, 422
219
+ unprocessable_error(validation_message(response.status, adobe_message), body, status: response.status)
220
+ when 401
221
+ AuthenticationError.new('Unauthorized — check your Adobe IMS token', status: 401)
222
+ when 403
223
+ AuthorizationError.new('Forbidden — token lacks permission for this resource', status: 403)
224
+ when 404
225
+ ResourceNotFoundError.new("Resource not found: #{response.env.url.path}", status: 404)
226
+ when 405
227
+ method_not_allowed_error(response)
228
+ when 409
229
+ conflict_error(adobe_message)
230
+ when 500..599
231
+ ServerError.new("Adobe API server error (HTTP #{response.status})", status: response.status)
232
+ else
233
+ Error.new("Unexpected response status: #{response.status}", status: response.status)
234
+ end
235
+ end
236
+
237
+ ##
238
+ # Returns the default validation message for 400/422 responses.
239
+ #
240
+ # @param status [Integer]
241
+ # @param adobe_message [String, nil]
242
+ # @return [String]
243
+ #
244
+ def validation_message(status, adobe_message)
245
+ return adobe_message if adobe_message
246
+ return 'Bad request — check payload structure and required relationships' if status == 400
247
+
248
+ 'Validation failed'
249
+ end
250
+
251
+ ##
252
+ # Builds a validation-style error with the Adobe errors array attached.
253
+ #
254
+ # @param message [String]
255
+ # @param body [Hash, nil]
256
+ # @param status [Integer]
257
+ # @return [ReactorSDK::UnprocessableEntityError]
258
+ #
259
+ def unprocessable_error(message, body, status:)
260
+ UnprocessableEntityError.new(
261
+ message,
262
+ validation_errors: Array(body&.dig('errors')),
263
+ status: status
264
+ )
265
+ end
266
+
267
+ ##
268
+ # Builds a 405 error with request context.
269
+ #
270
+ # @param response [Faraday::Response]
271
+ # @return [ReactorSDK::Error]
272
+ #
273
+ def method_not_allowed_error(response)
274
+ Error.new(
275
+ 'Method not allowed — check the correct endpoint for this operation. ' \
276
+ "Adobe returned 405 for #{response.env.method.upcase} #{response.env.url.path}",
277
+ status: 405
278
+ )
279
+ end
280
+
281
+ ##
282
+ # Builds a 409 conflict error with Launch-specific guidance.
283
+ #
284
+ # @param adobe_message [String, nil]
285
+ # @return [ReactorSDK::Error]
286
+ #
287
+ def conflict_error(adobe_message)
288
+ Error.new(
289
+ adobe_message || 'Conflict — resource may need to be revised before this operation. ' \
290
+ 'Call revise() on the resource before adding it to a library.',
291
+ status: 409
292
+ )
293
+ end
294
+
295
+ ##
296
+ # Extracts a human-readable message from the Adobe error response body.
297
+ # Adobe returns errors in JSON:API format under the "errors" array.
298
+ #
299
+ # @param body [Hash, nil] Parsed response body
300
+ # @return [String, nil] First error detail or title, or nil if not present
301
+ #
302
+ def extract_adobe_message(body)
303
+ return nil unless body.is_a?(Hash)
304
+
305
+ errors = body['errors']
306
+ return nil unless errors.is_a?(Array) && errors.any?
307
+
308
+ first = errors.first
309
+ first['detail'] || first['title']
310
+ end
311
+
312
+ ##
313
+ # Raises a RateLimitError with retry_after from the response header.
314
+ #
315
+ # @param response [Faraday::Response]
316
+ # @raise [ReactorSDK::RateLimitError]
317
+ #
318
+ def raise_rate_limit_error(response)
319
+ retry_after = response.headers['Retry-After']&.to_i
320
+ raise RateLimitError.new(
321
+ "Rate limit exceeded — retry after #{retry_after} seconds",
322
+ retry_after: retry_after,
323
+ status: 429
324
+ )
325
+ end
326
+
327
+ ##
328
+ # Parses a JSON string into a Ruby Hash.
329
+ #
330
+ # @param body [String] Raw response body
331
+ # @return [Hash, nil]
332
+ # @raise [ReactorSDK::ParseError] if body is not valid JSON
333
+ #
334
+ def parse_body(body)
335
+ return nil if body.nil? || body.strip.empty?
336
+
337
+ JSON.parse(body)
338
+ rescue JSON::ParserError => e
339
+ raise ParseError.new('Could not parse API response as JSON', cause: e)
340
+ end
341
+ end
342
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ReactorSDK
4
+ module Endpoints
5
+ class AppConfigurations < BaseEndpoint
6
+ def list_for_company(company_id)
7
+ list_resources("/companies/#{company_id}/app_configurations", Resources::AppConfiguration)
8
+ end
9
+
10
+ def find(config_id)
11
+ fetch_resource("/app_configurations/#{config_id}", Resources::AppConfiguration)
12
+ end
13
+
14
+ def create(company_id:, attributes:)
15
+ create_resource(
16
+ "/companies/#{company_id}/app_configurations",
17
+ 'app_configurations',
18
+ Resources::AppConfiguration,
19
+ attributes: attributes
20
+ )
21
+ end
22
+
23
+ def update(config_id, attributes)
24
+ update_resource(
25
+ "/app_configurations/#{config_id}",
26
+ config_id,
27
+ 'app_configurations',
28
+ Resources::AppConfiguration,
29
+ attributes: attributes
30
+ )
31
+ end
32
+
33
+ def delete(config_id)
34
+ delete_resource("/app_configurations/#{config_id}")
35
+ end
36
+
37
+ def company(config_id)
38
+ fetch_resource("/app_configurations/#{config_id}/company", Resources::Company)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # @file endpoints/audit_events.rb
5
+ # @description Endpoint group for Adobe Launch Audit Event resources.
6
+ #
7
+ # Audit events record every significant action taken within Adobe Launch —
8
+ # creates, updates, deletes, publishes, and state transitions.
9
+ # LaunchGuard syncs these events into its own audit log to provide a
10
+ # complete cross-platform activity record.
11
+ #
12
+ # @domain Endpoints
13
+ # @see https://developer.adobe.com/experience-platform/documentation/tags/api/endpoints/audit-events/
14
+ #
15
+
16
+ module ReactorSDK
17
+ module Endpoints
18
+ class AuditEvents < BaseEndpoint
19
+ ##
20
+ # Lists audit events from Adobe's current global audit events endpoint.
21
+ # Follows pagination automatically — returns all events.
22
+ #
23
+ # @param since [String, nil] ISO8601 timestamp — only return events after this time
24
+ # @param updated_at [String, nil] Optional updated_at filter
25
+ # @param type_of [String, nil] Optional event type filter
26
+ # @return [Array<ReactorSDK::Resources::AuditEvent>]
27
+ #
28
+ def list(since: nil, updated_at: nil, type_of: nil)
29
+ params = {}
30
+ params['created_at'] = "GT #{since}" if since
31
+ params['updated_at'] = updated_at if updated_at
32
+ params['type_of'] = type_of if type_of
33
+
34
+ list_resources('/audit_events', Resources::AuditEvent, params: params)
35
+ end
36
+
37
+ ##
38
+ # Backward-compatible wrapper for older SDK integrations.
39
+ #
40
+ # Adobe's current official Reactor OpenAPI documents only the global
41
+ # `/audit_events` listing endpoint, so property scoping is no longer
42
+ # performed through the request path.
43
+ #
44
+ # @param _property_id [String]
45
+ # @param since [String, nil]
46
+ # @return [Array<ReactorSDK::Resources::AuditEvent>]
47
+ #
48
+ def list_for_property(_property_id, since: nil)
49
+ list(since: since)
50
+ end
51
+
52
+ ##
53
+ # Retrieves a single audit event by its Adobe ID.
54
+ #
55
+ # @param audit_event_id [String] Adobe audit event ID
56
+ # @return [ReactorSDK::Resources::AuditEvent]
57
+ # @raise [ReactorSDK::ResourceNotFoundError] if the event does not exist
58
+ #
59
+ def find(audit_event_id)
60
+ fetch_resource("/audit_events/#{audit_event_id}", Resources::AuditEvent)
61
+ end
62
+ end
63
+ end
64
+ end