Class: SecApi::Middleware::ErrorHandler

Inherits:
Faraday::Middleware
  • Object
show all
Defined in:
lib/sec_api/middleware/error_handler.rb

Overview

Faraday middleware that converts HTTP status codes and Faraday exceptions into typed SecApi exceptions.

This middleware maps:

  • HTTP 400 → ValidationError (permanent)

  • HTTP 401 → AuthenticationError (permanent)

  • HTTP 403 → AuthenticationError (permanent)

  • HTTP 404 → NotFoundError (permanent)

  • HTTP 422 → ValidationError (permanent)

  • HTTP 429 → RateLimitError (transient)

  • HTTP 5xx → ServerError (transient)

  • Faraday::TimeoutError → NetworkError (transient)

  • Faraday::ConnectionFailed → NetworkError (transient)

  • Faraday::SSLError → NetworkError (transient)

Position in middleware stack: After retry/rate limiter, before adapter

Raises:

  • (ValidationError)

    when API returns 400 (Bad Request) or 422 (Unprocessable Entity)

  • (AuthenticationError)

    when API returns 401 (Unauthorized) or 403 (Forbidden)

  • (NotFoundError)

    when API returns 404 (Not Found)

  • (RateLimitError)

    when API returns 429 (Too Many Requests)

  • (ServerError)

    when API returns 5xx (Server Error)

  • (NetworkError)

    when network issues occur (timeout, connection failure, SSL error)

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ ErrorHandler

Initializes the error handler middleware.

Parameters:

  • app (Faraday::Middleware)

    The next middleware in the stack

  • options (Hash) (defaults to: {})

    Configuration options

Options Hash (options):

  • :config (SecApi::Config)

    The config object containing on_error callback



46
47
48
49
# File 'lib/sec_api/middleware/error_handler.rb', line 46

def initialize(app, options = {})
  super(app)
  @config = options[:config]
end

Instance Method Details

#call(env) ⇒ Faraday::Response

Processes the request and converts HTTP errors to typed exceptions.

Parameters:

  • env (Faraday::Env)

    The request/response environment

Returns:

  • (Faraday::Response)

    The response (if no error)

Raises:



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/sec_api/middleware/error_handler.rb', line 62

def call(env)
  response = @app.call(env)
  handle_response(response.env)
  response
rescue Faraday::RetriableResponse => e
  # Faraday retry raises this to signal a retry - we need to re-raise it
  # so retry middleware can catch it
  raise e
rescue Faraday::TimeoutError => e
  # Don't invoke on_error here - TransientErrors will be retried.
  # on_error is invoked by Instrumentation middleware after all retries exhausted.
  raise NetworkError.new(
    "Request timeout. " \
    "Check network connectivity or increase request_timeout in configuration. " \
    "Original error: #{e.message}.",
    request_id: env[:request_id]
  )
rescue Faraday::ConnectionFailed => e
  # Don't invoke on_error here - TransientErrors will be retried.
  # on_error is invoked by Instrumentation middleware after all retries exhausted.
  raise NetworkError.new(
    "Connection failed: #{e.message}. " \
    "Verify network connectivity and sec-api.io availability. " \
    "This is a temporary issue that will be retried automatically.",
    request_id: env[:request_id]
  )
rescue Faraday::SSLError => e
  # Don't invoke on_error here - TransientErrors will be retried.
  # on_error is invoked by Instrumentation middleware after all retries exhausted.
  raise NetworkError.new(
    "SSL/TLS error: #{e.message}. " \
    "This may indicate certificate validation issues or secure connection problems. " \
    "Verify your system's SSL certificates are up to date.",
    request_id: env[:request_id]
  )
end