Class: HttpResource::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/http_resource/client.rb

Overview

Net::HTTP transport for a single REST host. Resource-oriented: the verbs (get/post/put/patch/delete) are the primitives a Resource is built on, and also an escape hatch for endpoints not yet modelled.

client = HttpResource::Client.new(base_url: "https://api.example.org",
                                auth: HttpResource::Auth.bearer(token))
client.get(["api", "contacts", id])            # GET, id escaped as one segment
client.post(["api", "actions"], { ... })       # POST a JSON body
client.post(["oauth", "token"], form: { ... }) # POST a form body (OAuth, RFC 6749)

Reads return parsed JSON (a Hash/Array, or nil on an empty body). Every call raises an HttpResource::ApiError subclass on a non-2xx response or a transport failure.

SECURITY — path segments are UNTRUSTED. When path is an Array, each segment is percent-encoded as a single RFC-3986 path component, so an id/email/token can never escape into a second segment, the host, a query or a header. A String path is sent VERBATIM (trusted) — NEVER interpolate untrusted input into a String path; pass an Array.

Constant Summary collapse

DEFAULT_OPEN_TIMEOUT =
5
DEFAULT_READ_TIMEOUT =
15

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_url:, auth: nil, username: nil, password: nil, open_timeout: DEFAULT_OPEN_TIMEOUT, read_timeout: DEFAULT_READ_TIMEOUT, simulation: nil) ⇒ Client

Returns a new instance of Client.

Raises:



46
47
48
49
50
51
52
53
54
55
56
# File 'lib/http_resource/client.rb', line 46

def initialize(base_url:, auth: nil, username: nil, password: nil,
               open_timeout: DEFAULT_OPEN_TIMEOUT, read_timeout: DEFAULT_READ_TIMEOUT,
               simulation: nil)
  raise ConfigurationError, "base_url is required" if blank?(base_url)

  @base_url = base_url.to_s.sub(%r{/+\z}, "")
  @auth = auth || default_auth(username, password)
  @open_timeout = open_timeout
  @read_timeout = read_timeout
  @simulation = setup_simulation(simulation)
end

Instance Attribute Details

#base_urlObject (readonly)

Returns the value of attribute base_url.



33
34
35
# File 'lib/http_resource/client.rb', line 33

def base_url
  @base_url
end

#simulationObject (readonly)

The simulation backend when built with simulation:; nil in real mode.



36
37
38
# File 'lib/http_resource/client.rb', line 36

def simulation
  @simulation
end

Class Method Details

.simulation_backend_classObject

The Backend class simulation: true instantiates. Client-gem subclasses override this to point at their own Backend subclass (with its own registered handlers). Only called after the lazy require, so the constant resolves.



42
43
44
# File 'lib/http_resource/client.rb', line 42

def self.simulation_backend_class
  Simulation::Backend
end

Instance Method Details

#delete(path, open_timeout: nil, read_timeout: nil) ⇒ Object



87
88
89
# File 'lib/http_resource/client.rb', line 87

def delete(path, open_timeout: nil, read_timeout: nil)
  request(:delete, path, open_timeout:, read_timeout:)
end

#get(path, params: nil, open_timeout: nil, read_timeout: nil) ⇒ Object

Low-level REST verbs. path may be a String ("/api/foo") sent verbatim, or an Array of segments (["api", "contacts", email]) each individually escaped. Each verb accepts open_timeout:/read_timeout: to override the client's budget for that one call (e.g. a short read_timeout on a synchronous read).

The body-bearing verbs (post/put/patch) send EITHER a JSON body — the positional payload — or a form body — form: {...}, encoded as application/x-www-form-urlencoded (for the form-encoded endpoints OAuth consumers hit: RFC 6749 token, RFC 7662 introspection, …). Passing both is a caller bug and raises ArgumentError. The response side is identical either way: parsed JSON on a 2xx, a typed ApiError (with #status + #body) on a non-2xx — so a "400 invalid_grant" is a rescue-able ClientError whose #body carries the error payload.



71
72
73
# File 'lib/http_resource/client.rb', line 71

def get(path, params: nil, open_timeout: nil, read_timeout: nil)
  request(:get, path, params:, open_timeout:, read_timeout:)
end

#patch(path, payload = nil, form: nil, open_timeout: nil, read_timeout: nil) ⇒ Object



83
84
85
# File 'lib/http_resource/client.rb', line 83

def patch(path, payload = nil, form: nil, open_timeout: nil, read_timeout: nil)
  request(:patch, path, body: payload, form:, open_timeout:, read_timeout:)
end

#post(path, payload = nil, form: nil, open_timeout: nil, read_timeout: nil) ⇒ Object



75
76
77
# File 'lib/http_resource/client.rb', line 75

def post(path, payload = nil, form: nil, open_timeout: nil, read_timeout: nil)
  request(:post, path, body: payload, form:, open_timeout:, read_timeout:)
end

#put(path, payload = nil, form: nil, open_timeout: nil, read_timeout: nil) ⇒ Object



79
80
81
# File 'lib/http_resource/client.rb', line 79

def put(path, payload = nil, form: nil, open_timeout: nil, read_timeout: nil)
  request(:put, path, body: payload, form:, open_timeout:, read_timeout:)
end