Module: Supergood

Defined in:
lib/supergood.rb,
lib/supergood/api.rb,
lib/supergood/utils.rb,
lib/supergood/client.rb,
lib/supergood/logger.rb,
lib/supergood/version.rb,
lib/supergood/vendors/http.rb,
lib/supergood/vendors/net-http.rb

Defined Under Namespace

Modules: Utils, Vendor Classes: Api, Logger

Constant Summary collapse

DEFAULT_SUPERGOOD_BASE_URL =
'https://api.supergood.ai'
VERSION =
'1.0.1'.freeze

Class Method Summary collapse

Class Method Details

.apiObject



58
59
60
# File 'lib/supergood/client.rb', line 58

def api
  @api
end

.cache_request(request_id, requested_at, request) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/supergood/client.rb', line 153

def cache_request(request_id, requested_at, request)
  if !@config[:remote_config]
    return
  end

  begin
    request_payload = {
      'id' => request_id,
      'headers' => Supergood::Utils.safe_parse_json(request[:headers]),
      'method' => request[:method],
      'url' => request[:url],
      'path' => request[:path],
      'search' => request[:search] || '',
      'body' => Supergood::Utils.safe_parse_json(request[:body]),
      'requestedAt' => requested_at
    }
    @request_cache[request_id] = {
      'request' => request_payload
    }
  rescue => e
    log.error({ 'request' => request }, e, ERRORS[:CACHING_REQUEST])
  end
end

.cache_response(request_id, requested_at, response) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/supergood/client.rb', line 177

def cache_response(request_id, requested_at, response)
  if !@config[:remote_config]
    return
  end

  begin
    responded_at = Time.now
    duration = (responded_at - requested_at) * 1000
    request_payload = @request_cache[request_id]
    response_payload = {
      'headers' => Supergood::Utils.safe_parse_json(response[:headers]),
      'status' => response[:status],
      'statusText' => response[:statusText],
      'body' => Supergood::Utils.safe_parse_json(response[:body]),
      'respondedAt' => responded_at,
      'duration' => duration.round
    }
    @response_cache[request_id] = request_payload.merge({ 'response' => response_payload })
    @request_cache.delete(request_id)
  rescue => e
    log.error(
      { request: request_payload, response: response_payload },
      e, ERRORS[:CACHING_RESPONSE]
    )
  end
end

.cleanupObject



94
95
96
97
98
# File 'lib/supergood/client.rb', line 94

def cleanup()
  @interval_thread.kill
  @remote_config_thread.kill
  unpatch_all()
end

.close(force = true) ⇒ Object



100
101
102
103
104
# File 'lib/supergood/client.rb', line 100

def close(force = true)
  log.debug('Cleaning up, flushing cache gracefully.')
  flush_cache(force)
  cleanup()
end

.fetch_and_process_remote_configObject



62
63
64
65
66
67
68
69
# File 'lib/supergood/client.rb', line 62

def fetch_and_process_remote_config
  begin
    remote_config = @api.get_remote_config
    @config = @config.merge({ :remote_config => Supergood::Utils.process_remote_config(remote_config) })
  rescue => e
    log.error({}, e, ERRORS[:CONFIG_FETCH_ERROR])
  end
end

.flush_cache(force = false) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/supergood/client.rb', line 71

def flush_cache(force = false)
  # If there's notthing in the response cache, and we're not forcing a flush, then return

  if @response_cache.empty? && !force
    return
  elsif force && @response_cache.empty? && @request_cache.empty?
    return
  end

  data = Supergood::Utils.prepare_data(@response_cache.values, @config[:remote_config], @config[:forceRedactAll])
  data += Supergood::Utils.prepare_data(@request_cache.values, @config[:remote_config], @config[:forceRedactAll]) if force

  begin
    api.post_events(data)
  rescue => e
    log.error(data, e, e.message)
  ensure
    @response_cache.clear
    @request_cache.clear if force
  end

end

.ignored?(domain) ⇒ Boolean

Returns:

  • (Boolean)


204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/supergood/client.rb', line 204

def ignored?(domain)
  base_domain = Supergood::Utils.get_host_without_www(@base_url)
  if domain == base_domain
    return true
  elsif @allowed_domains.any?
    @allowed_domains.all? do |allowed_domain|
      !domain.include? allowed_domain
    end
  else
    @ignored_domains.any? do |ignored_domain|
      domain.include? ignored_domain
    end
  end
end

.init(config = {}) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/supergood/client.rb', line 16

def init(config={})
  supergood_client_id = config[:client_id] || ENV['SUPERGOOD_CLIENT_ID']
  supergood_client_secret = config[:client_secret] || ENV['SUPERGOOD_CLIENT_SECRET']

  if !supergood_client_id
    raise SupergoodException.new ERRORS[:NO_CLIENT_ID]
  end

  if !supergood_client_secret
    raise SupergoodException.new ERRORS[:NO_CLIENT_SECRET]
  end

  @base_url = ENV['SUPERGOOD_BASE_URL'] || DEFAULT_SUPERGOOD_BASE_URL
  @api = Supergood::Api.new(supergood_client_id, supergood_client_secret, @base_url)
  @config = Supergood::Utils.make_config(config)

  @allowed_domains = @config[:allowedDomains]
  @ignored_domains = @config[:ignoredDomains]
  @logger = Supergood::Logger.new(@api, @config, @api.header_options)

  @api.set_logger(@logger)

  @request_cache = {}
  @response_cache = {}

  @interval_thread = set_interval(@config[:flushInterval]) { flush_cache }
  @remote_config_thread = set_interval(@config[:remoteConfigFetchInterval]) { fetch_and_process_remote_config }

  @http_clients = [
    Supergood::Vendor::NetHTTP,
    Supergood::Vendor::HTTPrb
  ]

  fetch_and_process_remote_config
  patch_all
  self
end

.intercept(request) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/supergood/client.rb', line 127

def intercept(request)
  remote_config = @config[:remote_config]

  if remote_config.nil?
    response = yield
    return response[:original_response]
  end

  request_id = SecureRandom.uuid
  requested_at = Time.now

  endpoint_config = Supergood::Utils.get_endpoint_config(request.transform_keys(&:to_s), remote_config)
  ignore_endpoint = endpoint_config ? endpoint_config['ignored'] : false

  if !ignore_endpoint && !ignored?(request[:domain])
    cache_request(request_id, requested_at, request)
  end

  response = yield
  if !ignore_endpoint && !ignored?(request[:domain]) && defined?(response)
    cache_response(request_id, requested_at, response)
  end

  return response[:original_response]
end

.logObject



54
55
56
# File 'lib/supergood/client.rb', line 54

def log
  @logger
end

.patch_allObject



106
107
108
109
110
# File 'lib/supergood/client.rb', line 106

def patch_all
  @http_clients.each do |client|
    client.patch
  end
end

.set_interval(delay) ⇒ Object



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

def set_interval(delay)
  Thread.new do
    loop do
      sleep delay / 1000.0
      yield # call passed block
    end
  end
end

.unpatch_allObject



112
113
114
115
116
# File 'lib/supergood/client.rb', line 112

def unpatch_all
  @http_clients.each do |client|
    client.unpatch
  end
end