Class: ImmersiveCommons::Client

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(token: nil, base_url: DEFAULT_BASE_URL, sandbox: false, timeout: 30, transport: nil, user_agent: nil) ⇒ Client

transport: an object responding to call(method, url, headers, body) -> [status, headers, body_text]. Injectable for tests; defaults to Net::HTTP.



48
49
50
51
52
53
54
55
56
57
# File 'lib/immersivecommons/client.rb', line 48

def initialize(token: nil, base_url: DEFAULT_BASE_URL, sandbox: false,
               timeout: 30, transport: nil, user_agent: nil)
  @base_url = base_url.sub(%r{/+\z}, "")
  @token = token
  # Documentation flag only: a sandbox token already behaves sandbox
  # server-side. This just makes authorize request one by default.
  @sandbox = sandbox
  @user_agent = user_agent || "immersivecommons-ruby/#{API_VERSION}"
  @transport = transport || default_transport(timeout)
end

Instance Attribute Details

#base_urlObject (readonly)

Returns the value of attribute base_url.



43
44
45
# File 'lib/immersivecommons/client.rb', line 43

def base_url
  @base_url
end

#sandboxObject (readonly)

Returns the value of attribute sandbox.



43
44
45
# File 'lib/immersivecommons/client.rb', line 43

def sandbox
  @sandbox
end

#tokenObject

Returns the value of attribute token.



44
45
46
# File 'lib/immersivecommons/client.rb', line 44

def token
  @token
end

#user_agentObject (readonly)

Returns the value of attribute user_agent.



43
44
45
# File 'lib/immersivecommons/client.rb', line 43

def user_agent
  @user_agent
end

Instance Method Details

#ask_research(q, k: nil, sources: nil, synthesize: nil, model: nil) ⇒ Object



152
153
154
155
156
157
158
159
# File 'lib/immersivecommons/client.rb', line 152

def ask_research(q, k: nil, sources: nil, synthesize: nil, model: nil)
  body = { "q" => q }
  body["k"] = k unless k.nil?
  body["sources"] = sources.to_a unless sources.nil?
  body["synthesize"] = synthesize unless synthesize.nil?
  body["model"] = model unless model.nil?
  call("askResearch", body: body)
end

#authorize(scopes, client_name: "immersivecommons-ruby", sandbox: nil, on_prompt: nil, timeout_seconds: nil, sleeper: ->(s) { sleep(s) }) ⇒ Object

Runs the full RFC 8628 device-code grant and returns the minted token as { "token", "granted_scopes", "tier", "sandbox" }. Does not store the token on the client — assign client.token yourself if you want to. on_prompt receives the start response (user_code, verify_url). sleeper is injectable for tests.



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/immersivecommons/client.rb', line 194

def authorize(scopes, client_name: "immersivecommons-ruby", sandbox: nil,
              on_prompt: nil, timeout_seconds: nil, sleeper: ->(s) { sleep(s) })
  start = (scopes, client_name: client_name, sandbox: sandbox)
  on_prompt&.call(start)
  interval = [start["interval"].to_i, 1].max
  deadline = monotonic_now + (timeout_seconds || start["expires_in"] || 900)
  loop do
    raise Timeout::Error, "Device-code grant timed out before approval." if monotonic_now > deadline

    sleeper.call(interval)
    poll = (start.fetch("device_code"))
    case poll["status"]
    when "completed"
      unless poll["agent_token"]
        raise ApiError.new(500, "pollSignup", { "error" => "completed without token" })
      end
      return {
        "token" => poll["agent_token"],
        "granted_scopes" => poll["granted_scopes"] || [],
        "tier" => poll["tier"],
        "sandbox" => !!poll["sandbox"]
      }
    when "cancelled"
      raise "Device-code grant cancelled: #{poll['reason']}".strip
    end
    # pending -> keep polling
  end
end

#book_resource(resource_id, start_iso:, end_iso:, email:, purpose: nil, idempotency_key: nil) ⇒ Object



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

def book_resource(resource_id, start_iso:, end_iso:, email:, purpose: nil, idempotency_key: nil)
  body = { "resource_id" => resource_id, "start_iso" => start_iso,
           "end_iso" => end_iso, "email" => email }
  body["purpose"] = purpose unless purpose.nil?
  call("bookResource", body: body, idempotency_key: idempotency_key)
end

#call(operation_id, query: nil, body: nil, idempotency_key: nil, token: nil) ⇒ Object

---------------------------------------------------------------- generic

Raises:

  • (ArgumentError)


61
62
63
64
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/immersivecommons/client.rb', line 61

def call(operation_id, query: nil, body: nil, idempotency_key: nil, token: nil)
  spec = OPERATIONS[operation_id]
  raise ArgumentError, "Unknown operationId: #{operation_id}" unless spec

  headers = { "accept" => "application/json", "user-agent" => @user_agent }
  tok = token || @token
  headers["authorization"] = "Bearer #{tok}" if tok

  data = nil
  if spec[:has_body] && !body.nil?
    headers["content-type"] = "application/json"
    data = JSON.generate(body)
  end

  if idempotency_key
    unless spec[:idempotent]
      raise ArgumentError, "#{operation_id} does not accept an Idempotency-Key"
    end
    headers["idempotency-key"] = idempotency_key
  end

  url = @base_url + spec[:path] + query_string(query)
  status, _resp_headers, text = @transport.call(spec[:method], url, headers, data)
  parsed = begin
    text && !text.empty? ? JSON.parse(text) : nil
  rescue JSON::ParserError
    nil
  end
  unless (200..299).cover?(status)
    raise ApiError.new(status, operation_id, parsed || { "error" => text })
  end
  parsed
end

#get_donor_wall(limit: nil) ⇒ Object



121
122
123
# File 'lib/immersivecommons/client.rb', line 121

def get_donor_wall(limit: nil)
  call("getDonorWall", query: limit.nil? ? nil : { "limit" => limit })
end

#get_event_by_luma_url(luma) ⇒ Object



101
102
103
# File 'lib/immersivecommons/client.rb', line 101

def get_event_by_luma_url(luma)
  call("getEventByLumaUrl", query: { "luma" => luma })
end

#get_my_activity(limit: nil) ⇒ Object



113
114
115
# File 'lib/immersivecommons/client.rb', line 113

def get_my_activity(limit: nil)
  call("getMyActivity", query: limit.nil? ? nil : { "limit" => limit })
end

#get_my_leaderboard_statusObject



117
118
119
# File 'lib/immersivecommons/client.rb', line 117

def get_my_leaderboard_status
  call("getMyLeaderboardStatus")
end

#list_resourcesObject



109
110
111
# File 'lib/immersivecommons/client.rb', line 109

def list_resources
  call("listResources")
end

#list_upcoming_events(limit: nil) ⇒ Object

------------------------------------------------------------------ reads



97
98
99
# File 'lib/immersivecommons/client.rb', line 97

def list_upcoming_events(limit: nil)
  call("listUpcomingEvents", query: limit.nil? ? nil : { "limit" => limit })
end

#poll_signup(device_code) ⇒ Object



185
186
187
# File 'lib/immersivecommons/client.rb', line 185

def (device_code)
  call("pollSignup", query: { "device_code" => device_code })
end

#request_event(event, idempotency_key: nil) ⇒ Object



137
138
139
# File 'lib/immersivecommons/client.rb', line 137

def request_event(event, idempotency_key: nil)
  call("requestEvent", body: event, idempotency_key: idempotency_key)
end

#revoke_own_tokenObject



171
172
173
# File 'lib/immersivecommons/client.rb', line 171

def revoke_own_token
  call("revokeOwnToken")
end

#rsvp_to_event(event_url, email:, name: nil, idempotency_key: nil) ⇒ Object

----------------------------------------------------------------- writes



131
132
133
134
135
# File 'lib/immersivecommons/client.rb', line 131

def rsvp_to_event(event_url, email:, name: nil, idempotency_key: nil)
  body = { "event_url" => event_url, "email" => email }
  body["name"] = name unless name.nil?
  call("rsvpToEvent", body: body, idempotency_key: idempotency_key)
end

#search_directory(q: nil, limit: nil) ⇒ Object



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

def search_directory(q: nil, limit: nil)
  call("searchDirectory", query: { "q" => q, "limit" => limit })
end

#set_leaderboard_optin(opt_in) ⇒ Object



148
149
150
# File 'lib/immersivecommons/client.rb', line 148

def set_leaderboard_optin(opt_in)
  call("setLeaderboardOptIn", body: { "optIn" => opt_in })
end

#setup_checkObject



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

def setup_check
  call("setupCheck")
end

#start_signup(scopes, client_name: nil, sandbox: nil) ⇒ Object

--------------------------------------------------------- device-code auth



177
178
179
180
181
182
183
# File 'lib/immersivecommons/client.rb', line 177

def (scopes, client_name: nil, sandbox: nil)
  body = { "scopes" => scopes.to_a }
  body["client_name"] = client_name unless client_name.nil?
  want = sandbox.nil? ? @sandbox : sandbox
  body["sandbox"] = true if want
  call("startSignup", body: body)
end

#submit_feedback(kind, message, **extra) ⇒ Object



165
166
167
168
169
# File 'lib/immersivecommons/client.rb', line 165

def submit_feedback(kind, message, **extra)
  body = { "kind" => kind, "message" => message }
  extra.each { |k, v| body[k.to_s] = v }
  call("submitFeedback", body: body)
end

#submit_highlight_pending(story, idempotency_key: nil) ⇒ Object



161
162
163
# File 'lib/immersivecommons/client.rb', line 161

def submit_highlight_pending(story, idempotency_key: nil)
  call("submitHighlightPending", body: story, idempotency_key: idempotency_key)
end