Module: GetStreamRuby::ErrorMapping

Defined in:
lib/getstream_ruby/error_mapping.rb

Overview

Translates HTTP responses and Faraday errors into SDK exceptions.

Class Method Summary collapse

Class Method Details

.api_error_attrs(model, status, raw_body) ⇒ Object



45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/getstream_ruby/error_mapping.rb', line 45

def api_error_attrs(model, status, raw_body)
  {
    message: model.message || "Request failed with status #{status}",
    status_code: status,
    code: model.code || 0,
    exception_fields: model.exception_fields || {},
    unrecoverable: model.unrecoverable.nil? ? false : model.unrecoverable,
    raw_response_body: raw_body,
    more_info: model.more_info,
    details: model.details,
  }
end

.build_task_error(task_id, error_payload) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/getstream_ruby/error_mapping.rb', line 123

def build_task_error(task_id, error_payload)
  hash = if error_payload.respond_to?(:to_h)
           error_payload.to_h
         else
           error_payload || {}
         end
  TaskError.new(
    task_id: task_id,
    error_type: lookup(hash, :type) || '',
    description: lookup(hash, :description) || '',
    stack_trace: lookup(hash, :stacktrace),
    version: lookup(hash, :version),
  )
end

.classify_connection_failure(error) ⇒ Object



113
114
115
116
117
118
119
120
121
# File 'lib/getstream_ruby/error_mapping.rb', line 113

def classify_connection_failure(error)
  wrapped = error.respond_to?(:wrapped_exception) ? error.wrapped_exception : nil
  case wrapped
  when SocketError
    'dns_failure'
  else
    'connection_reset'
  end
end

.classify_faraday_error(error) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/getstream_ruby/error_mapping.rb', line 100

def classify_faraday_error(error)
  case error
  when Faraday::TimeoutError
    'timeout'
  when Faraday::SSLError
    'tls_handshake_failed'
  when Faraday::ConnectionFailed
    classify_connection_failure(error)
  else
    'unknown'
  end
end

.lookup(hash, key) ⇒ Object



138
139
140
# File 'lib/getstream_ruby/error_mapping.rb', line 138

def lookup(hash, key)
  hash[key] || hash[key.to_s]
end

.parse_error_body(body) ⇒ Object



58
59
60
61
62
63
64
65
# File 'lib/getstream_ruby/error_mapping.rb', line 58

def parse_error_body(body)
  return body if body.is_a?(Hash)
  return nil unless body.is_a?(String) && !body.empty?

  JSON.parse(body)
rescue JSON::ParserError
  nil
end

.parse_retry_after(header) ⇒ Object

Parse Retry-After header. Returns Float seconds. Returns nil when absent or unparseable. Past HTTP-dates clamp to 0.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/getstream_ruby/error_mapping.rb', line 84

def parse_retry_after(header)
  return nil if header.nil?

  value = header.to_s.strip
  return nil if value.empty?
  return value.to_f if value.match?(/\A\d+\z/)

  begin
    target = Time.httpdate(value)
    delta = target - Time.now
    delta.negative? ? 0.0 : delta.to_f
  rescue ArgumentError
    nil
  end
end

.raise_api_error(response) ⇒ Object

Raises the appropriate ‘ApiError` / `RateLimitError` for a non-2xx `Faraday::Response`.

Raises:



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/getstream_ruby/error_mapping.rb', line 15

def raise_api_error(response)
  parsed_body = parse_error_body(response.body)
  raw_body = stringify_body(response.body)

  if parsed_body.is_a?(Hash)
    api_error = GetStream::Generated::Models::APIError.new(parsed_body)
    attrs = api_error_attrs(api_error, response.status, raw_body)

    if response.status == 429
      raise RateLimitError.new(
        retry_after: parse_retry_after(response_header(response, 'Retry-After')),
        **attrs,
      )
    end

    raise ApiError.new(**attrs)
  end

  raise ApiError.new(
    message: 'failed to parse error response',
    status_code: response.status,
    code: 0,
    exception_fields: {},
    unrecoverable: false,
    raw_response_body: raw_body,
    more_info: nil,
    details: nil,
  )
end

.response_header(response, name) ⇒ Object



74
75
76
77
78
79
80
# File 'lib/getstream_ruby/error_mapping.rb', line 74

def response_header(response, name)
  headers = response.headers
  return nil if headers.nil?

  # Faraday normalizes header names to lowercase, but tolerate either form.
  headers[name] || headers[name.downcase] || headers[name.to_s]
end

.stringify_body(body) ⇒ Object



67
68
69
70
71
72
# File 'lib/getstream_ruby/error_mapping.rb', line 67

def stringify_body(body)
  return '' if body.nil?
  return body if body.is_a?(String)

  body.to_json
end