Skip to content

Commit 4e36056

Browse files
Fix failed action attempt handling, add custom action attempt errors (#61)
* Move http errors to new seam/errors.rb * Add action attempt errors and use them * Correctly handle error action attempts * Ensure default values for timeout and polling_interval are used in wait_until_finished * Default wait_for_action_attempt client setting to true * Fix error action attempt handling * Add wait_for_action_attepmt_spec.rb * Fix spec/request_spec.rb * Improve spec/seam_client/wait_for_action_attepmt_spec.rb
1 parent f972878 commit 4e36056

11 files changed

Lines changed: 265 additions & 82 deletions

lib/seam.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
require_relative "seam/client"
99
require_relative "seam/base_client"
1010
require_relative "seam/base_resource"
11+
require_relative "seam/errors"
1112

1213
require_relative "seam/routes/resources/index"
1314
require_relative "seam/routes/clients/index"

lib/seam/client.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Client
1010
attr_accessor :wait_for_action_attempt, :defaults
1111

1212
def initialize(api_key: nil, personal_access_token: nil, workspace_id: nil, endpoint: nil,
13-
wait_for_action_attempt: false, debug: false)
13+
wait_for_action_attempt: true, debug: false)
1414
options = SeamOptions.parse_options(api_key: api_key, personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint)
1515
@endpoint = options[:endpoint]
1616
@auth_headers = options[:auth_headers]

lib/seam/errors.rb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# frozen_string_literal: true
2+
3+
module Seam
4+
module Errors
5+
# HTTP
6+
class SeamHttpApiError < StandardError
7+
attr_reader :code, :status_code, :request_id, :data
8+
9+
def initialize(error, status_code, request_id)
10+
super(error[:message])
11+
@code = error[:type]
12+
@status_code = status_code
13+
@request_id = request_id
14+
@data = error[:data]
15+
end
16+
end
17+
18+
class SeamHttpUnauthorizedError < SeamHttpApiError
19+
def initialize(request_id)
20+
super({type: "unauthorized", message: "Unauthorized"}, 401, request_id)
21+
end
22+
end
23+
24+
class SeamHttpInvalidInputError < SeamHttpApiError
25+
attr_reader :validation_errors
26+
27+
def initialize(error, status_code, request_id)
28+
super(error, status_code, request_id)
29+
@code = "invalid_input"
30+
@validation_errors = error["validation_errors"] || {}
31+
end
32+
33+
def get_validation_error_messages(param_name)
34+
@validation_errors.dig(param_name, "_errors") || []
35+
end
36+
end
37+
38+
# Action attempt
39+
class SeamActionAttemptError < StandardError
40+
attr_reader :action_attempt
41+
42+
def initialize(message, action_attempt)
43+
super(message)
44+
@action_attempt = action_attempt
45+
end
46+
47+
def name
48+
self.class.name
49+
end
50+
end
51+
52+
class SeamActionAttemptFailedError < SeamActionAttemptError
53+
attr_reader :code
54+
55+
def initialize(action_attempt)
56+
super(action_attempt.error.message, action_attempt)
57+
@code = action_attempt.error.type
58+
end
59+
end
60+
61+
class SeamActionAttemptTimeoutError < SeamActionAttemptError
62+
def initialize(action_attempt, timeout)
63+
message = "Timed out waiting for action attempt after #{timeout}s"
64+
super(message, action_attempt)
65+
end
66+
end
67+
end
68+
end

lib/seam/request.rb

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,6 @@ module Seam
66
class Request
77
attr_reader :endpoint, :debug
88

9-
class Error < StandardError
10-
attr_reader :status, :response
11-
12-
def initialize(message, status, response)
13-
super(message)
14-
@status = status
15-
@response = response
16-
end
17-
end
18-
199
def initialize(auth_headers:, endpoint:, debug: false)
2010
@auth_headers = auth_headers
2111
@endpoint = endpoint
@@ -44,7 +34,7 @@ def handle_error_response(response, _method, _uri)
4434
status_code = response.status.code
4535
request_id = response.headers["seam-request-id"]
4636

47-
raise SeamHttpUnauthorizedError.new(request_id) if status_code == 401
37+
raise Errors::SeamHttpUnauthorizedError.new(request_id) if status_code == 401
4838

4939
error = response.parse["error"] || {}
5040
error_type = error["type"] || "unknown_error"
@@ -57,12 +47,12 @@ def handle_error_response(response, _method, _uri)
5747
if error_type == "invalid_input"
5848
error_details["validation_errors"] = error["validation_errors"]
5949

60-
raise SeamHttpInvalidInputError.new(
50+
raise Errors::SeamHttpInvalidInputError.new(
6151
error_details, status_code, request_id
6252
)
6353
end
6454

65-
raise SeamHttpApiError.new(error_details, status_code, request_id)
55+
raise Errors::SeamHttpApiError.new(error_details, status_code, request_id)
6656
end
6757

6858
def build_url(uri)
@@ -83,36 +73,4 @@ def user_agent
8373
"seam-ruby/#{Seam::VERSION}"
8474
end
8575
end
86-
87-
class SeamHttpApiError < StandardError
88-
attr_reader :code, :status_code, :request_id, :data
89-
90-
def initialize(error, status_code, request_id)
91-
super(error[:message])
92-
@code = error[:type]
93-
@status_code = status_code
94-
@request_id = request_id
95-
@data = error[:data]
96-
end
97-
end
98-
99-
class SeamHttpUnauthorizedError < SeamHttpApiError
100-
def initialize(request_id)
101-
super({type: "unauthorized", message: "Unauthorized"}, 401, request_id)
102-
end
103-
end
104-
105-
class SeamHttpInvalidInputError < SeamHttpApiError
106-
attr_reader :validation_errors
107-
108-
def initialize(error, status_code, request_id)
109-
super(error, status_code, request_id)
110-
@code = "invalid_input"
111-
@validation_errors = error["validation_errors"] || {}
112-
end
113-
114-
def get_validation_error_messages(param_name)
115-
@validation_errors.dig(param_name, "_errors") || []
116-
end
117-
end
11876
end

lib/seam/utils/action_attempt_utils.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,23 @@ def self.decide_and_wait(action_attempt, client, wait_for_action_attempt)
1414
end
1515
end
1616

17-
def self.wait_until_finished(action_attempt, client, timeout: 5.0, polling_interval: 0.5)
17+
def self.wait_until_finished(action_attempt, client, timeout: nil, polling_interval: nil)
18+
timeout = timeout.nil? ? 5.0 : timeout
19+
polling_interval = polling_interval.nil? ? 0.5 : polling_interval
20+
1821
time_waiting = 0.0
1922

2023
while action_attempt.status == "pending"
2124
sleep(polling_interval)
2225
time_waiting += polling_interval
2326

24-
raise "Timed out waiting for action attempt to be finished" if time_waiting > timeout
27+
raise Errors::SeamActionAttemptTimeoutError.new(action_attempt, timeout) if time_waiting > timeout
2528

2629
action_attempt = update_action_attempt(action_attempt, client)
27-
28-
raise "Action Attempt failed: #{action_attempt.error["message"]}" if action_attempt.status == "failed"
2930
end
3031

32+
raise Errors::SeamActionAttemptFailedError.new(action_attempt) if action_attempt.status == "error"
33+
3134
action_attempt
3235
end
3336

spec/request_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
it "parses the error" do
2424
expect { client.health }.to raise_error do |error|
25-
expect(error).to be_a(Seam::SeamHttpApiError)
25+
expect(error).to be_a(Seam::Errors::SeamHttpApiError)
2626
expect(error.message).to eq(message)
2727
expect(error.code).to eq(type)
2828
expect(error.request_id).to eq(request_id)
@@ -48,7 +48,7 @@
4848

4949
it "parses the error" do
5050
expect { client.health }.to raise_error do |error|
51-
expect(error).to be_a(Seam::SeamHttpApiError)
51+
expect(error).to be_a(Seam::Errors::SeamHttpApiError)
5252
expect(error.message).to eq(message)
5353
expect(error.code).to eq(type)
5454
expect(error.request_id).to eq(request_id)

spec/resources/action_attempt_spec.rb

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -73,29 +73,6 @@
7373
it "returns an updated ActionAttempt" do
7474
expect(result.status).to eq(finished_status)
7575
end
76-
77-
context "when action attempt fails" do
78-
let(:status) { "failed" }
79-
let(:error_message) { "Something went wrong" }
80-
81-
before do
82-
stub_seam_request(
83-
:post,
84-
"/action_attempts/get",
85-
{action_attempt: action_attempt_hash.merge(status: status, error: {"message" => error_message})}
86-
)
87-
end
88-
89-
it "raises an error" do
90-
expect { described_class.wait_until_finished(action_attempt, client) }.to raise_error("Action Attempt failed: #{error_message}")
91-
end
92-
end
93-
94-
context "when timeout is reached" do
95-
it "raises a timeout error" do
96-
expect { described_class.wait_until_finished(action_attempt, client, timeout: 0.1) }.to raise_error("Timed out waiting for action attempt to be finished")
97-
end
98-
end
9976
end
10077

10178
describe ".update_action_attempt" do

spec/seam_client/http_error_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
require "spec_helper"
22

3-
RSpec.describe Seam::SeamHttpInvalidInputError do
3+
RSpec.describe Seam::Errors::SeamHttpInvalidInputError do
44
let(:api_key) { "seam_apikey1_token" }
55
let(:client) { Seam::Client.new(api_key: api_key) }
66

@@ -28,7 +28,7 @@
2828

2929
expect {
3030
client.devices.list(device_ids: 123)
31-
}.to raise_error(Seam::SeamHttpInvalidInputError) do |error|
31+
}.to raise_error(Seam::Errors::SeamHttpInvalidInputError) do |error|
3232
expect(error.code).to eq("invalid_input")
3333
expect(error.status_code).to eq(400)
3434
expect(error.get_validation_error_messages("device_ids")).to eq(["Expected array, received number"])
@@ -44,7 +44,7 @@
4444

4545
begin
4646
client.devices.list(device_ids: 123)
47-
rescue Seam::SeamHttpInvalidInputError => error
47+
rescue Seam::Errors::SeamHttpInvalidInputError => error
4848
expect(error.get_validation_error_messages("non_existent_field")).to eq([])
4949
end
5050
end

spec/seam_client/init_seam_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
describe "#initialize" do
77
it "initializes Seam with fixture" do
88
expect(seam.lts_version).not_to be_nil
9-
expect(seam.wait_for_action_attempt).to be_falsey
9+
expect(seam.wait_for_action_attempt).to be true
1010
end
1111
end
1212
end

spec/seam_client/request_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
end
1313

1414
it "raises SeamHttpUnauthorizedError" do
15-
expect { seam.devices.list }.to raise_error(Seam::SeamHttpUnauthorizedError) do |error|
15+
expect { seam.devices.list }.to raise_error(Seam::Errors::SeamHttpUnauthorizedError) do |error|
1616
expect(error.message).to eq("Unauthorized")
1717
expect(error.request_id).to eq(request_id)
1818
end
@@ -37,7 +37,7 @@
3737
end
3838

3939
it "raises SeamHttpInvalidInputError" do
40-
expect { seam.devices.get(device_id: "invalid_device_id") }.to raise_error(Seam::SeamHttpInvalidInputError) do |error|
40+
expect { seam.devices.get(device_id: "invalid_device_id") }.to raise_error(Seam::Errors::SeamHttpInvalidInputError) do |error|
4141
expect(error.message).to eq(error_message)
4242
expect(error.status_code).to eq(error_status)
4343
expect(error.request_id).to eq(request_id)
@@ -64,7 +64,7 @@
6464
end
6565

6666
it "raises SeamHttpApiError with the correct details" do
67-
expect { seam.devices.list }.to raise_error(Seam::SeamHttpApiError) do |error|
67+
expect { seam.devices.list }.to raise_error(Seam::Errors::SeamHttpApiError) do |error|
6868
expect(error.message).to eq(error_message)
6969
expect(error.status_code).to eq(error_status)
7070
expect(error.request_id).to eq(request_id)

0 commit comments

Comments
 (0)