Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor errors #96

Merged
merged 27 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a1c0875
Rename SeamHttpApiError to HttpApiError
andrii-balitskyi Aug 20, 2024
7640fdf
Rename SeamHttpUnauthorizedError to UnauthorizedError
andrii-balitskyi Aug 20, 2024
d99db3b
Rename UnauthorizedError to HttpUnauthorizedError
andrii-balitskyi Aug 20, 2024
cfacfef
Rename SeamHttpInvalidInputError to InvalidInputError
andrii-balitskyi Aug 20, 2024
64c7ebd
Rename SeamActionAttemptError to ActionAttemptError
andrii-balitskyi Aug 20, 2024
df81b94
Rename SeamActionAttemptFailedError to ActionAttemptFailedError
andrii-balitskyi Aug 20, 2024
adabd21
More SeamActionAttemptFailedError rename
andrii-balitskyi Aug 20, 2024
4fb1ca3
Rename SeamActionAttemptTimeoutError to ActionAttemptTimeoutError
andrii-balitskyi Aug 20, 2024
88ed48d
ci: Format code
seambot Aug 20, 2024
62e8761
Fix tests, move SeamInvalidTokenError to Http module
andrii-balitskyi Aug 20, 2024
db2129a
Rename Http to Client and HttptMultiWorkspace to ClientMultiWorkspace
andrii-balitskyi Sep 19, 2024
b0d8526
Leftover renames
andrii-balitskyi Sep 19, 2024
5f69fd5
Renames in lib/seam.rb
andrii-balitskyi Sep 19, 2024
84b1b3c
Move http errors to http module, fix specs
andrii-balitskyi Sep 19, 2024
a7c1a17
Merge branch 'main' of github.com:seamapi/ruby-next into refactor-errors
andrii-balitskyi Sep 19, 2024
ec8edae
Put action attempt errors under errors module
andrii-balitskyi Sep 19, 2024
6a590df
Rename SeamWebhook to Webhook
andrii-balitskyi Oct 3, 2024
e8538f0
Make Http a module with http errors, SingleWorkspace and MultiWorkspa…
andrii-balitskyi Oct 3, 2024
4defdc8
Bump generator to 1.14.5 to put resource related stuff under Resource…
andrii-balitskyi Oct 3, 2024
aafc34f
Alias factory methods
andrii-balitskyi Oct 3, 2024
2d3558f
Clean up public exports
andrii-balitskyi Oct 3, 2024
8f192d3
Bump generator to include base client and resource imports in the routes
andrii-balitskyi Oct 3, 2024
5e105ce
Move action attempt errors under WaitForActionAttempt module
andrii-balitskyi Oct 3, 2024
a5c2c1d
Merge branch 'main' of github.com:seamapi/ruby-next into refactor-errors
andrii-balitskyi Oct 3, 2024
644f08b
Remove Http prefix from http errors
andrii-balitskyi Oct 10, 2024
781f22b
Reduce nesting of wait for action attempt errors
andrii-balitskyi Oct 10, 2024
fc69841
Refactor SeamOptions and SeamAuth
andrii-balitskyi Oct 10, 2024
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
33 changes: 14 additions & 19 deletions lib/seam/auth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,9 @@

require_relative "options"
require_relative "token"
require_relative "errors"

module SeamAuth
class SeamInvalidTokenError < StandardError
def initialize(message)
super("Seam received an invalid token: #{message}")
end
end

def self.get_auth_headers(api_key: nil, personal_access_token: nil, workspace_id: nil)
if SeamOptions.seam_http_options_with_api_key?(api_key: api_key, personal_access_token: personal_access_token)
return get_auth_headers_for_api_key(api_key)
Expand All @@ -29,23 +24,23 @@ def self.get_auth_headers(api_key: nil, personal_access_token: nil, workspace_id

def self.get_auth_headers_for_api_key(api_key)
if SeamAuth.client_session_token?(api_key)
raise SeamInvalidTokenError.new(
raise Http::SeamInvalidTokenError.new(
"A Client Session Token cannot be used as an api_key"
)
end

raise SeamInvalidTokenError.new("A JWT cannot be used as an api_key") if SeamAuth.jwt?(api_key)
raise Http::SeamInvalidTokenError.new("A JWT cannot be used as an api_key") if SeamAuth.jwt?(api_key)

raise SeamInvalidTokenError.new("An Access Token cannot be used as an api_key") if SeamAuth.access_token?(api_key)
raise Http::SeamInvalidTokenError.new("An Access Token cannot be used as an api_key") if SeamAuth.access_token?(api_key)

if SeamAuth.publishable_key?(api_key)
raise SeamInvalidTokenError.new(
raise Http::SeamInvalidTokenError.new(
"A Publishable Key cannot be used as an api_key"
)
end

unless SeamAuth.seam_token?(api_key)
raise SeamInvalidTokenError.new(
raise Http::SeamInvalidTokenError.new(
"Unknown or invalid api_key format, expected token to start with #{SeamAuth::TOKEN_PREFIX}"
)
end
Expand All @@ -55,25 +50,25 @@ def self.get_auth_headers_for_api_key(api_key)

def self.get_auth_headers_for_personal_access_token(personal_access_token, workspace_id)
if SeamAuth.jwt?(personal_access_token)
raise SeamInvalidTokenError.new(
raise Http::SeamInvalidTokenError.new(
"A JWT cannot be used as a personal_access_token"
)
end

if SeamAuth.client_session_token?(personal_access_token)
raise SeamInvalidTokenError.new(
raise Http::SeamInvalidTokenError.new(
"A Client Session Token cannot be used as a personal_access_token"
)
end

if SeamAuth.publishable_key?(personal_access_token)
raise SeamInvalidTokenError.new(
raise Http::SeamInvalidTokenError.new(
"A Publishable Key cannot be used as a personal_access_token"
)
end

unless SeamAuth.access_token?(personal_access_token)
raise SeamInvalidTokenError.new(
raise Http::SeamInvalidTokenError.new(
"Unknown or invalid personal_access_token format, expected token to start with #{SeamAuth::ACCESS_TOKEN_PREFIX}"
)
end
Expand All @@ -86,25 +81,25 @@ def self.get_auth_headers_for_personal_access_token(personal_access_token, works

def self.get_auth_headers_for_multi_workspace_personal_access_token(personal_access_token)
if SeamAuth.jwt?(personal_access_token)
raise SeamInvalidTokenError.new(
raise Http::SeamInvalidTokenError.new(
"A JWT cannot be used as a personal_access_token"
)
end

if SeamAuth.client_session_token?(personal_access_token)
raise SeamInvalidTokenError.new(
raise Http::SeamInvalidTokenError.new(
"A Client Session Token cannot be used as a personal_access_token"
)
end

if SeamAuth.publishable_key?(personal_access_token)
raise SeamInvalidTokenError.new(
raise Http::SeamInvalidTokenError.new(
"A Publishable Key cannot be used as a personal_access_token"
)
end

unless SeamAuth.access_token?(personal_access_token)
raise SeamInvalidTokenError.new(
raise Http::SeamInvalidTokenError.new(
"Unknown or invalid personal_access_token format, expected token to start with #{SeamAuth::ACCESS_TOKEN_PREFIX}"
)
end
Expand Down
97 changes: 51 additions & 46 deletions lib/seam/errors.rb
Original file line number Diff line number Diff line change
@@ -1,68 +1,73 @@
# frozen_string_literal: true

module Seam
module Errors
# HTTP
class SeamHttpApiError < StandardError
attr_reader :code, :status_code, :request_id, :data
class HttpApiError < StandardError
attr_reader :code, :status_code, :request_id, :data

def initialize(error, status_code, request_id)
super(error[:message])
@code = error[:type]
@status_code = status_code
@request_id = request_id
@data = error[:data]
end
def initialize(error, status_code, request_id)
super(error[:message])
@code = error[:type]
@status_code = status_code
@request_id = request_id
@data = error[:data]
end
end

class SeamHttpUnauthorizedError < SeamHttpApiError
def initialize(request_id)
super({type: "unauthorized", message: "Unauthorized"}, 401, request_id)
end
class HttpUnauthorizedError < HttpApiError
def initialize(request_id)
super({type: "unauthorized", message: "Unauthorized"}, 401, request_id)
end
end

class SeamHttpInvalidInputError < SeamHttpApiError
attr_reader :validation_errors
class InvalidInputError < HttpApiError
attr_reader :validation_errors

def initialize(error, status_code, request_id)
super(error, status_code, request_id)
@code = "invalid_input"
@validation_errors = error["validation_errors"] || {}
end
def initialize(error, status_code, request_id)
super(error, status_code, request_id)
@code = "invalid_input"
@validation_errors = error["validation_errors"] || {}
end

def get_validation_error_messages(param_name)
@validation_errors.dig(param_name, "_errors") || []
end
def get_validation_error_messages(param_name)
@validation_errors.dig(param_name, "_errors") || []
end
end

# Action attempt
class SeamActionAttemptError < StandardError
attr_reader :action_attempt
# Action attempt
class ActionAttemptError < StandardError
attr_reader :action_attempt

def initialize(message, action_attempt)
super(message)
@action_attempt = action_attempt
end
def initialize(message, action_attempt)
super(message)
@action_attempt = action_attempt
end

def name
self.class.name
end
def name
self.class.name
end
end

class SeamActionAttemptFailedError < SeamActionAttemptError
attr_reader :code
class ActionAttemptFailedError < ActionAttemptError
attr_reader :code

def initialize(action_attempt)
super(action_attempt.error.message, action_attempt)
@code = action_attempt.error.type
end
def initialize(action_attempt)
super(action_attempt.error.message, action_attempt)
@code = action_attempt.error.type
end
end

class ActionAttemptTimeoutError < ActionAttemptError
def initialize(action_attempt, timeout)
message = "Timed out waiting for action attempt after #{timeout}s"
super(message, action_attempt)
end
end
end

class SeamActionAttemptTimeoutError < SeamActionAttemptError
def initialize(action_attempt, timeout)
message = "Timed out waiting for action attempt after #{timeout}s"
super(message, action_attempt)
end
module Http
class SeamInvalidTokenError < StandardError
andrii-balitskyi marked this conversation as resolved.
Show resolved Hide resolved
def initialize(message)
super("Seam received an invalid token: #{message}")
end
end
end
6 changes: 3 additions & 3 deletions lib/seam/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def handle_error_response(response, _method, _uri)
status_code = response.status.code
request_id = response.headers["seam-request-id"]

raise Errors::SeamHttpUnauthorizedError.new(request_id) if status_code == 401
raise HttpUnauthorizedError.new(request_id) if status_code == 401

error = response.parse["error"] || {}
error_type = error["type"] || "unknown_error"
Expand All @@ -47,12 +47,12 @@ def handle_error_response(response, _method, _uri)
if error_type == "invalid_input"
error_details["validation_errors"] = error["validation_errors"]

raise Errors::SeamHttpInvalidInputError.new(
raise InvalidInputError.new(
error_details, status_code, request_id
)
end

raise Errors::SeamHttpApiError.new(error_details, status_code, request_id)
raise HttpApiError.new(error_details, status_code, request_id)
end

def build_url(uri)
Expand Down
4 changes: 2 additions & 2 deletions lib/seam/utils/action_attempt_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ def self.wait_until_finished(action_attempt, client, timeout: nil, polling_inter
sleep(polling_interval)
time_waiting += polling_interval

raise Errors::SeamActionAttemptTimeoutError.new(action_attempt, timeout) if time_waiting > timeout
raise ActionAttemptTimeoutError.new(action_attempt, timeout) if time_waiting > timeout

action_attempt = update_action_attempt(action_attempt, client)
end

raise Errors::SeamActionAttemptFailedError.new(action_attempt) if action_attempt.status == "error"
raise ActionAttemptFailedError.new(action_attempt) if action_attempt.status == "error"

action_attempt
end
Expand Down
4 changes: 2 additions & 2 deletions spec/request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

it "parses the error" do
expect { client.health }.to raise_error do |error|
expect(error).to be_a(Seam::Errors::SeamHttpApiError)
expect(error).to be_a(Seam::HttpApiError)
expect(error.message).to eq(message)
expect(error.code).to eq(type)
expect(error.request_id).to eq(request_id)
Expand All @@ -48,7 +48,7 @@

it "parses the error" do
expect { client.health }.to raise_error do |error|
expect(error).to be_a(Seam::Errors::SeamHttpApiError)
expect(error).to be_a(Seam::HttpApiError)
expect(error.message).to eq(message)
expect(error.code).to eq(type)
expect(error.request_id).to eq(request_id)
Expand Down
8 changes: 4 additions & 4 deletions spec/seam_client/api_key_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@
it "checks api key format" do
expect do
Seam.from_api_key("some-invalid-key-format")
end.to raise_error(SeamAuth::SeamInvalidTokenError, /Unknown/)
end.to raise_error(Http::SeamInvalidTokenError, /Unknown/)

expect do
Seam.from_api_key("ey")
end.to raise_error(SeamAuth::SeamInvalidTokenError, /JWT/)
end.to raise_error(Http::SeamInvalidTokenError, /JWT/)

expect do
Seam.from_api_key("seam_cst_token")
end.to raise_error(SeamAuth::SeamInvalidTokenError, /Client Session Token/)
end.to raise_error(Http::SeamInvalidTokenError, /Client Session Token/)

expect do
Seam.from_api_key("seam_at")
end.to raise_error(SeamAuth::SeamInvalidTokenError, /Access Token/)
end.to raise_error(Http::SeamInvalidTokenError, /Access Token/)
end
end
end
8 changes: 4 additions & 4 deletions spec/seam_client/client_multi_workspace_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@

describe "token format validation" do
it "raises SeamInvalidTokenError for invalid token formats" do
expect { described_class.from_personal_access_token("invalid_token") }.to raise_error(SeamAuth::SeamInvalidTokenError, /Unknown/)
expect { described_class.from_personal_access_token("seam_apikey_token") }.to raise_error(SeamAuth::SeamInvalidTokenError, /Unknown/)
expect { described_class.from_personal_access_token("seam_cst") }.to raise_error(SeamAuth::SeamInvalidTokenError, /Client Session Token/)
expect { described_class.from_personal_access_token("ey") }.to raise_error(SeamAuth::SeamInvalidTokenError, /JWT/)
expect { described_class.from_personal_access_token("invalid_token") }.to raise_error(Http::SeamInvalidTokenError, /Unknown/)
expect { described_class.from_personal_access_token("seam_apikey_token") }.to raise_error(Http::SeamInvalidTokenError, /Unknown/)
expect { described_class.from_personal_access_token("seam_cst") }.to raise_error(Http::SeamInvalidTokenError, /Client Session Token/)
expect { described_class.from_personal_access_token("ey") }.to raise_error(Http::SeamInvalidTokenError, /JWT/)
end
end
end
8 changes: 4 additions & 4 deletions spec/seam_client/http_error_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require "spec_helper"

RSpec.describe Seam::Errors::SeamHttpInvalidInputError do
RSpec.describe Seam::InvalidInputError do
let(:api_key) { "seam_apikey1_token" }
let(:client) { Seam.new(api_key: api_key) }

Expand All @@ -19,7 +19,7 @@
}
end

it "raises SeamHttpInvalidInputError with correct validation messages" do
it "raises InvalidInputError with correct validation messages" do
stub_seam_request(:post, "/devices/list",
error_response,
status: 400).with do |req|
Expand All @@ -28,7 +28,7 @@

expect do
client.devices.list(device_ids: 123)
end.to raise_error(Seam::Errors::SeamHttpInvalidInputError) do |error|
end.to raise_error(Seam::InvalidInputError) do |error|
expect(error.code).to eq("invalid_input")
expect(error.status_code).to eq(400)
expect(error.get_validation_error_messages("device_ids")).to eq(["Expected array, received number"])
Expand All @@ -44,7 +44,7 @@

begin
client.devices.list(device_ids: 123)
rescue Seam::Errors::SeamHttpInvalidInputError => e
rescue Seam::InvalidInputError => e
expect(e.get_validation_error_messages("non_existent_field")).to eq([])
end
end
Expand Down
8 changes: 4 additions & 4 deletions spec/seam_client/personal_access_token_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@
it "raises error for invalid personal access token formats" do
expect do
Seam.from_personal_access_token("some-invalid-key-format", workspace_id)
end.to raise_error(SeamAuth::SeamInvalidTokenError, /Unknown/)
end.to raise_error(Http::SeamInvalidTokenError, /Unknown/)

expect do
Seam.from_personal_access_token("seam_apikey_token", workspace_id)
end.to raise_error(SeamAuth::SeamInvalidTokenError, /Unknown/)
end.to raise_error(Http::SeamInvalidTokenError, /Unknown/)

expect do
Seam.from_personal_access_token("seam_cst", workspace_id)
end.to raise_error(SeamAuth::SeamInvalidTokenError, /Client Session Token/)
end.to raise_error(Http::SeamInvalidTokenError, /Client Session Token/)

expect do
Seam.from_personal_access_token("ey", workspace_id)
end.to raise_error(SeamAuth::SeamInvalidTokenError, /JWT/)
end.to raise_error(Http::SeamInvalidTokenError, /JWT/)
end
end
end
Loading