Class: Apertur::HttpClient

Inherits:
Object
  • Object
show all
Defined in:
lib/apertur/http_client.rb

Overview

Low-level HTTP wrapper around Net::HTTP for communicating with the Apertur API.

Handles JSON serialization, Bearer token authentication, multipart uploads, request signing, and error mapping.

Instance Method Summary collapse

Constructor Details

#initialize(base_url, token, signing_secret: nil) ⇒ HttpClient

Returns a new instance of HttpClient.

Parameters:

  • base_url (String)

    the API base URL (e.g. "https://api.aptr.ca")

  • token (String)

    the Bearer token (API key or OAuth token)

  • signing_secret (String, nil) (defaults to: nil)

    optional request signing secret. When present (non-nil, non-empty), every JSON request is automatically signed with X-Aptr-Signature / X-Aptr-Timestamp headers. Absent by default for backwards compatibility.



21
22
23
24
25
# File 'lib/apertur/http_client.rb', line 21

def initialize(base_url, token, signing_secret: nil)
  @base_url = base_url.chomp("/")
  @token = token
  @signing_secret = signing_secret
end

Instance Method Details

#request(method, path, body: nil, query: nil, headers: {}, read_timeout: nil) ⇒ Hash, ...

Perform an API request and return the parsed JSON response.

Parameters:

  • method (Symbol)

    HTTP method (:get, :post, :patch, :put, :delete)

  • path (String)

    the API path (e.g. "/api/v1/stats")

  • body (Hash, nil) (defaults to: nil)

    request body to be serialized as JSON

  • query (Hash, nil) (defaults to: nil)

    query parameters

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

    additional request headers

  • read_timeout (Integer, Float, nil) (defaults to: nil)

    override the per-request read timeout (seconds). Defaults to the connection default (60s).

Returns:

  • (Hash, Array, nil)

    parsed JSON response, or nil for 204

Raises:



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/apertur/http_client.rb', line 38

def request(method, path, body: nil, query: nil, headers: {}, read_timeout: nil)
  uri = build_uri(path, query)
  req = build_request(method, uri, headers)

  if body
    req["Content-Type"] = "application/json"
    req.body = body.is_a?(String) ? body : JSON.generate(body)
  end

  # Sign the full request-target (path + query) the server receives as
  # req.url — build_uri may have appended a query string, and the server
  # signs the query too. uri.request_uri is exactly what goes on the wire.
  sign_headers(method, uri.request_uri, req.body).each { |k, v| req[k] = v }

  response = execute(uri, req, read_timeout: read_timeout)
  handle_response(response)
end

#request_multipart(path, file_data, filename:, mime_type:, fields: {}, headers: {}) ⇒ Hash, ...

Note:

Multipart bodies are NOT signed. Net::HTTP streams the boundary and part framing after this point, so the exact bytes on the wire aren't available here to hash — signing an approximation would only produce a signature that fails server-side verification. Multipart uploads rely on Authorization (API key) auth instead; request signing covers the JSON request path only (mirrors the Node SDK).

Perform a multipart/form-data upload request.

Parameters:

  • path (String)

    the API path

  • file_data (String)

    raw file bytes

  • filename (String)

    the filename to use in the multipart part

  • mime_type (String)

    the MIME type of the file

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

    additional form fields

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

    additional request headers

Returns:

  • (Hash, Array, nil)

    parsed JSON response

Raises:



91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/apertur/http_client.rb', line 91

def request_multipart(path, file_data, filename:, mime_type:, fields: {}, headers: {})
  uri = build_uri(path)
  boundary = "AperturRubySDK#{SecureRandom.hex(16)}"

  body = build_multipart_body(boundary, file_data, filename, mime_type, fields)

  req = build_request(:post, uri, headers)
  req["Content-Type"] = "multipart/form-data; boundary=#{boundary}"
  req.body = body

  response = execute(uri, req)
  handle_response(response)
end

#request_raw(method, path, query: nil) ⇒ String

Perform an API request and return the raw response body as a binary String.

Parameters:

  • method (Symbol)

    HTTP method

  • path (String)

    the API path

  • query (Hash, nil) (defaults to: nil)

    query parameters

Returns:

  • (String)

    raw response body (binary)

Raises:



63
64
65
66
67
68
69
70
71
72
# File 'lib/apertur/http_client.rb', line 63

def request_raw(method, path, query: nil)
  uri = build_uri(path, query)
  req = build_request(method, uri)

  sign_headers(method, uri.request_uri, req.body).each { |k, v| req[k] = v }

  response = execute(uri, req)
  handle_error(response) unless response.is_a?(Net::HTTPSuccess)
  response.body
end