From 7fd3c24ab4dc1c9a642da0f3ba36e5b92fba3211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Quenneville?= Date: Mon, 10 Aug 2020 16:10:19 -0400 Subject: [PATCH] Create simple HTTP-logging middleware This creates a simple middleware that logs requests received to the given logging API endpoint in the format: `"GET http://example.com"`. The actual Readme.io payload format is more complicated than this. We will add support for it in a follow-up commit. When comparing Rack mock responses to real ones, we needed to convert to the raw status, headers, body triple ourselves since there isn't a built-in method to do so and we needed a way to compare against the real triple. _Note that Rack::MockResponse converts the array body into a single string for ease of testing so we had to convert back to an array as part of destructuring._ --- packages/ruby/Gemfile | 2 + packages/ruby/Gemfile.lock | 24 +++++++++++ packages/ruby/README.md | 21 +++++++++ packages/ruby/lib/readme/metrics.rb | 15 +++++-- packages/ruby/lib/readme/metrics/version.rb | 2 +- packages/ruby/readme-metrics.gemspec | 2 + packages/ruby/spec/readme/metrics_spec.rb | 47 +++++++++++++++++++++ 7 files changed, 109 insertions(+), 4 deletions(-) diff --git a/packages/ruby/Gemfile b/packages/ruby/Gemfile index 936bc050f6..55625dce22 100644 --- a/packages/ruby/Gemfile +++ b/packages/ruby/Gemfile @@ -8,3 +8,5 @@ gemspec gem "rake", "~> 12.0" gem "rspec", "~> 3.0" gem "standard" +gem "rack-test" +gem "webmock" diff --git a/packages/ruby/Gemfile.lock b/packages/ruby/Gemfile.lock index bbcaf3b76f..e55603b340 100644 --- a/packages/ruby/Gemfile.lock +++ b/packages/ruby/Gemfile.lock @@ -2,15 +2,32 @@ PATH remote: . specs: readme-metrics (0.1.0) + httparty GEM remote: https://rubygems.org/ specs: + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) ast (2.4.1) + crack (0.4.3) + safe_yaml (~> 1.0.0) diff-lcs (1.4.4) + hashdiff (1.0.1) + httparty (0.18.1) + mime-types (~> 3.0) + multi_xml (>= 0.5.2) + mime-types (3.3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2020.0512) + multi_xml (0.6.0) parallel (1.19.2) parser (2.7.1.4) ast (~> 2.4.1) + public_suffix (4.0.5) + rack (2.2.3) + rack-test (1.1.0) + rack (>= 1.0, < 3) rainbow (3.0.0) rake (12.3.3) regexp_parser (1.7.1) @@ -42,19 +59,26 @@ GEM rubocop-performance (1.6.1) rubocop (>= 0.71.0) ruby-progressbar (1.10.1) + safe_yaml (1.0.5) standard (0.4.7) rubocop (~> 0.85.0) rubocop-performance (~> 1.6.0) unicode-display_width (1.7.0) + webmock (3.8.3) + addressable (>= 2.3.6) + crack (>= 0.3.2) + hashdiff (>= 0.4.0, < 2.0.0) PLATFORMS ruby DEPENDENCIES + rack-test rake (~> 12.0) readme-metrics! rspec (~> 3.0) standard + webmock BUNDLED WITH 2.1.4 diff --git a/packages/ruby/README.md b/packages/ruby/README.md index a00fc07a96..8693f206ac 100644 --- a/packages/ruby/README.md +++ b/packages/ruby/README.md @@ -7,3 +7,24 @@ Track your API metrics within ReadMe. ## Installation ## Usage + +`Readme::Metrics` is a Rack middleware and is compatible with all Rack-based +apps, including Rails. + +### Rails + +```ruby +# application.rb +require "readme/metrics" + +config.middleware.use Readme::Metrics, "http://example.com/your/logging/api" +``` + +### Rack::Builder + +```ruby +Rack::Builder.new do |builder| + builder.use Readme::Metrics, "http://example.com/your/logging/api" + builder.run your_app +end +``` diff --git a/packages/ruby/lib/readme/metrics.rb b/packages/ruby/lib/readme/metrics.rb index 3d4c0e2f0b..3b5920527d 100644 --- a/packages/ruby/lib/readme/metrics.rb +++ b/packages/ruby/lib/readme/metrics.rb @@ -1,8 +1,17 @@ require "readme/metrics/version" +require "httparty" module Readme - module Metrics - class Error < StandardError; end - # Your code goes here... + class Metrics + def initialize(app, endpoint) + @app = app + @endpoint = endpoint + end + + def call(env) + path = "#{env["REQUEST_METHOD"]} #{env["PATH_INFO"]}" + HTTParty.post(@endpoint, body: {path: path}.to_json) + @app.call(env) + end end end diff --git a/packages/ruby/lib/readme/metrics/version.rb b/packages/ruby/lib/readme/metrics/version.rb index d91278b8b9..b080cb9a97 100644 --- a/packages/ruby/lib/readme/metrics/version.rb +++ b/packages/ruby/lib/readme/metrics/version.rb @@ -1,5 +1,5 @@ module Readme - module Metrics + class Metrics VERSION = "0.1.0" end end diff --git a/packages/ruby/readme-metrics.gemspec b/packages/ruby/readme-metrics.gemspec index c1c23ff742..f23da5055c 100644 --- a/packages/ruby/readme-metrics.gemspec +++ b/packages/ruby/readme-metrics.gemspec @@ -23,4 +23,6 @@ Gem::Specification.new do |spec| # spec.bindir = "exe" # spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] + + spec.add_runtime_dependency "httparty" end diff --git a/packages/ruby/spec/readme/metrics_spec.rb b/packages/ruby/spec/readme/metrics_spec.rb index 22ffb66827..c2c528bebd 100644 --- a/packages/ruby/spec/readme/metrics_spec.rb +++ b/packages/ruby/spec/readme/metrics_spec.rb @@ -1,5 +1,52 @@ +require "rack/test" +require "webmock/rspec" + RSpec.describe Readme::Metrics do + include Rack::Test::Methods + + before do + stub_request(:post, readme_endpoint) + end + it "has a version number" do expect(Readme::Metrics::VERSION).not_to be nil end + + it "doesn't modify the response" do + get "/" + + response_without_middleware = noop_app.call(double) + response_with_middleware = mock_response_to_raw(last_response) + + expect(response_with_middleware).to eq response_without_middleware + end + + it "posts request urls to Readme API" do + get "/api/foo" + post "/api/bar" + + expect(WebMock).to have_requested(:post, readme_endpoint) + .with(body: {path: "GET /api/foo"}.to_json) + + expect(WebMock).to have_requested(:post, readme_endpoint) + .with(body: {path: "POST /api/bar"}.to_json) + end + + def readme_endpoint + "http://example.com/" + end + + def app + Readme::Metrics.new(noop_app, readme_endpoint) + end + + def noop_app + lambda do |env| + [200, {"Content-Type" => "text/plain"}, ["OK"]] + end + end + + def mock_response_to_raw(mock_response) + [mock_response.status, mock_response.headers, [mock_response.body]] + end end