Class: SeatLayer::HTTPClient
- Inherits:
-
Object
- Object
- SeatLayer::HTTPClient
- Defined in:
- lib/seatlayer/http_client.rb
Overview
The transport: auth, idempotency, retry, and error mapping.
Built on net/http from the standard library rather than Faraday or HTTParty. A server SDK that drags in an HTTP stack becomes a supply-chain surface for every customer who installs it, and can conflict with whatever the host application already uses.
Constant Summary collapse
- DEFAULT_BASE_URL =
"https://api.seatlayer.io"- DEFAULT_MAX_RETRIES =
3- DEFAULT_TIMEOUT =
30.0- IDEMPOTENCY_KEY_PATTERN =
The API's own charset for Idempotency-Key.
/\A[A-Za-z0-9._:-]{1,128}\z/
Instance Attribute Summary collapse
-
#base_url ⇒ Object
readonly
Returns the value of attribute base_url.
-
#mode ⇒ String
readonly
"live", "test", or "unknown", derived from the key prefix.
Class Method Summary collapse
-
.encode(segment) ⇒ Object
Percent-encode a path segment, including slashes.
- .validate_idempotency_key!(key) ⇒ Object
Instance Method Summary collapse
- #delete(path) ⇒ Object
- #get(path, query = nil) ⇒ Object
-
#initialize(secret_key:, base_url: DEFAULT_BASE_URL, max_retries: DEFAULT_MAX_RETRIES, timeout: DEFAULT_TIMEOUT, transport: nil) ⇒ HTTPClient
constructor
A new instance of HTTPClient.
- #patch(path, body) ⇒ Object
- #post(path, body = nil, idempotency_key: nil) ⇒ Object
- #put(path, body) ⇒ Object
- #request(method, path, query: nil, body: nil, idempotency_key: nil) ⇒ Object
Constructor Details
#initialize(secret_key:, base_url: DEFAULT_BASE_URL, max_retries: DEFAULT_MAX_RETRIES, timeout: DEFAULT_TIMEOUT, transport: nil) ⇒ HTTPClient
Returns a new instance of HTTPClient.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/seatlayer/http_client.rb', line 27 def initialize(secret_key:, base_url: DEFAULT_BASE_URL, max_retries: DEFAULT_MAX_RETRIES, timeout: DEFAULT_TIMEOUT, transport: nil) raise ArgumentError, "A SeatLayer secret key is required." if secret_key.nil? || secret_key.empty? # Caught here rather than as a 401 three round-trips later. The pk_ case # gets its own message: it is the one people paste by mistake. if secret_key.start_with?("pk_") raise ArgumentError, "That is a publishable key. The server SDK needs a secret key (sk_live_… or sk_test_…)." end unless secret_key.start_with?("sk_") raise ArgumentError, "A SeatLayer secret key starts with sk_live_ or sk_test_." end @secret_key = secret_key @base_url = base_url.sub(%r{/+\z}, "") @max_retries = max_retries @timeout = timeout @transport = transport @mode = if secret_key.start_with?("sk_test_") then "test" elsif secret_key.start_with?("sk_live_") then "live" else "unknown" end end |
Instance Attribute Details
#base_url ⇒ Object (readonly)
Returns the value of attribute base_url.
25 26 27 |
# File 'lib/seatlayer/http_client.rb', line 25 def base_url @base_url end |
#mode ⇒ String (readonly)
Returns "live", "test", or "unknown", derived from the key prefix.
24 25 26 |
# File 'lib/seatlayer/http_client.rb', line 24 def mode @mode end |
Class Method Details
.encode(segment) ⇒ Object
Percent-encode a path segment, including slashes.
61 62 63 |
# File 'lib/seatlayer/http_client.rb', line 61 def self.encode(segment) URI.encode_www_form_component(segment.to_s).gsub("+", "%20") end |
.validate_idempotency_key!(key) ⇒ Object
52 53 54 55 56 57 58 |
# File 'lib/seatlayer/http_client.rb', line 52 def self.validate_idempotency_key!(key) return if IDEMPOTENCY_KEY_PATTERN.match?(key) raise ArgumentError, "Invalid Idempotency-Key #{key.inspect}: allowed characters are " \ "A-Z a-z 0-9 . _ : - and the length must be 1-128." end |
Instance Method Details
#delete(path) ⇒ Object
111 112 113 |
# File 'lib/seatlayer/http_client.rb', line 111 def delete(path) request("DELETE", path) end |
#get(path, query = nil) ⇒ Object
95 96 97 |
# File 'lib/seatlayer/http_client.rb', line 95 def get(path, query = nil) request("GET", path, query: query) end |
#patch(path, body) ⇒ Object
107 108 109 |
# File 'lib/seatlayer/http_client.rb', line 107 def patch(path, body) request("PATCH", path, body: body) end |
#post(path, body = nil, idempotency_key: nil) ⇒ Object
99 100 101 |
# File 'lib/seatlayer/http_client.rb', line 99 def post(path, body = nil, idempotency_key: nil) request("POST", path, body: body, idempotency_key: idempotency_key) end |
#put(path, body) ⇒ Object
103 104 105 |
# File 'lib/seatlayer/http_client.rb', line 103 def put(path, body) request("PUT", path, body: body) end |
#request(method, path, query: nil, body: nil, idempotency_key: nil) ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/seatlayer/http_client.rb', line 65 def request(method, path, query: nil, body: nil, idempotency_key: nil) url = @base_url + path if query pairs = query.compact url += "?#{URI.encode_www_form(pairs)}" unless pairs.empty? end headers = { "Authorization" => "Bearer #{@secret_key}", "Accept" => "application/json", "User-Agent" => "seatlayer-ruby" } payload = nil if body payload = JSON.generate(body) headers["Content-Type"] = "application/json" end # Every mutation carries one. A retried POST that creates a second hold is # worse than a failed POST, and the caller cannot tell from outside — so # the SDK, which knows it retried, is the right place to guarantee it. unless %w[GET HEAD].include?(method) key = idempotency_key || SecureRandom.uuid self.class.validate_idempotency_key!(key) headers["Idempotency-Key"] = key end execute(method, url, headers, payload) end |