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

Palanca client #11

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4c8181a
init
budipang Jul 13, 2018
8effa99
fix typo
budipang Jul 13, 2018
0866963
another typo
budipang Jul 13, 2018
275b687
remove rescue to debug
budipang Jul 13, 2018
72fb788
add client for mass toggle
budipang Jul 13, 2018
54c1d5f
missed json parse
budipang Jul 13, 2018
f58ad47
typo
budipang Jul 13, 2018
0cc1d91
fix mass toggle
budipang Jul 13, 2018
b0130d6
retry attempt toggleable
budipang Jul 17, 2018
4413d07
add toggle key
budipang Jul 18, 2018
d8619cd
add memoization to get key
budipang Jul 18, 2018
9c6887d
safe constantizing toggle class
budipang Jul 18, 2018
bdb857c
bug
budipang Jul 18, 2018
aeaf17e
remove conditions, minus spec
budipang Jul 19, 2018
d43a1df
remove unused callback
budipang Jul 19, 2018
cd37d7c
replace env with configuration
budipang Jul 20, 2018
092162a
minus spec
budipang Jul 20, 2018
d59a179
change routes
budipang Jul 22, 2018
30e63a0
update timeout and gemfile restclient
budipang Jul 23, 2018
388471b
fix url and add spec
budipang Jul 23, 2018
6e0189e
standardize for mothership
budipang Jul 27, 2018
888cae8
fix endpoint and lazy update to palanca
budipang Jul 31, 2018
eab1a82
fix bug
budipang Jul 31, 2018
5d4bef7
remove memoization
budipang Aug 2, 2018
f440c4a
fix another bug
budipang Aug 2, 2018
988bd61
add notify
budipang Aug 27, 2018
08c973e
add notify to toggle base
budipang Aug 28, 2018
25ce76b
Revert "add notify to toggle base"
budipang Aug 28, 2018
40c010f
Revert "add notify"
budipang Aug 28, 2018
9658f30
bump version and remove unnesccesary check
budipang Aug 28, 2018
e50023c
change notify to email and adjust things
budipang Sep 2, 2018
f8112d9
add info on notifying initializtion key
budipang Sep 2, 2018
a703c5c
typo
budipang Sep 2, 2018
5eb2ec9
fix possibly race condidtion
budipang Sep 7, 2018
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
29 changes: 28 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
PATH
remote: .
specs:
toggleable (0.1.5)
toggleable (0.1.7)
activesupport (>= 4.0.0)
json
rest-client (= 1.8.0)

GEM
remote: https://rubygems.org/
Expand All @@ -12,27 +14,43 @@ GEM
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
ast (2.4.0)
codecov (0.1.10)
json
simplecov
url
concurrent-ruby (1.0.5)
crack (0.4.3)
safe_yaml (~> 1.0.0)
diff-lcs (1.3)
docile (1.3.1)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.4.0)
hashdiff (0.3.7)
http-cookie (1.0.3)
domain_name (~> 0.5)
i18n (1.0.1)
concurrent-ruby (~> 1.0)
jaro_winkler (1.5.1)
json (2.1.0)
mime-types (2.99.3)
minitest (5.11.3)
netrc (0.11.0)
parallel (1.12.1)
parser (2.5.1.0)
ast (~> 2.4.0)
powerpack (0.1.2)
public_suffix (3.0.2)
rainbow (3.0.0)
rake (10.5.0)
redis (3.3.5)
rest-client (1.8.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 3.0)
netrc (~> 0.7)
rspec (3.7.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
Expand All @@ -57,6 +75,7 @@ GEM
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.9.0)
safe_yaml (1.0.4)
simplecov (0.16.1)
docile (~> 1.1)
json (>= 1.8, < 3)
Expand All @@ -65,8 +84,15 @@ GEM
thread_safe (0.3.6)
tzinfo (1.2.5)
thread_safe (~> 0.1)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.5)
unicode-display_width (1.4.0)
url (0.3.2)
webmock (3.4.2)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff

PLATFORMS
ruby
Expand All @@ -82,6 +108,7 @@ DEPENDENCIES
rubocop
simplecov (>= 0.16.1)
toggleable!
webmock

BUNDLED WITH
1.16.1
25 changes: 11 additions & 14 deletions lib/toggleable/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,21 @@ module Base
# it will generate these methods into included class.
module ClassMethods
def active?
return to_bool(toggle_active.to_s) unless toggle_active.nil?
toggle_status = toggle_active
return toggle_status.to_s == 'true' unless toggle_status.nil?

# Lazily register the key
Toggleable.configuration.storage.set_if_not_exist(key, DEFAULT_VALUE, namespace: Toggleable.configuration.namespace)
set_status = Toggleable.configuration.storage.set_if_not_exist(key, DEFAULT_VALUE, namespace: Toggleable.configuration.namespace)
Toggleable::FeatureToggler.instance.toggle_key(key, DEFAULT_VALUE, actor: 'key initialization') if Toggleable.configuration.enable_palanca && set_status
DEFAULT_VALUE
end

def activate!(actor: nil)
toggle_key(true, actor)
def activate!(actor: nil, email: nil)
toggle_key(true, actor, email)
end

def deactivate!(actor: nil)
toggle_key(false, actor)
def deactivate!(actor: nil, email: nil)
toggle_key(false, actor, email)
end

def key
Expand All @@ -48,9 +50,10 @@ def process

private

def toggle_key(value, actor)
Toggleable.configuration.logger&.log(key: key, value: value, actor: actor)
def toggle_key(value, actor, email)
Toggleable.configuration.storage.set(key, value, namespace: Toggleable.configuration.namespace)
Toggleable.configuration.logger&.log(key: key, value: value, actor: actor)
Toggleable::FeatureToggler.instance.toggle_key(key, value, actor: (email || actor)) if Toggleable.configuration.enable_palanca
end

def toggle_active
Expand All @@ -62,12 +65,6 @@ def toggle_active
def read_expired?
@_last_read_at < Time.now.localtime - Toggleable.configuration.expiration_time
end

def to_bool(value)
return true if value =~ /^(true|t|yes|y|1)$/i
return false if value.empty? || value =~ /^(false|f|no|n|0)$/i
raise ArgumentError, "invalid value for Boolean: \"#{value}\""
end
end
end
end
4 changes: 4 additions & 0 deletions lib/toggleable/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ module Toggleable
# Toggleable::Configuration yields the configuration of toggleable.
class Configuration
attr_accessor :expiration_time ## expiration time for memoization. default: 5 minutes
attr_accessor :enable_palanca ## enable palanca api call
attr_accessor :palanca_host
attr_accessor :palanca_user
attr_accessor :palanca_password
attr_accessor :storage ## storage used. default: memory store
attr_accessor :namespace ## required for prefixing the keys. default: `toggleable``
attr_accessor :logger ## optional, it will not log if not configured.
Expand Down
84 changes: 79 additions & 5 deletions lib/toggleable/feature_toggler.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# frozen_string_literal: true

require 'singleton'
require 'rest-client'
require 'active_support/inflector'
require 'json'

module Toggleable
# Toggleable::FeatureToggler provides an instance to manage all toggleable keys.
class FeatureToggler
include Singleton

MAX_ATTEMPT = 3

attr_reader :features

def initialize
Expand All @@ -17,14 +22,81 @@ def register(key)
features << key
end

def get_key(key)
@_toggle_active ||= {}
@_last_key_read_at ||= {}
return @_toggle_active[key] if !@_toggle_active[key].nil? && !read_key_expired?(key)

@_last_key_read_at[key] = Time.now.localtime
response = ''
attempt = 1
url = "#{Toggleable.configuration.palanca_host}/_internal/toggle-features?feature=#{key}"
resource = RestClient::Resource.new(url, Toggleable.configuration.palanca_user, Toggleable.configuration.palanca_password)

while response.empty?
begin
response = resource.get timeout: 2, open_timeout: 1
response = ::JSON.parse(response)
@_toggle_active[key] = response['data']['status']
rescue StandardError => e
if attempt >= MAX_ATTEMPT
Toggleable.configuration.logger.error(message: "GET #{key} TIMEOUT")
raise e
end
attempt += 1
end
end
@_toggle_active[key]
end

def toggle_key(key, value, actor: nil)
response = ''
attempt = 1
url = "#{Toggleable.configuration.palanca_host}/_internal/toggle-features"
payload = { feature: key, status: value, user_id: actor }.to_json
resource = RestClient::Resource.new(url, Toggleable.configuration.palanca_user, Toggleable.configuration.palanca_password)

while response.empty?
begin
response = resource.put payload, timeout: 2, open_timeout: 1
rescue StandardError => e
if attempt >= MAX_ATTEMPT
Toggleable.configuration.logger.error(message: "TOGGLE #{key} TIMEOUT")
raise e
end
attempt += 1
end
end
end

def available_features(memoize: Toggleable.configuration.use_memoization)
available_features = memoize ? memoized_keys : keys
available_features.slice(*features)
end

def mass_toggle!(mapping, actor: nil)
def mass_toggle!(mapping, actor:, email:)
log_changes(mapping, actor) if Toggleable.configuration.logger
Toggleable.configuration.storage.mass_set(mapping, namespace: Toggleable.configuration.namespace)
mass_set_palanca!(mapping, actor: email) if Toggleable.configuration.enable_palanca
end

def mass_set_palanca!(mapping, actor:)
response = ''
attempt = 1
url = "#{Toggleable.configuration.palanca_host}/_internal/toggle-features/bulk-update"
payload = { mappings: mapping, user_id: actor }.to_json
resource = RestClient::Resource.new(url, Toggleable.configuration.palanca_user, Toggleable.configuration.palanca_password)
while response.empty?
begin
response = resource.post payload, timeout: 2, open_timeout: 1
rescue StandardError => e
if attempt >= MAX_ATTEMPT
Toggleable.configuration.logger.error(message: 'MASS TOGGLE TIMEOUT')
raise e
end
attempt += 1
end
end
end

private
Expand All @@ -34,19 +106,21 @@ def keys
end

def memoized_keys
return @_memoized_keys if defined?(@_memoized_keys) && !read_expired?
return @_memoized_keys if defined?(@_memoized_keys) && !read_all_keys_expired?
@_last_read_at = Time.now.localtime
@_memoized_keys = Toggleable.configuration.storage.get_all(namespace: Toggleable.configuration.namespace)
end

def read_expired?
def read_all_keys_expired?
@_last_read_at < Time.now.localtime - Toggleable.configuration.expiration_time
end

def read_key_expired?(key)
@_last_key_read_at[key] < Time.now.localtime - Toggleable.configuration.expiration_time
end

def log_changes(mapping, actor)
previous_values = available_features
mapping.each do |key, val|
next if previous_values[key].to_s == val.to_s
Toggleable.configuration.logger.log(key: key, value: val, actor: actor)
end
end
Expand Down
10 changes: 5 additions & 5 deletions lib/toggleable/storage/memory_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ class MemoryStore < ActiveSupport::Cache::MemoryStore
## the self you provide must implement these methods
## namespace parameter is optional, only if you provide namespace configuration

def get(key, namespace:)
def get(key, namespace: nil)
read(key, namespace: namespace)
end

def get_all(namespace:)
def get_all(namespace: nil)
read_multi(*keys, namespace: namespace)
end

def set(key, value, namespace:)
def set(key, value, namespace: nil)
write(key, value, namespace: namespace)
end

def set_if_not_exist(key, value, namespace:)
def set_if_not_exist(key, value, namespace: nil)
fetch(key, namespace: namespace) do
value
end
end

def mass_set(mappings, namespace:)
def mass_set(mappings, namespace: nil)
write_multi(mappings, namespace: namespace)
end

Expand Down
2 changes: 1 addition & 1 deletion lib/toggleable/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Toggleable
VERSION = '0.1.5'
VERSION = '0.1.9'
end
3 changes: 3 additions & 0 deletions spec/class_initializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ def log(key:, value:, actor:)

Toggleable.configure do |t|
t.logger = logger
t.palanca_host = 'localhost:8027'
t.use_memoization = false
t.enable_palanca = true
end
8 changes: 2 additions & 6 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@

SimpleCov.start do
add_filter '/spec/'
end

require 'codecov'

SimpleCov.start do
add_filter '/spec/'
add_filter '/lib/toggleable/storage'
end

require 'codecov'
Expand All @@ -22,4 +17,5 @@
SimpleCov::Formatter::HTMLFormatter
end

require 'webmock/rspec'
require 'class_initializer'
Loading