readme-metrics 1.1.1 → 2.1.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: f22a117a4ef3e32682439eb3c6532766c442a32e067429857ed33548c2305ee1
4
- data.tar.gz: 436f9b30983241c5ac5b1790e49c85268a7ef3afc3c07b208b13967f88ebfde6
3
+ metadata.gz: edf64efc0c1f3a7ba72f9f6d30aaebbd1caf64774258fe7dc69e343751358adb
4
+ data.tar.gz: 36dd67292da34691382b5e3d9f7234275c9d14d4b133f8067ed22c2c0c861a02
5
5
  SHA512:
6
- metadata.gz: 432a22c8576ffb597876b7a899b18d1128c48226b27f4f0f30ae40f4855f50ce7586b169c0390257ab8da295a6f85aab618d9d3bbff24cc5f6bf00cf91dcaa91
7
- data.tar.gz: abe8ecf0008727d3b05b984661db799ab5510ff201f3df5d8c05eb521e2f34231da44a915745dd9cea7ebd027c0c6fe48df312aa081cf75b93fc4bfd3049b5c6
6
+ metadata.gz: 20c0d88a3e19f49a742ad52555b22f4cfa1d6fc34eeea60dbeb3410f97050d546ed9caa4d00ba5c518822296d9660a6e24c84c7232165724251cafd68dadab53
7
+ data.tar.gz: c29cb1edddfd487996d345be984f402353a1940c4626aad107b4df33537800de0cab8ba41cf294d9a7bfaae5f221cf266012bea8d925ac8794e717b7059c3238
data/.rubocop.yml ADDED
@@ -0,0 +1,67 @@
1
+ require:
2
+ - rubocop-performance
3
+ - rubocop-rspec
4
+
5
+ AllCops:
6
+ NewCops: enable
7
+ TargetRubyVersion: 2.7
8
+
9
+ # https://docs.rubocop.org/rubocop/cops_layout.html
10
+ Layout/LineLength:
11
+ Max: 150
12
+
13
+ # https://docs.rubocop.org/rubocop/cops_metrics.html
14
+ Metrics/AbcSize:
15
+ Enabled: false
16
+ Metrics/CyclomaticComplexity:
17
+ Max: 20
18
+ Metrics/BlockLength:
19
+ Exclude:
20
+ - spec/readme/*.rb
21
+ - spec/readme/har/*.rb
22
+ Metrics/ClassLength:
23
+ Enabled: false
24
+ Metrics/MethodLength:
25
+ Enabled: false
26
+ Metrics/PerceivedComplexity:
27
+ Max: 20
28
+
29
+ # https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec
30
+ RSpec/AnyInstance:
31
+ Enabled: false
32
+ RSpec/ExampleLength:
33
+ Enabled: false
34
+ RSpec/LeakyConstantDeclaration:
35
+ Exclude:
36
+ - spec/readme/*.rb
37
+ RSpec/MultipleExpectations:
38
+ Enabled: false
39
+ RSpec/VerifiedDoubles:
40
+ Enabled: false
41
+
42
+ # https://docs.rubocop.org/rubocop/cops_style.html
43
+ Style/BlockDelimiters:
44
+ Exclude:
45
+ - spec/**/*
46
+ Style/ClassVars:
47
+ Exclude:
48
+ - 'lib/readme/metrics.rb'
49
+ Style/Documentation:
50
+ Enabled: false
51
+ Style/ExpandPathArguments:
52
+ Enabled: false
53
+ Style/FrozenStringLiteralComment:
54
+ Enabled: false
55
+ Style/GuardClause:
56
+ Exclude:
57
+ - 'lib/readme/filter.rb'
58
+ - 'lib/readme/metrics.rb'
59
+ Style/IfUnlessModifier:
60
+ Exclude:
61
+ - 'lib/readme/metrics.rb'
62
+ Style/RescueStandardError:
63
+ # We should ideally have this rule enabled but I'm paranoid that we don't have full documentation
64
+ # on all types of errors that might be thrown in the couple places this rule is being suppressed
65
+ # and by only catching `StandardError` we'll miss out on something else and potentially take down
66
+ # someones integration.
67
+ Enabled: false
data/Gemfile CHANGED
@@ -1,13 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source "https://rubygems.org"
3
+ source 'https://rubygems.org'
4
4
 
5
5
  # Specify your gem's dependencies in readme-metrics.gemspec
6
6
  gemspec
7
7
 
8
- gem "json-schema"
9
- gem "rack-test"
10
- gem "rake", "~> 12.0"
11
- gem "rspec", "~> 3.0"
12
- gem "standard"
13
- gem "webmock"
8
+ gem 'json-schema'
9
+ gem 'rack-test'
10
+ gem 'rake', '~> 12.0'
11
+ gem 'rspec', '~> 3.0'
12
+ gem 'rubocop', require: false
13
+ gem 'rubocop-performance', require: false
14
+ gem 'rubocop-rspec', require: false
15
+ gem 'uuid'
16
+ gem 'webmock'
data/Gemfile.lock CHANGED
@@ -1,33 +1,36 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- readme-metrics (1.1.0)
4
+ readme-metrics (2.0.1)
5
5
  httparty (~> 0.18)
6
+ uuid (~> 2.3.8)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
10
- addressable (2.7.0)
11
+ addressable (2.8.0)
11
12
  public_suffix (>= 2.0.2, < 5.0)
12
13
  ast (2.4.1)
13
14
  crack (0.4.3)
14
15
  safe_yaml (~> 1.0.0)
15
16
  diff-lcs (1.4.4)
16
17
  hashdiff (1.0.1)
17
- httparty (0.18.1)
18
+ httparty (0.20.0)
18
19
  mime-types (~> 3.0)
19
20
  multi_xml (>= 0.5.2)
20
21
  json-schema (2.8.1)
21
22
  addressable (>= 2.4)
22
- mime-types (3.3.1)
23
+ macaddr (1.7.2)
24
+ systemu (~> 2.6.5)
25
+ mime-types (3.4.1)
23
26
  mime-types-data (~> 3.2015)
24
- mime-types-data (3.2021.0225)
27
+ mime-types-data (3.2022.0105)
25
28
  multi_xml (0.6.0)
26
29
  parallel (1.19.2)
27
30
  parser (2.7.1.4)
28
31
  ast (~> 2.4.1)
29
- public_suffix (4.0.5)
30
- rack (2.2.3)
32
+ public_suffix (4.0.6)
33
+ rack (2.2.3.1)
31
34
  rack-test (1.1.0)
32
35
  rack (>= 1.0, < 3)
33
36
  rainbow (3.0.0)
@@ -58,14 +61,16 @@ GEM
58
61
  unicode-display_width (>= 1.4.0, < 2.0)
59
62
  rubocop-ast (0.3.0)
60
63
  parser (>= 2.7.1.4)
61
- rubocop-performance (1.6.1)
62
- rubocop (>= 0.71.0)
64
+ rubocop-performance (1.7.1)
65
+ rubocop (>= 0.82.0)
66
+ rubocop-rspec (1.41.0)
67
+ rubocop (>= 0.68.1)
63
68
  ruby-progressbar (1.10.1)
64
69
  safe_yaml (1.0.5)
65
- standard (0.4.7)
66
- rubocop (~> 0.85.0)
67
- rubocop-performance (~> 1.6.0)
70
+ systemu (2.6.5)
68
71
  unicode-display_width (1.7.0)
72
+ uuid (2.3.9)
73
+ macaddr (~> 1.0)
69
74
  webmock (3.8.3)
70
75
  addressable (>= 2.3.6)
71
76
  crack (>= 0.3.2)
@@ -80,7 +85,10 @@ DEPENDENCIES
80
85
  rake (~> 12.0)
81
86
  readme-metrics!
82
87
  rspec (~> 3.0)
83
- standard
88
+ rubocop
89
+ rubocop-performance
90
+ rubocop-rspec
91
+ uuid
84
92
  webmock
85
93
 
86
94
  BUNDLED WITH
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2020, ReadMe
1
+ Copyright (c) 2022, ReadMe
2
2
 
3
3
  Permission to use, copy, modify, and/or distribute this software for any purpose
4
4
  with or without fee is hereby granted, provided that the above copyright notice
data/Makefile ADDED
@@ -0,0 +1,14 @@
1
+ install: ## Install all dependencies
2
+ bundle
3
+
4
+ lint: ## Run code standard checks
5
+ bundle exec rubocop
6
+
7
+ lint-fix: ## Attempt to automatically fix any code standard violations
8
+ bundle exec rubocop --auto-correct
9
+
10
+ test: ## Run unit tests
11
+ rake spec
12
+
13
+ help: ## Show this help
14
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
data/README.md CHANGED
@@ -1,109 +1,30 @@
1
- # readme-metrics
1
+ # ReadMe Metrics
2
2
 
3
- Track your API metrics within ReadMe.
3
+ <p align="center">
4
+ <img src="https://user-images.githubusercontent.com/33762/182927634-2aebeb46-c215-4ac3-9e98-61f931e33583.png" />
5
+ </p>
4
6
 
5
- [![RubyGems](https://img.shields.io/gem/v/readme-metrics)](https://rubygems.org/gems/readme-metrics)
6
- [![Build](https://github.com/readmeio/metrics-sdks/workflows/ruby/badge.svg)](https://github.com/readmeio/metrics-sdks)
7
+ <p align="center">
8
+ Track usage of your API and troubleshoot issues faster.
9
+ </p>
7
10
 
8
- [![](https://d3vv6lp55qjaqc.cloudfront.net/items/1M3C3j0I0s0j3T362344/Untitled-2.png)](https://readme.io)
11
+ <p align="center">
12
+ <a href="https://rubygems.org/gems/readme-metrics"><img src="https://img.shields.io/gem/v/readme-metrics.svg?style=for-the-badge" alt="Latest release"></a>
13
+ <a href="https://github.com/readmeio/metrics-sdks"><img src="https://img.shields.io/github/workflow/status/readmeio/metrics-sdks/ruby.svg?style=for-the-badge" alt="Build status"></a>
14
+ </p>
9
15
 
10
- ## Installation
16
+ With [ReadMe's Metrics API](https://readme.com/metrics) your team can get deep insights into your API's usage. If you're a developer, it takes a few small steps to send your API logs to [ReadMe](http://readme.com). Here's an overview of how the integration works:
11
17
 
12
- Add it to your Gemfile
18
+ - You add the ReadMe middleware to your Rails application.
19
+ - The middleware sends to ReadMe the request and response objects that your Express server generates each time a user makes a request to your API. The entire objects are sent, unless you allow or deny keys.
20
+ - ReadMe extracts information to display in Metrics, such as which endpoint is being called, response code, and error messages. It also identifies the customer who called your API, using whichever keys in the middleware you call out as containing relevant customer info.
13
21
 
14
- `gem "readme-metrics"`
15
-
16
- ## Usage
17
-
18
- `Readme::Metrics` is a Rack middleware and is compatible with all Rack-based
19
- apps, including Rails.
20
-
21
- When configuring the middleware, you must provide a block to tell the
22
- middleware how to get values for the current user. These may be values taken
23
- from the environment, or you may hardcode them.
24
-
25
- If you're using Warden-based authentication like Devise, you may fetch the
26
- current_user for a given request from the environment.
27
-
28
- ### Batching requests
29
-
30
- By default, the middleware will batch requests to the ReadMe API in groups of 10.
31
- For every 10 requests made to your application, the middleware will make a
32
- single request to ReadMe. If you wish to override this, provide a
33
- `buffer_length` option when configuring the middleware.
34
-
35
- ### Sensitive Data
36
-
37
- If you have sensitive data you'd like to prevent from being sent to the Metrics
38
- API via headers, query params or payload bodies, you can specify a list of keys
39
- to filter via the `reject_params` option. Key-value pairs matching these keys
40
- will not be included in the request to the Metrics API.
41
-
42
- You are also able to specify a set of `allow_only` which should only be sent through.
43
- Any header or body values not matching these keys will be filtered out and not
44
- send to the API.
45
-
46
- You may only specify either `reject_params` or `allow_only` keys, not both.
47
-
48
- ### Rails
49
-
50
- ```ruby
51
- # config/environments/development.rb or config/environments/production.rb
52
- require "readme/metrics"
53
-
54
- options = {
55
- api_key: "<<apiKey>>",
56
- development: false,
57
- reject_params: ["not_included", "dont_send"],
58
- buffer_length: 5,
59
- }
60
-
61
- config.middleware.use Readme::Metrics, options do |env|
62
- current_user = env['warden'].authenticate
63
-
64
- if current_user.present?
65
- {
66
- api_key: current_user.api_key, # Not the same as the ReadMe API Key
67
- label: current_user.name,
68
- email: current_user.email
69
- }
70
- else
71
- {
72
- api_key: "guest",
73
- label: "Guest User",
74
- email: "guest@example.com"
75
- }
76
- end
77
- end
22
+ ```bash
23
+ gem "readme-metrics"
78
24
  ```
79
25
 
80
- ### Rack
81
-
82
- ```ruby
83
- # config.ru
84
- options = {
85
- api_key: "<<apiKey>>",
86
- development: false,
87
- reject_params: ["not_included", "dont_send"]
88
- }
89
-
90
- use Readme::Metrics, options do |env|
91
- {
92
- api_key: "owlbert_api_key"
93
- label: "Owlbert",
94
- email: "owlbert@example.com"
95
- }
96
- end
97
-
98
- run YourApp.new
99
- ```
100
-
101
- ### Sample Applications
102
-
103
- - [Rails](https://github.com/readmeio/metrics-sdk-rails-sample)
104
- - [Rack](https://github.com/readmeio/metrics-sdk-racks-sample)
105
- - [Sinatra](https://github.com/readmeio/metrics-sdk-sinatra-example)
106
-
107
- ## License
26
+ **For more information on setup, check out our [integration documentation](https://docs.readme.com/docs/ruby-api-metrics-set-up).**
108
27
 
109
- [View our license here](https://github.com/readmeio/metrics-sdks/tree/main/packages/ruby/LICENSE)
28
+ > 🚧 Any Issues?
29
+ >
30
+ > Integrations can be tricky! [Contact support](https://docs.readme.com/guides/docs/contact-support) if you have any questions/issues.
data/Rakefile CHANGED
@@ -1,5 +1,5 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
data/bin/console CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "readme/metrics"
3
+ require 'bundler/setup'
4
+ require 'readme/metrics'
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +10,5 @@ require "readme/metrics"
10
10
  # require "pry"
11
11
  # Pry.start
12
12
 
13
- require "irb"
13
+ require 'irb'
14
14
  IRB.start(__FILE__)
@@ -3,12 +3,12 @@ module Readme
3
3
  # Assumes the includer has a `content_type` method defined.
4
4
 
5
5
  JSON_MIME_TYPES = [
6
- "application/json",
7
- "application/x-json",
8
- "text/json",
9
- "text/x-json",
10
- "+json"
11
- ]
6
+ 'application/json',
7
+ 'application/x-json',
8
+ 'text/json',
9
+ 'text/x-json',
10
+ '+json'
11
+ ].freeze
12
12
 
13
13
  def json?
14
14
  JSON_MIME_TYPES.any? { |mime_type| content_type.include?(mime_type) }
data/lib/readme/errors.rb CHANGED
@@ -1,10 +1,12 @@
1
+ # frozen-string-literal: true
2
+
1
3
  module Readme
2
4
  class Errors
3
- API_KEY_ERROR = "Missing API Key"
4
- REJECT_PARAMS_ERROR = "The `reject_params` option must be an array of strings"
5
- ALLOW_ONLY_ERROR = "The `allow_only` option must be an array of strings"
6
- BUFFER_LENGTH_ERROR = "The `buffer_length` must be an Integer"
7
- DEVELOPMENT_ERROR = "The `development` option must be a boolean"
5
+ API_KEY_ERROR = 'Missing API Key'
6
+ REJECT_PARAMS_ERROR = 'The `reject_params` option must be an array of strings'
7
+ ALLOW_ONLY_ERROR = 'The `allow_only` option must be an array of strings'
8
+ BUFFER_LENGTH_ERROR = 'The `buffer_length` must be an Integer'
9
+ DEVELOPMENT_ERROR = 'The `development` option must be a boolean'
8
10
  LOGGER_ERROR = <<~MESSAGE
9
11
  The `logger` option must be class that responds to the following messages:
10
12
  :unkown, :fatal, :error, :warn, :info, :debug, :level
data/lib/readme/filter.rb CHANGED
@@ -15,7 +15,7 @@ module Readme
15
15
  def self.redact(rejected_params)
16
16
  rejected_params.each_with_object({}) do |(k, v), hash|
17
17
  # If it's a string then return the length of the redacted field
18
- hash[k.to_str] = "[REDACTED#{v.is_a?(String) ? " #{v.length}" : ""}]"
18
+ hash[k.to_str] = "[REDACTED#{v.is_a?(String) ? " #{v.length}" : ''}]"
19
19
  end
20
20
  end
21
21
 
@@ -65,7 +65,7 @@ module Readme
65
65
 
66
66
  class FilterArgsError < StandardError
67
67
  def initialize
68
- msg = "Can only supply either reject_params or allow_only, not both."
68
+ msg = 'Can only supply either reject_params or allow_only, not both.'
69
69
  super(msg)
70
70
  end
71
71
  end
@@ -11,7 +11,7 @@ module Readme
11
11
  end
12
12
 
13
13
  def to_a
14
- filtered_hash.map { |name, value| {name: name, value: value} }
14
+ filtered_hash.map { |name, value| { name: name, value: value } }
15
15
  end
16
16
 
17
17
  private
@@ -1,10 +1,11 @@
1
- require "readme/har/collection"
2
- require "readme/filter"
1
+ require 'cgi'
2
+ require 'readme/har/collection'
3
+ require 'readme/filter'
3
4
 
4
5
  module Readme
5
6
  module Har
6
7
  class RequestSerializer
7
- def initialize(request, filter = Filter::None.new)
8
+ def initialize(request, filter = Readme::Filter::None.new)
8
9
  @request = request
9
10
  @filter = filter
10
11
  end
@@ -13,11 +14,11 @@ module Readme
13
14
  {
14
15
  method: @request.request_method,
15
16
  queryString: Har::Collection.new(@filter, @request.query_params).to_a,
16
- url: @request.url,
17
+ url: url,
17
18
  httpVersion: @request.http_version,
18
19
  headers: Har::Collection.new(@filter, @request.headers).to_a,
19
20
  cookies: Har::Collection.new(@filter, @request.cookies).to_a,
20
- postData: postData,
21
+ postData: post_data,
21
22
  headersSize: -1,
22
23
  bodySize: @request.content_length
23
24
  }.compact
@@ -25,7 +26,17 @@ module Readme
25
26
 
26
27
  private
27
28
 
28
- def postData
29
+ def url
30
+ url = URI(@request.url)
31
+ headers = @request.headers
32
+ forward_proto = headers['X-Forwarded-Proto']
33
+ forward_host = headers['X-Forwarded-Host']
34
+ url.host = forward_host if forward_host.is_a?(String)
35
+ url.scheme = forward_proto if forward_proto.is_a?(String)
36
+ url.to_s
37
+ end
38
+
39
+ def post_data
29
40
  if @request.content_type.nil?
30
41
  nil
31
42
  elsif @request.form_data?
@@ -48,18 +59,34 @@ module Readme
48
59
  def request_body
49
60
  if @filter.pass_through?
50
61
  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.
62
+ elsif form_urlencoded?
63
+ form_urlencoded_body
64
+ elsif json?
54
65
  json_body
66
+ else
67
+ @request.body
55
68
  end
56
69
  end
57
70
 
71
+ def json?
72
+ ['application/json', 'application/x-json', 'text/json', 'text/x-json']
73
+ .include?(@request.content_type) || @request.content_type.include?('+json')
74
+ end
75
+
76
+ def form_urlencoded?
77
+ @request.content_type == 'application/x-www-form-urlencoded'
78
+ end
79
+
58
80
  def json_body
59
81
  parsed_body = JSON.parse(@request.body)
60
82
  Har::Collection.new(@filter, parsed_body).to_h.to_json
61
83
  end
62
84
 
85
+ def form_urlencoded_body
86
+ parsed_body = CGI.parse(@request.body).transform_values(&:first)
87
+ Har::Collection.new(@filter, parsed_body).to_h.to_json
88
+ end
89
+
63
90
  def pass_through_body
64
91
  @request.body
65
92
  end
@@ -1,5 +1,5 @@
1
- require "rack/utils"
2
- require "readme/har/collection"
1
+ require 'rack/utils'
2
+ require 'readme/har/collection'
3
3
 
4
4
  module Readme
5
5
  module Har
@@ -37,7 +37,7 @@ module Readme
37
37
  end
38
38
 
39
39
  def empty_content
40
- {mimeType: "", size: 0}
40
+ { mimeType: '', size: 0 }
41
41
  end
42
42
 
43
43
  def json_content
@@ -1,13 +1,15 @@
1
- require "rack"
2
- require "readme/metrics"
3
- require "readme/har/request_serializer"
4
- require "readme/har/response_serializer"
5
- require "readme/har/collection"
1
+ # frozen-string-literal: true
2
+
3
+ require 'rack'
4
+ require 'readme/metrics'
5
+ require 'readme/har/request_serializer'
6
+ require 'readme/har/response_serializer'
7
+ require 'readme/har/collection'
6
8
 
7
9
  module Readme
8
10
  module Har
9
11
  class Serializer
10
- HAR_VERSION = "1.2"
12
+ HAR_VERSION = '1.2'
11
13
 
12
14
  def initialize(request, response, start_time, end_time, filter)
13
15
  @http_request = request
@@ -17,7 +19,7 @@ module Readme
17
19
  @filter = filter
18
20
  end
19
21
 
20
- def to_json
22
+ def to_json(*_args)
21
23
  {
22
24
  log: {
23
25
  version: HAR_VERSION,
@@ -31,8 +33,9 @@ module Readme
31
33
 
32
34
  def creator
33
35
  {
34
- name: Readme::Metrics::SDK_NAME,
35
- version: Readme::Metrics::VERSION
36
+ name: 'readme-metrics (ruby)',
37
+ version: Readme::Metrics::VERSION,
38
+ comment: "#{RUBY_PLATFORM}/#{RUBY_VERSION}" # arm64-darwin21/2.7.2
36
39
  }
37
40
  end
38
41
 
@@ -1,6 +1,6 @@
1
- require "rack"
2
- require "rack/request"
3
- require_relative "content_type_helper"
1
+ require 'rack'
2
+ require 'rack/request'
3
+ require_relative 'content_type_helper'
4
4
 
5
5
  module Readme
6
6
  class HttpRequest
@@ -11,7 +11,7 @@ module Readme
11
11
  Rack::HTTP_VERSION,
12
12
  Rack::HTTP_HOST,
13
13
  Rack::HTTP_PORT
14
- ]
14
+ ].freeze
15
15
 
16
16
  def initialize(env)
17
17
  @request = Rack::Request.new(env)
@@ -50,7 +50,7 @@ module Readme
50
50
  end
51
51
 
52
52
  def options?
53
- @request.request_method == "OPTIONS"
53
+ @request.request_method == 'OPTIONS'
54
54
  end
55
55
 
56
56
  def headers
@@ -82,20 +82,22 @@ module Readme
82
82
  # Other "headers" like version and host are prefixed with `HTTP_` by Rack but
83
83
  # don't seem to be considered legit HTTP headers.
84
84
  def http_header?(name)
85
- name.start_with?("HTTP") && !HTTP_NON_HEADERS.include?(name)
85
+ name.start_with?('HTTP') && !HTTP_NON_HEADERS.include?(name)
86
86
  end
87
87
 
88
88
  # Headers like `Content-Type: application/json` come into rack like
89
89
  # `"HTTP_CONTENT_TYPE" => "application/json"`.
90
90
  def normalize_header_name(header)
91
- header.delete_prefix("HTTP_").split("_").map(&:capitalize).join("-")
91
+ header.delete_prefix('HTTP_').split('_').map(&:capitalize).join('-')
92
92
  end
93
93
 
94
94
  # These special headers are explicitly _not_ prefixed with HTTP_ in the Rack
95
95
  # env so we need to add them in manually
96
96
  def unprefixed_headers
97
- {"Content-Type" => @request.content_type,
98
- "Content-Length" => @request.content_length}.compact
97
+ {
98
+ 'Content-Type' => @request.content_type,
99
+ 'Content-Length' => @request.content_length
100
+ }.compact
99
101
  end
100
102
  end
101
103
  end
@@ -1,6 +1,6 @@
1
- require "rack"
2
- require "rack/response"
3
- require_relative "content_type_helper"
1
+ require 'rack'
2
+ require 'rack/response'
3
+ require_relative 'content_type_helper'
4
4
 
5
5
  module Readme
6
6
  class HttpResponse < SimpleDelegator
@@ -13,22 +13,22 @@ module Readme
13
13
  def body
14
14
  if raw_body.respond_to?(:rewind)
15
15
  raw_body.rewind
16
- content = raw_body.each.reduce("", :+)
16
+ content = raw_body.each.reduce('', :+)
17
17
  raw_body.rewind
18
18
 
19
19
  content
20
20
  else
21
- raw_body.each.reduce("", :+)
21
+ raw_body.each.reduce('', :+)
22
22
  end
23
23
  end
24
24
 
25
25
  def content_length
26
26
  if empty_body_status?
27
27
  0
28
- elsif !headers["Content-Length"]
28
+ elsif !headers['Content-Length']
29
29
  body.bytesize
30
30
  else
31
- headers["Content-Length"].to_i
31
+ headers['Content-Length'].to_i
32
32
  end
33
33
  end
34
34
 
@@ -1,5 +1,7 @@
1
+ # frozen-string-literal: true
2
+
1
3
  module Readme
2
4
  class Metrics
3
- VERSION = "1.1.1"
5
+ VERSION = '2.1.0'
4
6
  end
5
7
  end
@@ -1,21 +1,21 @@
1
- require "readme/metrics/version"
2
- require "readme/har/serializer"
3
- require "readme/filter"
4
- require "readme/payload"
5
- require "readme/request_queue"
6
- require "readme/errors"
7
- require "readme/http_request"
8
- require "readme/http_response"
9
- require "httparty"
10
- require "logger"
1
+ # frozen-string-literal: true
2
+
3
+ require 'readme/metrics/version'
4
+ require 'readme/har/serializer'
5
+ require 'readme/filter'
6
+ require 'readme/payload'
7
+ require 'readme/request_queue'
8
+ require 'readme/errors'
9
+ require 'readme/http_request'
10
+ require 'readme/http_response'
11
+ require 'httparty'
12
+ require 'logger'
11
13
 
12
14
  module Readme
13
15
  class Metrics
14
- SDK_NAME = "Readme.io Ruby SDK"
15
- DEFAULT_BUFFER_LENGTH = 10
16
- ENDPOINT = "https://metrics.readme.io/v1/request"
17
- USER_INFO_KEYS = [:api_key, :label, :email]
18
- USER_INFO_KEYS_DEPRECATED = [:id, :label, :email]
16
+ SDK_NAME = 'readme-metrics'
17
+ DEFAULT_BUFFER_LENGTH = 1
18
+ ENDPOINT = 'https://metrics.readme.io/v1/request'
19
19
 
20
20
  def self.logger
21
21
  @@logger
@@ -65,16 +65,17 @@ module Readme
65
65
  request = HttpRequest.new(env)
66
66
  har = Har::Serializer.new(request, response, start_time, end_time, @filter)
67
67
  user_info = @get_user_info.call(env)
68
+ ip = env['REMOTE_ADDR']
68
69
 
69
70
  if !user_info_valid?(user_info)
70
71
  Readme::Metrics.logger.warn Errors.bad_block_message(user_info)
71
72
  elsif request.options?
72
- Readme::Metrics.logger.info "OPTIONS request omitted from ReadMe API logging"
73
+ Readme::Metrics.logger.info 'OPTIONS request omitted from ReadMe API logging'
73
74
  elsif !can_filter? request, response
74
75
  Readme::Metrics.logger.warn "Request or response body MIME type isn't supported for filtering. Omitting request from ReadMe API logging"
75
76
  else
76
- payload = Payload.new(har, user_info, development: @development)
77
- @@request_queue.push(payload.to_json)
77
+ payload = Payload.new(har, user_info, ip, development: @development)
78
+ @@request_queue.push(payload.to_json) unless payload.ignore
78
79
  end
79
80
  end
80
81
 
@@ -109,36 +110,36 @@ module Readme
109
110
  raise Errors::ConfigurationError, Errors::BUFFER_LENGTH_ERROR
110
111
  end
111
112
 
112
- if options[:development] && !is_a_boolean?(options[:development])
113
+ if options[:development] && !a_boolean?(options[:development])
113
114
  raise Errors::ConfigurationError, Errors::DEVELOPMENT_ERROR
114
115
  end
115
116
 
116
- if options[:logger] && has_logger_inferface?(options[:logger])
117
+ if options[:logger] && logger_inferface?(options[:logger])
117
118
  raise Errors::ConfigurationError, Errors::LOGGER_ERROR
118
119
  end
119
120
  end
120
121
 
121
- def has_logger_inferface?(logger)
122
- [
123
- :unknown,
124
- :fatal,
125
- :error,
126
- :warn,
127
- :info,
128
- :debug
122
+ def logger_inferface?(logger)
123
+ %i[
124
+ unknown
125
+ fatal
126
+ error
127
+ warn
128
+ info
129
+ debug
129
130
  ].any? { |message| !logger.respond_to? message }
130
131
  end
131
132
 
132
- def is_a_boolean?(arg)
133
- arg == true || arg == false
133
+ def a_boolean?(arg)
134
+ [true, false].include?(arg)
134
135
  end
135
136
 
137
+ # rubocop:disable Style/InverseMethods
136
138
  def user_info_valid?(user_info)
137
- sorted_user_info_keys = user_info.keys.sort
138
139
  !user_info.nil? &&
139
140
  !user_info.values.any?(&:nil?) &&
140
- (sorted_user_info_keys === USER_INFO_KEYS.sort ||
141
- sorted_user_info_keys === USER_INFO_KEYS_DEPRECATED.sort)
141
+ user_info.key?(:api_key) || user_info.key?(:id)
142
142
  end
143
+ # rubocop:enable Style/InverseMethods
143
144
  end
144
145
  end
@@ -1,17 +1,26 @@
1
+ require 'socket'
2
+ require 'uuid'
3
+
1
4
  module Readme
2
5
  class Payload
3
- def initialize(har, user_info, development:)
6
+ attr_reader :ignore
7
+
8
+ def initialize(har, info, ip_address, development:)
4
9
  @har = har
5
- # swap api_key for id
6
- user_info[:id] = user_info.delete :api_key unless user_info[:api_key].nil?
7
- @user_info = user_info
10
+ @user_info = info.slice(:id, :label, :email)
11
+ @user_info[:id] = info[:api_key] unless info[:api_key].nil? # swap api_key for id if api_key is present
12
+ @log_id = info[:log_id]
13
+ @ignore = info[:ignore]
14
+ @ip_address = ip_address
8
15
  @development = development
16
+ @uuid = UUID.new
9
17
  end
10
18
 
11
- def to_json
19
+ def to_json(*_args)
12
20
  {
21
+ logId: UUID.validate(@log_id) ? @log_id : @uuid.generate,
13
22
  group: @user_info,
14
- clientIPAddress: "1.1.1.1",
23
+ clientIPAddress: @ip_address,
15
24
  development: @development,
16
25
  request: JSON.parse(@har.to_json)
17
26
  }.to_json
@@ -1,4 +1,4 @@
1
- require "readme/metrics"
1
+ require 'readme/metrics'
2
2
 
3
3
  module Readme
4
4
  class RequestQueue
@@ -30,8 +30,8 @@ module Readme
30
30
  Thread.new do
31
31
  HTTParty.post(
32
32
  Readme::Metrics::ENDPOINT,
33
- basic_auth: {username: @api_key, password: ""},
34
- headers: {"Content-Type" => "application/json"},
33
+ basic_auth: { username: @api_key, password: '' },
34
+ headers: { 'Content-Type' => 'application/json' },
35
35
  body: to_json(payloads)
36
36
  )
37
37
  end
@@ -42,7 +42,7 @@ module Readme
42
42
  end
43
43
 
44
44
  def to_json(payloads)
45
- "[#{payloads.join(", ")}]"
45
+ "[#{payloads.join(', ')}]"
46
46
  end
47
47
  end
48
48
  end
@@ -1,29 +1,31 @@
1
- require_relative "lib/readme/metrics/version"
1
+ require_relative 'lib/readme/metrics/version'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
- spec.name = "readme-metrics"
4
+ spec.name = 'readme-metrics'
5
5
  spec.version = Readme::Metrics::VERSION
6
- spec.authors = ["ReadMe"]
7
- spec.email = ["support@readme.io"]
8
- spec.license = "ISC"
6
+ spec.authors = ['ReadMe']
7
+ spec.email = ['support@readme.io']
8
+ spec.license = 'ISC'
9
9
 
10
10
  spec.summary = "SDK for Readme's metrics API"
11
11
  spec.description = "Middleware for logging requests to Readme's metrics API"
12
- spec.homepage = "https://docs.readme.com/metrics/docs/getting-started-with-api-metrics"
13
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
12
+ spec.homepage = 'https://docs.readme.com/metrics/docs/getting-started-with-api-metrics'
13
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
14
14
 
15
- spec.metadata["homepage_uri"] = spec.homepage
16
- spec.metadata["source_code_uri"] = "https://github.com/readmeio/metrics-sdks/blob/main/packages/ruby"
17
- spec.metadata["changelog_uri"] = "https://github.com/readmeio/metrics-sdks/blob/main/packages/ruby/CHANGELOG.md"
15
+ spec.metadata['homepage_uri'] = spec.homepage
16
+ spec.metadata['source_code_uri'] = 'https://github.com/readmeio/metrics-sdks/tree/main/packages/ruby'
17
+ spec.metadata['changelog_uri'] = 'https://github.com/readmeio/metrics-sdks/blob/main/CHANGELOG.md'
18
18
 
19
19
  # Specify which files should be added to the gem when it is released.
20
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
- spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
22
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
23
  end
24
+
24
25
  # spec.bindir = "exe"
25
26
  # spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
- spec.require_paths = ["lib"]
27
+ spec.require_paths = ['lib']
27
28
 
28
- spec.add_runtime_dependency "httparty", "~> 0.18"
29
+ spec.add_runtime_dependency 'httparty', '~> 0.18'
30
+ spec.add_runtime_dependency 'uuid', '~> 2.3.8'
29
31
  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: 1.1.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ReadMe
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-02 00:00:00.000000000 Z
11
+ date: 2022-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.18'
27
+ - !ruby/object:Gem::Dependency
28
+ name: uuid
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.8
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.3.8
27
41
  description: Middleware for logging requests to Readme's metrics API
28
42
  email:
29
43
  - support@readme.io
@@ -33,9 +47,11 @@ extra_rdoc_files: []
33
47
  files:
34
48
  - ".gitignore"
35
49
  - ".rspec"
50
+ - ".rubocop.yml"
36
51
  - Gemfile
37
52
  - Gemfile.lock
38
53
  - LICENSE
54
+ - Makefile
39
55
  - README.md
40
56
  - Rakefile
41
57
  - bin/console
@@ -59,9 +75,9 @@ licenses:
59
75
  - ISC
60
76
  metadata:
61
77
  homepage_uri: https://docs.readme.com/metrics/docs/getting-started-with-api-metrics
62
- source_code_uri: https://github.com/readmeio/metrics-sdks/blob/main/packages/ruby
63
- changelog_uri: https://github.com/readmeio/metrics-sdks/blob/main/packages/ruby/CHANGELOG.md
64
- post_install_message:
78
+ source_code_uri: https://github.com/readmeio/metrics-sdks/tree/main/packages/ruby
79
+ changelog_uri: https://github.com/readmeio/metrics-sdks/blob/main/CHANGELOG.md
80
+ post_install_message:
65
81
  rdoc_options: []
66
82
  require_paths:
67
83
  - lib
@@ -76,8 +92,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
92
  - !ruby/object:Gem::Version
77
93
  version: '0'
78
94
  requirements: []
79
- rubygems_version: 3.1.4
80
- signing_key:
95
+ rubygems_version: 3.0.3.1
96
+ signing_key:
81
97
  specification_version: 4
82
98
  summary: SDK for Readme's metrics API
83
99
  test_files: []