Module: Anypost::Errors Private
- Defined in:
- lib/anypost/errors.rb
Overview
This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.
Maps an HTTP response into the right Error subclass. Keys primarily on the canonical ‘error.type`, falling back to the HTTP status.
Constant Summary collapse
- REQUEST_ID_HEADERS =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
["anypost-request-id", "x-anypost-request-id", "x-request-id"].freeze
Class Method Summary collapse
- .build(status, type, message, errors, request_id, raw, headers) ⇒ Object private
- .by_status(status, type, message, errors, headers, common) ⇒ Object private
- .default_message(status) ⇒ Object private
- .from_response(status, body, headers) ⇒ Object private
-
.header(headers, name) ⇒ Object
private
Case-insensitive single-value header lookup over a Hash or Faraday headers.
- .read_request_id(headers) ⇒ Object private
-
.retry_after_seconds(headers) ⇒ Object
private
Parse a Retry-After header (delta-seconds or HTTP-date) into seconds.
- .type_from_status(status) ⇒ Object private
Class Method Details
.build(status, type, message, errors, request_id, raw, headers) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/anypost/errors.rb', line 126 def build(status, type, , errors, request_id, raw, headers) common = {status: status, request_id: request_id, raw: raw} case type when "validation_error" ValidationError.new(, errors: errors, type: type, **common) when "authentication_error" AuthenticationError.new(, type: type, **common) when "permission_error" PermissionError.new(, type: type, **common) when "not_found" NotFoundError.new(, type: type, **common) when "conflict", "idempotency_concurrent", "webhook_rotation_in_progress" ConflictError.new(, type: type, **common) when "idempotency_mismatch" IdempotencyMismatchError.new(, type: type, **common) when "rate_limit_exceeded" RateLimitError.new(, retry_after: retry_after_seconds(headers), type: type, **common) when "payload_too_large" PayloadTooLargeError.new(, type: type, **common) when "provisioning_error", "internal_error" APIError.new(, type: type, **common) else by_status(status, type, , errors, headers, common) end end |
.by_status(status, type, message, errors, headers, common) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
152 153 154 155 156 157 158 159 160 161 162 163 164 |
# File 'lib/anypost/errors.rb', line 152 def by_status(status, type, , errors, headers, common) case status when 401 then AuthenticationError.new(, type: type, **common) when 403 then PermissionError.new(, type: type, **common) when 404 then NotFoundError.new(, type: type, **common) when 409 then ConflictError.new(, type: type, **common) when 413 then PayloadTooLargeError.new(, type: type, **common) when 429 then RateLimitError.new(, retry_after: retry_after_seconds(headers), type: type, **common) when 400, 422 then ValidationError.new(, errors: errors, type: type, **common) else (status >= 500) ? APIError.new(, type: type, **common) : Error.new(, type: type, **common) end end |
.default_message(status) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
180 181 182 |
# File 'lib/anypost/errors.rb', line 180 def (status) "Anypost request failed with status #{status}." end |
.from_response(status, body, headers) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/anypost/errors.rb', line 88 def from_response(status, body, headers) request_id = read_request_id(headers) envelope = body.is_a?(Hash) ? body : {} error = envelope["error"] errors = {} case error when Hash # Canonical envelope: { error: { type, message, errors? } }. type = error["type"] || type_from_status(status) = error["message"] || (status) errors = error["errors"] if error["errors"].is_a?(Hash) when String # Flat envelope: { error: "<code>", message? }. type = error = envelope["message"] || error.tr("_", " ") else type = type_from_status(status) = (status) end build(status, type, , errors || {}, request_id, body, headers) end |
.header(headers, name) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Case-insensitive single-value header lookup over a Hash or Faraday headers.
193 194 195 196 197 198 199 200 201 |
# File 'lib/anypost/errors.rb', line 193 def header(headers, name) return nil if headers.nil? name = name.downcase headers.each do |key, value| return value if key.to_s.downcase == name end nil end |
.read_request_id(headers) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
184 185 186 187 188 189 190 |
# File 'lib/anypost/errors.rb', line 184 def read_request_id(headers) REQUEST_ID_HEADERS.each do |name| value = header(headers, name) return value if value && !value.empty? end nil end |
.retry_after_seconds(headers) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Parse a Retry-After header (delta-seconds or HTTP-date) into seconds.
113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/anypost/errors.rb', line 113 def retry_after_seconds(headers) value = header(headers, "retry-after") return nil if value.nil? || value.empty? return [value.to_f, 0.0].max if /\A\s*\d+(\.\d+)?\s*\z/.match?(value) begin target = Time.httpdate(value) rescue ArgumentError return nil end [target.to_f - Time.now.to_f, 0.0].max end |
.type_from_status(status) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/anypost/errors.rb', line 166 def type_from_status(status) case status when 400, 422 then "validation_error" when 401 then "authentication_error" when 403 then "permission_error" when 404 then "not_found" when 409 then "conflict" when 413 then "payload_too_large" when 429 then "rate_limit_exceeded" else (status >= 500) ? "internal_error" : "api_error" end end |