Class: SecApi::Config
- Inherits:
-
Anyway::Config
- Object
- Anyway::Config
- SecApi::Config
- Defined in:
- lib/sec_api/config.rb
Overview
Configuration for the SecApi client.
Configuration Layering (via anyway_config): Sources are applied in order of increasing precedence:
-
Defaults (defined in initialize method)
-
YAML file (config/secapi.yml)
-
Environment variables (SECAPI_API_KEY, SECAPI_BASE_URL, etc.)
Later sources override earlier ones - env vars always win. This allows production deployments to use env vars while keeping YAML for development defaults.
Instance Attribute Summary collapse
-
#default_logging ⇒ Boolean
When true and logger is configured, automatically sets up structured logging callbacks for all request lifecycle events using StructuredLogger.
-
#log_level ⇒ Symbol
Log level for rate limit events.
-
#logger ⇒ Logger?
Optional logger instance for structured rate limit logging.
-
#metrics_backend ⇒ Object?
Metrics backend instance (StatsD, Datadog::Statsd, etc.).
-
#on_callback_error ⇒ Proc?
Optional callback invoked when a stream callback raises an exception.
-
#on_dequeue ⇒ Proc?
Optional callback invoked when a request exits the queue after waiting for rate limit reset.
-
#on_error ⇒ Proc?
Optional callback invoked when a REST API request ultimately fails (after all retry attempts are exhausted).
-
#on_excessive_wait ⇒ Proc?
Optional callback invoked when queue wait time exceeds queue_wait_warning_threshold.
-
#on_filing ⇒ Proc?
Optional callback invoked when a filing is received via stream.
-
#on_queue ⇒ Proc?
Optional callback invoked when a request is queued due to exhausted rate limit (remaining = 0).
-
#on_rate_limit ⇒ Proc?
Optional callback invoked when a 429 rate limit response is received and will be retried.
-
#on_reconnect ⇒ Proc?
Optional callback invoked when WebSocket reconnection succeeds.
-
#on_request ⇒ Proc?
Optional callback invoked BEFORE each REST API request is sent.
-
#on_response ⇒ Proc?
Optional callback invoked AFTER each REST API response is received.
-
#on_retry ⇒ Proc?
Optional callback invoked BEFORE each retry attempt for transient errors.
-
#on_throttle ⇒ Proc?
Optional callback invoked when proactive throttling occurs.
-
#queue_wait_warning_threshold ⇒ Integer
Threshold in seconds for excessive wait warnings.
-
#rate_limit_threshold ⇒ Float
Threshold for proactive throttling (0.0-1.0).
-
#stream_backoff_multiplier ⇒ Integer, Float
Multiplier for exponential backoff between reconnection attempts.
-
#stream_initial_reconnect_delay ⇒ Float
Initial delay in seconds before the first reconnection attempt.
-
#stream_latency_warning_threshold ⇒ Float
Latency threshold in seconds before logging a warning (default: 120).
-
#stream_max_reconnect_attempts ⇒ Integer
Maximum number of WebSocket reconnection attempts before giving up and raising ReconnectionError.
-
#stream_max_reconnect_delay ⇒ Float
Maximum delay in seconds between reconnection attempts.
Instance Method Summary collapse
-
#initialize ⇒ Config
constructor
Default values with rationale for each setting.
-
#validate! ⇒ void
Validates configuration and raises ConfigurationError for invalid values.
Constructor Details
#initialize ⇒ Config
Default values with rationale for each setting. These defaults are chosen to balance reliability with responsiveness.
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 |
# File 'lib/sec_api/config.rb', line 490 def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end |
Instance Attribute Details
#default_logging ⇒ Boolean
Returns When true and logger is configured, automatically sets up structured logging callbacks for all request lifecycle events using StructuredLogger. Default: false. Explicit callback configurations take precedence over default logging.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#log_level ⇒ Symbol
Returns Log level for rate limit events. Default is :info. Valid values: :debug, :info, :warn, :error.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#logger ⇒ Logger?
Returns Optional logger instance for structured rate limit logging. When configured, the middleware will log rate limit events (throttle, queue, 429) as JSON for compatibility with monitoring tools like ELK, Splunk, and Datadog. Compatible with Ruby Logger and ActiveSupport::Logger interfaces. Set to nil (default) to disable logging.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#metrics_backend ⇒ Object?
Returns Metrics backend instance (StatsD, Datadog::Statsd, etc.). When configured, automatically sets up metrics callbacks for all operations using MetricsCollector. The backend must respond to increment, histogram, and/or gauge methods. Default: nil. Explicit callback configurations take precedence over default metrics.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#on_callback_error ⇒ Proc?
Returns Optional callback invoked when a stream callback raises an exception. The stream continues processing after this callback returns. Receives a hash with the following keys:
-
:error [Exception] - The exception that was raised
-
:filing [SecApi::Objects::StreamFiling] - The filing being processed
-
:accession_no [String] - SEC accession number
-
:ticker [String, nil] - Stock ticker symbol.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#on_dequeue ⇒ Proc?
Returns Optional callback invoked when a request exits the queue after waiting for rate limit reset. Receives a hash with:
-
:queue_size [Integer] - Number of requests remaining in queue
-
:waited [Float] - Actual seconds the request waited
-
:request_id [String] - UUID for tracing this request across callbacks.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#on_error ⇒ Proc?
This callback is distinct from on_retry. on_error fires on FINAL failure (all retries exhausted), while on_retry fires BEFORE each retry attempt.
Returns Optional callback invoked when a REST API request ultimately fails (after all retry attempts are exhausted). Use for error tracking and alerting. Receives a hash with the following keyword arguments:
-
:request_id [String] - UUID for correlating with request/response callbacks
-
:error [Exception] - The exception that caused the failure
-
:url [String] - Request URL
-
:method [Symbol] - HTTP method.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#on_excessive_wait ⇒ Proc?
Returns Optional callback invoked when queue wait time exceeds queue_wait_warning_threshold. The request continues waiting after callback. Receives a hash with:
-
:wait_time [Float] - Seconds the request will wait
-
:threshold [Integer] - The configured warning threshold
-
:reset_at [Time] - When the rate limit resets
-
:request_id [String] - UUID for tracing this request across callbacks.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#on_filing ⇒ Proc?
Returns Optional callback invoked when a filing is received via stream. Called for ALL filings before filtering and before the user callback. Use for instrumentation and latency monitoring of the full filing stream.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#on_queue ⇒ Proc?
Returns Optional callback invoked when a request is queued due to exhausted rate limit (remaining = 0). Receives a hash with:
-
:queue_size [Integer] - Number of requests currently queued
-
:wait_time [Float] - Estimated seconds until rate limit resets
-
:reset_at [Time] - When the rate limit window resets
-
:request_id [String] - UUID for tracing this request across callbacks.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#on_rate_limit ⇒ Proc?
Returns Optional callback invoked when a 429 rate limit response is received and will be retried. This is the reactive callback (after hitting the limit), distinct from on_throttle which is proactive (before hitting limit). Receives a hash with the following keys:
-
:retry_after [Integer, nil] - Seconds to wait (from Retry-After header)
-
:reset_at [Time, nil] - When the rate limit resets (from X-RateLimit-Reset)
-
:attempt [Integer] - Current retry attempt number
-
:request_id [String, nil] - UUID for tracing this request across callbacks.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#on_reconnect ⇒ Proc?
Returns Optional callback invoked when WebSocket reconnection succeeds. Receives a hash with the following keys:
-
:attempt_count [Integer] - Number of reconnection attempts before success
-
:downtime_seconds [Float] - Total time disconnected in seconds.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#on_request ⇒ Proc?
Returns Optional callback invoked BEFORE each REST API request is sent. Use for request logging, tracing, and custom instrumentation. Receives a hash with the following keyword arguments:
-
:request_id [String] - UUID for correlating this request across all callbacks
-
:method [Symbol] - HTTP method (:get, :post, etc.)
-
:url [String] - Full request URL
-
:headers [Hash] - Request headers (Authorization header is sanitized/excluded).
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#on_response ⇒ Proc?
Returns Optional callback invoked AFTER each REST API response is received. Use for response metrics, latency tracking, and observability dashboards. Receives a hash with the following keyword arguments:
-
:request_id [String] - UUID for correlating with the corresponding on_request callback
-
:status [Integer] - HTTP status code (200, 429, 500, etc.)
-
:duration_ms [Integer] - Request duration in milliseconds
-
:url [String] - Request URL
-
:method [Symbol] - HTTP method.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#on_retry ⇒ Proc?
This callback is distinct from on_error. on_retry fires BEFORE each retry attempt, while on_error fires on FINAL failure (all retries exhausted).
Returns Optional callback invoked BEFORE each retry attempt for transient errors. Use for retry monitoring and alerting on degraded API connectivity. Receives a hash with the following keyword arguments:
-
:request_id [String] - UUID for correlating with request/response callbacks
-
:attempt [Integer] - Current retry attempt number (1-indexed)
-
:max_attempts [Integer] - Maximum retry attempts configured
-
:error_class [String] - Name of the exception class that triggered retry
-
:error_message [String] - Exception message
-
:will_retry_in [Float] - Seconds until retry (from exponential backoff).
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#on_throttle ⇒ Proc?
Returns Optional callback invoked when proactive throttling occurs. Receives a hash with the following keys:
-
:remaining [Integer] - Requests remaining in current window
-
:limit [Integer] - Total requests allowed per window
-
:delay [Float] - Seconds the request will be delayed
-
:reset_at [Time] - When the rate limit window resets
-
:request_id [String] - UUID for tracing this request across callbacks.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#queue_wait_warning_threshold ⇒ Integer
Returns Threshold in seconds for excessive wait warnings. When a request is queued and the wait time exceeds this threshold, the on_excessive_wait callback is invoked. Default is 300 (5 minutes).
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#rate_limit_threshold ⇒ Float
Returns Threshold for proactive throttling (0.0-1.0). When the percentage of remaining requests drops below this value, the middleware will sleep until the rate limit window resets. Default is 0.1 (10%). Set to 0.0 to disable proactive throttling, or 1.0 to always throttle.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#stream_backoff_multiplier ⇒ Integer, Float
Returns Multiplier for exponential backoff between reconnection attempts. Delay formula: min(initial * (multiplier ^ attempt), max_delay). Default is 2 (delays: 1s, 2s, 4s, 8s, …, capped at max).
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#stream_initial_reconnect_delay ⇒ Float
Returns Initial delay in seconds before the first reconnection attempt. Subsequent attempts use exponential backoff. Default is 1.0 second.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#stream_latency_warning_threshold ⇒ Float
Returns Latency threshold in seconds before logging a warning (default: 120). When a filing’s delivery latency exceeds this threshold, a warning is logged. Set to 120 seconds (2 minutes) to align with NFR1 requirements.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#stream_max_reconnect_attempts ⇒ Integer
Returns Maximum number of WebSocket reconnection attempts before giving up and raising ReconnectionError. Default is 10. Set to 0 to disable auto-reconnect entirely.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
#stream_max_reconnect_delay ⇒ Float
Returns Maximum delay in seconds between reconnection attempts. Caps the exponential backoff to prevent excessively long waits. Default is 60.0 seconds.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 |
# File 'lib/sec_api/config.rb', line 454 class Config < Anyway::Config config_name :secapi attr_config :api_key, :base_url, :retry_max_attempts, :retry_initial_delay, :retry_max_delay, :retry_backoff_factor, :request_timeout, :rate_limit_threshold, :queue_wait_warning_threshold, :on_request, :on_response, :on_retry, :on_error, :on_throttle, :on_rate_limit, :on_queue, :on_dequeue, :on_excessive_wait, :on_callback_error, :on_reconnect, :on_filing, :logger, :log_level, :stream_max_reconnect_attempts, :stream_initial_reconnect_delay, :stream_max_reconnect_delay, :stream_backoff_multiplier, :stream_latency_warning_threshold, :default_logging, :metrics_backend # Default values with rationale for each setting. # These defaults are chosen to balance reliability with responsiveness. def initialize(*) super self.base_url ||= "https://api.sec-api.io" # Retry defaults (NFR5: 95%+ automatic recovery from transient failures) # 5 attempts: Empirically provides >95% recovery for typical transient issues. # Formula: P(all_fail) = 0.1^5 = 0.00001 (assuming 10% failure rate per attempt) self.retry_max_attempts ||= 5 # 1 second initial delay: Fast enough to feel responsive, slow enough to allow # transient issues (network blips, brief overloads) to resolve. self.retry_initial_delay ||= 1.0 # 60 second max delay: Acceptable for backfill/batch operations, prevents # excessive delays for interactive use cases. self.retry_max_delay ||= 60.0 # Factor 2: Standard exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 60s). # Doubles each attempt, providing geometric spacing per industry convention. self.retry_backoff_factor ||= 2 self.request_timeout ||= 30 # Rate limiting defaults (FR5: proactive throttling) # 10% threshold: Safety buffer to avoid 429 responses. At 100 req/min limit, # this gives ~10 requests buffer. Lower risks 429s; higher wastes capacity. self.rate_limit_threshold ||= 0.1 self.queue_wait_warning_threshold ||= 300 # 5 minutes self.log_level ||= :info # Stream reconnection defaults (Story 6.4) self.stream_max_reconnect_attempts ||= 10 self.stream_initial_reconnect_delay ||= 1.0 self.stream_max_reconnect_delay ||= 60.0 self.stream_backoff_multiplier ||= 2 # Stream latency defaults (Story 6.5 / NFR1: <2 minute delivery) self.stream_latency_warning_threshold ||= 120.0 # Structured logging defaults (Story 7.3) self.default_logging = false if default_logging.nil? end # Validates configuration and raises ConfigurationError for invalid values. # Called automatically during Client initialization. # # Validation philosophy: Fail fast with actionable error messages. # Invalid config should never reach the API - catch it at startup. # # @raise [ConfigurationError] if any configuration value is invalid # @return [void] def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end private def "api_key is required. " \ "Configure in config/secapi.yml or set SECAPI_API_KEY environment variable. " \ "Get your API key from https://sec-api.io" end def "api_key appears to be invalid (placeholder or too short). " \ "Expected a valid API key from https://sec-api.io. " \ "Check your configuration in config/secapi.yml or SECAPI_API_KEY environment variable." end end |
Instance Method Details
#validate! ⇒ void
This method returns an undefined value.
Validates configuration and raises ConfigurationError for invalid values. Called automatically during Client initialization.
Validation philosophy: Fail fast with actionable error messages. Invalid config should never reach the API - catch it at startup.
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 |
# File 'lib/sec_api/config.rb', line 535 def validate! # API key validation: Reject nil, empty, and obviously invalid keys. # Why check length < 10? Real sec-api.io keys are ~40 chars. Short strings # are likely test values or typos that would cause confusing 401 errors. if api_key.nil? || api_key.empty? raise ConfigurationError, end # Reject placeholder values that users copy from documentation. # Better to fail here with clear message than get cryptic 401 from API. if api_key.include?("your_api_key_here") || api_key.length < 10 raise ConfigurationError, end # Retry configuration validation if retry_max_attempts <= 0 raise ConfigurationError, "retry_max_attempts must be positive" end if retry_initial_delay <= 0 raise ConfigurationError, "retry_initial_delay must be positive" end if retry_max_delay <= 0 raise ConfigurationError, "retry_max_delay must be positive" end if retry_max_delay < retry_initial_delay raise ConfigurationError, "retry_max_delay must be >= retry_initial_delay" end if retry_backoff_factor < 2 raise ConfigurationError, "retry_backoff_factor must be >= 2 for exponential backoff (use 2 for standard exponential: 1s, 2s, 4s, 8s...)" end # Rate limit threshold validation if rate_limit_threshold < 0 || rate_limit_threshold > 1 raise ConfigurationError, "rate_limit_threshold must be between 0.0 and 1.0" end end |