Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions lib/privy/public_api/services/key_quorums.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,20 @@ def create(key_quorum_create_params:, request_options: nil)
# @option key_quorum_update_params [Array<String>, nil] :user_ids User IDs to authorize.
# @option key_quorum_update_params [Array<String>, nil] :key_quorum_ids Nested key quorum IDs.
# @param authorization_context [Privy::Authorization::AuthorizationContext, nil] Authorization context for signing.
# @param request_expiry [Integer, nil] Absolute Unix-ms timestamp at which the
# request expires. Defaults to the value computed by the client's
# PrivyRequestExpiryOptions.
# @param request_options [Privy::RequestOptions, Hash, nil] Transport-level config (timeouts, retries).
#
# @return [Privy::Models::KeyQuorum]
def update(key_quorum_id, key_quorum_update_params:, authorization_context: nil, request_options: nil)
def update(key_quorum_id, key_quorum_update_params:, authorization_context: nil, request_expiry: nil, request_options: nil)
prepared = Privy::Authorization.prepare_request(
privy_client,
method: :patch,
url: Privy::Authorization.signed_url(privy_client, "v1/key_quorums/#{key_quorum_id}"),
body: key_quorum_update_params,
authorization_context: authorization_context
authorization_context: authorization_context,
request_expiry: privy_client.compute_request_expiry(request_expiry)
)
combined_params = key_quorum_update_params.merge(request_options: request_options)
Privy::Authorization.merge_prepared_headers!(combined_params, prepared.headers)
Expand All @@ -70,16 +74,20 @@ def update(key_quorum_id, key_quorum_update_params:, authorization_context: nil,
#
# @param key_quorum_id [String] ID of the key quorum to delete.
# @param authorization_context [Privy::Authorization::AuthorizationContext, nil] Authorization context for signing.
# @param request_expiry [Integer, nil] Absolute Unix-ms timestamp at which the
# request expires. Defaults to the value computed by the client's
# PrivyRequestExpiryOptions.
# @param request_options [Privy::RequestOptions, Hash, nil] Transport-level config (timeouts, retries).
#
# @return [Privy::Models::SuccessResponse]
def delete(key_quorum_id, authorization_context: nil, request_options: nil)
def delete(key_quorum_id, authorization_context: nil, request_expiry: nil, request_options: nil)
prepared = Privy::Authorization.prepare_request(
privy_client,
method: :delete,
url: Privy::Authorization.signed_url(privy_client, "v1/key_quorums/#{key_quorum_id}"),
body: "",
authorization_context: authorization_context
authorization_context: authorization_context,
request_expiry: privy_client.compute_request_expiry(request_expiry)
)
# Workaround: the Stainless-generated Ruby client sends Content-Type: application/json on
# every request (lib/privy/internal/transport/base_client.rb:204), even bodyless DELETEs.
Expand Down
104 changes: 104 additions & 0 deletions test/privy/public_api/services/key_quorums_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# frozen_string_literal: true

require_relative "../../test_helper"

class Privy::Services::KeyQuorumsTest < Minitest::Test
extend Minitest::Serial
include WebMock::API

BASE_URL = "https://api.staging.privy.io"

def before_all
super
WebMock.enable!
end

def after_all
WebMock.disable!
super
end

def teardown
WebMock.reset!
super
end

def build_client(**opts)
Privy::PrivyClient.new(app_id: "app-abc", app_secret: "secret", environment: :staging, **opts)
end

def key_quorum_response_body(**overrides)
{
id: "kq-1",
authorization_keys: [],
authorization_threshold: 1.0,
display_name: nil,
user_ids: nil
}.merge(overrides).to_json
end

def stub_json(method, url, body:)
stub_request(method, url).to_return(
status: 200,
body: body,
headers: {"content-type" => "application/json"}
)
end

# --- request_expiry on update ----------------------------------------------

def test_update_default_request_expiry_is_auto_set
stub_json(:patch, "#{BASE_URL}/v1/key_quorums/kq-1", body: key_quorum_response_body)
before = Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
build_client.key_quorums.update("kq-1", key_quorum_update_params: {authorization_threshold: 1})
assert_requested(:patch, "#{BASE_URL}/v1/key_quorums/kq-1") do |req|
assert_in_delta(before + (15 * 60 * 1_000), req.headers["Privy-Request-Expiry"].to_i, 5_000)
end
end

def test_update_per_call_request_expiry_overrides_default
stub_json(:patch, "#{BASE_URL}/v1/key_quorums/kq-1", body: key_quorum_response_body)
build_client.key_quorums.update(
"kq-1",
key_quorum_update_params: {authorization_threshold: 1},
request_expiry: 1_750_000_000_000
)
assert_requested(:patch, "#{BASE_URL}/v1/key_quorums/kq-1") do |req|
assert_equal("1750000000000", req.headers["Privy-Request-Expiry"])
end
end

def test_update_disabled_options_omit_header
stub_json(:patch, "#{BASE_URL}/v1/key_quorums/kq-1", body: key_quorum_response_body)
build_client(request_expiry: Privy::PrivyRequestExpiryOptions.build(disabled: true))
.key_quorums.update("kq-1", key_quorum_update_params: {authorization_threshold: 1})
assert_requested(:patch, "#{BASE_URL}/v1/key_quorums/kq-1") do |req|
refute(req.headers.key?("Privy-Request-Expiry"))
end
end

def test_update_disabled_options_still_honor_explicit_override
stub_json(:patch, "#{BASE_URL}/v1/key_quorums/kq-1", body: key_quorum_response_body)
build_client(request_expiry: Privy::PrivyRequestExpiryOptions.build(disabled: true))
.key_quorums.update(
"kq-1",
key_quorum_update_params: {authorization_threshold: 1},
request_expiry: 1_750_000_000_000
)
assert_requested(:patch, "#{BASE_URL}/v1/key_quorums/kq-1") do |req|
assert_equal("1750000000000", req.headers["Privy-Request-Expiry"])
end
end

# --- request_expiry on delete ----------------------------------------------

def test_delete_per_call_request_expiry_overrides_default
stub_request(:delete, "#{BASE_URL}/v1/key_quorums/kq-1").to_return(
status: 200, body: '{"success":true}', headers: {"content-type" => "application/json"}
)
build_client.key_quorums.delete("kq-1", request_expiry: 1_750_000_000_000)
assert_requested(:delete, "#{BASE_URL}/v1/key_quorums/kq-1") do |req|
assert_equal("1750000000000", req.headers["Privy-Request-Expiry"])
end
end
end
Loading