Skip to content

Commit

Permalink
feat(ruby): add Readme::Webhook.verify() function to verify webhooks (#…
Browse files Browse the repository at this point in the history
…568)

* chore: initial commit of lightweight rails app

Generated using this, which seemed to give me as lightweight a rails app
as humanly possible:

```sh
rails new metrics-rails --api --minimal --skip-keeps --skip-test
```

* feat(ruby): add very basic integration test scaffolding

* chore(integration): add test watch command for mocha with nodemon

* fix(ruby): send valid v4 uuid

Update from `logId` to `_id`
Remove uuid dependency and replace with std library securerandom:
https://docs.ruby-lang.org/en/3.1/SecureRandom.html

* fix(ruby): update to use UTC formatted date for startedDateTime

* fix(integration): add support for rails-style querystrings

* chore(integration): update code comment to be runnable

* fix(ruby): ensure we're returning with host header

Rack doesn't return with the port in the Host header, unless it is one
of the known ports (80/443)
https://rubydoc.info/gems/rack/Rack/Request/Helpers#DEFAULT_PORTS-constant

So I opted to just use `host` instead of `host_with_port`, since I figured
we'd actually probably only want the port if it was non-standard, not the
other way around.

* fix(ruby): allow endpoint to be overridden via METRICS_SERVER env var

* feat(integration/ruby/rails): get the Metrics middleware configured

* chore(integration/ruby/rails): lint

* chore(integration/ruby/rails): update README.md

* feat(integration/ruby/rails): get test working in Docker

* chore(ruby): bump rubocop and lint everything

* feat(integration/ruby/rails): webhooks code working

* chore(integration/ruby/rails): switch default port to 8000 to match others

* chore(integration/ruby/rails): add to docker and github action

* chore(integration/ruby/rails): add webhooks example to readme.md

* chore(integration/ruby/rails): lint

* feat(ruby): add Readme::Webhook.verify() function to verify webhooks

* chore(integration/ruby/rails): switch app to use Readme::Webhook.verify

* chore(prettier): lint

* Update packages/ruby/examples/metrics-rails/README.md

Co-authored-by: Jon Ursenbach <[email protected]>

* Update packages/ruby/examples/metrics-rails/app/controllers/metrics_controller.rb

Co-authored-by: Kelly Joseph Price <[email protected]>

* refactor(ruby): use custom error classes

Co-authored-by: Kelly Joseph Price <[email protected]>

* refactor(ruby): switched to using more specific exception types

* chore(ruby): lint and fix tests

* refactor(ruby/webhooks): switch to using rspec raise_error matcher in tests

* refactor(ruby/integration): switch base image to ruby:3.1.2

* feat: ruby sdk snippet (#584)

* refactor(ruby/webhooks): add consistent code comments with other snippets

* feat(ruby/sdk-snippets): add webhook code snippet

* chore(ruby): lint

* chore(ruby/integration): fix up ruby.yml for gh actions

* ci: try to make sure external webserver is up before spinning mock metrics

* ci: attempt to sleep for 500ms in test before resolving isListening

* chore(integration): try and sleep before running tests

* chore(integration): add comment about `sleep 5`

Co-authored-by: Jon Ursenbach <[email protected]>
Co-authored-by: Kelly Joseph Price <[email protected]>
  • Loading branch information
3 people authored Sep 7, 2022
1 parent 65117ef commit 9189727
Show file tree
Hide file tree
Showing 70 changed files with 1,707 additions and 140 deletions.
17 changes: 17 additions & 0 deletions .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@ name: ruby
on: [push]

jobs:
integration:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16

- run: npm ci --ignore-scripts
- run: make test-metrics-ruby-rails
- run: make test-webhooks-ruby-rails

- name: Cleanup
if: always()
run: docker-compose down

build:
runs-on: ubuntu-latest
defaults:
Expand Down
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,24 @@ test-webhooks-python-flask: ## Run webhooks tests against the Python SDK + Flask
docker-compose up --build --detach integration_python_flask_webhooks
npm run test:integration-webhooks || make cleanup-failure
@make cleanup

##
## Ruby
##

# For some reason the Rails server kept failing without the `sleep 5` and we
# ran out of patience to try and figure out why. You can see our frustrated
# attempts to fix it over here: https://github.com/readmeio/metrics-sdks/pull/590
# This is the only thing that works consistently. It's not great but it'll do.

test-metrics-ruby-rails: ## Run Metrics tests against the Ruby SDK + Rails
docker-compose up --build --detach integration_ruby_rails
sleep 5
npm run test:integration-metrics || make cleanup-failure
@make cleanup

test-webhooks-ruby-rails: ## Run webhooks tests against the Ruby SDK + Rails
docker-compose up --build --detach integration_ruby_rails
sleep 5
npm run test:integration-webhooks || make cleanup-failure
@make cleanup
26 changes: 20 additions & 6 deletions __tests__/integration-metrics.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ function isListening(port, attempt = 0) {

socket.once('connect', () => {
socket.destroy();
return resolve();
// Sometimes the TCP connection is resolving before
// the HTTP server is ready to receive connections
// So just sleeping for 500ms to be sure.
return setTimeout(() => resolve(), 500);
});
});
}
Expand Down Expand Up @@ -78,6 +81,8 @@ describe('Metrics SDK Integration Tests', function () {
});

before(async function () {
await isListening(PORT);

server = http
.createServer((req, res) => {
// Frameworks are funny. If we run this test suite with PHP we can access our Metrics
Expand Down Expand Up @@ -109,9 +114,7 @@ describe('Metrics SDK Integration Tests', function () {
sockets.add(socket);
});

await once(server, 'listening');

return isListening(PORT);
return once(server, 'listening');
});

after(function () {
Expand Down Expand Up @@ -162,7 +165,7 @@ describe('Metrics SDK Integration Tests', function () {
/**
* `startedDateTime` should look like the following, with optional microseconds component:
*
* JavaScript: `new Date.toISOString()`
* JavaScript: `new Date().toISOString()`
* - 2022-06-30T10:21:55.394Z
* PHP: `date('Y-m-d\TH:i:sp')`
* - 2022-08-17T19:23:31Z
Expand All @@ -180,7 +183,10 @@ describe('Metrics SDK Integration Tests', function () {
'close',
'keep-alive', // Running this suite with Node 18 the `connection` header is different.
]);
expect(request.headers).to.have.header('host', `localhost:${PORT}`);
expect(request.headers).to.have.header('host', [
`localhost:${PORT}`,
'localhost', // rails does not include the port
]);

expect(response.status).to.equal(200);
expect(response.statusText).to.match(/OK|200/); // Django returns with "200"
Expand Down Expand Up @@ -214,6 +220,10 @@ describe('Metrics SDK Integration Tests', function () {
{ name: 'arr', value: '{"1":"3"}' },
{ name: 'val', value: '1' },
]),
JSON.stringify([
{ name: 'arr', value: { 1: '3' } },
{ name: 'val', value: '1' },
]), // Rails
]);

expect(request.postData).to.be.undefined;
Expand Down Expand Up @@ -251,6 +261,10 @@ describe('Metrics SDK Integration Tests', function () {
{ name: 'arr', value: '{"1":"3"}' },
{ name: 'val', value: '1' },
]),
JSON.stringify([
{ name: 'arr', value: { 1: '3' } },
{ name: 'val', value: '1' },
]), // Rails
]);

expect(request.postData).to.deep.equal({
Expand Down
14 changes: 14 additions & 0 deletions __tests__/integrations/ruby/rails.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM ruby:3.1.2

RUN bundle config --global frozen 1

WORKDIR /usr/src/app

COPY packages/ruby .
RUN bundle install

# Install example dependencies
WORKDIR /usr/src/app/examples/metrics-rails
RUN bundle install

CMD ["bin/rails", "server", "-b", "0.0.0.0", "-p", "8000"]
13 changes: 13 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,16 @@ services:
extra_hosts: *default-extra_hosts
environment:
<<: *server-config

#
# Ruby
#
integration_ruby_rails:
build:
context: .
dockerfile: ./__tests__/integrations/ruby/rails.Dockerfile
ports:
- 8000:8000
extra_hosts: *default-extra_hosts
environment:
<<: *server-config
5 changes: 5 additions & 0 deletions packages/ruby/.rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ AllCops:
# https://docs.rubocop.org/rubocop/cops_layout.html
Layout/LineLength:
Max: 150
Exclude:
- examples/metrics-rails/bin/*

# https://docs.rubocop.org/rubocop/cops_metrics.html
Metrics/AbcSize:
Expand Down Expand Up @@ -65,3 +67,6 @@ Style/RescueStandardError:
# and by only catching `StandardError` we'll miss out on something else and potentially take down
# someones integration.
Enabled: false

Gemspec/RequireMFA:
Enabled: false
1 change: 0 additions & 1 deletion packages/ruby/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@ gem 'rspec', '~> 3.0'
gem 'rubocop', require: false
gem 'rubocop-performance', require: false
gem 'rubocop-rspec', require: false
gem 'uuid'
gem 'webmock'
52 changes: 24 additions & 28 deletions packages/ruby/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,42 +1,40 @@
PATH
remote: .
specs:
readme-metrics (2.0.1)
readme-metrics (2.1.0)
httparty (~> 0.18)
uuid (~> 2.3.8)

GEM
remote: https://rubygems.org/
specs:
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
ast (2.4.1)
ast (2.4.2)
crack (0.4.3)
safe_yaml (~> 1.0.0)
diff-lcs (1.4.4)
hashdiff (1.0.1)
httparty (0.20.0)
mime-types (~> 3.0)
multi_xml (>= 0.5.2)
json (2.6.2)
json-schema (2.8.1)
addressable (>= 2.4)
macaddr (1.7.2)
systemu (~> 2.6.5)
mime-types (3.4.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2022.0105)
multi_xml (0.6.0)
parallel (1.19.2)
parser (2.7.1.4)
parallel (1.22.1)
parser (3.1.2.1)
ast (~> 2.4.1)
public_suffix (4.0.6)
rack (2.2.3.1)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rainbow (3.0.0)
rainbow (3.1.1)
rake (12.3.3)
regexp_parser (1.7.1)
rexml (3.2.4)
regexp_parser (2.5.0)
rexml (3.2.5)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
Expand All @@ -50,27 +48,26 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.9.0)
rspec-support (3.9.3)
rubocop (0.85.1)
rubocop (1.36.0)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 2.7.0.1)
parser (>= 3.1.2.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.7)
rexml
rubocop-ast (>= 0.0.3)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.20.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (0.3.0)
parser (>= 2.7.1.4)
rubocop-performance (1.7.1)
rubocop (>= 0.82.0)
rubocop-rspec (1.41.0)
rubocop (>= 0.68.1)
ruby-progressbar (1.10.1)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.21.0)
parser (>= 3.1.1.0)
rubocop-performance (1.14.3)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
rubocop-rspec (2.12.1)
rubocop (~> 1.31)
ruby-progressbar (1.11.0)
safe_yaml (1.0.5)
systemu (2.6.5)
unicode-display_width (1.7.0)
uuid (2.3.9)
macaddr (~> 1.0)
unicode-display_width (2.2.0)
webmock (3.8.3)
addressable (>= 2.3.6)
crack (>= 0.3.2)
Expand All @@ -88,7 +85,6 @@ DEPENDENCIES
rubocop
rubocop-performance
rubocop-rspec
uuid
webmock

BUNDLED WITH
Expand Down
6 changes: 5 additions & 1 deletion packages/ruby/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.PHONY: help
API_KEY ?= $(shell bash -c 'read -p "ReadMe API Key: " api_key; echo $$api_key')

install: ## Install all dependencies
bundle
Expand All @@ -7,7 +8,10 @@ lint: ## Run code standard checks
bundle exec rubocop

lint-fix: ## Attempt to automatically fix any code standard violations
bundle exec rubocop --auto-correct
bundle exec rubocop --autocorrect

serve-metrics-rails: ## Start the local Express server to test Metrics
README_API_KEY=$(API_KEY) ./examples/metrics-rails/bin/rails server

test: ## Run unit tests
rake spec
Expand Down
7 changes: 7 additions & 0 deletions packages/ruby/examples/metrics-rails/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# See https://git-scm.com/docs/gitattributes for more about git attribute files.

# Mark the database schema as having been generated.
db/schema.rb linguist-generated

# Mark any vendored files as having been vendored.
vendor/* linguist-vendored
20 changes: 20 additions & 0 deletions packages/ruby/examples/metrics-rails/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'

# Ignore bundler config.
/.bundle

# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-*

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*


# Ignore master key for decrypting credentials and more.
/config/master.key
1 change: 1 addition & 0 deletions packages/ruby/examples/metrics-rails/.ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ruby-3.1.2
31 changes: 31 additions & 0 deletions packages/ruby/examples/metrics-rails/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '3.1.2'

# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem 'rails', '~> 7.0.3', '>= 7.0.3.1'

# Use sqlite3 as the database for Active Record
gem 'sqlite3', '~> 1.4'

# Use the Puma web server [https://github.com/puma/puma]
gem 'puma', '~> 5.0'

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]

# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
# gem "rack-cors"

group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem 'debug', platforms: %i[mri mingw x64_mingw]
end

group :development do
# Speed up commands on slow machines / big apps [https://github.com/rails/spring]
# gem "spring"
end

gem 'readme-metrics', path: '../../'
Loading

0 comments on commit 9189727

Please sign in to comment.