supergood 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5cc6a38a2204140f0764b5875faa3a86e40770b12b9783b2c62d79349a587e2
4
- data.tar.gz: 776779840ec476c214c227d86de870fe6834738776c22ae14e5acf21c446adc7
3
+ metadata.gz: 8b55de34e8a2ab872a9d2556a4e1e8a4e544bff0536c02fcf123fb93c30fde87
4
+ data.tar.gz: 974a1af1a188f5a85e77c603e9545370c360367edeb4327c009c6b12ec44a8b9
5
5
  SHA512:
6
- metadata.gz: 21dd980b5e7a766943b055d414112155687672fdaac6842d823f9f4920196ad2ea5012454ed6d38f5a27ae8151aa7d8da682c5d73b7b2032cb314f68802f447b
7
- data.tar.gz: 3ae1572a26f4cba3416892c8faa3dc806580cbe4411edac101a8389364ac0dcef1950e6f09bd557a0cad5d00c90589c95cb1b852b68f3e712cd36b27fd266014
6
+ metadata.gz: c0c8f7c3b9af56109324314aa9af0220624bf9d05d2e06a8d2706b5cfac3f6d6ba2e7e8613b82803cc7365e66e6e76d85b1180c62d368ae01f55892276b730c3
7
+ data.tar.gz: 5bf07755f863ca866c5381f465957689616826f6db09734beda707a15099fc55b3a1fcee4b76f5c9df6623a5b5b1d9b26cb1782924a480e5f66dc279ae353a8b
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- supergood (0.1.0)
4
+ supergood (0.1.1)
5
5
  rudash (~> 4.0, >= 4.0.2)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,50 +1,50 @@
1
- # Supergood
1
+ # Ruby
2
2
 
3
- Monitor the cost and performance of your external API's with two lines of code.
3
+ The Supergood Ruby client connects Supergood to your Ruby application. Follow these steps to integrate with the Ruby client.
4
4
 
5
- Interested in learning more? Check us out at https://supergood.ai or Reach out to alex@supergood.ai .
5
+ ## 1. Install the Supergood library
6
6
 
7
- Not built on Ruby? We've got a node client, python client and golang client as well.
7
+ ```bash
8
+ gem install supergood
9
+ ```
8
10
 
9
- ## Installation
11
+ ## 2. Initialize the Supergood Library
10
12
 
11
- Add this line to your application's Gemfile:
13
+ **Environment variables**
12
14
 
13
- ```ruby
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
- And then execute:
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
- $ bundle install
20
-
21
- Or install it yourself as:
19
+ ```ruby
20
+ require 'supergood'
22
21
 
23
- $ gem install supergood
22
+ Supergood.init()
23
+ ```
24
24
 
25
- ## Usage
25
+ **Passing keys**
26
26
 
27
- 1. Head over to https://dashboard.supergood.ai and make an account, make sure to use your work email address!
28
- 2. Click on the tab labeled "API Keys" and generate a client id and client secret.
29
- 3. Head back to your code and initialize the Supergood client one of two ways:
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(<client_id>, <client_secret>)
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
- ## Development
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
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
41
+ ## 3. Monitor your API calls
44
42
 
45
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
43
+ You're all set to use Supergood!
46
44
 
47
- ## Contributing
45
+ Head back to your [dashboard](https://dashboard.supergood.ai) to start monitoring your API calls and receiving reports.
48
46
 
49
- Bug reports and pull requests are welcome on GitHub at https://github.com/supergoodsystems/supergood-rb.
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(header_options, base_url)
7
+ def initialize(client_id, client_secret, base_url)
8
8
  @base_url = base_url
9
- @header_options = 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
- uri = URI(@base_url + @event_sink_endpoint)
30
- response = Net::HTTP.post(uri, payload.to_json, @header_options)
31
- if response.code == '200'
32
- return JSON.parse(response.body, symbolize_names: true)
33
- elsif response.code == '401'
34
- raise SupergoodException.new ERRORS[:UNAUTHORIZED]
35
- elsif response.code != '200' && response.code != '201'
36
- raise SupergoodException.new ERRORS[:POSTING_EVENTS]
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
- uri = URI(@base_url + @error_sink_endpoint)
42
- response = Net::HTTP.post(uri, payload.to_json, @header_options)
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
- @log.warn(ERRORS[:POSTING_ERRORS])
47
- end
48
- end
49
-
50
- def fetch_config
51
- uri = URI(@base_url + '/api/config')
52
- request = Net::HTTP::Get.new(uri)
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
@@ -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(supergood_client_id=nil, supergood_client_secret=nil, base_url=nil)
18
- supergood_client_id = supergood_client_id || ENV['SUPERGOOD_CLIENT_ID']
19
- supergood_client_secret = supergood_client_secret || ENV['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 = base_url || ENV['SUPERGOOD_BASE_URL'] || DEFAULT_SUPERGOOD_BASE_URL
30
- header_options = {
31
- 'Content-Type' => 'application/json',
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
- log.debug("Using config %s" % @config.inspect)
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]
@@ -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 Purposes',
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
@@ -68,5 +68,9 @@ module Supergood
68
68
  def self.request_url(http, request)
69
69
  URI::DEFAULT_PARSER.unescape("http#{"s" if http.use_ssl?}://#{http.address}#{request.path}")
70
70
  end
71
+
72
+ def self.make_config(config)
73
+ return DEFAULT_CONFIG.merge(config)
74
+ end
71
75
  end
72
76
  end
@@ -1,33 +1,59 @@
1
1
  module Supergood
2
2
  module Vendor
3
3
  module HTTPrb
4
- if defined?(HTTP::Client)
5
- HTTP::Client.class_eval {
6
- alias original_perform perform
7
- def perform(original_request_payload, original_options)
8
- request = {
9
- headers: original_request_payload.headers.to_hash,
10
- method: original_request_payload.verb.upcase.to_s,
11
- body: Supergood::Utils.safe_parse_json(original_request_payload.body.source),
12
- url: original_request_payload.uri.to_s,
13
- path: original_request_payload.uri.path,
14
- search: original_request_payload.uri.query,
15
- domain: original_request_payload.uri.host
16
- }
17
- Supergood.intercept(request) do
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
- block = lambda do |x|
9
- alias original_request_method request
10
- def request(original_request_payload, body = nil, &block)
11
- http = self;
12
- url = Supergood::Utils.request_url(http, original_request_payload)
13
- uri = URI.parse(url)
14
- request = {
15
- headers: Supergood::Utils.get_header(original_request_payload),
16
- method: original_request_payload.method,
17
- body: original_request_payload.body,
18
- url: url,
19
- path: original_request_payload.path,
20
- search: uri.query,
21
- domain: uri.host,
22
- }
23
- Supergood.intercept(request) do
24
- original_response = original_request_method(original_request_payload, body, &block)
25
- {
26
- headers: Supergood::Utils.get_header(original_response),
27
- status: original_response.code,
28
- statusText: original_response.message,
29
- body: original_response.body,
30
- original_response: original_response
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
- if defined?(Net::HTTP)
37
- Net::HTTP.class_eval(&block)
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
- if defined?(::WebMock)
41
- WebMock::HttpLibAdapters::NetHttpAdapter.instance_variable_get("@webMockNetHTTP").class_eval(&block)
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
@@ -1,3 +1,3 @@
1
1
  module Supergood
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '0.1.3'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: supergood
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Klarfeld