Skip to content

Commit

Permalink
Allow passing a list of custom strategy classes when instantiating th…
Browse files Browse the repository at this point in the history
…e client
  • Loading branch information
rarruda committed Oct 2, 2021
1 parent b777c56 commit 442a5cd
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 3 deletions.
25 changes: 25 additions & 0 deletions examples/simple.rb
Expand Up @@ -3,6 +3,29 @@
require 'unleash'
require 'unleash/context'



class StrategyCompanyId
PARAM = 'companyIds'.freeze

def name
'companyId'
end

# need: params['companyIds'], context.properties['companyId'],
def is_enabled?(params = {}, context = nil)
return false unless params.is_a?(Hash) && params.has_key?(PARAM)
return false unless params.fetch(PARAM, nil).is_a? String
return false unless context.class.name == 'Unleash::Context'

# Custom strategy code goes here...
company_id_from_context = context&.properties&.values_at('companyId', :companyId).compact.first
params[PARAM].split(",").map(&:strip).include?(company_id_from_context)
end
end

klasses = [StrategyCompanyId, nil]

puts ">> START simple.rb"

# Unleash.configure do |config|
Expand All @@ -26,6 +49,8 @@
metrics_interval: 2,
retry_limit: 2,
custom_http_headers: {'Authorization': '943ca9171e2c884c545c5d82417a655fb77cec970cc3b78a8ff87f4406b495d0'},
log_level: Logger::DEBUG,
custom_strategies: klasses
)

# feature_name = "AwesomeFeature"
Expand Down
3 changes: 2 additions & 1 deletion lib/unleash/client.rb
Expand Up @@ -114,7 +114,8 @@ def info
'appName': Unleash.configuration.app_name,
'instanceId': Unleash.configuration.instance_id,
'sdkVersion': "unleash-client-ruby:" + Unleash::VERSION,
'strategies': Unleash::STRATEGIES.keys,
# 'strategies': Unleash::STRATEGIES.keys,
'strategies': Unleash.configuration.strategies.keys,
'started': Time.now.iso8601(Unleash::TIME_RESOLUTION),
'interval': Unleash.configuration.metrics_interval_in_millis
}
Expand Down
47 changes: 46 additions & 1 deletion lib/unleash/configuration.rb
Expand Up @@ -18,7 +18,8 @@ class Configuration
:metrics_interval,
:backup_file,
:logger,
:log_level
:log_level,
:custom_strategies

def initialize(opts = {})
ensure_valid_opts(opts)
Expand All @@ -39,6 +40,7 @@ def validate!

raise ArgumentError, "URL and app_name are required parameters." if self.app_name.nil? || self.url.nil?
raise ArgumentError, "custom_http_headers must be a hash." unless self.custom_http_headers.is_a?(Hash)
raise ArgumentError, "custom_strategies must be an array." unless self.custom_strategies.is_a?(Array)
end

def refresh_backup_file!
Expand Down Expand Up @@ -66,12 +68,54 @@ def client_register_url
"#{self.url}/client/register"
end

# dynamically configured, should be cached!
def strategies
puts STRATEGIES
puts "Unleash.configuration.custom_strategies: #{Unleash.configuration.custom_strategies}"

cstats = Unleash.configuration.custom_strategies
.flatten
.compact
.map do |klass|
class_name_downcased = downcase_class_name(klass)
[class_name_downcased.to_sym, klass.new]
end
.to_h

# .map do |c|
# puts "c: #{c}"
# lowered_c = c.name.tap{ |c| c[0] = c[0].downcase }
# lowered_c[0] = lowered_c[0].downcase
# [lowered_c.to_sym, c.new]
# end
# .to_h

puts cstats
STRATEGIES.merge!(cstats)
end

private

# attr_accessor :strategies

def downcase_class_name(klass)
klass.name.map do |c|
c
.each_with_index
.map{ |c, i| (i == 0) ? c.downcase : c }
.join("")
end
end

def ensure_valid_opts(opts)
unless opts[:custom_http_headers].is_a?(Hash) || opts[:custom_http_headers].nil?
raise ArgumentError, "custom_http_headers must be a hash."
end

unless opts[:custom_strategies].is_a?(Array) && opts[:custom_strategies].each { |k| k&.method_defined? 'is_enabled?' } || opts[:custom_strategies].nil?
puts "a: #{opts[:custom_strategies]}"
raise ArgumentError, "custom_strategies must be an Arry of classes that respond to is_enabled? method."
end
end

def set_defaults
Expand All @@ -90,6 +134,7 @@ def set_defaults
self.log_level = Logger::WARN

self.custom_http_headers = {}
self.custom_strategies = []
end

def initialize_default_logger
Expand Down
7 changes: 6 additions & 1 deletion lib/unleash/feature_toggle.rb
Expand Up @@ -14,6 +14,10 @@ def initialize(params = {})

self.name = params.fetch('name', nil)
self.enabled = params.fetch('enabled', false)
self.strategies = params.fetch('strategies', [])
.select{ |s| s.has_key?('name') && Unleash.configuration.strategies.has_key?(s['name'].to_sym) }
.map{ |s| ActivationStrategy.new(s['name'], s['parameters'] || {}) } || []
# .select{ |s| s.has_key?('name') && Unleash::STRATEGIES.has_key?(s['name'].to_sym) }

self.strategies = initialize_strategies(params)
self.variant_definitions = initialize_variant_definitions(params)
Expand Down Expand Up @@ -72,7 +76,8 @@ def am_enabled?(context, default_result)
end

def strategy_enabled?(strategy, context)
r = Unleash::STRATEGIES.fetch(strategy.name.to_sym, :unknown).is_enabled?(strategy.params, context)
# r = Unleash::STRATEGIES.fetch(strategy.name.to_sym, :unknown).is_enabled?(strategy.params, context)
r = Unleash.configuration.strategies.fetch(strategy.name.to_sym, :unknown).is_enabled?(strategy.params, context)
Unleash.logger.debug "Unleash::FeatureToggle.strategy_enabled? Strategy #{strategy.name} returned #{r} with context: #{context}"
r
end
Expand Down
13 changes: 13 additions & 0 deletions spec/unleash/client_spec.rb
Expand Up @@ -76,6 +76,16 @@
.with(headers: { 'X-API-KEY': '123', 'Content-Type': 'application/json' })
.with(headers: { 'UNLEASH-APPNAME': 'my-test-app' })
.with(headers: { 'UNLEASH-INSTANCEID': 'rspec/test' })
.with(body: {
'appName': 'my-test-app',
'instanceId': 'rspec/test',
'bucket': {
'start': /.*/, #start.iso8601(Unleash::TIME_RESOLUTION),
'stop': /.*/, #stop.iso8601(Unleash::TIME_RESOLUTION),
'toggles': {} #Unleash.toggle_metrics.features
}
}
)
).to have_been_made.once
end

Expand Down Expand Up @@ -352,6 +362,9 @@
unleash_client.is_enabled?('any_feature', {}, true)
).to eq(true)

expect(WebMock).not_to have_requested(:get, /.*/)
expect(WebMock).not_to have_requested(:post, /.*/)

expect(WebMock).not_to have_requested(:get, 'http://test-url/')
expect(WebMock).not_to have_requested(:get, 'http://test-url//client/features')
expect(WebMock).not_to have_requested(:post, 'http://test-url//client/features')
Expand Down
1 change: 1 addition & 0 deletions spec/unleash/client_specification_spec.rb
Expand Up @@ -66,6 +66,7 @@
else
xit "Skipped client-specification tests. #{SPECIFICATION_PATH} not found." do
# If you want to run the client-specification tests locally, run from the root path of the repo:
# Add "--branch v3.2.0" if you want to test against a specific versioned release of the spec.
# git clone --depth 5 https://github.com/Unleash/client-specification.git client-specification
end
end
Expand Down

0 comments on commit 442a5cd

Please sign in to comment.