Skip to content

Commit 7878957

Browse files
andrii-balitskyirazor-xseambot
authored
Implement transparent client pattern and middleware error handling (#147)
Co-authored-by: Evan Sosenko <[email protected]> Co-authored-by: Seam Bot <[email protected]>
1 parent de36e0f commit 7878957

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+755
-1165
lines changed

lib/seam/base_client.rb

Lines changed: 0 additions & 21 deletions
This file was deleted.

lib/seam/deep_hash_accessor.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ def initialize(data)
99
create_accessor_methods
1010
end
1111

12+
def [](key)
13+
instance_variable_get(:"@#{key}")
14+
end
15+
1216
private
1317

1418
def create_accessor_methods

lib/seam/helpers/action_attempt.rb

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ module Seam
44
module Helpers
55
module ActionAttempt
66
def self.decide_and_wait(action_attempt, client, wait_for_action_attempt)
7-
wait_decision = wait_for_action_attempt.nil? ? client.defaults.wait_for_action_attempt : wait_for_action_attempt
8-
9-
if wait_decision == true
7+
if wait_for_action_attempt == true
108
return wait_until_finished(action_attempt, client)
11-
elsif wait_decision.is_a?(Hash)
12-
return wait_until_finished(action_attempt, client, timeout: wait_decision[:timeout],
13-
polling_interval: wait_decision[:polling_interval])
9+
elsif wait_for_action_attempt.is_a?(Hash)
10+
return wait_until_finished(action_attempt, client, timeout: wait_for_action_attempt[:timeout],
11+
polling_interval: wait_for_action_attempt[:polling_interval])
1412
end
1513

1614
action_attempt
@@ -37,13 +35,7 @@ def self.wait_until_finished(action_attempt, client, timeout: nil, polling_inter
3735
end
3836

3937
def self.update_action_attempt(action_attempt, client)
40-
response = client.request_seam(
41-
:post,
42-
"/action_attempts/get",
43-
body: {
44-
action_attempt_id: action_attempt.action_attempt_id
45-
}
46-
)
38+
response = client.get("/action_attempts/get", {action_attempt_id: action_attempt.action_attempt_id})
4739

4840
action_attempt.update_from_response(response.body["action_attempt"])
4941
action_attempt

lib/seam/http_multi_workspace.rb

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def lts_version
2929
end
3030

3131
def workspaces
32-
@workspaces ||= WorkspacesProxy.new(Seam::Clients::Workspaces.new(self))
32+
@workspaces ||= WorkspacesProxy.new(Seam::Clients::Workspaces.new(client: @client, defaults: @defaults))
3333
end
3434

3535
def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_for_action_attempt: true, faraday_options: {}, faraday_retry_options: {})
@@ -41,16 +41,6 @@ def self.from_personal_access_token(personal_access_token, endpoint: nil, wait_f
4141
faraday_retry_options: faraday_retry_options
4242
)
4343
end
44-
45-
def request_seam_object(method, path, klass, inner_object, config = {})
46-
response = Seam::Http::Request.request_seam(@client, @endpoint, method, path, config)
47-
data = response.body[inner_object]
48-
klass.load_from_response(data, self)
49-
end
50-
51-
def request_seam(method, path, config = {})
52-
Seam::Http::Request.request_seam(@client, @endpoint, method, path, config)
53-
end
5444
end
5545

5646
class WorkspacesProxy

lib/seam/http_single_workspace.rb

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ def initialize(client: nil, api_key: nil, personal_access_token: nil, workspace_
1818
@endpoint = options[:endpoint]
1919
@auth_headers = options[:auth_headers]
2020
@defaults = Seam::DeepHashAccessor.new({"wait_for_action_attempt" => wait_for_action_attempt})
21-
2221
@client = client || Seam::Http::Request.create_faraday_client(@endpoint, @auth_headers, faraday_options,
2322
faraday_retry_options)
23+
24+
initialize_routes(client: @client, defaults: @defaults)
2425
end
2526

2627
def lts_version
@@ -36,16 +37,6 @@ def self.from_personal_access_token(personal_access_token, workspace_id, endpoin
3637
new(personal_access_token: personal_access_token, workspace_id: workspace_id, endpoint: endpoint,
3738
wait_for_action_attempt: wait_for_action_attempt, faraday_options: faraday_options, faraday_retry_options: faraday_retry_options)
3839
end
39-
40-
def request_seam_object(method, path, klass, inner_object, config = {})
41-
response = Seam::Http::Request.request_seam(@client, @endpoint, method, path, config)
42-
data = response.body[inner_object]
43-
klass.load_from_response(data, self)
44-
end
45-
46-
def request_seam(method, path, config = {})
47-
Seam::Http::Request.request_seam(@client, @endpoint, method, path, config)
48-
end
4940
end
5041
end
5142
end

lib/seam/request.rb

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,42 +23,9 @@ def self.create_faraday_client(endpoint, auth_headers, faraday_options = {}, far
2323

2424
Faraday.new(options) do |builder|
2525
builder.request :json
26-
builder.request :retry, faraday_retry_options
2726
builder.response :json
28-
end
29-
end
30-
31-
def self.handle_error_response(response, _method, _path)
32-
status_code = response.status
33-
request_id = response.headers["seam-request-id"]
34-
35-
raise Http::UnauthorizedError.new(request_id) if status_code == 401
36-
37-
error = response.body.is_a?(Hash) ? response.body["error"] || {} : {}
38-
error_type = error["type"] || "unknown_error"
39-
error_message = error["message"] || "Unknown error"
40-
error_details = {
41-
type: error_type,
42-
message: error_message,
43-
data: error["data"]
44-
}
45-
46-
if error_type == "invalid_input"
47-
error_details["validation_errors"] = error["validation_errors"]
48-
raise Http::InvalidInputError.new(error_details, status_code, request_id)
49-
end
50-
51-
raise Http::ApiError.new(error_details, status_code, request_id)
52-
end
53-
54-
def self.request_seam(client, endpoint, method, path, config = {})
55-
url = "#{endpoint}#{path}"
56-
response = client.run_request(method, url, config[:body], config[:headers])
57-
58-
if response.success?
59-
response
60-
else
61-
handle_error_response(response, method, path)
27+
builder.use ResponseMiddleware
28+
builder.request :retry, faraday_retry_options
6229
end
6330
end
6431

@@ -72,6 +39,53 @@ def self.default_headers
7239
}
7340
end
7441

42+
class ResponseMiddleware < Faraday::Response::RaiseError
43+
def on_complete(env)
44+
return if env.success?
45+
46+
status_code = env.status
47+
request_id = env.response_headers["seam-request-id"]
48+
49+
raise Http::UnauthorizedError.new(request_id) if status_code == 401
50+
51+
if seam_api_error_response?(env)
52+
body = JSON.parse(env.body)
53+
error = body["error"]
54+
error_details = {
55+
type: error["type"] || "unknown_error",
56+
message: error["message"] || "Unknown error",
57+
data: error["data"]
58+
}
59+
60+
if error["type"] == "invalid_input"
61+
error_details["validation_errors"] = error["validation_errors"]
62+
raise Http::InvalidInputError.new(error_details, status_code, request_id)
63+
end
64+
65+
raise Http::ApiError.new(error_details, status_code, request_id)
66+
end
67+
68+
super
69+
end
70+
71+
def seam_api_error_response?(env)
72+
return false unless env.response_headers
73+
74+
content_type = env.response_headers["Content-Type"]
75+
return false unless content_type&.start_with?("application/json")
76+
77+
begin
78+
body = JSON.parse(env.body)
79+
return false unless body.is_a?(Hash) && body["error"].is_a?(Hash)
80+
81+
error = body["error"]
82+
error["type"].is_a?(String) && error["message"].is_a?(String)
83+
rescue JSON::ParserError
84+
false
85+
end
86+
end
87+
end
88+
7589
def self.deep_merge(hash1, hash2)
7690
result = hash1.dup
7791
hash2.each do |key, value|

lib/seam/routes/clients/access_codes.rb

Lines changed: 29 additions & 60 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/seam/routes/clients/access_codes_simulate.rb

Lines changed: 9 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)