Class: Mobiscroll::Connect::ApiClient

Inherits:
Object
  • Object
show all
Defined in:
lib/mobiscroll/connect/api_client.rb

Overview

Internal HTTP layer. Mirrors sdks/node/src/client.ts and sdks/go/transport.go:

  • injects ‘Authorization: Bearer <access_token>` on every API request

  • on 401 with a stored refresh_token, refreshes once and retries the original call exactly once. Concurrent 401s share a single in-flight refresh.

  • maps non-2xx responses to typed errors via ‘Connect.map_response_error`.

The token-exchange / refresh Faraday connection is held separately so it cannot recurse into the 401 retry loop.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ ApiClient

Returns a new instance of ApiClient.



21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/mobiscroll/connect/api_client.rb', line 21

def initialize(config)
  @config = config
  @credentials = nil
  @monitor = Monitor.new
  @refresh_cond = @monitor.new_cond
  @refresh_in_flight = false
  @refresh_result = nil
  @refresh_error = nil
  @on_tokens_refreshed = config.on_tokens_refreshed

  @api_conn = build_connection(api: true)
  @token_conn = build_connection(api: false)
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



19
20
21
# File 'lib/mobiscroll/connect/api_client.rb', line 19

def config
  @config
end

Instance Method Details

#credentialsObject



39
40
41
# File 'lib/mobiscroll/connect/api_client.rb', line 39

def credentials
  @monitor.synchronize { @credentials }
end

#delete(path, query: nil, headers: nil) ⇒ Object



59
60
61
# File 'lib/mobiscroll/connect/api_client.rb', line 59

def delete(path, query: nil, headers: nil)
  execute(:delete, path, query: query, body: nil, headers: headers)
end

#get(path, query: nil, headers: nil) ⇒ Object



47
48
49
# File 'lib/mobiscroll/connect/api_client.rb', line 47

def get(path, query: nil, headers: nil)
  execute(:get, path, query: query, body: nil, headers: headers)
end

#on_tokens_refreshed(&block) ⇒ Object



43
44
45
# File 'lib/mobiscroll/connect/api_client.rb', line 43

def on_tokens_refreshed(&block)
  @on_tokens_refreshed = block
end

#post(path, body: nil, query: nil, headers: nil) ⇒ Object



51
52
53
# File 'lib/mobiscroll/connect/api_client.rb', line 51

def post(path, body: nil, query: nil, headers: nil)
  execute(:post, path, query: query, body: body, headers: headers)
end

#post_form(path, form) ⇒ Object

POST application/x-www-form-urlencoded against the token endpoint with Basic auth + CLIENT_ID header. Does not participate in the 401 retry loop because it uses ‘@token_conn`.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/mobiscroll/connect/api_client.rb', line 66

def post_form(path, form)
  creds = Base64.strict_encode64("#{@config.client_id}:#{@config.client_secret}")
  response = @token_conn.post(path.sub(%r{\A/}, '')) do |req|
    req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
    req.headers['Authorization'] = "Basic #{creds}"
    req.headers['CLIENT_ID'] = @config.client_id
    req.body = URI.encode_www_form(form)
  end
  parsed = parse_body(response.body)
  raise_for_status(response, parsed)
  parsed
rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e
  raise NetworkError.new(e.message, cause: e)
end

#put(path, body: nil, query: nil, headers: nil) ⇒ Object



55
56
57
# File 'lib/mobiscroll/connect/api_client.rb', line 55

def put(path, body: nil, query: nil, headers: nil)
  execute(:put, path, query: query, body: body, headers: headers)
end

#set_credentials(tokens) ⇒ Object



35
36
37
# File 'lib/mobiscroll/connect/api_client.rb', line 35

def set_credentials(tokens)
  @monitor.synchronize { @credentials = tokens }
end