From 37a2b6d6b1579082ba02a9fa5cf90ca8927d5534 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Mon, 21 Oct 2024 17:42:24 +0200 Subject: [PATCH 01/14] Add faraday, refactor Request module to use the new http client --- Gemfile.lock | 98 +++++++++++++++++++--------------------- lib/seam/request.rb | 108 ++++++++++++++++++++------------------------ seam.gemspec | 3 +- 3 files changed, 97 insertions(+), 112 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4740027..885fcf3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,123 +2,119 @@ PATH remote: . specs: seam (2.0.0a2) - http (~> 5.2) + faraday (~> 2.7) + faraday-retry (~> 2.2.1) svix (~> 1.30) GEM remote: https://rubygems.org/ specs: - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) ansi (1.5.0) ast (2.4.2) - base64 (0.2.0) - bigdecimal (3.1.7) + bigdecimal (3.1.8) crack (1.0.0) bigdecimal rexml diff-lcs (1.5.1) - docile (1.4.0) - domain_name (0.6.20240107) + docile (1.4.1) ethon (0.16.0) ffi (>= 1.15.0) - ffi (1.16.3) - ffi-compiler (1.3.2) - ffi (>= 1.15.5) - rake + faraday (2.12.0) + faraday-net_http (>= 2.0, < 3.4) + json + logger + faraday-net_http (3.3.0) + net-http + faraday-retry (2.2.1) + faraday (~> 2.0) + ffi (1.17.0) + ffi (1.17.0-x86_64-linux-gnu) gem-release (2.2.2) - hashdiff (1.1.0) - http (5.2.0) - addressable (~> 2.8) - base64 (~> 0.1) - http-cookie (~> 1.0) - http-form_data (~> 2.2) - llhttp-ffi (~> 0.5.0) - http-cookie (1.0.5) - domain_name (~> 0.5) - http-form_data (2.3.0) + hashdiff (1.1.1) json (2.7.2) language_server-protocol (3.17.0.3) lint_roller (1.1.0) - llhttp-ffi (0.5.0) - ffi-compiler (~> 1.0) - rake (~> 13.0) + logger (1.6.1) multi_json (1.15.0) - parallel (1.24.0) + net-http (0.4.1) + uri + parallel (1.26.3) parse_gemspec (1.0.0) parse_gemspec-cli (1.0.0) multi_json parse_gemspec thor - parser (3.3.0.5) + parser (3.3.5.0) ast (~> 2.4.1) racc - public_suffix (5.0.5) - racc (1.7.3) + public_suffix (6.0.1) + racc (1.8.1) rainbow (3.1.1) rake (13.2.1) - regexp_parser (2.9.0) - rexml (3.2.6) + regexp_parser (2.9.2) + rexml (3.3.8) rspec (3.13.0) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) rspec-mocks (~> 3.13.0) - rspec-core (3.13.0) + rspec-core (3.13.1) rspec-support (~> 3.13.0) - rspec-expectations (3.13.0) + rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.0) + rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.1) - rubocop (1.62.1) + rubocop (1.66.1) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.31.1, < 2.0) + regexp_parser (>= 2.4, < 3.0) + rubocop-ast (>= 1.32.2, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.31.2) - parser (>= 3.3.0.4) - rubocop-performance (1.20.2) + rubocop-ast (1.32.3) + parser (>= 3.3.1.0) + rubocop-performance (1.22.1) rubocop (>= 1.48.1, < 2.0) - rubocop-ast (>= 1.30.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (1.13.0) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) - simplecov-console (0.9.1) + simplecov-console (0.9.2) ansi simplecov terminal-table - simplecov-html (0.12.3) + simplecov-html (0.13.1) simplecov_json_formatter (0.1.4) - standard (1.35.1) + standard (1.41.1) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.0) - rubocop (~> 1.62.0) + rubocop (~> 1.66.0) standard-custom (~> 1.0.0) - standard-performance (~> 1.3) + standard-performance (~> 1.5) standard-custom (1.0.2) lint_roller (~> 1.0) rubocop (~> 1.50) - standard-performance (1.3.1) + standard-performance (1.5.0) lint_roller (~> 1.1) - rubocop-performance (~> 1.20.2) - svix (1.30.0) + rubocop-performance (~> 1.22.0) + svix (1.38.0) typhoeus (~> 1.0, >= 1.0.1) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - thor (1.3.1) + thor (1.3.2) typhoeus (1.4.1) ethon (>= 0.9.0) - unicode-display_width (2.5.0) + unicode-display_width (2.6.0) + uri (0.13.1) webmock (3.0.1) addressable (>= 2.3.6) crack (>= 0.3.2) diff --git a/lib/seam/request.rb b/lib/seam/request.rb index de4b3cd..cde94df 100644 --- a/lib/seam/request.rb +++ b/lib/seam/request.rb @@ -1,76 +1,64 @@ # frozen_string_literal: true -require "http" +require "faraday" +require "faraday/retry" module Seam - class Request - attr_reader :endpoint, :debug - - def initialize(auth_headers:, endpoint:, debug: false) - @auth_headers = auth_headers - @endpoint = endpoint - @debug = debug - end - - def perform(method, uri, config = {}) - Logger.info("Request: #{method} #{uri} #{config}") if debug - - config[:body] = config[:body].to_json if config[:body] - - response = HTTP.request( - method, - build_url(uri), - {headers: headers}.merge(config) - ) - - return response.parse if response.status.success? - - handle_error_response(response, method, uri) - end - - protected + module Http + module Request + def self.create_faraday_client(endpoint, auth_headers, debug) + Faraday.new(endpoint) do |builder| + builder.request :json + builder.request :retry # TODO: provide retry options + builder.response :json + builder.response :logger if debug + builder.headers = auth_headers + end + end - def handle_error_response(response, _method, _uri) - status_code = response.status.code - request_id = response.headers["seam-request-id"] + def self.handle_error_response(response, method, path) + status_code = response.status + request_id = response.headers["seam-request-id"] - raise Http::UnauthorizedError.new(request_id) if status_code == 401 + raise Http::UnauthorizedError.new(request_id) if status_code == 401 - error = response.parse["error"] || {} - error_type = error["type"] || "unknown_error" - error_details = { - type: error_type, - message: error["message"] || "Unknown error", - data: error["data"] - } + error = response.body["error"] || {} + error_type = error["type"] || "unknown_error" + error_details = { + type: error_type, + message: error["message"] || "Unknown error", + data: error["data"] + } - if error_type == "invalid_input" - error_details["validation_errors"] = error["validation_errors"] + if error_type == "invalid_input" + error_details["validation_errors"] = error["validation_errors"] + raise Http::InvalidInputError.new(error_details, status_code, request_id) + end - raise Http::InvalidInputError.new( - error_details, status_code, request_id - ) + raise Http::ApiError.new(error_details, status_code, request_id) end - raise Http::ApiError.new(error_details, status_code, request_id) - end - - def build_url(uri) - "#{endpoint}#{uri}" - end + def self.request_seam(client, endpoint, method, path, config = {}) + url = "#{endpoint}#{path}" + headers = config[:headers] || {} + response = client.run_request(method, url, config[:body], headers.merge(default_headers)) - def headers - { - "User-Agent" => user_agent, - "Content-Type" => "application/json", - :"seam-sdk-name" => "seamapi/ruby", - :"seam-sdk-version" => Seam::VERSION, - :"seam-lts-version" => Seam::LTS_VERSION - }.merge(@auth_headers) - end + if response.success? + response + else + handle_error_response(response, method, path) + end + end - def user_agent - "seam-ruby/#{Seam::VERSION}" + def self.default_headers + { + "User-Agent" => "seam-ruby/#{Seam::VERSION}", + "Content-Type" => "application/json", + :"seam-sdk-name" => "seamapi/ruby", + :"seam-sdk-version" => Seam::VERSION, + :"seam-lts-version" => Seam::LTS_VERSION + } + end end end end diff --git a/seam.gemspec b/seam.gemspec index 7b6cf4b..3ad235f 100644 --- a/seam.gemspec +++ b/seam.gemspec @@ -26,7 +26,8 @@ Gem::Specification.new do |spec| spec.files = Dir["lib/**/*.rb"].reject { |f| f.end_with?("_spec.rb") } spec.files += Dir["[A-Z]*"] - spec.add_dependency "http", "~> 5.2" + spec.add_dependency "faraday", "~> 2.7" + spec.add_dependency "faraday-retry", "~> 2.2.1" spec.add_dependency "svix", "~> 1.30" spec.add_development_dependency "bundler", "~> 2.0" From 4a4d7ce6376fade5df6dd9dffd4dfdfccf8ad976 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Mon, 21 Oct 2024 17:43:14 +0200 Subject: [PATCH 02/14] Adjust SingleWorkspace and MultiWorkspace to use new setup --- lib/seam/http_multi_workspace.rb | 26 +++++++++++--------------- lib/seam/http_single_workspace.rb | 23 ++++++++--------------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/lib/seam/http_multi_workspace.rb b/lib/seam/http_multi_workspace.rb index 36c3441..1113187 100644 --- a/lib/seam/http_multi_workspace.rb +++ b/lib/seam/http_multi_workspace.rb @@ -1,20 +1,22 @@ # frozen_string_literal: true +require_relative "request" require_relative "parse_options" require_relative "lts_version" require_relative "auth" -require_relative "request" module Seam module Http class MultiWorkspace - attr_reader :wait_for_action_attempt, :defaults + attr_reader :client, :wait_for_action_attempt, :defaults - def initialize(personal_access_token:, endpoint: nil, wait_for_action_attempt: true) + def initialize(personal_access_token:, endpoint: nil, wait_for_action_attempt: true, debug: false) @wait_for_action_attempt = wait_for_action_attempt @defaults = {"wait_for_action_attempt" => wait_for_action_attempt} @endpoint = Http::Options.get_endpoint(endpoint) @auth_headers = Http::Auth.get_auth_headers_for_multi_workspace_personal_access_token(personal_access_token) + @debug = debug + @client = Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, @debug) end def self.lts_version @@ -29,29 +31,23 @@ def workspaces @workspaces ||= WorkspacesProxy.new(Seam::Clients::Workspaces.new(self)) end - def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_for_action_attempt: true) + def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_for_action_attempt: true, debug: false) new( personal_access_token: personal_access_token, endpoint: endpoint, - wait_for_action_attempt: wait_for_action_attempt + wait_for_action_attempt: wait_for_action_attempt, + debug: debug ) end def request_seam_object(method, path, klass, inner_object, config = {}) - response = request_seam(method, path, config) - - data = response[inner_object] - + response = Seam::Http::Request.request_seam(@client, @endpoint, method, path, config) + data = response.body[inner_object] klass.load_from_response(data, self) end def request_seam(method, path, config = {}) - Seam::Request.new( - auth_headers: @auth_headers, - endpoint: @endpoint - ).perform( - method, path, config - ) + Seam::Http::Request.request_seam(@client, @endpoint, method, path, config) end end end diff --git a/lib/seam/http_single_workspace.rb b/lib/seam/http_single_workspace.rb index f17d5d7..f35b030 100644 --- a/lib/seam/http_single_workspace.rb +++ b/lib/seam/http_single_workspace.rb @@ -1,24 +1,25 @@ # frozen_string_literal: true +require_relative "request" require_relative "parse_options" require_relative "routes/routes" -require_relative "request" module Seam module Http class SingleWorkspace include Seam::Routes - attr_accessor :defaults + attr_reader :client, :defaults - def initialize(api_key: nil, personal_access_token: nil, workspace_id: nil, endpoint: nil, + def initialize(client: nil, api_key: nil, personal_access_token: nil, workspace_id: nil, endpoint: nil, wait_for_action_attempt: true, debug: false) options = Http::Options.parse_options(api_key: api_key, personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint) @endpoint = options[:endpoint] @auth_headers = options[:auth_headers] @debug = debug - @wait_for_action_attempt = wait_for_action_attempt @defaults = Seam::DeepHashAccessor.new({"wait_for_action_attempt" => wait_for_action_attempt}) + + @client = client || Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, @debug) end def lts_version @@ -34,21 +35,13 @@ def self.from_personal_access_token(personal_access_token, workspace_id, endpoin end def request_seam_object(method, path, klass, inner_object, config = {}) - response = request_seam(method, path, config) - - data = response[inner_object] - + response = Seam::Http::Request.request_seam(@client, @endpoint, method, path, config) + data = response.body[inner_object] klass.load_from_response(data, self) end def request_seam(method, path, config = {}) - Seam::Request.new( - auth_headers: @auth_headers, - endpoint: @endpoint, - debug: @debug - ).perform( - method, path, config - ) + Seam::Http::Request.request_seam(@client, @endpoint, method, path, config) end end end From 494165431503d32a4e40d408ee858da5c3aac372 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Mon, 21 Oct 2024 17:43:25 +0200 Subject: [PATCH 03/14] Fix tests --- lib/seam/helpers/action_attempt.rb | 2 +- spec/clients/access_codes_spec.rb | 16 ++++++------- spec/clients/action_attempts_spec.rb | 2 +- spec/clients/connect_webviews_spec.rb | 4 ++-- spec/clients/connected_accounts_spec.rb | 6 ++--- spec/clients/devices_spec.rb | 10 ++++---- spec/clients/events_spec.rb | 4 ++-- spec/clients/locks_spec.rb | 4 ++-- spec/clients/unmanaged_access_codes_spec.rb | 12 +++++----- spec/clients/unmanaged_devices_spec.rb | 6 ++--- spec/resources/action_attempt_spec.rb | 4 ++-- spec/seam_client/http_error_spec.rb | 4 ++-- .../wait_for_action_attepmt_spec.rb | 24 +++++++++---------- 13 files changed, 49 insertions(+), 49 deletions(-) diff --git a/lib/seam/helpers/action_attempt.rb b/lib/seam/helpers/action_attempt.rb index 5b499e9..89dbc59 100644 --- a/lib/seam/helpers/action_attempt.rb +++ b/lib/seam/helpers/action_attempt.rb @@ -45,7 +45,7 @@ def self.update_action_attempt(action_attempt, client) } ) - action_attempt.update_from_response(response["action_attempt"]) + action_attempt.update_from_response(response.body["action_attempt"]) action_attempt end end diff --git a/spec/clients/access_codes_spec.rb b/spec/clients/access_codes_spec.rb index 415f5ae..969da42 100644 --- a/spec/clients/access_codes_spec.rb +++ b/spec/clients/access_codes_spec.rb @@ -13,7 +13,7 @@ before do stub_seam_request(:post, "/access_codes/list", {access_codes: [access_code_hash]}).with do |req| - req.body.source == {device_id: device_id}.to_json + req.body == {device_id: device_id}.to_json end end @@ -30,7 +30,7 @@ before do stub_seam_request(:post, "/access_codes/list", {access_codes: [access_code_hash]}).with do |req| - req.body.source == {access_code_ids: [access_code_id]}.to_json + req.body == {access_code_ids: [access_code_id]}.to_json end end @@ -58,7 +58,7 @@ errors: [failed_to_set_error], warnings: [delay_in_setting_warning] )} - ).with { |req| req.body.source == {access_code_id: access_code_id}.to_json } + ).with { |req| req.body == {access_code_id: access_code_id}.to_json } end let(:result) { client.access_codes.get(access_code_id: access_code_id) } @@ -100,7 +100,7 @@ stub_seam_request( :post, "/access_codes/delete", {action_attempt: action_attempt_hash} ).with do |req| - req.body.source == {access_code_id: access_code_id}.to_json + req.body == {access_code_id: access_code_id}.to_json end stub_seam_request( @@ -111,7 +111,7 @@ status: "success" } } - ).with { |req| req.body.source == {action_attempt_id: action_attempt_hash[:action_attempt_id]}.to_json } + ).with { |req| req.body == {action_attempt_id: action_attempt_hash[:action_attempt_id]}.to_json } end let(:result) { client.access_codes.delete(access_code_id: access_code_id) } @@ -129,7 +129,7 @@ stub_seam_request( :post, "/access_codes/update", {action_attempt: action_attempt_hash} ).with do |req| - req.body.source == {access_code_id: access_code_id, type: "ongoing"}.to_json + req.body == {access_code_id: access_code_id, type: "ongoing"}.to_json end stub_seam_request( @@ -140,7 +140,7 @@ status: "success" } } - ).with { |req| req.body.source == {action_attempt_id: action_attempt_hash[:action_attempt_id]}.to_json } + ).with { |req| req.body == {action_attempt_id: action_attempt_hash[:action_attempt_id]}.to_json } end let(:result) { client.access_codes.update(access_code_id: access_code_id, type: "ongoing") } @@ -158,7 +158,7 @@ stub_seam_request( :post, "/access_codes/pull_backup_access_code", {backup_access_code: access_code_hash} ).with do |req| - req.body.source == {access_code_id: access_code_id}.to_json + req.body == {access_code_id: access_code_id}.to_json end end diff --git a/spec/clients/action_attempts_spec.rb b/spec/clients/action_attempts_spec.rb index 5514472..e2411ad 100644 --- a/spec/clients/action_attempts_spec.rb +++ b/spec/clients/action_attempts_spec.rb @@ -10,7 +10,7 @@ before do stub_seam_request( :post, "/action_attempts/get", {action_attempt: action_attempt_hash} - ).with { |req| req.body.source == {action_attempt_id: action_attempt_id}.to_json } + ).with { |req| req.body == {action_attempt_id: action_attempt_id}.to_json } end let(:result) { client.action_attempts.get(action_attempt_id: action_attempt_id) } diff --git a/spec/clients/connect_webviews_spec.rb b/spec/clients/connect_webviews_spec.rb index 6d3de61..32b6eed 100644 --- a/spec/clients/connect_webviews_spec.rb +++ b/spec/clients/connect_webviews_spec.rb @@ -27,7 +27,7 @@ before do stub_seam_request( :post, "/connect_webviews/get", {connect_webview: connect_webview_hash} - ).with { |req| req.body.source == {connect_webview_id: connect_webview_id}.to_json } + ).with { |req| req.body == {connect_webview_id: connect_webview_id}.to_json } end let(:result) { client.connect_webviews.get(connect_webview_id: connect_webview_id) } @@ -49,7 +49,7 @@ stub_seam_request( :post, "/connect_webviews/create", {connect_webview: connect_webview_hash} ).with do |req| - req.body.source == { + req.body == { accepted_providers: accepted_providers, automatically_manage_new_devices: automatically_manage_new_devices, custom_redirect_failure_url: custom_redirect_failure_url, diff --git a/spec/clients/connected_accounts_spec.rb b/spec/clients/connected_accounts_spec.rb index 0ba41dd..129e1db 100644 --- a/spec/clients/connected_accounts_spec.rb +++ b/spec/clients/connected_accounts_spec.rb @@ -11,7 +11,7 @@ before do stub_seam_request( :post, "/connected_accounts/get", {connected_account: connected_account_hash} - ).with { |req| req.body.source == {connected_account_id: connected_account_id}.to_json } + ).with { |req| req.body == {connected_account_id: connected_account_id}.to_json } end let(:result) { client.connected_accounts.get(connected_account_id: connected_account_id) } @@ -27,7 +27,7 @@ before do stub_seam_request( :post, "/connected_accounts/get", {connected_account: connected_account_hash} - ).with { |req| req.body.source == {email: email}.to_json } + ).with { |req| req.body == {email: email}.to_json } end let(:result) { client.connected_accounts.get(email: email) } @@ -47,7 +47,7 @@ errors: [account_disconnected_error], warnings: [limit_reached_warning] )} - ).with { |req| req.body.source == {connected_account_id: connected_account_id}.to_json } + ).with { |req| req.body == {connected_account_id: connected_account_id}.to_json } end let(:result) { client.connected_accounts.get(connected_account_id: connected_account_id) } diff --git a/spec/clients/devices_spec.rb b/spec/clients/devices_spec.rb index 81faca2..07e2ac5 100644 --- a/spec/clients/devices_spec.rb +++ b/spec/clients/devices_spec.rb @@ -26,7 +26,7 @@ before do stub_seam_request(:post, "/devices/get", {device: device_hash}).with do |req| - req.body.source == {device_id: device_id}.to_json + req.body == {device_id: device_id}.to_json end end @@ -44,7 +44,7 @@ before do stub_seam_request(:post, "/devices/get", {device: device_hash}).with do |req| - req.body.source == {name: name}.to_json + req.body == {name: name}.to_json end end @@ -68,7 +68,7 @@ errors: [device_removed_error], warnings: [device_privacy_warning] ) - }).with { |req| req.body.source == {device_id: device_id}.to_json } + }).with { |req| req.body == {device_id: device_id}.to_json } end let(:result) { client.devices.get(device_id: device_id) } @@ -123,7 +123,7 @@ before do stub_seam_request(:post, "/devices/list_device_providers", {device_providers: [stable_device_provider_hash]}) - .with { |req| req.body.source == {provider_category: "stable"}.to_json } + .with { |req| req.body == {provider_category: "stable"}.to_json } end let(:device_providers) { client.devices.list_device_providers(provider_category: "stable") } @@ -146,7 +146,7 @@ before do stub_seam_request(:post, "/devices/update", nil) .with do |req| - req.body.source == {device_id: device_id, name: name}.to_json + req.body == {device_id: device_id, name: name}.to_json end end diff --git a/spec/clients/events_spec.rb b/spec/clients/events_spec.rb index 7301507..d25d756 100644 --- a/spec/clients/events_spec.rb +++ b/spec/clients/events_spec.rb @@ -8,7 +8,7 @@ before do stub_seam_request(:post, "/events/list", {events: [event_hash]}).with do |req| - req.body.source == {since: "asd"}.to_json + req.body == {since: "asd"}.to_json end end @@ -27,7 +27,7 @@ before do stub_seam_request(:post, "/events/get", {event: event_hash}).with do |req| - req.body.source == {event_id: event_id}.to_json + req.body == {event_id: event_id}.to_json end end diff --git a/spec/clients/locks_spec.rb b/spec/clients/locks_spec.rb index 8a85895..dd49a50 100644 --- a/spec/clients/locks_spec.rb +++ b/spec/clients/locks_spec.rb @@ -25,7 +25,7 @@ before do stub_seam_request(:post, "/locks/get", {device: locks_hash}).with do |req| - req.body.source == {device_id: device_id}.to_json + req.body == {device_id: device_id}.to_json end end @@ -56,7 +56,7 @@ action_attempt: action_attempt_hash } ).with do |req| - req.body.source == {device_id: device_id}.to_json + req.body == {device_id: device_id}.to_json end end diff --git a/spec/clients/unmanaged_access_codes_spec.rb b/spec/clients/unmanaged_access_codes_spec.rb index cd1eec2..98d1126 100644 --- a/spec/clients/unmanaged_access_codes_spec.rb +++ b/spec/clients/unmanaged_access_codes_spec.rb @@ -10,7 +10,7 @@ before do stub_seam_request( :post, "/access_codes/unmanaged/get", {access_code: unmanaged_access_code_hash} - ).with { |req| req.body.source == {access_code_id: access_code_id}.to_json } + ).with { |req| req.body == {access_code_id: access_code_id}.to_json } end let(:result) { client.unmanaged_access_codes.get(access_code_id: access_code_id) } @@ -27,7 +27,7 @@ before do stub_seam_request(:post, "/access_codes/unmanaged/list", {access_codes: [unmanaged_access_code_hash]}).with do |req| - req.body.source == {device_id: device_id}.to_json + req.body == {device_id: device_id}.to_json end end @@ -48,14 +48,14 @@ stub_seam_request( :post, "/access_codes/unmanaged/convert_to_managed", {action_attempt: action_attempt_hash} ).with do |req| - req.body.source == {access_code_id: access_code_id}.to_json + req.body == {access_code_id: access_code_id}.to_json end stub_seam_request( :post, "/action_attempts/get", nil - ).with { |req| req.body.source == {action_attempt_id: action_attempt_hash[:action_attempt_id]}.to_json } + ).with { |req| req.body == {action_attempt_id: action_attempt_hash[:action_attempt_id]}.to_json } end let(:result) { client.unmanaged_access_codes.convert_to_managed(access_code_id: access_code_id) } @@ -73,14 +73,14 @@ stub_seam_request( :post, "/access_codes/unmanaged/delete", {action_attempt: action_attempt_hash} ).with do |req| - req.body.source == {access_code_id: access_code_id}.to_json + req.body == {access_code_id: access_code_id}.to_json end stub_seam_request( :post, "/action_attempts/get", nil - ).with { |req| req.body.source == {action_attempt_id: action_attempt_hash[:action_attempt_id]}.to_json } + ).with { |req| req.body == {action_attempt_id: action_attempt_hash[:action_attempt_id]}.to_json } end let(:result) { client.unmanaged_access_codes.delete(access_code_id: access_code_id) } diff --git a/spec/clients/unmanaged_devices_spec.rb b/spec/clients/unmanaged_devices_spec.rb index f272e20..4b1f247 100644 --- a/spec/clients/unmanaged_devices_spec.rb +++ b/spec/clients/unmanaged_devices_spec.rb @@ -11,7 +11,7 @@ before do stub_seam_request( :post, "/devices/unmanaged/get", {device: device_hash} - ).with { |req| req.body.source == {device_id: device_id}.to_json } + ).with { |req| req.body == {device_id: device_id}.to_json } end let(:result) { client.unmanaged_devices.get(device_id: device_id) } @@ -28,7 +28,7 @@ before do stub_seam_request( :post, "/devices/unmanaged/get", {device: device_hash} - ).with { |req| req.body.source == {name: name}.to_json } + ).with { |req| req.body == {name: name}.to_json } end let(:result) { client.unmanaged_devices.get(name: name) } @@ -62,7 +62,7 @@ before do stub_seam_request(:post, "/devices/unmanaged/update", nil) .with do |req| - req.body.source == {device_id: device_id, is_managed: is_managed}.to_json + req.body == {device_id: device_id, is_managed: is_managed}.to_json end end diff --git a/spec/resources/action_attempt_spec.rb b/spec/resources/action_attempt_spec.rb index ac1bc7f..f94efc2 100644 --- a/spec/resources/action_attempt_spec.rb +++ b/spec/resources/action_attempt_spec.rb @@ -55,7 +55,7 @@ :post, "/action_attempts/get", {action_attempt: action_attempt_hash} - ).with { |req| req.body.source == {action_attempt_id: action_attempt_id}.to_json } + ).with { |req| req.body == {"action_attempt_id" => action_attempt_id}.to_json } .times(2) .then .to_return( @@ -84,7 +84,7 @@ :post, "/action_attempts/get", {action_attempt: updated_action_attempt_hash} - ).with { |req| req.body.source == {action_attempt_id: action_attempt_id}.to_json } + ).with { |req| req.body == {action_attempt_id: action_attempt_id}.to_json } end it "updates the ActionAttempt" do diff --git a/spec/seam_client/http_error_spec.rb b/spec/seam_client/http_error_spec.rb index 5a32eac..0c90a9a 100644 --- a/spec/seam_client/http_error_spec.rb +++ b/spec/seam_client/http_error_spec.rb @@ -23,7 +23,7 @@ stub_seam_request(:post, "/devices/list", error_response, status: 400).with do |req| - req.body.source == {device_ids: 123}.to_json + req.body == {device_ids: 123}.to_json end expect do @@ -39,7 +39,7 @@ stub_seam_request(:post, "/devices/list", error_response, status: 400).with do |req| - req.body.source == {device_ids: 123}.to_json + req.body == {device_ids: 123}.to_json end begin diff --git a/spec/seam_client/wait_for_action_attepmt_spec.rb b/spec/seam_client/wait_for_action_attepmt_spec.rb index 00103bc..59bab28 100644 --- a/spec/seam_client/wait_for_action_attepmt_spec.rb +++ b/spec/seam_client/wait_for_action_attepmt_spec.rb @@ -39,7 +39,7 @@ client = described_class.new(api_key: api_key, wait_for_action_attempt: false) stub_seam_request(:post, "/locks/unlock_door", success_response) - .with { |req| req.body.source == {device_id: device_id}.to_json } + .with { |req| req.body == {device_id: device_id}.to_json } action_attempt = client.locks.unlock_door(device_id: device_id, wait_for_action_attempt: true) expect(action_attempt.status).to eq("success") @@ -49,7 +49,7 @@ client = described_class.new(api_key: api_key) stub_seam_request(:post, "/locks/unlock_door", success_response) - .with { |req| req.body.source == {device_id: device_id}.to_json } + .with { |req| req.body == {device_id: device_id}.to_json } action_attempt = client.locks.unlock_door(device_id: device_id) expect(action_attempt.status).to eq("success") @@ -59,7 +59,7 @@ client = described_class.new(api_key: api_key, wait_for_action_attempt: false) stub_seam_request(:post, "/locks/unlock_door", pending_response) - .with { |req| req.body.source == {device_id: device_id}.to_json } + .with { |req| req.body == {device_id: device_id}.to_json } action_attempt = client.locks.unlock_door(device_id: device_id) expect(action_attempt.status).to eq("pending") @@ -69,7 +69,7 @@ client = described_class.new(api_key: api_key, wait_for_action_attempt: {timeout: 5}) stub_seam_request(:post, "/locks/unlock_door", success_response) - .with { |req| req.body.source == {device_id: device_id}.to_json } + .with { |req| req.body == {device_id: device_id}.to_json } action_attempt = client.locks.unlock_door(device_id: device_id) expect(action_attempt.status).to eq("success") @@ -79,13 +79,13 @@ client = described_class.new(api_key: api_key, wait_for_action_attempt: false) stub_seam_request(:post, "/locks/unlock_door", pending_response) - .with { |req| req.body.source == {device_id: device_id}.to_json } + .with { |req| req.body == {device_id: device_id}.to_json } action_attempt = client.locks.unlock_door(device_id: device_id) expect(action_attempt.status).to eq("pending") stub_seam_request(:post, "/action_attempts/get", success_response) - .with { |req| req.body.source == {action_attempt_id: "1234"}.to_json } + .with { |req| req.body == {action_attempt_id: "1234"}.to_json } successful_action_attempt = client.action_attempts.get( action_attempt_id: action_attempt.action_attempt_id @@ -106,13 +106,13 @@ client = described_class.new(api_key: api_key, wait_for_action_attempt: false) stub_seam_request(:post, "/locks/unlock_door", pending_response) - .with { |req| req.body.source == {device_id: device_id}.to_json } + .with { |req| req.body == {device_id: device_id}.to_json } action_attempt = client.locks.unlock_door(device_id: device_id) expect(action_attempt.status).to eq("pending") stub_seam_request(:post, "/action_attempts/get", pending_response) - .with { |req| req.body.source == {action_attempt_id: "1234"}.to_json } + .with { |req| req.body == {action_attempt_id: "1234"}.to_json } expect do client.action_attempts.get( @@ -129,13 +129,13 @@ client = described_class.new(api_key: api_key, wait_for_action_attempt: false) stub_seam_request(:post, "/locks/unlock_door", pending_response) - .with { |req| req.body.source == {device_id: device_id}.to_json } + .with { |req| req.body == {device_id: device_id}.to_json } action_attempt = client.locks.unlock_door(device_id: device_id) expect(action_attempt.status).to eq("pending") stub_seam_request(:post, "/action_attempts/get", error_response) - .with { |req| req.body.source == {action_attempt_id: "1234"}.to_json } + .with { |req| req.body == {action_attempt_id: "1234"}.to_json } expect do client.action_attempts.get( @@ -154,13 +154,13 @@ client = described_class.new(api_key: api_key, wait_for_action_attempt: false) stub_seam_request(:post, "/locks/unlock_door", pending_response) - .with { |req| req.body.source == {device_id: device_id}.to_json } + .with { |req| req.body == {device_id: device_id}.to_json } action_attempt = client.locks.unlock_door(device_id: device_id) expect(action_attempt.status).to eq("pending") stub_seam_request(:post, "/action_attempts/get", pending_response) - .with { |req| req.body.source == {action_attempt_id: "1234"}.to_json } + .with { |req| req.body == {action_attempt_id: "1234"}.to_json } expect do client.action_attempts.get( From ba09efc47a7289dd4d836ad0c4df88bb6a0e829e Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Mon, 21 Oct 2024 15:44:01 +0000 Subject: [PATCH 04/14] ci: Generate code --- lib/seam/http.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/seam/http.rb b/lib/seam/http.rb index cdbd2cf..477991c 100644 --- a/lib/seam/http.rb +++ b/lib/seam/http.rb @@ -40,7 +40,7 @@ class InvalidInputError < ApiError attr_reader :validation_errors def initialize(error, status_code, request_id) - super(error, status_code, request_id) + super @code = "invalid_input" @validation_errors = error["validation_errors"] || {} end From 3e034e067c38ebed60df3a9aef24fbebf7426575 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Mon, 21 Oct 2024 19:24:57 +0200 Subject: [PATCH 05/14] Remove debug flag --- lib/seam.rb | 8 ++++---- lib/seam/http.rb | 9 ++++----- lib/seam/http_multi_workspace.rb | 10 ++++------ lib/seam/http_single_workspace.rb | 13 ++++++------- lib/seam/request.rb | 3 +-- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/lib/seam.rb b/lib/seam.rb index a2a5928..6a6030d 100644 --- a/lib/seam.rb +++ b/lib/seam.rb @@ -10,12 +10,12 @@ def self.new(**args) Http.new(**args) end - def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false, debug: false) - Http.from_api_key(api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, debug: debug) + def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false) + Http.from_api_key(api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt) end - def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false, debug: false) - Http.from_personal_access_token(personal_access_token, workspace_id, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, debug: debug) + def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false) + Http.from_personal_access_token(personal_access_token, workspace_id, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt) end def self.lts_version diff --git a/lib/seam/http.rb b/lib/seam/http.rb index 477991c..658d984 100644 --- a/lib/seam/http.rb +++ b/lib/seam/http.rb @@ -8,14 +8,13 @@ def self.new(**args) Http::SingleWorkspace.new(**args) end - def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false, debug: false) - Http::SingleWorkspace.from_api_key(api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, - debug: debug) + def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false) + Http::SingleWorkspace.from_api_key(api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt) end - def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false, debug: false) + def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false) Http::SingleWorkspace.from_personal_access_token(personal_access_token, workspace_id, endpoint: endpoint, - wait_for_action_attempt: wait_for_action_attempt, debug: debug) + wait_for_action_attempt: wait_for_action_attempt) end class ApiError < StandardError diff --git a/lib/seam/http_multi_workspace.rb b/lib/seam/http_multi_workspace.rb index 1113187..0cb95e7 100644 --- a/lib/seam/http_multi_workspace.rb +++ b/lib/seam/http_multi_workspace.rb @@ -10,13 +10,12 @@ module Http class MultiWorkspace attr_reader :client, :wait_for_action_attempt, :defaults - def initialize(personal_access_token:, endpoint: nil, wait_for_action_attempt: true, debug: false) + def initialize(personal_access_token:, endpoint: nil, wait_for_action_attempt: true) @wait_for_action_attempt = wait_for_action_attempt @defaults = {"wait_for_action_attempt" => wait_for_action_attempt} @endpoint = Http::Options.get_endpoint(endpoint) @auth_headers = Http::Auth.get_auth_headers_for_multi_workspace_personal_access_token(personal_access_token) - @debug = debug - @client = Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, @debug) + @client = Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers) end def self.lts_version @@ -31,12 +30,11 @@ def workspaces @workspaces ||= WorkspacesProxy.new(Seam::Clients::Workspaces.new(self)) end - def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_for_action_attempt: true, debug: false) + def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_for_action_attempt: true) new( personal_access_token: personal_access_token, endpoint: endpoint, - wait_for_action_attempt: wait_for_action_attempt, - debug: debug + wait_for_action_attempt: wait_for_action_attempt ) end diff --git a/lib/seam/http_single_workspace.rb b/lib/seam/http_single_workspace.rb index f35b030..9f6499d 100644 --- a/lib/seam/http_single_workspace.rb +++ b/lib/seam/http_single_workspace.rb @@ -12,26 +12,25 @@ class SingleWorkspace attr_reader :client, :defaults def initialize(client: nil, api_key: nil, personal_access_token: nil, workspace_id: nil, endpoint: nil, - wait_for_action_attempt: true, debug: false) + wait_for_action_attempt: true) options = Http::Options.parse_options(api_key: api_key, personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint) @endpoint = options[:endpoint] @auth_headers = options[:auth_headers] - @debug = debug @defaults = Seam::DeepHashAccessor.new({"wait_for_action_attempt" => wait_for_action_attempt}) - @client = client || Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, @debug) + @client = client || Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers) end def lts_version Seam::LTS_VERSION end - def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false, debug: false) - new(api_key: api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, debug: debug) + def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false) + new(api_key: api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt) end - def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false, debug: false) - new(personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, debug: debug) + def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false) + new(personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt) end def request_seam_object(method, path, klass, inner_object, config = {}) diff --git a/lib/seam/request.rb b/lib/seam/request.rb index cde94df..f54cb67 100644 --- a/lib/seam/request.rb +++ b/lib/seam/request.rb @@ -6,12 +6,11 @@ module Seam module Http module Request - def self.create_faraday_client(endpoint, auth_headers, debug) + def self.create_faraday_client(endpoint, auth_headers) Faraday.new(endpoint) do |builder| builder.request :json builder.request :retry # TODO: provide retry options builder.response :json - builder.response :logger if debug builder.headers = auth_headers end end From 93df84689c8d765371fd4eac34626ed0955a58d0 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Mon, 21 Oct 2024 19:29:31 +0200 Subject: [PATCH 06/14] Set retry backoff_factor to 2 --- lib/seam/request.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/seam/request.rb b/lib/seam/request.rb index f54cb67..5ee8aa0 100644 --- a/lib/seam/request.rb +++ b/lib/seam/request.rb @@ -9,7 +9,7 @@ module Request def self.create_faraday_client(endpoint, auth_headers) Faraday.new(endpoint) do |builder| builder.request :json - builder.request :retry # TODO: provide retry options + builder.request :retry, backoff_factor: 2 builder.response :json builder.headers = auth_headers end From fde0e7829e56341624891ba8fb05e51cecd50da3 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Mon, 21 Oct 2024 19:33:50 +0200 Subject: [PATCH 07/14] Set default headers when creating the client --- lib/seam/request.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/seam/request.rb b/lib/seam/request.rb index 5ee8aa0..e0b5c8f 100644 --- a/lib/seam/request.rb +++ b/lib/seam/request.rb @@ -11,7 +11,7 @@ def self.create_faraday_client(endpoint, auth_headers) builder.request :json builder.request :retry, backoff_factor: 2 builder.response :json - builder.headers = auth_headers + builder.headers = auth_headers.merge(default_headers) end end @@ -39,8 +39,7 @@ def self.handle_error_response(response, method, path) def self.request_seam(client, endpoint, method, path, config = {}) url = "#{endpoint}#{path}" - headers = config[:headers] || {} - response = client.run_request(method, url, config[:body], headers.merge(default_headers)) + response = client.run_request(method, url, config[:body], config[:headers]) if response.success? response From bee5fbf6d1d2bae34582cbd5bd52cb8385632734 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Mon, 21 Oct 2024 19:49:57 +0200 Subject: [PATCH 08/14] Support passing http client options --- lib/seam/http_multi_workspace.rb | 9 ++--- lib/seam/http_single_workspace.rb | 12 +++---- lib/seam/request.rb | 26 ++++++++++++-- spec/seam_client/client_options_spec.rb | 46 +++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 spec/seam_client/client_options_spec.rb diff --git a/lib/seam/http_multi_workspace.rb b/lib/seam/http_multi_workspace.rb index 0cb95e7..8992e9d 100644 --- a/lib/seam/http_multi_workspace.rb +++ b/lib/seam/http_multi_workspace.rb @@ -10,12 +10,12 @@ module Http class MultiWorkspace attr_reader :client, :wait_for_action_attempt, :defaults - def initialize(personal_access_token:, endpoint: nil, wait_for_action_attempt: true) + def initialize(personal_access_token:, endpoint: nil, wait_for_action_attempt: true, client_options: {}) @wait_for_action_attempt = wait_for_action_attempt @defaults = {"wait_for_action_attempt" => wait_for_action_attempt} @endpoint = Http::Options.get_endpoint(endpoint) @auth_headers = Http::Auth.get_auth_headers_for_multi_workspace_personal_access_token(personal_access_token) - @client = Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers) + @client = Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, client_options) end def self.lts_version @@ -30,11 +30,12 @@ def workspaces @workspaces ||= WorkspacesProxy.new(Seam::Clients::Workspaces.new(self)) end - def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_for_action_attempt: true) + def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_for_action_attempt: true, client_options: {}) new( personal_access_token: personal_access_token, endpoint: endpoint, - wait_for_action_attempt: wait_for_action_attempt + wait_for_action_attempt: wait_for_action_attempt, + client_options: client_options ) end diff --git a/lib/seam/http_single_workspace.rb b/lib/seam/http_single_workspace.rb index 9f6499d..9e9bb46 100644 --- a/lib/seam/http_single_workspace.rb +++ b/lib/seam/http_single_workspace.rb @@ -12,25 +12,25 @@ class SingleWorkspace attr_reader :client, :defaults def initialize(client: nil, api_key: nil, personal_access_token: nil, workspace_id: nil, endpoint: nil, - wait_for_action_attempt: true) + wait_for_action_attempt: true, client_options: {}) options = Http::Options.parse_options(api_key: api_key, personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint) @endpoint = options[:endpoint] @auth_headers = options[:auth_headers] @defaults = Seam::DeepHashAccessor.new({"wait_for_action_attempt" => wait_for_action_attempt}) - @client = client || Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers) + @client = client || Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, client_options) end def lts_version Seam::LTS_VERSION end - def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false) - new(api_key: api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt) + def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false, client_options: {}) + new(api_key: api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, client_options: client_options) end - def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false) - new(personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt) + def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false, client_options: {}) + new(personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, client_options: client_options) end def request_seam_object(method, path, klass, inner_object, config = {}) diff --git a/lib/seam/request.rb b/lib/seam/request.rb index e0b5c8f..8a829aa 100644 --- a/lib/seam/request.rb +++ b/lib/seam/request.rb @@ -6,12 +6,18 @@ module Seam module Http module Request - def self.create_faraday_client(endpoint, auth_headers) - Faraday.new(endpoint) do |builder| + def self.create_faraday_client(endpoint, auth_headers, client_options = {}) + default_options = { + url: endpoint, + headers: auth_headers.merge(default_headers) + } + + options = deep_merge(default_options, client_options) + + Faraday.new(options) do |builder| builder.request :json builder.request :retry, backoff_factor: 2 builder.response :json - builder.headers = auth_headers.merge(default_headers) end end @@ -57,6 +63,20 @@ def self.default_headers :"seam-lts-version" => Seam::LTS_VERSION } end + + def self.deep_merge(hash1, hash2) + result = hash1.dup + hash2.each do |key, value| + result[key] = if value.is_a?(Hash) && result[key].is_a?(Hash) + deep_merge(result[key], value) + else + value + end + end + result + end + + private_class_method :deep_merge end end end diff --git a/spec/seam_client/client_options_spec.rb b/spec/seam_client/client_options_spec.rb new file mode 100644 index 0000000..94a1935 --- /dev/null +++ b/spec/seam_client/client_options_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Seam::Http::SingleWorkspace do + let(:api_key) { "seam_test_api_key" } + let(:endpoint) { "https://example.com/api" } + let(:client_options) do + { + headers: { "Custom-Header" => "Test-Value" }, + request: { timeout: 30 } + } + end + + describe "client options" do + it "passes client_options to the Faraday client" do + expect(Faraday).to receive(:new).with( + hash_including( + headers: hash_including("Custom-Header" => "Test-Value"), + request: { timeout: 30 } + ) + ).and_call_original + + client = described_class.new( + api_key: api_key, + endpoint: endpoint, + client_options: client_options + ) + + expect(client).to be_a(Seam::Http::SingleWorkspace) + end + + it "merges client_options with default options" do + client = described_class.new( + api_key: api_key, + endpoint: endpoint, + client_options: client_options + ) + + faraday_client = client.instance_variable_get(:@client) + expect(faraday_client.headers["Custom-Header"]).to eq("Test-Value") + expect(faraday_client.options.timeout).to eq(30) + expect(faraday_client.headers["Authorization"]).to eq("Bearer #{api_key}") + end + end +end From d0a849e11009872078ab52d3c819fcaee66becf0 Mon Sep 17 00:00:00 2001 From: Seam Bot Date: Mon, 21 Oct 2024 17:50:23 +0000 Subject: [PATCH 09/14] ci: Format code --- spec/seam_client/client_options_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/seam_client/client_options_spec.rb b/spec/seam_client/client_options_spec.rb index 94a1935..8f7e1de 100644 --- a/spec/seam_client/client_options_spec.rb +++ b/spec/seam_client/client_options_spec.rb @@ -7,8 +7,8 @@ let(:endpoint) { "https://example.com/api" } let(:client_options) do { - headers: { "Custom-Header" => "Test-Value" }, - request: { timeout: 30 } + headers: {"Custom-Header" => "Test-Value"}, + request: {timeout: 30} } end @@ -17,7 +17,7 @@ expect(Faraday).to receive(:new).with( hash_including( headers: hash_including("Custom-Header" => "Test-Value"), - request: { timeout: 30 } + request: {timeout: 30} ) ).and_call_original From f890b064dfa222fab3922c0bfb29b438a94f70c5 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Mon, 21 Oct 2024 20:04:14 +0200 Subject: [PATCH 10/14] Support passing retry options --- lib/seam/http_multi_workspace.rb | 9 +++++---- lib/seam/http_single_workspace.rb | 12 ++++++------ lib/seam/request.rb | 11 +++++++++-- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/seam/http_multi_workspace.rb b/lib/seam/http_multi_workspace.rb index 8992e9d..1960b77 100644 --- a/lib/seam/http_multi_workspace.rb +++ b/lib/seam/http_multi_workspace.rb @@ -10,12 +10,12 @@ module Http class MultiWorkspace attr_reader :client, :wait_for_action_attempt, :defaults - def initialize(personal_access_token:, endpoint: nil, wait_for_action_attempt: true, client_options: {}) + def initialize(personal_access_token:, endpoint: nil, wait_for_action_attempt: true, client_options: {}, retry_options: {}) @wait_for_action_attempt = wait_for_action_attempt @defaults = {"wait_for_action_attempt" => wait_for_action_attempt} @endpoint = Http::Options.get_endpoint(endpoint) @auth_headers = Http::Auth.get_auth_headers_for_multi_workspace_personal_access_token(personal_access_token) - @client = Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, client_options) + @client = Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, client_options, retry_options) end def self.lts_version @@ -30,12 +30,13 @@ def workspaces @workspaces ||= WorkspacesProxy.new(Seam::Clients::Workspaces.new(self)) end - def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_for_action_attempt: true, client_options: {}) + def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_for_action_attempt: true, client_options: {}, retry_options: {}) new( personal_access_token: personal_access_token, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, - client_options: client_options + client_options: client_options, + retry_options: retry_options ) end diff --git a/lib/seam/http_single_workspace.rb b/lib/seam/http_single_workspace.rb index 9e9bb46..b1bfd90 100644 --- a/lib/seam/http_single_workspace.rb +++ b/lib/seam/http_single_workspace.rb @@ -12,25 +12,25 @@ class SingleWorkspace attr_reader :client, :defaults def initialize(client: nil, api_key: nil, personal_access_token: nil, workspace_id: nil, endpoint: nil, - wait_for_action_attempt: true, client_options: {}) + wait_for_action_attempt: true, client_options: {}, retry_options: {}) options = Http::Options.parse_options(api_key: api_key, personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint) @endpoint = options[:endpoint] @auth_headers = options[:auth_headers] @defaults = Seam::DeepHashAccessor.new({"wait_for_action_attempt" => wait_for_action_attempt}) - @client = client || Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, client_options) + @client = client || Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, client_options, retry_options) end def lts_version Seam::LTS_VERSION end - def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false, client_options: {}) - new(api_key: api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, client_options: client_options) + def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false, client_options: {}, retry_options: {}) + new(api_key: api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, client_options: client_options, retry_options: retry_options) end - def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false, client_options: {}) - new(personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, client_options: client_options) + def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false, client_options: {}, retry_options: {}) + new(personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, client_options: client_options, retry_options: retry_options) end def request_seam_object(method, path, klass, inner_object, config = {}) diff --git a/lib/seam/request.rb b/lib/seam/request.rb index 8a829aa..af7d573 100644 --- a/lib/seam/request.rb +++ b/lib/seam/request.rb @@ -6,7 +6,7 @@ module Seam module Http module Request - def self.create_faraday_client(endpoint, auth_headers, client_options = {}) + def self.create_faraday_client(endpoint, auth_headers, client_options = {}, retry_options = {}) default_options = { url: endpoint, headers: auth_headers.merge(default_headers) @@ -14,9 +14,16 @@ def self.create_faraday_client(endpoint, auth_headers, client_options = {}) options = deep_merge(default_options, client_options) + default_retry_options = { + max: 2, + backoff_factor: 2 + } + + retry_options = default_retry_options.merge(retry_options) + Faraday.new(options) do |builder| builder.request :json - builder.request :retry, backoff_factor: 2 + builder.request :retry, retry_options builder.response :json end end From 031392e9c67011887205fecc7a6de3ef1930f7a0 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Tue, 22 Oct 2024 16:59:16 +0200 Subject: [PATCH 11/14] Test retry --- lib/seam/request.rb | 5 ++-- spec/seam_client/retry_options_spec.rb | 36 ++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 spec/seam_client/retry_options_spec.rb diff --git a/lib/seam/request.rb b/lib/seam/request.rb index af7d573..a947e9f 100644 --- a/lib/seam/request.rb +++ b/lib/seam/request.rb @@ -34,11 +34,12 @@ def self.handle_error_response(response, method, path) raise Http::UnauthorizedError.new(request_id) if status_code == 401 - error = response.body["error"] || {} + error = response.body.is_a?(Hash) ? response.body["error"] || {} : {} error_type = error["type"] || "unknown_error" + error_message = error["message"] || "Unknown error" error_details = { type: error_type, - message: error["message"] || "Unknown error", + message: error_message, data: error["data"] } diff --git a/spec/seam_client/retry_options_spec.rb b/spec/seam_client/retry_options_spec.rb new file mode 100644 index 0000000..4fa82b4 --- /dev/null +++ b/spec/seam_client/retry_options_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Seam::Http::SingleWorkspace do + let(:api_key) { "seam_test_api_key" } + let(:endpoint) { "https://example.com/api" } + let(:retry_options) do + { + max: 3, + interval: 0.1, + methods: %i[post], + retry_statuses: [500] + } + end + + describe "retry options" do + it "passes retry_options to the Faraday client and uses them" do + client = described_class.new( + api_key: api_key, + endpoint: endpoint, + retry_options: retry_options + ) + + stub_request(:post, "#{endpoint}/devices/list") + .to_return(status: 500, body: {"error" => {"type" => "server_error", "message" => "Internal Server Error"}}.to_json, headers: {"Content-Type" => "application/json"}) + .to_return(status: 500, body: {"error" => {"type" => "server_error", "message" => "Internal Server Error"}}.to_json, headers: {"Content-Type" => "application/json"}) + .to_return(status: 200, body: {devices: []}.to_json, headers: {"Content-Type" => "application/json"}) + + result = client.devices.list + expect(result).to eq([]) + + expect(a_request(:post, "#{endpoint}/devices/list")).to have_been_made.times(3) + end + end +end From bb58961f5961058a4fa6605e3b48d960d7e5daab Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Tue, 22 Oct 2024 17:02:05 +0200 Subject: [PATCH 12/14] Remove wait_for_action_attempt from MultiWorkspace's attributes --- lib/seam/http_multi_workspace.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/seam/http_multi_workspace.rb b/lib/seam/http_multi_workspace.rb index 1960b77..099af26 100644 --- a/lib/seam/http_multi_workspace.rb +++ b/lib/seam/http_multi_workspace.rb @@ -8,7 +8,7 @@ module Seam module Http class MultiWorkspace - attr_reader :client, :wait_for_action_attempt, :defaults + attr_reader :client, :defaults def initialize(personal_access_token:, endpoint: nil, wait_for_action_attempt: true, client_options: {}, retry_options: {}) @wait_for_action_attempt = wait_for_action_attempt From f810be7cddc744d67302d11adc132090031db66d Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 23 Oct 2024 15:20:28 +0200 Subject: [PATCH 13/14] Rename client_options and retry_options to faraday_options and faraday_retry_options --- lib/seam/http_multi_workspace.rb | 12 +++++++----- lib/seam/http_single_workspace.rb | 18 +++++++++++------- lib/seam/request.rb | 12 ++++++------ ...options_spec.rb => faraday_options_spec.rb} | 10 +++++----- ...s_spec.rb => faraday_retry_options_spec.rb} | 6 +++--- 5 files changed, 32 insertions(+), 26 deletions(-) rename spec/seam_client/{client_options_spec.rb => faraday_options_spec.rb} (82%) rename spec/seam_client/{retry_options_spec.rb => faraday_retry_options_spec.rb} (87%) diff --git a/lib/seam/http_multi_workspace.rb b/lib/seam/http_multi_workspace.rb index cb64338..516c0d1 100644 --- a/lib/seam/http_multi_workspace.rb +++ b/lib/seam/http_multi_workspace.rb @@ -10,12 +10,14 @@ module Http class MultiWorkspace attr_reader :client, :defaults - def initialize(personal_access_token:, endpoint: nil, wait_for_action_attempt: true, client_options: {}, retry_options: {}) + def initialize(personal_access_token:, endpoint: nil, wait_for_action_attempt: true, faraday_options: {}, + faraday_retry_options: {}) @wait_for_action_attempt = wait_for_action_attempt @defaults = {"wait_for_action_attempt" => wait_for_action_attempt} @endpoint = Http::Options.get_endpoint(endpoint) @auth_headers = Http::Auth.get_auth_headers_for_multi_workspace_personal_access_token(personal_access_token) - @client = Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, client_options, retry_options) + @client = Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, faraday_options, + faraday_retry_options) end def self.lts_version @@ -30,13 +32,13 @@ def workspaces @workspaces ||= WorkspacesProxy.new(Seam::Clients::Workspaces.new(self)) end - def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_for_action_attempt: true, client_options: {}, retry_options: {}) + def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_for_action_attempt: true, faraday_options: {}, faraday_retry_options: {}) new( personal_access_token: personal_access_token, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, - client_options: client_options, - retry_options: retry_options + faraday_options: faraday_options, + faraday_retry_options: faraday_retry_options ) end diff --git a/lib/seam/http_single_workspace.rb b/lib/seam/http_single_workspace.rb index b1bfd90..98d7880 100644 --- a/lib/seam/http_single_workspace.rb +++ b/lib/seam/http_single_workspace.rb @@ -12,25 +12,29 @@ class SingleWorkspace attr_reader :client, :defaults def initialize(client: nil, api_key: nil, personal_access_token: nil, workspace_id: nil, endpoint: nil, - wait_for_action_attempt: true, client_options: {}, retry_options: {}) - options = Http::Options.parse_options(api_key: api_key, personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint) + wait_for_action_attempt: true, faraday_options: {}, faraday_retry_options: {}) + options = Http::Options.parse_options(api_key: api_key, personal_access_token: personal_access_token, + workspace_id: workspace_id, endpoint: endpoint) @endpoint = options[:endpoint] @auth_headers = options[:auth_headers] @defaults = Seam::DeepHashAccessor.new({"wait_for_action_attempt" => wait_for_action_attempt}) - @client = client || Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, client_options, retry_options) + @client = client || Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, faraday_options, + faraday_retry_options) end def lts_version Seam::LTS_VERSION end - def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false, client_options: {}, retry_options: {}) - new(api_key: api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, client_options: client_options, retry_options: retry_options) + def self.from_api_key(api_key, endpoint: nil, wait_for_action_attempt: false, faraday_options: {}, faraday_retry_options: {}) + new(api_key: api_key, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, + faraday_options: faraday_options, faraday_retry_options: faraday_retry_options) end - def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false, client_options: {}, retry_options: {}) - new(personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint, wait_for_action_attempt: wait_for_action_attempt, client_options: client_options, retry_options: retry_options) + def self.from_personal_access_token(personal_access_token, workspace_id, endpoint: nil, wait_for_action_attempt: false, faraday_options: {}, faraday_retry_options: {}) + new(personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint, + wait_for_action_attempt: wait_for_action_attempt, faraday_options: faraday_options, faraday_retry_options: faraday_retry_options) end def request_seam_object(method, path, klass, inner_object, config = {}) diff --git a/lib/seam/request.rb b/lib/seam/request.rb index a947e9f..039838d 100644 --- a/lib/seam/request.rb +++ b/lib/seam/request.rb @@ -6,29 +6,29 @@ module Seam module Http module Request - def self.create_faraday_client(endpoint, auth_headers, client_options = {}, retry_options = {}) + def self.create_faraday_client(endpoint, auth_headers, faraday_options = {}, faraday_retry_options = {}) default_options = { url: endpoint, headers: auth_headers.merge(default_headers) } - options = deep_merge(default_options, client_options) + options = deep_merge(default_options, faraday_options) - default_retry_options = { + default_faraday_retry_options = { max: 2, backoff_factor: 2 } - retry_options = default_retry_options.merge(retry_options) + faraday_retry_options = default_faraday_retry_options.merge(faraday_retry_options) Faraday.new(options) do |builder| builder.request :json - builder.request :retry, retry_options + builder.request :retry, faraday_retry_options builder.response :json end end - def self.handle_error_response(response, method, path) + def self.handle_error_response(response, _method, _path) status_code = response.status request_id = response.headers["seam-request-id"] diff --git a/spec/seam_client/client_options_spec.rb b/spec/seam_client/faraday_options_spec.rb similarity index 82% rename from spec/seam_client/client_options_spec.rb rename to spec/seam_client/faraday_options_spec.rb index 8f7e1de..8e87ae2 100644 --- a/spec/seam_client/client_options_spec.rb +++ b/spec/seam_client/faraday_options_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Seam::Http::SingleWorkspace do let(:api_key) { "seam_test_api_key" } let(:endpoint) { "https://example.com/api" } - let(:client_options) do + let(:faraday_options) do { headers: {"Custom-Header" => "Test-Value"}, request: {timeout: 30} @@ -13,7 +13,7 @@ end describe "client options" do - it "passes client_options to the Faraday client" do + it "passes faraday_options to the Faraday client" do expect(Faraday).to receive(:new).with( hash_including( headers: hash_including("Custom-Header" => "Test-Value"), @@ -24,17 +24,17 @@ client = described_class.new( api_key: api_key, endpoint: endpoint, - client_options: client_options + faraday_options: faraday_options ) expect(client).to be_a(Seam::Http::SingleWorkspace) end - it "merges client_options with default options" do + it "merges faraday_options with default options" do client = described_class.new( api_key: api_key, endpoint: endpoint, - client_options: client_options + faraday_options: faraday_options ) faraday_client = client.instance_variable_get(:@client) diff --git a/spec/seam_client/retry_options_spec.rb b/spec/seam_client/faraday_retry_options_spec.rb similarity index 87% rename from spec/seam_client/retry_options_spec.rb rename to spec/seam_client/faraday_retry_options_spec.rb index 4fa82b4..8f0424e 100644 --- a/spec/seam_client/retry_options_spec.rb +++ b/spec/seam_client/faraday_retry_options_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Seam::Http::SingleWorkspace do let(:api_key) { "seam_test_api_key" } let(:endpoint) { "https://example.com/api" } - let(:retry_options) do + let(:faraday_retry_options) do { max: 3, interval: 0.1, @@ -15,11 +15,11 @@ end describe "retry options" do - it "passes retry_options to the Faraday client and uses them" do + it "passes faraday_retry_options to the Faraday client and uses them" do client = described_class.new( api_key: api_key, endpoint: endpoint, - retry_options: retry_options + faraday_retry_options: faraday_retry_options ) stub_request(:post, "#{endpoint}/devices/list") From 3b3ef6a910b8ea2604382e994232540d584bf6d6 Mon Sep 17 00:00:00 2001 From: Andrii Balitskyi <10balian10@gmail.com> Date: Wed, 23 Oct 2024 15:21:57 +0200 Subject: [PATCH 14/14] Update faraday-retry version --- Gemfile.lock | 4 ++-- seam.gemspec | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 885fcf3..aa728de 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,7 +3,7 @@ PATH specs: seam (2.0.0a2) faraday (~> 2.7) - faraday-retry (~> 2.2.1) + faraday-retry (~> 2.2) svix (~> 1.30) GEM @@ -59,7 +59,7 @@ GEM rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) rspec-mocks (~> 3.13.0) - rspec-core (3.13.1) + rspec-core (3.13.2) rspec-support (~> 3.13.0) rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) diff --git a/seam.gemspec b/seam.gemspec index 3ad235f..6db9421 100644 --- a/seam.gemspec +++ b/seam.gemspec @@ -27,7 +27,7 @@ Gem::Specification.new do |spec| spec.files += Dir["[A-Z]*"] spec.add_dependency "faraday", "~> 2.7" - spec.add_dependency "faraday-retry", "~> 2.2.1" + spec.add_dependency "faraday-retry", "~> 2.2" spec.add_dependency "svix", "~> 1.30" spec.add_development_dependency "bundler", "~> 2.0"