supergood 0.1.1 → 0.1.3
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/README.md +28 -28
- data/lib/supergood/api.rb +31 -40
- data/lib/supergood/client.rb +32 -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: 8b55de34e8a2ab872a9d2556a4e1e8a4e544bff0536c02fcf123fb93c30fde87
|
4
|
+
data.tar.gz: 974a1af1a188f5a85e77c603e9545370c360367edeb4327c009c6b12ec44a8b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0c8f7c3b9af56109324314aa9af0220624bf9d05d2e06a8d2706b5cfac3f6d6ba2e7e8613b82803cc7365e66e6e76d85b1180c62d368ae01f55892276b730c3
|
7
|
+
data.tar.gz: 5bf07755f863ca866c5381f465957689616826f6db09734beda707a15099fc55b3a1fcee4b76f5c9df6623a5b5b1d9b26cb1782924a480e5f66dc279ae353a8b
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,50 +1,50 @@
|
|
1
|
-
#
|
1
|
+
# Ruby
|
2
2
|
|
3
|
-
|
3
|
+
The Supergood Ruby client connects Supergood to your Ruby application. Follow these steps to integrate with the Ruby client.
|
4
4
|
|
5
|
-
|
5
|
+
## 1. Install the Supergood library
|
6
6
|
|
7
|
-
|
7
|
+
```bash
|
8
|
+
gem install supergood
|
9
|
+
```
|
8
10
|
|
9
|
-
##
|
11
|
+
## 2. Initialize the Supergood Library
|
10
12
|
|
11
|
-
|
13
|
+
**Environment variables**
|
12
14
|
|
13
|
-
|
14
|
-
gem 'supergood'
|
15
|
-
```
|
15
|
+
Set the environment variables `SUPERGOOD_CLIENT_ID` and `SUPERGOOD_CLIENT_SECRET` using the API keys generated in the [getting started instructions](../../getting-started.md).
|
16
16
|
|
17
|
-
|
17
|
+
Initialize the Supergood client at the root of your application, or anywhere you're making API calls with the following code:
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
Or install it yourself as:
|
19
|
+
```ruby
|
20
|
+
require 'supergood'
|
22
21
|
|
23
|
-
|
22
|
+
Supergood.init()
|
23
|
+
```
|
24
24
|
|
25
|
-
|
25
|
+
**Passing keys**
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
You can also pass the API keys in manually without setting environment variables.\
|
28
|
+
\
|
29
|
+
Replace `<CLIENT_ID>` and `<CLIENT_SECRET>` with the API keys you generated in the [getting started instructions](../../getting-started.md).
|
30
30
|
|
31
|
-
```
|
31
|
+
```ruby
|
32
32
|
require 'supergood'
|
33
33
|
|
34
|
-
Supergood.init(
|
34
|
+
Supergood.init({ client_id: "<CLIENT_ID>", client_secret: "<CLIENT_SECRET>" })
|
35
35
|
```
|
36
|
-
OR
|
37
|
-
|
38
|
-
set `SUPERGOOD_CLIENT_ID` and `SUPERGOOD_CLIENT_SECRET` as environment variables and leave the init function as `Supergood.init`
|
39
36
|
|
37
|
+
#### Local development
|
40
38
|
|
41
|
-
|
39
|
+
Setting the `CLIENT_ID` and `CLIENT_SECRET_ID` to `local-client-id` and `local-client-secret`, respectively, will disable making API calls to the supergood.ai server and instead log the payloads to the local console.
|
42
40
|
|
43
|
-
|
41
|
+
## 3. Monitor your API calls
|
44
42
|
|
45
|
-
|
43
|
+
You're all set to use Supergood!
|
46
44
|
|
47
|
-
|
45
|
+
Head back to your [dashboard](https://dashboard.supergood.ai) to start monitoring your API calls and receiving reports.
|
48
46
|
|
49
|
-
|
47
|
+
## Links
|
50
48
|
|
49
|
+
* [Supergood RubyGems Project](https://rubygems.org/gems/supergood)
|
50
|
+
* [Supergood-rb Source Code](https://github.com/supergoodsystems/supergood-rb)
|
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
|
|
@@ -83,10 +82,27 @@ module Supergood
|
|
83
82
|
|
84
83
|
end
|
85
84
|
|
85
|
+
def cleanup()
|
86
|
+
@interval_thread.kill
|
87
|
+
unpatch_all()
|
88
|
+
end
|
89
|
+
|
86
90
|
def close(force = true)
|
87
91
|
log.debug('Cleaning up, flushing cache gracefully.')
|
88
|
-
@interval_thread.kill
|
89
92
|
flush_cache(force)
|
93
|
+
cleanup()
|
94
|
+
end
|
95
|
+
|
96
|
+
def patch_all
|
97
|
+
@http_clients.each do |client|
|
98
|
+
client.patch
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def unpatch_all
|
103
|
+
@http_clients.each do |client|
|
104
|
+
client.unpatch
|
105
|
+
end
|
90
106
|
end
|
91
107
|
|
92
108
|
def set_interval(delay)
|
@@ -98,14 +114,6 @@ module Supergood
|
|
98
114
|
end
|
99
115
|
end
|
100
116
|
|
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
117
|
def intercept(request)
|
110
118
|
request_id = SecureRandom.uuid
|
111
119
|
requested_at = Time.now
|
@@ -159,7 +167,6 @@ module Supergood
|
|
159
167
|
}), @keys_to_hash)
|
160
168
|
@request_cache.delete(request_id)
|
161
169
|
rescue => e
|
162
|
-
puts e
|
163
170
|
log.error(
|
164
171
|
{ request: request_payload, response: response_payload },
|
165
172
|
e, ERRORS[:CACHING_RESPONSE]
|
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