Skip to content

Commit

Permalink
Merge pull request #68 from ResultadosDigitais/ns-releases-migration
Browse files Browse the repository at this point in the history
Adds possibility to migrate feature_keys to resource_keys
  • Loading branch information
henrich-m committed Aug 3, 2020
2 parents bb74c2b + 6d3f2fd commit c8d028c
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/feature_flagger/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class Manager
def self.detached_feature_keys
persisted_features = FeatureFlagger.control.feature_keys
mapped_feature_keys = FeatureFlagger.config.mapped_feature_keys

persisted_features - mapped_feature_keys
end

Expand Down
55 changes: 55 additions & 0 deletions lib/feature_flagger/storage/feature_keys_migration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

module FeatureFlagger
module Storage
class FeatureKeysMigration

def initialize(from_redis, to_control)
@from_redis = from_redis
@to_control = to_control
end

# call migrates features key from the old fashioned to the new
# format.
#
# It must replicate feature keys with changes:
#
# from "avenue:traffic_lights" => 42
# to "avenue:42" => traffic_lights
def call
@from_redis.scan_each(match: "*", count: FeatureFlagger::Storage::Redis::SCAN_EACH_BATCH_SIZE) do |redis_key|
# filter out resource_keys
next if redis_key.start_with?("#{FeatureFlagger::Storage::Redis::RESOURCE_PREFIX}:")

migrate_key(redis_key)
end
end

private

def migrate_key(key)
return migrate_release_to_all(key) if feature_released_to_all?(key)

migrate_release(key)
end

def migrate_release_to_all(key)
features = @from_redis.smembers(key)

features.each do |feature_key|
@to_control.release_to_all(feature_key)
end
end

def feature_released_to_all?(key)
FeatureFlagger::Control::RELEASED_FEATURES == key
end

def migrate_release(key)
resource_ids = @from_redis.smembers(key)

@to_control.release(key, resource_ids)
end
end
end
end
7 changes: 7 additions & 0 deletions lib/feature_flagger/storage/redis.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ def feature_keys
feature_keys
end

def synchronize_feature_and_resource
FeatureFlagger::Storage::FeatureKeysMigration.new(
@redis,
FeatureFlagger.control,
).call
end

private

def resource_key(resource_name, resource_id)
Expand Down
6 changes: 6 additions & 0 deletions lib/tasks/feature_flagger.rake
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ namespace :feature_flagger do
end
end

desc "Synchronizes resource_keys with feature_keys, recommended to apps that installed feature flagger before v.1.2.0"
task :migrate_to_resource_keys => :environment do
storage = FeatureFlagger.config.storage
storage.synchronize_feature_and_resource
end

desc "Release feature to given identifiers, Usage: `$ bundle exec rake feature_flagger:release\[Account,email_marketing:whitelabel,1,2,3,4\]`"
task :release, [:entity_name, :feature_key] => :environment do |_, args|
entity = args.entity_name.constantize
Expand Down
73 changes: 73 additions & 0 deletions spec/feature_flagger/storage/feature_keys_migration_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# frozen_string_literal: true

require 'spec_helper'
require 'feature_flagger/storage/feature_keys_migration'

RSpec.describe FeatureFlagger::Storage::FeatureKeysMigration do
subject(:migrator) { described_class.new(redis, control) }

let(:redis) { FakeRedis::Redis.new }
let(:control) { FeatureFlagger::Control.new(FeatureFlagger::Storage::Redis.new(redis)) }
let(:global_key) { FeatureFlagger::Control::RELEASED_FEATURES }

before do
filepath = File.expand_path('../fixtures/rollout_example.yml', __dir__)
FeatureFlagger.config.yaml_filepath = filepath
end

describe '.call' do
context 'when there are keys in the old format' do
before do
redis.sadd('feature_flagger_dummy_class:email_marketing:behavior_score', 42)
redis.sadd('feature_flagger_dummy_class:email_marketing:whitelabel', 42)
redis.sadd('feature_flagger_dummy_class:email_marketing:whitelabel', 1)
redis.sadd(global_key, 'other_feature_flagger_dummy_class:feature_c:feature_c_1')
redis.sadd(global_key, 'other_feature_flagger_dummy_class:feature_c:feature_c_2')
redis.sadd(global_key, 'account')

migrator.call
end

it 'migrates feature keys to the new format' do
expect(control.released?('feature_flagger_dummy_class:email_marketing:behavior_score', 42)).to be_truthy
expect(control.released?('feature_flagger_dummy_class:email_marketing:whitelabel', 42)).to be_truthy
expect(control.released?('feature_flagger_dummy_class:email_marketing:whitelabel', 1)).to be_truthy
end

it 'migrates all released feature keys to the new format' do
expect(control.released_to_all?('other_feature_flagger_dummy_class:feature_c:feature_c_2')).to be_truthy
expect(control.released_to_all?('other_feature_flagger_dummy_class:feature_c:feature_c_1')).to be_truthy
end
end

context 'when there are keys in both formats' do
before do
redis.sadd('feature_flagger_dummy_class:email_marketing:behavior_score', 42)
redis.sadd('feature_flagger_dummy_class:email_marketing:whitelabel', 42)
redis.sadd('feature_flagger_dummy_class:email_marketing:whitelabel', 1)
redis.sadd(global_key, 'feature_flagger_dummy_class:email_marketing:whitelabel')

control.release('other_feature_flagger_dummy_class:feature_b', 42)
control.release_to_all('other_feature_flagger_dummy_class:feature_c:feature_c_1')

migrator.call
end

it 'migrates feature keys to the new format' do
expect(control.released?('feature_flagger_dummy_class:email_marketing:behavior_score', 42)).to be_truthy
expect(control.released?('feature_flagger_dummy_class:email_marketing:whitelabel', 42)).to be_truthy
expect(control.released?('feature_flagger_dummy_class:email_marketing:whitelabel', 1)).to be_truthy
expect(control.released?('other_feature_flagger_dummy_class:feature_b', 42)).to be_truthy
end

it 'does not migrate internal keys' do
expect(redis.keys.count).to eq(7)
end

it 'migrates all released feature keys to the new format ' do
expect(control.released_to_all?('feature_flagger_dummy_class:email_marketing:whitelabel')).to be_truthy
expect(control.released_to_all?('other_feature_flagger_dummy_class:feature_c:feature_c_1')).to be_truthy
end
end
end
end

0 comments on commit c8d028c

Please sign in to comment.