supergood 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/supergood/api.rb +31 -40
- data/lib/supergood/client.rb +35 -25
- data/lib/supergood/constants.rb +10 -2
- data/lib/supergood/utils.rb +4 -0
- data/lib/supergood/vendors/http.rb +49 -23
- data/lib/supergood/vendors/net-http.rb +53 -28
- data/lib/supergood/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0cdb2377e5748a6ed76d61765fe3b15b648f397a97d6c9cdd28b592d68fd323
|
4
|
+
data.tar.gz: '099acaa41f71ef93c745960d9ec046b6643f68501fd95d140a1dd7c62eec76d1'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c15df0347deb5fcb0a0fdf04054595f8c4c9ef8b4da4fabc7879d6098fbe9595c507902ed6f7ccab95b47877f139d9121a1ecea885cb5f814ec86942cb37b3b
|
7
|
+
data.tar.gz: 97beed9cd863cd1175756b3084601a40f53928c205b3a3583ca04931a88f6a4804041b8506dfd7277f4232ed1bb7bad3a6eaf5494fe847955dff22439e1882c7
|
data/Gemfile.lock
CHANGED
data/lib/supergood/api.rb
CHANGED
@@ -4,9 +4,17 @@ Dotenv.load
|
|
4
4
|
|
5
5
|
module Supergood
|
6
6
|
class Api
|
7
|
-
def initialize(
|
7
|
+
def initialize(client_id, client_secret, base_url)
|
8
8
|
@base_url = base_url
|
9
|
-
@header_options =
|
9
|
+
@header_options = {
|
10
|
+
'Content-Type' => 'application/json',
|
11
|
+
'Authorization' => 'Basic ' + Base64.encode64(client_id + ':' + client_secret).gsub(/\n/, '')
|
12
|
+
}
|
13
|
+
@local_only = client_id == LOCAL_CLIENT_ID && client_secret == LOCAL_CLIENT_SECRET
|
14
|
+
end
|
15
|
+
|
16
|
+
def header_options
|
17
|
+
@header_options
|
10
18
|
end
|
11
19
|
|
12
20
|
def log
|
@@ -17,50 +25,33 @@ module Supergood
|
|
17
25
|
@log = logger
|
18
26
|
end
|
19
27
|
|
20
|
-
def set_event_sink_endpoint(endpoint)
|
21
|
-
@event_sink_endpoint = endpoint
|
22
|
-
end
|
23
|
-
|
24
|
-
def set_error_sink_endpoint(endpoint)
|
25
|
-
@error_sink_endpoint = endpoint
|
26
|
-
end
|
27
|
-
|
28
28
|
def post_events(payload)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
29
|
+
if @local_only
|
30
|
+
@log.debug(payload)
|
31
|
+
else
|
32
|
+
uri = URI(@base_url + '/api/events')
|
33
|
+
response = Net::HTTP.post(uri, payload.to_json, @header_options)
|
34
|
+
if response.code == '200'
|
35
|
+
return JSON.parse(response.body, symbolize_names: true)
|
36
|
+
elsif response.code == '401'
|
37
|
+
raise SupergoodException.new ERRORS[:UNAUTHORIZED]
|
38
|
+
elsif response.code != '200' && response.code != '201'
|
39
|
+
raise SupergoodException.new ERRORS[:POSTING_EVENTS]
|
40
|
+
end
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
44
|
def post_errors(payload)
|
41
|
-
|
42
|
-
|
43
|
-
if response.code == '200'
|
44
|
-
return JSON.parse(response.body, symbolize_names: true)
|
45
|
+
if @local_only
|
46
|
+
@log.debug(payload)
|
45
47
|
else
|
46
|
-
@
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
response = Net::HTTP.start(uri.hostname, uri.port) do |http|
|
54
|
-
request['Content-Type'] = 'application/json'
|
55
|
-
request['Authorization'] = @header_options['Authorization']
|
56
|
-
http.request(request)
|
57
|
-
end
|
58
|
-
if response.code == '200'
|
59
|
-
return JSON.parse(response.body, symbolize_names: true)
|
60
|
-
elsif response.code == '401'
|
61
|
-
raise SupergoodException.new ERRORS[:UNAUTHORIZED]
|
62
|
-
elsif response.code != '200' && response.code != '201'
|
63
|
-
raise SupergoodException.new ERRORS[:FETCHING_CONFIG]
|
48
|
+
uri = URI(@base_url + '/api/errors')
|
49
|
+
response = Net::HTTP.post(uri, payload.to_json, @header_options)
|
50
|
+
if response.code == '200'
|
51
|
+
return JSON.parse(response.body, symbolize_names: true)
|
52
|
+
else
|
53
|
+
@log.warn(ERRORS[:POSTING_ERRORS])
|
54
|
+
end
|
64
55
|
end
|
65
56
|
end
|
66
57
|
end
|
data/lib/supergood/client.rb
CHANGED
@@ -12,11 +12,10 @@ Dotenv.load
|
|
12
12
|
module Supergood
|
13
13
|
|
14
14
|
DEFAULT_SUPERGOOD_BASE_URL = 'https://dashboard.supergood.ai'
|
15
|
-
|
16
15
|
class << self
|
17
|
-
def init(
|
18
|
-
supergood_client_id =
|
19
|
-
supergood_client_secret =
|
16
|
+
def init(config={})
|
17
|
+
supergood_client_id = config[:client_id] || ENV['SUPERGOOD_CLIENT_ID']
|
18
|
+
supergood_client_secret = config[:client_secret] || ENV['SUPERGOOD_CLIENT_SECRET']
|
20
19
|
|
21
20
|
if !supergood_client_id
|
22
21
|
raise SupergoodException.new ERRORS[:NO_CLIENT_ID]
|
@@ -26,27 +25,27 @@ module Supergood
|
|
26
25
|
raise SupergoodException.new ERRORS[:NO_CLIENT_SECRET]
|
27
26
|
end
|
28
27
|
|
29
|
-
@base_url =
|
30
|
-
|
31
|
-
|
32
|
-
'Authorization' => 'Basic ' + Base64.encode64(supergood_client_id + ':' + supergood_client_secret).gsub(/\n/, '')
|
33
|
-
}
|
28
|
+
@base_url = ENV['SUPERGOOD_BASE_URL'] || DEFAULT_SUPERGOOD_BASE_URL
|
29
|
+
@api = Supergood::Api.new(supergood_client_id, supergood_client_secret, @base_url)
|
30
|
+
@config = Supergood::Utils.make_config(config)
|
34
31
|
|
35
|
-
@api = Supergood::Api.new(header_options, @base_url)
|
36
|
-
@config = @api.fetch_config
|
37
32
|
@ignored_domains = @config[:ignoredDomains]
|
38
33
|
@keys_to_hash = @config[:keysToHash]
|
39
|
-
@logger = Supergood::Logger.new(@api, @config, header_options)
|
34
|
+
@logger = Supergood::Logger.new(@api, @config, @api.header_options)
|
40
35
|
|
41
|
-
@api.set_error_sink_endpoint(@config[:errorSinkEndpoint])
|
42
|
-
@api.set_event_sink_endpoint(@config[:eventSinkEndpoint])
|
43
36
|
@api.set_logger(@logger)
|
44
37
|
|
45
38
|
@request_cache = {}
|
46
39
|
@response_cache = {}
|
47
40
|
|
48
41
|
@interval_thread = set_interval(@config[:flushInterval]) { flush_cache }
|
49
|
-
|
42
|
+
|
43
|
+
@http_clients = [
|
44
|
+
Supergood::Vendor::NetHTTP,
|
45
|
+
Supergood::Vendor::HTTPrb
|
46
|
+
]
|
47
|
+
|
48
|
+
patch_all()
|
50
49
|
self
|
51
50
|
end
|
52
51
|
|
@@ -76,6 +75,7 @@ module Supergood
|
|
76
75
|
api.post_events(data)
|
77
76
|
rescue => e
|
78
77
|
log.error(data, e, e.message)
|
78
|
+
cleanup()
|
79
79
|
ensure
|
80
80
|
@response_cache.clear
|
81
81
|
@request_cache.clear if force
|
@@ -83,10 +83,27 @@ module Supergood
|
|
83
83
|
|
84
84
|
end
|
85
85
|
|
86
|
+
def cleanup()
|
87
|
+
@interval_thread.kill
|
88
|
+
unpatch_all()
|
89
|
+
end
|
90
|
+
|
86
91
|
def close(force = true)
|
87
92
|
log.debug('Cleaning up, flushing cache gracefully.')
|
88
|
-
@interval_thread.kill
|
89
93
|
flush_cache(force)
|
94
|
+
cleanup()
|
95
|
+
end
|
96
|
+
|
97
|
+
def patch_all
|
98
|
+
@http_clients.each do |client|
|
99
|
+
client.patch
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def unpatch_all
|
104
|
+
@http_clients.each do |client|
|
105
|
+
client.unpatch
|
106
|
+
end
|
90
107
|
end
|
91
108
|
|
92
109
|
def set_interval(delay)
|
@@ -98,14 +115,6 @@ module Supergood
|
|
98
115
|
end
|
99
116
|
end
|
100
117
|
|
101
|
-
def self.intercept(*args, &block)
|
102
|
-
instance.intercept(*args, &block)
|
103
|
-
end
|
104
|
-
|
105
|
-
def self.instance
|
106
|
-
@instance ||= Supergood.new
|
107
|
-
end
|
108
|
-
|
109
118
|
def intercept(request)
|
110
119
|
request_id = SecureRandom.uuid
|
111
120
|
requested_at = Time.now
|
@@ -138,6 +147,7 @@ module Supergood
|
|
138
147
|
}
|
139
148
|
rescue => e
|
140
149
|
log.error({ request: request }, e, ERRORS[:CACHING_REQUEST])
|
150
|
+
cleanup()
|
141
151
|
end
|
142
152
|
end
|
143
153
|
|
@@ -159,11 +169,11 @@ module Supergood
|
|
159
169
|
}), @keys_to_hash)
|
160
170
|
@request_cache.delete(request_id)
|
161
171
|
rescue => e
|
162
|
-
puts e
|
163
172
|
log.error(
|
164
173
|
{ request: request_payload, response: response_payload },
|
165
174
|
e, ERRORS[:CACHING_RESPONSE]
|
166
175
|
)
|
176
|
+
cleanup()
|
167
177
|
end
|
168
178
|
end
|
169
179
|
|
data/lib/supergood/constants.rb
CHANGED
@@ -4,9 +4,8 @@ ERRORS = {
|
|
4
4
|
DUMPING_DATA_TO_DISK: 'Error Dumping Data to Disk',
|
5
5
|
POSTING_EVENTS: 'Error Posting Events',
|
6
6
|
POSTING_ERRORS: 'Error Posting Errors',
|
7
|
-
FETCHING_CONFIG: 'Error Fetching Config',
|
8
7
|
WRITING_TO_DISK: 'Error writing to disk',
|
9
|
-
TEST_ERROR: 'Test Error for Testing
|
8
|
+
TEST_ERROR: 'Test Error for Testing Purpos es',
|
10
9
|
UNAUTHORIZED: 'Unauthorized: Invalid Client ID or Secret. Exiting.',
|
11
10
|
NO_CLIENT_ID:
|
12
11
|
'No Client ID Provided, set SUPERGOOD_CLIENT_ID or pass it as an argument',
|
@@ -14,8 +13,17 @@ ERRORS = {
|
|
14
13
|
'No Client Secret Provided, set SUPERGOOD_CLIENT_SECRET or pass it as an argument'
|
15
14
|
};
|
16
15
|
|
16
|
+
LOCAL_CLIENT_ID = 'local-client-id';
|
17
|
+
LOCAL_CLIENT_SECRET = 'local-client-secret';
|
18
|
+
|
17
19
|
DEFAULT_SUPERGOOD_BYTE_LIMIT = 500000
|
18
20
|
|
21
|
+
DEFAULT_CONFIG = {
|
22
|
+
keysToHash: [],
|
23
|
+
flushInterval: 1000,
|
24
|
+
ignoredDomains: []
|
25
|
+
}
|
26
|
+
|
19
27
|
# GZIP_START_BYTES = b'\x1f\x8b'
|
20
28
|
|
21
29
|
class SupergoodException < StandardError
|
data/lib/supergood/utils.rb
CHANGED
@@ -1,33 +1,59 @@
|
|
1
1
|
module Supergood
|
2
2
|
module Vendor
|
3
3
|
module HTTPrb
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
original_response = original_perform(original_request_payload, original_options)
|
19
|
-
status, statusText = original_response.status.to_s.split(' ')
|
20
|
-
{
|
21
|
-
headers: original_response.headers.to_hash,
|
22
|
-
status: status,
|
23
|
-
statusText: statusText,
|
24
|
-
body: Supergood::Utils.safe_parse_json(original_response),
|
25
|
-
original_response: original_response
|
4
|
+
def self.patch
|
5
|
+
if !self.existing_patch?
|
6
|
+
|
7
|
+
block = lambda do |x|
|
8
|
+
alias original_perform perform
|
9
|
+
def perform(original_request_payload, original_options)
|
10
|
+
request = {
|
11
|
+
headers: original_request_payload.headers.to_hash,
|
12
|
+
method: original_request_payload.verb.upcase.to_s,
|
13
|
+
body: Supergood::Utils.safe_parse_json(original_request_payload.body.source),
|
14
|
+
url: original_request_payload.uri.to_s,
|
15
|
+
path: original_request_payload.uri.path,
|
16
|
+
search: original_request_payload.uri.query,
|
17
|
+
domain: original_request_payload.uri.host
|
26
18
|
}
|
19
|
+
Supergood.intercept(request) do
|
20
|
+
original_response = original_perform(original_request_payload, original_options)
|
21
|
+
status, statusText = original_response.status.to_s.split(' ')
|
22
|
+
{
|
23
|
+
headers: original_response.headers.to_hash,
|
24
|
+
status: status,
|
25
|
+
statusText: statusText,
|
26
|
+
body: Supergood::Utils.safe_parse_json(original_response),
|
27
|
+
original_response: original_response
|
28
|
+
}
|
29
|
+
end
|
27
30
|
end
|
28
31
|
end
|
29
|
-
|
32
|
+
|
33
|
+
if defined?(HTTP::Client)
|
34
|
+
HTTP::Client.class_eval(&block)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.unpatch
|
41
|
+
if self.existing_patch?
|
42
|
+
block = lambda do |x|
|
43
|
+
alias perform original_perform
|
44
|
+
end
|
45
|
+
|
46
|
+
if defined?(HTTP::Client)
|
47
|
+
HTTP::Client.class_eval(&block)
|
48
|
+
HTTP::Client.undef_method :original_perform
|
49
|
+
end
|
50
|
+
end
|
30
51
|
end
|
52
|
+
|
53
|
+
def self.existing_patch?
|
54
|
+
defined?(HTTP::Client) && HTTP::Client.method_defined?(:original_perform)
|
55
|
+
end
|
56
|
+
|
31
57
|
end
|
32
58
|
end
|
33
59
|
end
|
@@ -5,41 +5,66 @@ require 'uri'
|
|
5
5
|
module Supergood
|
6
6
|
module Vendor
|
7
7
|
module NetHTTP
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
8
|
+
def self.patch
|
9
|
+
if !self.existing_patch?
|
10
|
+
block = lambda do |x|
|
11
|
+
alias original_request_method request
|
12
|
+
def request(original_request_payload, body = nil, &block)
|
13
|
+
http = self;
|
14
|
+
url = Supergood::Utils.request_url(http, original_request_payload)
|
15
|
+
uri = URI.parse(url)
|
16
|
+
request = {
|
17
|
+
headers: Supergood::Utils.get_header(original_request_payload),
|
18
|
+
method: original_request_payload.method,
|
19
|
+
body: original_request_payload.body,
|
20
|
+
url: url,
|
21
|
+
path: original_request_payload.path,
|
22
|
+
search: uri.query,
|
23
|
+
domain: uri.host,
|
24
|
+
}
|
25
|
+
Supergood.intercept(request) do
|
26
|
+
original_response = original_request_method(original_request_payload, body, &block)
|
27
|
+
{
|
28
|
+
headers: Supergood::Utils.get_header(original_response),
|
29
|
+
status: original_response.code,
|
30
|
+
statusText: original_response.message,
|
31
|
+
body: original_response.body,
|
32
|
+
original_response: original_response
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
32
36
|
end
|
37
|
+
|
38
|
+
if defined?(Net::HTTP)
|
39
|
+
Net::HTTP.class_eval(&block)
|
40
|
+
elsif defined?(::WebMock)
|
41
|
+
WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_get("@webMockNetHTTP").class_eval(&block)
|
42
|
+
end
|
43
|
+
|
33
44
|
end
|
34
45
|
end
|
35
46
|
|
36
|
-
|
37
|
-
|
47
|
+
def self.unpatch
|
48
|
+
if self.existing_patch?
|
49
|
+
block = lambda do |x|
|
50
|
+
alias request original_request_method
|
51
|
+
end
|
52
|
+
|
53
|
+
if defined?(Net::HTTP)
|
54
|
+
Net::HTTP.class_eval(&block)
|
55
|
+
Net::HTTP.undef_method :original_request_method
|
56
|
+
elsif defined?(::WebMock)
|
57
|
+
WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_get("@webMockNetHTTP").class_eval(&block)
|
58
|
+
WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_get("@webMockNetHTTP").undef_method :original_request_method
|
59
|
+
end
|
60
|
+
end
|
38
61
|
end
|
39
62
|
|
40
|
-
|
41
|
-
|
63
|
+
def self.existing_patch?
|
64
|
+
(defined?(Net::HTTP) && Net::HTTP.method_defined?(:original_request_method)) ||
|
65
|
+
(defined?(::Webmock) && WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_get("@webMockNetHTTP").method_defined?(:original_request_method))
|
42
66
|
end
|
67
|
+
|
43
68
|
end
|
44
69
|
end
|
45
70
|
end
|
data/lib/supergood/version.rb
CHANGED