diff --git a/.gitignore b/.gitignore index e3200e0..4867ff5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,56 +1,12 @@ -*.gem -*.rbc -/.config +/.bundle/ +/.yardoc +/_yardoc/ /coverage/ -/InstalledFiles +/doc/ /pkg/ /spec/reports/ -/spec/examples.txt -/test/tmp/ -/test/version_tmp/ /tmp/ +/.idea/ -# Used by dotenv library to load environment variables. -# .env - -# Ignore Byebug command history file. -.byebug_history - -## Specific to RubyMotion: -.dat* -.repl_history -build/ -*.bridgesupport -build-iPhoneOS/ -build-iPhoneSimulator/ - -## Specific to RubyMotion (use of CocoaPods): -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control -# -# vendor/Pods/ - -## Documentation cache and generated files: -/.yardoc/ -/_yardoc/ -/doc/ -/rdoc/ - -## Environment normalization: -/.bundle/ -/vendor/bundle -/lib/bundler/man/ - -# for a library or gem, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# Gemfile.lock -# .ruby-version -# .ruby-gemset - -# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: -.rvmrc - -# Used by RuboCop. Remote config files pulled in from inherit_from directive. -# .rubocop-https?--* +# rspec failure tracking +.rspec_status \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..e1505f7 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +/.bundle/ +/.yardoc +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ +/.idea/ +.rspec_status diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..a9fee71 --- /dev/null +++ b/.rspec @@ -0,0 +1,3 @@ +--format documentation +--color +--require spec_helper \ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..fd8cf78 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,21 @@ +require: + - rubocop-performance + - rubocop-rspec + - rubocop-rake + - rubocop-thread_safety + +AllCops: + TargetRubyVersion: 3.3.3 + NewCops: enable + +Metrics/MethodLength: + Max: 20 + +Metrics/BlockLength: + Max: 50 + +RSpec/ExampleLength: + Max: 10 + +RSpec/MultipleExpectations: + Max: 2 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0aee3f9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +--- +language: ruby +cache: bundler +rvm: + - 3.3.3 +before_install: gem install bundler -v 2.5.23 \ No newline at end of file diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 0000000..3ab0aa4 --- /dev/null +++ b/Changelog.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] + +- First release of the stoplight-honeybager gem \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..1a84ab7 --- /dev/null +++ b/Gemfile @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +# Specify your gem's dependencies in stoplight-honeybadger.gemspec +gemspec + +gem 'honeybadger', '>= 5.0.0' +gem 'rake', '13.2.1' +gem 'stoplight', '>= 3.0' + +group :test, :development do + gem 'rspec', '~> 3.13.0' + + # Use Rubocop as a ruby static code analyzer + gem 'rubocop', '~> 1.69.2', require: false + gem 'rubocop-performance', '~> 1.23.0', require: false + gem 'rubocop-rake', '~> 0.6.0', require: false + gem 'rubocop-rspec', '~> 3.3.0', require: false + gem 'rubocop-thread_safety', '~> 0.6.0', require: false +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..c1a5f8d --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,89 @@ +PATH + remote: . + specs: + stoplight-honeybadger (1.0.0) + +GEM + remote: https://rubygems.org/ + specs: + ast (2.4.2) + connection_pool (2.4.1) + diff-lcs (1.5.1) + honeybadger (5.26.1) + logger + json (2.9.1) + language_server-protocol (3.17.0.3) + logger (1.6.4) + parallel (1.26.3) + parser (3.3.6.0) + ast (~> 2.4.1) + racc + racc (1.8.1) + rainbow (3.1.1) + rake (13.2.1) + redis (5.3.0) + redis-client (>= 0.22.0) + redis-client (0.23.0) + connection_pool + redlock (1.3.2) + redis (>= 3.0.0, < 6.0) + regexp_parser (2.10.0) + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.2) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.2) + rubocop (1.69.2) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.36.2, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.37.0) + parser (>= 3.3.1.0) + rubocop-performance (1.23.0) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + rubocop-rake (0.6.0) + rubocop (~> 1.0) + rubocop-rspec (3.3.0) + rubocop (~> 1.61) + rubocop-thread_safety (0.6.0) + rubocop (>= 1.48.1) + ruby-progressbar (1.13.0) + stoplight (4.1.0) + redlock (~> 1.0) + unicode-display_width (3.1.3) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + honeybadger (>= 5.0.0) + rake (= 13.2.1) + rspec (~> 3.13.0) + rubocop (~> 1.69.2) + rubocop-performance (~> 1.23.0) + rubocop-rake (~> 0.6.0) + rubocop-rspec (~> 3.3.0) + rubocop-thread_safety (~> 0.6.0) + stoplight (>= 3.0) + stoplight-honeybadger! + +BUNDLED WITH + 2.5.19 diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..4964751 --- /dev/null +++ b/Rakefile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'bundler/gem_tasks' +require 'rspec/core/rake_task' + +RSpec::Core::RakeTask.new(:spec) + +require 'rubocop/rake_task' + +RuboCop::RakeTask.new + +task default: %i[spec rubocop] diff --git a/bin/console b/bin/console new file mode 100644 index 0000000..52b946a --- /dev/null +++ b/bin/console @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +#!/usr/bin/env ruby + +require 'bundler/setup' +require 'stoplight-honeybadger' + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require 'irb' +IRB.start(__FILE__) \ No newline at end of file diff --git a/bin/setup b/bin/setup new file mode 100644 index 0000000..b64275c --- /dev/null +++ b/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here \ No newline at end of file diff --git a/lib/stoplight/honeybadger.rb b/lib/stoplight/honeybadger.rb new file mode 100644 index 0000000..bd435a6 --- /dev/null +++ b/lib/stoplight/honeybadger.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'honeybadger' +require 'stoplight' +require_relative 'honeybadger/version' +require_relative 'honeybadger/notifier' + +# Stoplight module +module Stoplight +end diff --git a/lib/stoplight/honeybadger/notifier.rb b/lib/stoplight/honeybadger/notifier.rb new file mode 100644 index 0000000..9647fb9 --- /dev/null +++ b/lib/stoplight/honeybadger/notifier.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Stoplight + module Honeybadger + # Usage: + # + # notifier = Stoplight::Honeybadger::Notifier.new('api key') + # Stoplight::Light.default_notifiers += [notifier] + # + class Notifier < ::Stoplight::Notifier::Base + DEFAULT_OPTIONS = { + parameters: {}, + session: {}, + context: {} + }.freeze + + # @return [String] + attr_reader :api_key + # @return [Proc] + attr_reader :formatter + # @return [Hash{Symbol => Object}] + attr_reader :options + + # rubocop:disable Lint/MissingSuper + # @param api_key [String] + # @param formatter [Proc, nil] + # @param options [Hash{Symbol => Object}] + # @option options [Hash] :parameters + # @option options [Hash] :session + # @option options [Hash] :context + def initialize(api_key, formatter = nil, options = {}) + @api_key = api_key + @formatter = formatter || Stoplight::Default::FORMATTER + @options = DEFAULT_OPTIONS.merge(options) + end + # rubocop:enable Lint/MissingSuper + + # @param light [Stoplight::Light] + # @param from_color [String] + # @param to_color [String] + # @param error [StandardError] + # @return [String] + def notify(light, from_color, to_color, error) + message = formatter.call(light, from_color, to_color, error) + ::Honeybadger.notify(options.merge( + api_key: api_key, + error_message: message, + backtrace: error&.backtrace + )) + message + end + end + end +end diff --git a/lib/stoplight/honeybadger/version.rb b/lib/stoplight/honeybadger/version.rb new file mode 100644 index 0000000..29e4414 --- /dev/null +++ b/lib/stoplight/honeybadger/version.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Stoplight + module Honeybadger + VERSION = '1.0.0' + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..a4c93d1 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'stoplight/honeybadger' + +RSpec.configure do |config| + # Enable flags like --only-failures and --next-failure + config.example_status_persistence_file_path = '.rspec_status' + + # Disable RSpec exposing methods globally on `Module` and `main` + config.disable_monkey_patching! + + config.expect_with :rspec do |c| + c.syntax = :expect + end +end diff --git a/spec/stoplight/honeybadger/notifier_spec.rb b/spec/stoplight/honeybadger/notifier_spec.rb new file mode 100644 index 0000000..54b55e0 --- /dev/null +++ b/spec/stoplight/honeybadger/notifier_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +require 'securerandom' + +# rubocop:disable RSpec/MultipleMemoizedHelpers +RSpec.describe Stoplight::Honeybadger::Notifier do + it 'is a class' do + expect(described_class).to be_a(Class) + end + + it 'is a subclass of Base' do + expect(described_class).to be < Stoplight::Notifier::Base + end + + describe '#formatter' do + it 'is initially the default' do + expect(described_class.new(nil).formatter).to eql(Stoplight::Default::FORMATTER) + end + + it 'reads the formatter' do + formatter = proc {} + expect(described_class.new(nil, formatter).formatter).to eql(formatter) + end + end + + describe '#options' do + it 'is initially the default' do + expect(described_class.new(nil).options).to eql(Stoplight::Honeybadger::Notifier::DEFAULT_OPTIONS) + end + + it 'reads the options' do + options = { key: :value } + expect(described_class.new(nil, nil, options).options) + .to eql(Stoplight::Honeybadger::Notifier::DEFAULT_OPTIONS.merge(options)) + end + end + + describe '#notify' do + let(:light) { instance_double(Stoplight::Light, name: 'light-name') } + let(:name) { ('a'..'z').to_a.shuffle.join } + let(:code) { -> {} } + let(:from_color) { Stoplight::Color::GREEN } + let(:to_color) { Stoplight::Color::RED } + let(:notifier) { described_class.new(api_key) } + let(:api_key) { ('a'..'z').to_a.shuffle.join } + + before do + allow(Honeybadger).to receive(:notify) + end + + it 'returns the message' do + error = nil + message = notifier.formatter.call(light, from_color, to_color, error) + expect(notifier.notify(light, from_color, to_color, error)).to eql(message) + expect(Honeybadger).to have_received(:notify).with(hash_including(api_key: api_key, error_message: message)) + end + + it 'returns the message with an error' do + error = ZeroDivisionError.new('divided by 0') + message = notifier.formatter.call(light, from_color, to_color, error) + expect(notifier.notify(light, from_color, to_color, error)).to eql(message) + expect(Honeybadger).to have_received(:notify).with( + hash_including(api_key: api_key, error_message: message, backtrace: error.backtrace) + ) + end + end +end +# rubocop:enable RSpec/MultipleMemoizedHelpers diff --git a/spec/stoplight/honeybadger_spec.rb b/spec/stoplight/honeybadger_spec.rb new file mode 100644 index 0000000..c1d11ab --- /dev/null +++ b/spec/stoplight/honeybadger_spec.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +RSpec.describe Stoplight::Honeybadger do + it 'has a version number' do + expect(Stoplight::Honeybadger::VERSION).not_to be_nil + end +end diff --git a/stoplight-honeybadger.gemspec b/stoplight-honeybadger.gemspec new file mode 100644 index 0000000..47488c2 --- /dev/null +++ b/stoplight-honeybadger.gemspec @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require_relative 'lib/stoplight/honeybadger/version' + +Gem::Specification.new do |spec| + spec.name = 'stoplight-honeybadger' + spec.version = '1.0.0' + spec.authors = ['QoQa'] + spec.email = ['dev@qoqa.com'] + + spec.summary = 'Honeybadger notifier for Stoplight' + spec.description = spec.summary + spec.homepage = 'https://github.com/qoqa/stoplight-honeybadger' + spec.license = 'MIT' + spec.required_ruby_version = '>= 3.3.3' + + spec.metadata['homepage_uri'] = spec.homepage + spec.metadata['source_code_uri'] = spec.homepage + spec.metadata['changelog_uri'] = "#{spec.homepage}/releases" + + spec.files = ['LICENSE.txt', 'README.md'] + + Dir.glob(File.join('lib', '**', '*.rb')) + spec.require_paths = ['lib'] + + spec.metadata['rubygems_mfa_required'] = 'true' +end