Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for feature flags #758

Merged
merged 24 commits into from
Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fd37682
Add FeatureFlag class
imjoehaines Nov 14, 2022
e4ca5e0
Add FeatureFlagDelegate class
imjoehaines Nov 14, 2022
9fcc5c8
Add feature flags to events
imjoehaines Nov 15, 2022
92c8950
Add FeatureFlag#valid?
imjoehaines Nov 16, 2022
5e25613
Drop feature flags that are invalid
imjoehaines Nov 16, 2022
21b7edb
Add tests for feature flags in Rack apps
imjoehaines Nov 16, 2022
0e2171a
Test feature flags in Rails 7
imjoehaines Nov 17, 2022
eca29f4
Test feature flags in Rails 6
imjoehaines Nov 17, 2022
9c4506f
Test feature flags in Rails 5
imjoehaines Nov 17, 2022
ff88b6c
Test feature flags in Rails 4
imjoehaines Nov 17, 2022
748f556
Test feature flags in Rails 3
imjoehaines Nov 17, 2022
4562158
Allow storing feature flags in Configuration
imjoehaines Nov 17, 2022
3901a48
Allow FeatureFlagDelegate instances to be dup-ed
imjoehaines Nov 17, 2022
33cc5e6
Include configuration feature flags in events
imjoehaines Nov 17, 2022
9735247
Forward feature flags API calls to configuration
imjoehaines Nov 17, 2022
6abf4ba
Add flags to configuration in Rack tests
imjoehaines Nov 21, 2022
f1f416e
Add global flag in Rack tests
imjoehaines Nov 21, 2022
7ff633c
Add flags to configuration in Rails tests
imjoehaines Nov 21, 2022
d32fe09
Add global flag in Rails tests
imjoehaines Nov 21, 2022
7d943a8
Store feature flags per-request
imjoehaines Nov 22, 2022
b18801e
Refactor feature flag methods into a new module
imjoehaines Nov 23, 2022
08b3be4
Enable YARD's 'embed-mixins' option
imjoehaines Nov 23, 2022
92d2c5c
Remove feature flag API from Configuration
imjoehaines Nov 24, 2022
25784ea
Update changelog
imjoehaines Nov 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .yardopts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
--no-private
--protected
--title "bugsnag-ruby API Documentation"
--embed-mixins
lib/**/*.rb
-
README.md
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changelog
=========

## TBD

### Enhancements

* Add support for feature flags & experiments. For more information, please see https://docs.bugsnag.com/product/features-experiments
| [#758](https://github.com/bugsnag/bugsnag-ruby/pull/758)

## v6.24.2 (21 January 2022)

### Fixes
Expand Down
40 changes: 40 additions & 0 deletions features/fixtures/rack/app/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,36 @@
require 'rack'
require 'json'

$clear_all_flags = false

Bugsnag.configure do |config|
config.api_key = ENV['BUGSNAG_API_KEY']
config.endpoint = ENV['BUGSNAG_ENDPOINT']

if ENV.key?('BUGSNAG_METADATA_FILTERS')
config.meta_data_filters = JSON.parse(ENV['BUGSNAG_METADATA_FILTERS'])
end

config.add_on_error(proc do |event|
event.add_feature_flags([
Bugsnag::FeatureFlag.new('from config 1'),
Bugsnag::FeatureFlag.new('from config 2', 'abc xyz'),
])

event.clear_feature_flag('should be removed!')

if $clear_all_flags
event.clear_feature_flags
end
end)
end

class BugsnagTests
def call(env)
req = Rack::Request.new(env)

$clear_all_flags = !!req.params['clear_all_flags']

case req.env['REQUEST_PATH']
when '/unhandled'
raise 'Unhandled error'
Expand All @@ -24,6 +41,29 @@ def call(env)
rescue StandardError => e
Bugsnag.notify(e)
end
when '/feature-flags/unhandled'
Bugsnag.add_feature_flag('a', '1')

Bugsnag.add_feature_flags([
Bugsnag::FeatureFlag.new('b'),
Bugsnag::FeatureFlag.new('c', '3'),
])

Bugsnag.add_feature_flag('d')
Bugsnag.add_feature_flag('should be removed!')

raise 'Unhandled error'
when '/feature-flags/handled'
Bugsnag.add_feature_flag('x')

Bugsnag.add_feature_flags([
Bugsnag::FeatureFlag.new('y', '1234'),
Bugsnag::FeatureFlag.new('z'),
])

Bugsnag.add_feature_flag('should be removed!')

Bugsnag.notify(RuntimeError.new('oh no'))
end

res = Rack::Response.new
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class FeatureFlagsController < ActionController::Base
protect_from_forgery

before_bugsnag_notify :add_feature_flags

def unhandled
Bugsnag.add_feature_flag('unhandled')

raise 'oh no'
end

def handled
Bugsnag.add_feature_flag('handled')

Bugsnag.notify(RuntimeError.new('ahhh'))

render json: {}
end

private

def add_feature_flags(event)
params['flags'].each do |key, value|
event.add_feature_flag(key, value)
end

if params.key?('clear_all_flags')
event.add_metadata(:clear_all_flags, :a, 1)
else
event.clear_feature_flag('should be removed!')
end
end
end
11 changes: 11 additions & 0 deletions features/fixtures/rails3/app/config/initializers/bugsnag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,15 @@
report.request[:params][:another_thing] = "hi"
end)
end

config.add_on_error(proc do |event|
event.add_feature_flags([
Bugsnag::FeatureFlag.new('from config 1'),
Bugsnag::FeatureFlag.new('from config 2', 'abc xyz'),
])

if event.metadata.key?(:clear_all_flags)
event.clear_feature_flags
end
end)
end
1 change: 1 addition & 0 deletions features/fixtures/rails3/app/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
get "/send_environment/(:action)", controller: 'send_environment'
get "/warden/(:action)", controller: 'warden'
get "/breadcrumbs/(:action)", controller: 'breadcrumbs'
get "/features/(:action)", controller: 'feature_flags'
get "/(:action)", controller: 'application'
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class FeatureFlagsController < ActionController::Base
protect_from_forgery

before_bugsnag_notify :add_feature_flags

def unhandled
Bugsnag.add_feature_flag('unhandled')

raise 'oh no'
end

def handled
Bugsnag.add_feature_flag('handled')

Bugsnag.notify(RuntimeError.new('ahhh'))

render json: {}
end

private

def add_feature_flags(event)
params['flags'].each do |key, value|
event.add_feature_flag(key, value)
end

if params.key?('clear_all_flags')
event.add_metadata(:clear_all_flags, :a, 1)
else
event.clear_feature_flag('should be removed!')
end
end
end
11 changes: 11 additions & 0 deletions features/fixtures/rails4/app/config/initializers/bugsnag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,15 @@
report.request[:params][:another_thing] = "hi"
end)
end

config.add_on_error(proc do |event|
event.add_feature_flags([
Bugsnag::FeatureFlag.new('from config 1'),
Bugsnag::FeatureFlag.new('from config 2', 'abc xyz'),
])

if event.metadata.key?(:clear_all_flags)
event.clear_feature_flags
end
end)
end
1 change: 1 addition & 0 deletions features/fixtures/rails4/app/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@
get "/breadcrumbs/(:action)", controller: 'breadcrumbs'
get "/mongo/(:action)", controller: 'mongo'
get "/active_job/(:action)", controller: 'active_job'
get "/features/(:action)", controller: 'feature_flags'
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class FeatureFlagsController < ActionController::Base
protect_from_forgery

before_bugsnag_notify :add_feature_flags

def unhandled
Bugsnag.add_feature_flag('unhandled')

raise 'oh no'
end

def handled
Bugsnag.add_feature_flag('handled')

Bugsnag.notify(RuntimeError.new('ahhh'))
end

private

def add_feature_flags(event)
params['flags'].each do |key, value|
event.add_feature_flag(key, value)
end

if params.key?('clear_all_flags')
event.add_metadata(:clear_all_flags, :a, 1)
else
event.clear_feature_flag('should be removed!')
end
end
end
11 changes: 11 additions & 0 deletions features/fixtures/rails5/app/config/initializers/bugsnag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,15 @@
report.request[:params][:another_thing] = "hi"
end)
end

config.add_on_error(proc do |event|
event.add_feature_flags([
Bugsnag::FeatureFlag.new('from config 1'),
Bugsnag::FeatureFlag.new('from config 2', 'abc xyz'),
])

if event.metadata.key?(:clear_all_flags)
event.clear_feature_flags
end
end)
end
3 changes: 3 additions & 0 deletions features/fixtures/rails5/app/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,7 @@

get 'active_job/handled', to: 'active_job#handled'
get 'active_job/unhandled', to: 'active_job#unhandled'

get 'features/handled', to: 'feature_flags#handled'
get 'features/unhandled', to: 'feature_flags#unhandled'
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class FeatureFlagsController < ActionController::Base
protect_from_forgery

before_bugsnag_notify :add_feature_flags

def unhandled
Bugsnag.add_feature_flag('unhandled')

raise 'oh no'
end

def handled
Bugsnag.add_feature_flag('handled')

Bugsnag.notify(RuntimeError.new('ahhh'))
end

private

def add_feature_flags(event)
params['flags'].each do |key, value|
event.add_feature_flag(key, value)
end

if params.key?('clear_all_flags')
event.add_metadata(:clear_all_flags, :a, 1)
else
event.clear_feature_flag('should be removed!')
end
end
end
11 changes: 11 additions & 0 deletions features/fixtures/rails6/app/config/initializers/bugsnag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,15 @@
report.request[:params][:another_thing] = "hi"
end)
end

config.add_on_error(proc do |event|
event.add_feature_flags([
Bugsnag::FeatureFlag.new('from config 1'),
Bugsnag::FeatureFlag.new('from config 2', 'abc xyz'),
])

if event.metadata.key?(:clear_all_flags)
event.clear_feature_flags
end
end)
end
3 changes: 3 additions & 0 deletions features/fixtures/rails6/app/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,7 @@

get 'active_job/handled', to: 'active_job#handled'
get 'active_job/unhandled', to: 'active_job#unhandled'

get 'features/handled', to: 'feature_flags#handled'
get 'features/unhandled', to: 'feature_flags#unhandled'
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class FeatureFlagsController < ActionController::Base
protect_from_forgery

before_bugsnag_notify :add_feature_flags

def unhandled
Bugsnag.add_feature_flag('unhandled')

raise 'oh no'
end

def handled
Bugsnag.add_feature_flag('handled')

Bugsnag.notify(RuntimeError.new('ahhh'))
end

private

def add_feature_flags(event)
params['flags'].each do |key, value|
event.add_feature_flag(key, value)
end

if params.key?('clear_all_flags')
event.add_metadata(:clear_all_flags, :a, 1)
else
event.clear_feature_flag('should be removed!')
end
end
end
11 changes: 11 additions & 0 deletions features/fixtures/rails7/app/config/initializers/bugsnag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,15 @@
report.request[:params][:another_thing] = "hi"
end)
end

config.add_on_error(proc do |event|
event.add_feature_flags([
Bugsnag::FeatureFlag.new('from config 1'),
Bugsnag::FeatureFlag.new('from config 2', 'abc xyz'),
])

if event.metadata.key?(:clear_all_flags)
event.clear_feature_flags
end
end)
end
3 changes: 3 additions & 0 deletions features/fixtures/rails7/app/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,7 @@

get 'active_job/handled', to: 'active_job#handled'
get 'active_job/unhandled', to: 'active_job#unhandled'

get 'features/handled', to: 'feature_flags#handled'
get 'features/unhandled', to: 'feature_flags#unhandled'
end
Loading