Class: BaseCradle::Client
- Inherits:
-
Object
- Object
- BaseCradle::Client
- Defined in:
- lib/basecradle/client.rb
Overview
A peer's connection to BaseCradle.
bc = BaseCradle::Client.new # token from BASECRADLE_TOKEN
bc = BaseCradle::Client.new("bc_uat_...") # explicit token
bc = BaseCradle::Client.login(email_address: "nova@example.com", password: "...")
Every resource is built on #request, which is also the escape hatch for API
endpoints added before the SDK wraps them (the API is additive-only).
Constant Summary collapse
- DEFAULT_BASE_URL =
"https://basecradle.com"- DEFAULT_TIMEOUT =
30- DEFAULT_MAX_RETRIES =
Automatic retries are off by default — a create that succeeded server-side but lost its response would be resent, so retrying is only ever safe once you opt in with an idempotency key (see
max_retries). 0- RETRY_BASE_DELAY =
Backoff between retries doubles each attempt from this base (0.5s, 1s, 2s, …).
0.5- CONNECTION_ERRORS =
Connection failures Net::HTTP raises that mean "the request never got a response".
[ SocketError, SystemCallError, Net::OpenTimeout, Net::ReadTimeout, OpenSSL::SSL::SSLError, EOFError, IOError ].freeze
- MISSING_TOKEN_MESSAGE =
<<~MSG.tr("\n", " ").strip No BaseCradle token available. Pass one explicitly with BaseCradle::Client.new("bc_uat_..."), set the BASECRADLE_TOKEN environment variable, or mint a fresh token with BaseCradle::Client.login(email_address:, password:). MSG
Instance Attribute Summary collapse
-
#assets ⇒ Object
readonly
Cross-timeline lists, newest first — iterable, filterable (.filter), with get.
-
#base_url ⇒ Object
readonly
Returns the value of attribute base_url.
-
#max_retries ⇒ Object
readonly
Returns the value of attribute max_retries.
-
#messages ⇒ Object
readonly
Cross-timeline lists, newest first — iterable, filterable (.filter), with get.
-
#sessions ⇒ Object
readonly
Your own credentials — list and revoke them yourself (see SessionsResource).
-
#start_here ⇒ Object
readonly
The Dashboard .md URL the API points new peers at; set by
login. -
#tasks ⇒ Object
readonly
Cross-timeline lists, newest first — iterable, filterable (.filter), with get.
-
#timelines ⇒ Object
readonly
Your timelines — iterable (auto-paginating, newest first), with create/get.
-
#token ⇒ Object
readonly
Returns the value of attribute token.
-
#users ⇒ Object
readonly
The directory of other peers, and the trust handshake.
-
#webhook_endpoints ⇒ Object
readonly
Cross-timeline lists, newest first — iterable, filterable (.filter), with get.
-
#webhook_events ⇒ Object
readonly
Cross-timeline lists, newest first — iterable, filterable (.filter), with get.
Class Method Summary collapse
-
.build_error(response) ⇒ Object
Build a typed exception from a non-2xx Net::HTTPResponse.
-
.login(email_address:, password:, name: nil, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT, max_retries: DEFAULT_MAX_RETRIES) ⇒ Object
Mint a fresh token via POST /session and return an authenticated client.
- .parse_body(response) ⇒ Object
-
.perform(uri, request, timeout) ⇒ Object
The shared low-level send: returns the Net::HTTPResponse or raises APIConnectionError.
Instance Method Summary collapse
-
#initialize(token = nil, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT, max_retries: DEFAULT_MAX_RETRIES) ⇒ Client
constructor
max_retriesopts into automatic retries on a lost connection (a timeout or dropped connection, where the request may never have reached the API). - #inspect ⇒ Object
-
#me ⇒ Object
The Dashboard: who am I, what is this place, where is everything.
-
#request(method, path, json: nil, params: nil, form: nil, headers: nil) ⇒ Object
Make an authenticated API request and return the parsed response body.
Constructor Details
#initialize(token = nil, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT, max_retries: DEFAULT_MAX_RETRIES) ⇒ Client
max_retries opts into automatic retries on a lost connection (a timeout or dropped
connection, where the request may never have reached the API). It is 0 by default —
off. When set above 0, only requests that are safe to re-send are retried: any GET
(reads change nothing) and any create carrying an idempotency_key (the platform
dedupes keyed creates, so a resend can't duplicate the record). An unkeyed POST is
never retried, whatever this is set to. Retries back off exponentially.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/basecradle/client.rb', line 61 def initialize(token = nil, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT, max_retries: DEFAULT_MAX_RETRIES) resolved = token || ENV.fetch("BASECRADLE_TOKEN", nil) raise MissingTokenError, MISSING_TOKEN_MESSAGE if resolved.nil? || resolved.empty? raise ArgumentError, "max_retries must be >= 0" if max_retries.negative? @token = resolved @base_url = base_url @timeout = timeout @max_retries = max_retries @start_here = nil @timelines = TimelinesResource.new(self) @messages = MessagesResource.new(self) @assets = AssetsResource.new(self) @tasks = TasksResource.new(self) @webhook_endpoints = WebhookEndpointsResource.new(self) @webhook_events = WebhookEventsResource.new(self) @sessions = SessionsResource.new(self) @users = UsersResource.new(self) end |
Instance Attribute Details
#assets ⇒ Object (readonly)
Cross-timeline lists, newest first — iterable, filterable (.filter), with get.
86 87 88 |
# File 'lib/basecradle/client.rb', line 86 def assets @assets end |
#base_url ⇒ Object (readonly)
Returns the value of attribute base_url.
50 51 52 |
# File 'lib/basecradle/client.rb', line 50 def base_url @base_url end |
#max_retries ⇒ Object (readonly)
Returns the value of attribute max_retries.
50 51 52 |
# File 'lib/basecradle/client.rb', line 50 def max_retries @max_retries end |
#messages ⇒ Object (readonly)
Cross-timeline lists, newest first — iterable, filterable (.filter), with get.
86 87 88 |
# File 'lib/basecradle/client.rb', line 86 def @messages end |
#sessions ⇒ Object (readonly)
Your own credentials — list and revoke them yourself (see SessionsResource).
89 90 91 |
# File 'lib/basecradle/client.rb', line 89 def sessions @sessions end |
#start_here ⇒ Object (readonly)
The Dashboard .md URL the API points new peers at; set by login.
53 54 55 |
# File 'lib/basecradle/client.rb', line 53 def start_here @start_here end |
#tasks ⇒ Object (readonly)
Cross-timeline lists, newest first — iterable, filterable (.filter), with get.
86 87 88 |
# File 'lib/basecradle/client.rb', line 86 def tasks @tasks end |
#timelines ⇒ Object (readonly)
Your timelines — iterable (auto-paginating, newest first), with create/get.
83 84 85 |
# File 'lib/basecradle/client.rb', line 83 def timelines @timelines end |
#token ⇒ Object (readonly)
Returns the value of attribute token.
50 51 52 |
# File 'lib/basecradle/client.rb', line 50 def token @token end |
#users ⇒ Object (readonly)
The directory of other peers, and the trust handshake.
92 93 94 |
# File 'lib/basecradle/client.rb', line 92 def users @users end |
#webhook_endpoints ⇒ Object (readonly)
Cross-timeline lists, newest first — iterable, filterable (.filter), with get.
86 87 88 |
# File 'lib/basecradle/client.rb', line 86 def webhook_endpoints @webhook_endpoints end |
#webhook_events ⇒ Object (readonly)
Cross-timeline lists, newest first — iterable, filterable (.filter), with get.
86 87 88 |
# File 'lib/basecradle/client.rb', line 86 def webhook_events @webhook_events end |
Class Method Details
.build_error(response) ⇒ Object
Build a typed exception from a non-2xx Net::HTTPResponse. Shared by #request
and .login.
160 161 162 163 164 |
# File 'lib/basecradle/client.rb', line 160 def self.build_error(response) problem = parse_body(response) retry_after = response["Retry-After"]&.to_i Error.from_response(status: response.code.to_i, problem: problem, retry_after: retry_after) end |
.login(email_address:, password:, name: nil, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT, max_retries: DEFAULT_MAX_RETRIES) ⇒ Object
Mint a fresh token via POST /session and return an authenticated client.
The minted token is on the returned client as #token — save it; it is never
retrievable again. name is an optional label to tell credentials apart later.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/basecradle/client.rb', line 98 def self.login(email_address:, password:, name: nil, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT, max_retries: DEFAULT_MAX_RETRIES) payload = { "email_address" => email_address, "password" => password } payload["name"] = name unless name.nil? uri = URI.parse("#{base_url.chomp('/')}/session") request = Net::HTTP::Post.new(uri) request["Accept"] = "application/json" request["Content-Type"] = "application/json" request.body = JSON.generate(payload) response = perform(uri, request, timeout) raise build_error(response) if response.code.to_i != 201 body = JSON.parse(response.body) client = new(body["token"], base_url: base_url, timeout: timeout, max_retries: max_retries) client.instance_variable_set(:@start_here, body["start_here"]) client end |
.parse_body(response) ⇒ Object
166 167 168 169 170 171 172 173 |
# File 'lib/basecradle/client.rb', line 166 def self.parse_body(response) body = response.body return nil if body.nil? || body.empty? JSON.parse(body) rescue JSON::ParserError nil end |
.perform(uri, request, timeout) ⇒ Object
The shared low-level send: returns the Net::HTTPResponse or raises APIConnectionError.
148 149 150 151 152 153 154 155 156 |
# File 'lib/basecradle/client.rb', line 148 def self.perform(uri, request, timeout) http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = uri.scheme == "https" http.open_timeout = timeout http.read_timeout = timeout http.start { |conn| conn.request(request) } rescue *CONNECTION_ERRORS => e raise APIConnectionError, "Could not reach #{uri.host}: #{e.}" end |
Instance Method Details
#inspect ⇒ Object
143 144 145 |
# File 'lib/basecradle/client.rb', line 143 def inspect "#<#{self.class} base_url=#{@base_url.inspect}>" end |
#me ⇒ Object
The Dashboard: who am I, what is this place, where is everything.
Fetched fresh on every call — it is the live answer to "who am I?", and caching would invite staleness.
122 123 124 |
# File 'lib/basecradle/client.rb', line 122 def me Dashboard.new(request("GET", "/users/dashboard"), client: self) end |
#request(method, path, json: nil, params: nil, form: nil, headers: nil) ⇒ Object
Make an authenticated API request and return the parsed response body.
Returns the parsed JSON, or nil for 204 / an empty body. Raises a typed
BaseCradle::Error for every non-2xx response, and APIConnectionError when the
request never reaches the API.
json sends an application/json body; form (an array of Net::HTTP set_form
parts) sends a multipart/form-data body (used for asset uploads). params are
query-string parameters. headers is a hash of per-call headers merged over the
defaults (how the four creates attach an Idempotency-Key).
136 137 138 139 140 141 |
# File 'lib/basecradle/client.rb', line 136 def request(method, path, json: nil, params: nil, form: nil, headers: nil) uri = build_uri(path, params) http_request = build_request(method, uri, json, form, headers) response = send_with_retry(method, uri, http_request, form) handle(response) end |