readme-metrics 0.2.0 → 1.0.0

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: 66f7b071458adb5af02621626935bc6d9520bad8e246369a5f500f9037c3ea3b
4
- data.tar.gz: f3d313877ae15673722a1c749a5529e971ec17491a7eef639924cf3991419c49
3
+ metadata.gz: 17dad1b40b974d05ae3d57a664e6f95c393cbaf8e807071631c11800b2ee118c
4
+ data.tar.gz: 2d3595d321dea5bac6b5577180f608a2f9045e333592e5c33cca4c52a3e997ea
5
5
  SHA512:
6
- metadata.gz: be4ec7898257dbd67adf3e27070ea4cb1ee37b0d54d80de7ca2418356bfe3330a0fcdfa8ea3b16fed15afaa03a65ae0c58882f634385f37e76c275a2209f681d
7
- data.tar.gz: 76323ca324090a903898b7bd0ff818097272a6270466b4ed9eb58e06897c9becc6cc47264a0e64ee81e4a5a577aa7241ebd09f4d2bda9c3027e914b512b946e9
6
+ metadata.gz: a586f8ae1273269473451ea60bfeccb5e9fd01b6fa4ddacfd99b5918dfed24aa340a9e61aba6036a96b572c1eef50c4d2e1a9cd87162442197a02a4ff2456b2c
7
+ data.tar.gz: a7d894c039bdc676164f1dcd083650ae8ce026152baad7d83ea790a3ee4850e4de59246e922a641d6446943389c456557c1712b8b34c2085d4398e226c323c86
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- readme-metrics (0.1.0)
4
+ readme-metrics (0.2.0)
5
5
  httparty (~> 0.18)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -47,7 +47,7 @@ You may only specify either `reject_params` or `allow_only` keys, not both.
47
47
  ### Rails
48
48
 
49
49
  ```ruby
50
- # application.rb
50
+ # config/environments/development.rb or config/environments/production.rb
51
51
  require "readme/metrics"
52
52
 
53
53
  options = {
@@ -58,39 +58,51 @@ options = {
58
58
  }
59
59
 
60
60
  config.middleware.use Readme::Metrics, options do |env|
61
- current_user = env['warden'].authenticate(scope: :current_user)
61
+ current_user = env['warden'].authenticate
62
62
 
63
- {
64
- id: current_user.id
65
- label: current_user.full_name,
66
- email: current_user.email
67
- }
63
+ if current_user.present?
64
+ {
65
+ id: current_user.id,
66
+ label: current_user.name,
67
+ email: current_user.email
68
+ }
69
+ else
70
+ {
71
+ id: "guest",
72
+ label: "Guest User",
73
+ email: "guest@example.com"
74
+ }
75
+ end
68
76
  end
69
77
  ```
70
78
 
71
- ### Rack::Builder
79
+ ### Rack
72
80
 
73
81
  ```ruby
74
- Rack::Builder.new do |builder|
75
- options = {
76
- api_key: "YOUR_API_KEY",
77
- development: false,
78
- reject_params: ["not_included", "dont_send"]
79
- }
80
-
81
- builder.use Readme::Metrics, options do |env|
82
+ # config.ru
83
+ options = {
84
+ api_key: "YOUR_API_KEY",
85
+ development: false,
86
+ reject_params: ["not_included", "dont_send"]
87
+ }
88
+
89
+ use Readme::Metrics, options do |env|
82
90
  {
83
91
  id: "my_application_id"
84
92
  label: "My Application",
85
93
  email: "my.application@example.com"
86
94
  }
87
- end
88
- builder.run your_app
89
95
  end
96
+
97
+ run YourApp.new
90
98
  ```
91
99
 
92
- ## License
100
+ ### Sample Applications
93
101
 
94
- [View our license here](https://github.com/readmeio/metrics-sdks/tree/master/packages/ruby/LICENSE)
102
+ - [Rails](https://github.com/readmeio/metrics-sdk-rails-sample)
103
+ - [Rack](https://github.com/readmeio/metrics-sdk-racks-sample)
104
+ - [Sinatra](https://github.com/readmeio/metrics-sdk-sinatra-example)
95
105
 
106
+ ## License
96
107
 
108
+ [View our license here](https://github.com/readmeio/metrics-sdks/tree/master/packages/ruby/LICENSE)
@@ -0,0 +1,15 @@
1
+ module ContentTypeHelper
2
+ # Assumes the includer has a `content_type` method defined.
3
+
4
+ JSON_MIME_TYPES = [
5
+ "application/json",
6
+ "application/x-json",
7
+ "text/json",
8
+ "text/x-json",
9
+ "+json"
10
+ ]
11
+
12
+ def json?
13
+ JSON_MIME_TYPES.any? { |mime_type| content_type.include?(mime_type) }
14
+ end
15
+ end
@@ -1,7 +1,10 @@
1
1
  require "rack"
2
2
  require "rack/request"
3
+ require "content_type_helper"
3
4
 
4
5
  class HttpRequest
6
+ include ContentTypeHelper
7
+
5
8
  HTTP_NON_HEADERS = [
6
9
  Rack::HTTP_COOKIE,
7
10
  Rack::HTTP_VERSION,
@@ -55,6 +58,7 @@ class HttpRequest
55
58
  .select { |key, _| http_header?(key) }
56
59
  .to_h
57
60
  .transform_keys { |header| normalize_header_name(header) }
61
+ .merge unprefixed_headers
58
62
  end
59
63
 
60
64
  def body
@@ -85,4 +89,11 @@ class HttpRequest
85
89
  def normalize_header_name(header)
86
90
  header.delete_prefix("HTTP_").split("_").map(&:capitalize).join("-")
87
91
  end
92
+
93
+ # These special headers are explicitly _not_ prefixed with HTTP_ in the Rack
94
+ # env so we need to add them in manually
95
+ def unprefixed_headers
96
+ {"Content-Type" => @request.content_type,
97
+ "Content-Length" => @request.content_length}.compact
98
+ end
88
99
  end
@@ -0,0 +1,43 @@
1
+ require "rack"
2
+ require "rack/response"
3
+ require "content_type_helper"
4
+
5
+ class HttpResponse < SimpleDelegator
6
+ include ContentTypeHelper
7
+
8
+ def self.from_parts(status, headers, body)
9
+ new(Rack::Response.new(body, status, headers))
10
+ end
11
+
12
+ def body
13
+ if raw_body.respond_to?(:rewind)
14
+ raw_body.rewind
15
+ content = raw_body.each.reduce("", :+)
16
+ raw_body.rewind
17
+
18
+ content
19
+ else
20
+ raw_body.each.reduce("", :+)
21
+ end
22
+ end
23
+
24
+ def content_length
25
+ if empty_body_status?
26
+ 0
27
+ elsif !headers["Content-Length"]
28
+ body.bytesize
29
+ else
30
+ headers["Content-Length"].to_i
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def raw_body
37
+ __getobj__.body
38
+ end
39
+
40
+ def empty_body_status?
41
+ Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status.to_i)
42
+ end
43
+ end
@@ -13,21 +13,29 @@ class Filter
13
13
 
14
14
  class AllowOnly
15
15
  def initialize(filter_values)
16
- @filter_values = filter_values
16
+ @filter_values = filter_values.map(&:downcase)
17
17
  end
18
18
 
19
19
  def filter(hash)
20
- hash.select { |key, _value| @filter_values.include?(key) }
20
+ hash.select { |key, _value| @filter_values.include?(key.downcase) }
21
+ end
22
+
23
+ def pass_through?
24
+ false
21
25
  end
22
26
  end
23
27
 
24
28
  class RejectParams
25
29
  def initialize(filter_values)
26
- @filter_values = filter_values
30
+ @filter_values = filter_values.map(&:downcase)
27
31
  end
28
32
 
29
33
  def filter(hash)
30
- hash.reject { |key, _value| @filter_values.include?(key) }
34
+ hash.reject { |key, _value| @filter_values.include?(key.downcase) }
35
+ end
36
+
37
+ def pass_through?
38
+ false
31
39
  end
32
40
  end
33
41
 
@@ -35,6 +43,10 @@ class Filter
35
43
  def filter(hash)
36
44
  hash
37
45
  end
46
+
47
+ def pass_through?
48
+ true
49
+ end
38
50
  end
39
51
 
40
52
  class FilterArgsError < StandardError
@@ -46,9 +46,21 @@ module Readme
46
46
  end
47
47
 
48
48
  def request_body
49
+ if @filter.pass_through?
50
+ pass_through_body
51
+ else
52
+ # Only JSON allowed for non-pass-through situations. It will raise
53
+ # if the body can't be parsed as JSON, aborting the request.
54
+ json_body
55
+ end
56
+ end
57
+
58
+ def json_body
49
59
  parsed_body = JSON.parse(@request.body)
50
60
  Har::Collection.new(@filter, parsed_body).to_h.to_json
51
- rescue
61
+ end
62
+
63
+ def pass_through_body
52
64
  @request.body
53
65
  end
54
66
  end
@@ -4,8 +4,6 @@ require "readme/har/collection"
4
4
  module Readme
5
5
  module Har
6
6
  class ResponseSerializer
7
- JSON_MIME_TYPES = ["application/json", "application/x-json", "text/json", "text/x-json", "+json"]
8
-
9
7
  def initialize(request, response, filter)
10
8
  @request = request
11
9
  @response = response
@@ -29,9 +27,9 @@ module Readme
29
27
  private
30
28
 
31
29
  def content
32
- if response_body.nil?
30
+ if @response.body.empty?
33
31
  empty_content
34
- elsif content_type_is_json?
32
+ elsif @response.json?
35
33
  json_content
36
34
  else
37
35
  pass_through_content
@@ -43,37 +41,23 @@ module Readme
43
41
  end
44
42
 
45
43
  def json_content
46
- parsed_body = JSON.parse(response_body)
44
+ parsed_body = JSON.parse(@response.body)
47
45
 
48
- {mimeType: @response.content_type,
49
- size: @response.content_length,
50
- text: Har::Collection.new(@filter, parsed_body).to_h.to_json}
46
+ {
47
+ mimeType: @response.content_type,
48
+ size: @response.content_length,
49
+ text: Har::Collection.new(@filter, parsed_body).to_h.to_json
50
+ }
51
51
  rescue
52
52
  pass_through_content
53
53
  end
54
54
 
55
55
  def pass_through_content
56
- {mimeType: @response.content_type,
57
- size: @response.content_length,
58
- text: response_body}
59
- end
60
-
61
- def response_body
62
- if @response.body.nil?
63
- nil
64
- elsif @response.body.respond_to?(:rewind)
65
- @response.body.rewind
66
- body = @response.body.each.reduce(:+)
67
- @response.body.rewind
68
-
69
- body
70
- else
71
- @response.body.each.reduce(:+)
72
- end
73
- end
74
-
75
- def content_type_is_json?
76
- JSON_MIME_TYPES.include? @response.content_type
56
+ {
57
+ mimeType: @response.content_type,
58
+ size: @response.content_length,
59
+ text: @response.body
60
+ }
77
61
  end
78
62
  end
79
63
  end
@@ -5,6 +5,7 @@ require "readme/payload"
5
5
  require "readme/request_queue"
6
6
  require "readme/errors"
7
7
  require "http_request"
8
+ require "http_response"
8
9
  require "httparty"
9
10
  require "logger"
10
11
 
@@ -40,16 +41,17 @@ module Readme
40
41
  start_time = Time.now
41
42
  status, headers, body = @app.call(env)
42
43
  end_time = Time.now
43
- response = Rack::Response.new(body, status, headers)
44
44
 
45
45
  begin
46
+ response = HttpResponse.from_parts(status, headers, body)
46
47
  process_response(
47
48
  response: response,
48
49
  env: env,
49
50
  start_time: start_time,
50
51
  end_time: end_time
51
52
  )
52
- rescue
53
+ rescue => e
54
+ Readme::Metrics.logger.warn "The following error occured when trying to log to the ReadMe metrics API: #{e.message}. Request not logged."
53
55
  [status, headers, body]
54
56
  end
55
57
 
@@ -64,15 +66,33 @@ module Readme
64
66
  user_info = @get_user_info.call(env)
65
67
 
66
68
  if user_info_invalid?(user_info)
67
- Readme::Metrics.logger.error Errors.bad_block_message(user_info)
69
+ Readme::Metrics.logger.warn Errors.bad_block_message(user_info)
68
70
  elsif request.options?
69
71
  Readme::Metrics.logger.info "OPTIONS request omitted from ReadMe API logging"
72
+ elsif !can_filter? request, response
73
+ Readme::Metrics.logger.warn "Request or response body MIME type isn't supported for filtering. Omitting request from ReadMe API logging"
70
74
  else
71
75
  payload = Payload.new(har, user_info, development: @development)
72
76
  @@request_queue.push(payload.to_json)
73
77
  end
74
78
  end
75
79
 
80
+ def can_filter?(request, response)
81
+ @filter.pass_through? || can_parse_bodies?(request, response)
82
+ end
83
+
84
+ def can_parse_bodies?(request, response)
85
+ parseable_request?(request) && parseable_response?(response)
86
+ end
87
+
88
+ def parseable_response?(response)
89
+ response.body.empty? || response.json?
90
+ end
91
+
92
+ def parseable_request?(request)
93
+ request.body.empty? || request.json? || request.form_data?
94
+ end
95
+
76
96
  def validate_options(options)
77
97
  raise Errors::ConfigurationError, Errors::API_KEY_ERROR if options[:api_key].nil?
78
98
 
@@ -1,5 +1,5 @@
1
1
  module Readme
2
2
  class Metrics
3
- VERSION = "0.2.0"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: readme-metrics
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ReadMe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-26 00:00:00.000000000 Z
11
+ date: 2020-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -40,7 +40,9 @@ files:
40
40
  - Rakefile
41
41
  - bin/console
42
42
  - bin/setup
43
+ - lib/content_type_helper.rb
43
44
  - lib/http_request.rb
45
+ - lib/http_response.rb
44
46
  - lib/readme/errors.rb
45
47
  - lib/readme/filter.rb
46
48
  - lib/readme/har/collection.rb