noresponse 10.0.0 → 10.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e55989c19fddccb91ddc256165c976402eb3d9a5712b00bbb2b0a0a6f1f4afa
4
- data.tar.gz: 71626ebd2ea9eb53dda2073c04042a2c845e27c2b0bde7ace9334f59ff1f872b
3
+ metadata.gz: d4580d3276d8e1a6e9b3d9456c55be2baf736602067bf61df070a896ee39903b
4
+ data.tar.gz: 768921ebf728b70d51b2241e9ec839917f4791d22e0d0b665537b40d9b8a8aea
5
5
  SHA512:
6
- metadata.gz: cc890872c6ce60805f9f331962d4b47d2e81bb36974832b5168ef092b8a6aede034c6a3df16667dacdc66730a15a9d51e1bb9dd88ae27b87fbc0fb025798c6d6
7
- data.tar.gz: bc2dab6ef691946033ebca01af8fa3718a270b79ff611aca52fb93f7a23811da5fc742eab40c5d511d86e6b35e51613583094de4160acba2ea833a2c4fdb4582
6
+ metadata.gz: 88f56f17d0374b4df2a7886ee75ee575198c6cae98cf25042f2d4f72708b4311c301d2e6d1dc0a99c8dbe4cb6652c0f48d6c8f2852f0abe8df7a6c8f319feef9
7
+ data.tar.gz: 2c988fcfe40dc2d6731842e349a229e32b3bf43489adcf4ccaf0af840256208f804c143622412892f899a0b4fdc4de0a6d28e01d80f0b7af971878419da52eeb
@@ -50,7 +50,7 @@ module NoResponse
50
50
  }
51
51
  def create(body:, workspace_id:, idempotency_key: nil, timeout_ms: nil, http_headers: nil)
52
52
  # create - Request an asynchronous customer export.
53
- # Request an encrypted JSON Lines snapshot of retained account or audit records. Requires exports plus history or audit scope; detailed account data also needs evidence or detailed scope. Criteria are frozen and hashed. Each environment permits four active jobs, 64 MiB per artifact, and 256 KiB per chunk. Artifacts expire after 24 hours. Send an Idempotency-Key to safely repeat a request. Every operation checks current permissions and source retention.
53
+ # Request an encrypted JSON Lines snapshot of retained account or audit records. Requires exports plus history or audit scope. An account export without legacy history needs investigate with evidence or detailed, because it holds submitted identifiers; detailed account data always needs evidence or detailed. Criteria are frozen and hashed. Each environment permits four active jobs, 64 MiB per artifact, and 256 KiB per chunk. Artifacts expire after 24 hours. Send an Idempotency-Key to safely repeat a request. Every operation checks current permissions and source retention.
54
54
  request = Models::Operations::CreateCustomerExportRequest.new(
55
55
  workspace_id: workspace_id,
56
56
  body: body,
@@ -73,26 +73,81 @@ module NoResponse
73
73
  # current risk. A delivery that fails verification raises
74
74
  # WebhookVerification::VerificationError before anything is read; any
75
75
  # other event, and a test event, does nothing.
76
+ #
77
+ # with_account_lock serializes enforcement of one account across your
78
+ # workers. It is called with the account id and a callable: hold an
79
+ # exclusive lock on the account, call the callable, store the result's
80
+ # risk_version and any state change, then release the lock. An exception
81
+ # must propagate. Inside the lock the handler reads the account's risk and
82
+ # your state again, so a stale event or listed account cannot undo a newer
83
+ # decision. Without it, overlapping calls can act on an old decision after
84
+ # a newer one.
76
85
  def handle_risk_change(client, payload, headers, secrets, provider:, get_account_state:, flows: {},
77
- tolerance_seconds: WebhookVerification::DEFAULT_TOLERANCE_SECONDS, now: nil)
86
+ tolerance_seconds: WebhookVerification::DEFAULT_TOLERANCE_SECONDS, now: nil,
87
+ with_account_lock: nil)
78
88
  event = WebhookVerification.construct_event(payload, headers, secrets, tolerance_seconds: tolerance_seconds, now: now)
79
89
  if event.type != 'account.risk_changed' || (event.respond_to?(:test) && event.test == true)
80
90
  return EnforcementResult.new(event: event, skipped: 'not_actionable', enforcements: [])
81
91
  end
82
92
 
83
- begin
84
- account = client.accounts.get(account_id: event.data.account.id).result
85
- rescue Models::Errors::NotFoundError
86
- return EnforcementResult.new(event: event, skipped: 'account_gone', enforcements: [])
87
- end
88
- result = enforce_account(client, account, provider: provider, get_account_state: get_account_state, flows: flows)
93
+ result = under_account_lock(client, event.data.account.id, provider, get_account_state, flows, with_account_lock)
89
94
  result.event = event
90
95
  result
91
96
  end
92
97
 
93
- # Enforce one account as read: the account a handler fetched, or one from
94
- # accounts.list_changed(unenforced: true) when catching up.
95
- def enforce_account(client, account, provider:, get_account_state:, flows: {})
98
+ # Enforce one account: the account a handler fetched, or one from
99
+ # accounts.list_changed(unenforced: true) when catching up. With
100
+ # with_account_lock the account is read again inside the lock; without it
101
+ # the account is enforced as given.
102
+ def enforce_account(client, account, provider:, get_account_state:, flows: {}, with_account_lock: nil)
103
+ return under_account_lock(client, account.id, provider, get_account_state, flows, with_account_lock) if with_account_lock
104
+
105
+ enforce_as_read(client, account, provider, get_account_state, flows)
106
+ end
107
+
108
+ # Clerk, WorkOS or Auth0, run from the shared action table. token is the
109
+ # Clerk secret key, WorkOS API key or Auth0 Management API token; domain
110
+ # is the Auth0 tenant domain. Pass http (a Faraday connection) for a proxy or for tests.
111
+ def identity_provider(name, token:, domain: nil, base_url: nil, http: nil)
112
+ name = name.to_s
113
+ spec = TABLE.fetch('providers')[name] or raise ArgumentError, "unknown identity provider #{name.inspect}"
114
+ raise ArgumentError, "#{name} needs token" if token.to_s.empty?
115
+
116
+ base = base_url || spec.fetch('base_url')
117
+ if base.include?('{domain}')
118
+ raise ArgumentError, "#{name} needs domain or base_url" if domain.to_s.empty?
119
+
120
+ base = base.sub('{domain}', domain)
121
+ end
122
+ TableProvider.new(name, spec, base.sub(%r{/+\z}, ''), token, http || Faraday.new(request: { timeout: 10 }))
123
+ end
124
+
125
+ # Your own user store: one callable per action you support, keyed by
126
+ # action. A callable may return other actions it ran. An action without one fails.
127
+ def custom_provider(handlers)
128
+ CustomProvider.new(handlers.transform_keys(&:to_s))
129
+ end
130
+
131
+ private
132
+
133
+ # Read the account's current risk and enforce it, inside with_account_lock when you gave one.
134
+ def under_account_lock(client, account_id, provider, get_account_state, flows, with_account_lock)
135
+ enforce = lambda do
136
+ begin
137
+ account = client.accounts.get(account_id: account_id).result
138
+ rescue Models::Errors::NotFoundError
139
+ next EnforcementResult.new(skipped: 'account_gone', enforcements: [])
140
+ end
141
+ enforce_as_read(client, account, provider, get_account_state, flows)
142
+ end
143
+ return enforce.call unless with_account_lock
144
+
145
+ result = nil
146
+ with_account_lock.call(account_id, -> { result = enforce.call })
147
+ result or raise 'with_account_lock returned without running enforce'
148
+ end
149
+
150
+ def enforce_as_read(client, account, provider, get_account_state, flows)
96
151
  answer = get_account_state.call(account)
97
152
  return EnforcementResult.new(account: account, skipped: 'unknown_user', enforcements: []) if answer.nil?
98
153
 
@@ -149,31 +204,6 @@ module NoResponse
149
204
  EnforcementResult.new(account: account, state: state, risk_version: risk_version, enforcements: enforcements)
150
205
  end
151
206
 
152
- # Clerk, WorkOS or Auth0, run from the shared action table. token is the
153
- # Clerk secret key, WorkOS API key or Auth0 Management API token; domain
154
- # is the Auth0 tenant domain. Pass http (a Faraday connection) for a proxy or for tests.
155
- def identity_provider(name, token:, domain: nil, base_url: nil, http: nil)
156
- name = name.to_s
157
- spec = TABLE.fetch('providers')[name] or raise ArgumentError, "unknown identity provider #{name.inspect}"
158
- raise ArgumentError, "#{name} needs token" if token.to_s.empty?
159
-
160
- base = base_url || spec.fetch('base_url')
161
- if base.include?('{domain}')
162
- raise ArgumentError, "#{name} needs domain or base_url" if domain.to_s.empty?
163
-
164
- base = base.sub('{domain}', domain)
165
- end
166
- TableProvider.new(name, spec, base.sub(%r{/+\z}, ''), token, http || Faraday.new(request: { timeout: 10 }))
167
- end
168
-
169
- # Your own user store: one callable per action you support, keyed by
170
- # action. A callable may return other actions it ran. An action without one fails.
171
- def custom_provider(handlers)
172
- CustomProvider.new(handlers.transform_keys(&:to_s))
173
- end
174
-
175
- private
176
-
177
207
  # The error_code of a failed action: the provider's code, else its HTTP status, as <provider>.<code>.
178
208
  def failure_code(provider, error)
179
209
  code = error.respond_to?(:code) ? error.code : nil
@@ -948,7 +948,7 @@ module NoResponse
948
948
  }
949
949
  def preview(body:, workspace_id:, environment:, retries: nil, timeout_ms: nil, http_headers: nil)
950
950
  # preview - Preview a list override change without side effects.
951
- # Preview canonical list matches and current override effects with settings or management. The result states its bounded coverage and changes no list entries.
951
+ # Preview canonical list matches and current override effects with settings or management, plus legacy history, or investigate with evidence or detailed. The result states its bounded coverage and changes no list entries.
952
952
  request = Models::Operations::PreviewListRequest.new(
953
953
  workspace_id: workspace_id,
954
954
  environment: environment,
@@ -95,9 +95,9 @@ module NoResponse
95
95
 
96
96
  @language = "ruby"
97
97
  @openapi_doc_version = "1"
98
- @sdk_version = "10.0.0"
98
+ @sdk_version = "10.0.1"
99
99
  @gen_version = "internal"
100
- @user_agent = "noresponse-sdk/ruby 10.0.0 internal 1 noresponse"
100
+ @user_agent = "noresponse-sdk/ruby 10.0.1 internal 1 noresponse"
101
101
  end
102
102
 
103
103
  sig { returns([String, T::Hash[Symbol, String]]) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: noresponse
3
3
  version: !ruby/object:Gem::Version
4
- version: 10.0.0
4
+ version: 10.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - NoResponse