Class: Posthaste::HttpClient
- Inherits:
-
Object
- Object
- Posthaste::HttpClient
- Defined in:
- lib/posthaste/http_client.rb
Overview
Authentication, the retry policy, and turning a refusal into an exception.
The retry rules are ported from the Python and TypeScript SDKs rather than reinvented, so a Rails app and a Python worker hitting the same API behave the same way under the same throttle.
Constant Summary collapse
- DEFAULT_MAX_RETRIES =
2- DEFAULT_MAX_RETRY_DELAY =
60.0- BASE_BACKOFF =
First backoff step, in seconds. Doubles per attempt, capped at 8s, with full jitter applied on top.
0.5- MAX_BACKOFF =
8.0
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(api_key:, base_url: DEFAULT_BASE_URL, transport: nil, max_retries: DEFAULT_MAX_RETRIES, max_retry_delay: DEFAULT_MAX_RETRY_DELAY, open_timeout: 10, read_timeout: 30, user_agent: nil, sleeper: nil, randomizer: nil) ⇒ HttpClient
constructor
A new instance of HttpClient.
-
#inspect ⇒ Object
(also: #to_s)
Never prints the key.
-
#send_email(payload, idempotent:) ⇒ Object
POST /v1/emails.
Constructor Details
#initialize(api_key:, base_url: DEFAULT_BASE_URL, transport: nil, max_retries: DEFAULT_MAX_RETRIES, max_retry_delay: DEFAULT_MAX_RETRY_DELAY, open_timeout: 10, read_timeout: 30, user_agent: nil, sleeper: nil, randomizer: nil) ⇒ HttpClient
Returns a new instance of HttpClient.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/posthaste/http_client.rb', line 83 def initialize(api_key:, base_url: DEFAULT_BASE_URL, transport: nil, max_retries: DEFAULT_MAX_RETRIES, max_retry_delay: DEFAULT_MAX_RETRY_DELAY, open_timeout: 10, read_timeout: 30, user_agent: nil, sleeper: nil, randomizer: nil) @api_key = api_key.to_s @base_url = base_url.to_s.sub(%r{/+\z}, '') @transport = transport || NetHttpTransport.new(open_timeout: open_timeout, read_timeout: read_timeout) @max_retries = max_retries @max_retry_delay = max_retry_delay @user_agent = user_agent || self.class.default_user_agent # Injected so the retry tests do not actually wait, and so the jitter is # deterministic under test without the policy itself knowing it is a test. @sleeper = sleeper || ->(seconds) { sleep(seconds) } @randomizer = randomizer || -> { Kernel.rand } end |
Class Method Details
.default_user_agent ⇒ Object
100 101 102 103 |
# File 'lib/posthaste/http_client.rb', line 100 def self.default_user_agent mailer = defined?(::ActionMailer::VERSION::STRING) ? ::ActionMailer::VERSION::STRING : 'none' "posthaste-rails/#{Posthaste::VERSION} ruby/#{RUBY_VERSION} actionmailer/#{mailer}" end |
Instance Method Details
#inspect ⇒ Object Also known as: to_s
Never prints the key. See Posthaste::Redaction for why this matters.
120 121 122 123 |
# File 'lib/posthaste/http_client.rb', line 120 def inspect "#<Posthaste::HttpClient base_url=#{@base_url.inspect} " \ "api_key=#{Redaction.describe_key(@api_key).inspect} max_retries=#{@max_retries}>" end |
#send_email(payload, idempotent:) ⇒ Object
POST /v1/emails.
Two success statuses, and they mean different things:
202 {"status":"queued"} — accepted, a new message exists.
200 {"status":"duplicate"} — an idempotency replay. No new message was
created; the id is the original's.
idempotent: is the whole retry rule. Without an idempotency key a retry
after a lost response sends the email TWICE, so a send is only repeated
automatically when the caller supplied one.
115 116 117 |
# File 'lib/posthaste/http_client.rb', line 115 def send_email(payload, idempotent:) decode(perform('post', '/v1/emails', payload, idempotent: idempotent)) end |