Exception: SerpCheap::Error

Inherits:
StandardError
  • Object
show all
Defined in:
lib/serpcheap/error.rb

Constant Summary collapse

RETRYABLE =
%w[
  rate_limited
  too_many_concurrent_requests
  service_temporarily_unavailable
  result_timeout
  client_timeout
  network_error
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(error_code, message, status: nil, retry_after_ms: nil, details: nil) ⇒ Error

Returns a new instance of Error.



16
17
18
19
20
21
22
# File 'lib/serpcheap/error.rb', line 16

def initialize(error_code, message, status: nil, retry_after_ms: nil, details: nil)
  super(message)
  @error_code = error_code
  @status = status
  @retry_after_ms = retry_after_ms
  @details = details
end

Instance Attribute Details

#detailsObject (readonly)

Returns the value of attribute details.



14
15
16
# File 'lib/serpcheap/error.rb', line 14

def details
  @details
end

#error_codeObject (readonly)

Returns the value of attribute error_code.



14
15
16
# File 'lib/serpcheap/error.rb', line 14

def error_code
  @error_code
end

#retry_after_msObject (readonly)

Returns the value of attribute retry_after_ms.



14
15
16
# File 'lib/serpcheap/error.rb', line 14

def retry_after_ms
  @retry_after_ms
end

#statusObject (readonly)

Returns the value of attribute status.



14
15
16
# File 'lib/serpcheap/error.rb', line 14

def status
  @status
end

Class Method Details

.from_api(status, body) ⇒ Object

Map a non-2xx response (status + parsed body Hash) to a typed error.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/serpcheap/error.rb', line 29

def self.from_api(status, body)
  body = {} unless body.is_a?(Hash)
  code = body["error"].is_a?(String) ? body["error"] : ""
  msg = body["message"].is_a?(String) ? body["message"] : ""

  case code
  when "invalid_request"
    return new("invalid_request", msg.empty? ? "The request parameters were rejected." : msg, status: status, details: body["details"])
  when "missing_api_key"
    return new("missing_api_key", msg.empty? ? "No API key was sent." : msg, status: status)
  when "unknown_api_key"
    return new("unknown_api_key", "The API key is not recognized.", status: status)
  when "inactive_api_key"
    return new("inactive_api_key", "The API key is inactive.", status: status)
  when "account_blocked"
    return new("account_blocked", msg.empty? ? "This account is blocked." : msg, status: status)
  when "insufficient_credits"
    required = numeric(body["required"])
    balance = numeric(body["balance"])
    detail = required && balance ? " (needs #{required}, balance #{balance})" : ""
    return new("insufficient_credits", "Not enough credits#{detail}.", status: status)
  when "rate_limited"
    ra = numeric(body["retry_after_ms"])&.to_i
    suffix = ra ? "; retry in #{ra} ms" : ""
    return new("rate_limited", "Rate limit exceeded#{suffix}.", status: status, retry_after_ms: ra)
  when "request_in_progress"
    return new("request_in_progress", "An identical request is in flight.", status: status)
  when "too_many_concurrent_requests"
    return new("too_many_concurrent_requests", msg.empty? ? "Too many concurrent requests." : msg, status: status)
  when "service_temporarily_unavailable"
    return new("service_temporarily_unavailable", msg.empty? ? "Temporarily unavailable." : msg, status: status)
  when "result_timeout"
    return new("result_timeout", msg.empty? ? "The search timed out." : msg, status: status)
  end

  return new("unknown_api_key", msg.empty? ? "Authentication failed." : msg, status: status) if status == 401
  return new("account_blocked", msg.empty? ? "Access denied." : msg, status: status) if status == 403
  return new("rate_limited", "Rate limit exceeded.", status: status) if status == 429
  return new("service_temporarily_unavailable", "HTTP #{status}.", status: status) if status >= 500

  new("internal", msg.empty? ? "serp.cheap API returned HTTP #{status}." : msg, status: status)
end

Instance Method Details

#retryable?Boolean

Returns:

  • (Boolean)


24
25
26
# File 'lib/serpcheap/error.rb', line 24

def retryable?
  RETRYABLE.include?(@error_code)
end