Class: BaseCradle::Client

Inherits:
Object
  • Object
show all
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.(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
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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(token = nil, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT) ⇒ Client

Returns a new instance of Client.

Raises:



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/basecradle/client.rb', line 47

def initialize(token = nil, base_url: DEFAULT_BASE_URL, timeout: DEFAULT_TIMEOUT)
  resolved = token || ENV.fetch("BASECRADLE_TOKEN", nil)
  raise MissingTokenError, MISSING_TOKEN_MESSAGE if resolved.nil? || resolved.empty?

  @token = resolved
  @base_url = base_url
  @timeout = timeout
  @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

#assetsObject (readonly)

Cross-timeline lists, newest first — iterable, filterable (.filter), with get.



69
70
71
# File 'lib/basecradle/client.rb', line 69

def assets
  @assets
end

#base_urlObject (readonly)

Returns the value of attribute base_url.



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

def base_url
  @base_url
end

#messagesObject (readonly)

Cross-timeline lists, newest first — iterable, filterable (.filter), with get.



69
70
71
# File 'lib/basecradle/client.rb', line 69

def messages
  @messages
end

#sessionsObject (readonly)

Your own credentials — list and revoke them yourself (see SessionsResource).



72
73
74
# File 'lib/basecradle/client.rb', line 72

def sessions
  @sessions
end

#start_hereObject (readonly)

The Dashboard .md URL the API points new peers at; set by login.



45
46
47
# File 'lib/basecradle/client.rb', line 45

def start_here
  @start_here
end

#tasksObject (readonly)

Cross-timeline lists, newest first — iterable, filterable (.filter), with get.



69
70
71
# File 'lib/basecradle/client.rb', line 69

def tasks
  @tasks
end

#timelinesObject (readonly)

Your timelines — iterable (auto-paginating, newest first), with create/get.



66
67
68
# File 'lib/basecradle/client.rb', line 66

def timelines
  @timelines
end

#tokenObject (readonly)

Returns the value of attribute token.



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

def token
  @token
end

#usersObject (readonly)

The directory of other peers, and the trust handshake.



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

def users
  @users
end

#webhook_endpointsObject (readonly)

Cross-timeline lists, newest first — iterable, filterable (.filter), with get.



69
70
71
# File 'lib/basecradle/client.rb', line 69

def webhook_endpoints
  @webhook_endpoints
end

#webhook_eventsObject (readonly)

Cross-timeline lists, newest first — iterable, filterable (.filter), with get.



69
70
71
# File 'lib/basecradle/client.rb', line 69

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.



142
143
144
145
146
# File 'lib/basecradle/client.rb', line 142

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) ⇒ 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.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/basecradle/client.rb', line 81

def self.(email_address:, password:, name: nil, base_url: DEFAULT_BASE_URL,
               timeout: DEFAULT_TIMEOUT)
  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)
  client.instance_variable_set(:@start_here, body["start_here"])
  client
end

.parse_body(response) ⇒ Object



148
149
150
151
152
153
154
155
# File 'lib/basecradle/client.rb', line 148

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.



130
131
132
133
134
135
136
137
138
# File 'lib/basecradle/client.rb', line 130

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.message}"
end

Instance Method Details

#inspectObject



125
126
127
# File 'lib/basecradle/client.rb', line 125

def inspect
  "#<#{self.class} base_url=#{@base_url.inspect}>"
end

#meObject

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.



105
106
107
# File 'lib/basecradle/client.rb', line 105

def me
  Dashboard.new(request("GET", "/users/dashboard"), client: self)
end

#request(method, path, json: nil, params: nil, form: 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.



118
119
120
121
122
123
# File 'lib/basecradle/client.rb', line 118

def request(method, path, json: nil, params: nil, form: nil)
  uri = build_uri(path, params)
  http_request = build_request(method, uri, json, form)
  response = self.class.perform(uri, http_request, @timeout)
  handle(response)
end