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 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