diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-20 17:22:11 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-20 17:22:11 +0300 |
commit | 0c872e02b2c822e3397515ec324051ff540f0cd5 (patch) | |
tree | ce2fb6ce7030e4dad0f4118d21ab6453e5938cdd /app/services/admin/set_feature_flag_service.rb | |
parent | f7e05a6853b12f02911494c4b3fe53d9540d74fc (diff) |
Add latest changes from gitlab-org/gitlab@15-7-stable-eev15.7.0-rc42
Diffstat (limited to 'app/services/admin/set_feature_flag_service.rb')
-rw-r--r-- | app/services/admin/set_feature_flag_service.rb | 145 |
1 files changed, 103 insertions, 42 deletions
diff --git a/app/services/admin/set_feature_flag_service.rb b/app/services/admin/set_feature_flag_service.rb index d72a18a6a58..3378be7eddd 100644 --- a/app/services/admin/set_feature_flag_service.rb +++ b/app/services/admin/set_feature_flag_service.rb @@ -2,82 +2,143 @@ module Admin class SetFeatureFlagService + UnknownOperationError = Class.new(StandardError) + def initialize(feature_flag_name:, params:) @name = feature_flag_name + @target = Feature::Target.new(params) @params = params + @force = params[:force] end def execute - unless params[:force] + unless force error = validate_feature_flag_name return ServiceResponse.error(message: error, reason: :invalid_feature_flag) if error end - flag_target = Feature::Target.new(params) - value = gate_value(params) - - case value - when true - enable!(flag_target) - when false - disable!(flag_target) + if target.gate_specified? + update_targets else - enable_partially!(value, params) + update_global end feature_flag = Feature.get(name) # rubocop:disable Gitlab/AvoidFeatureGet ServiceResponse.success(payload: { feature_flag: feature_flag }) - rescue Feature::Target::UnknowTargetError => e + rescue Feature::InvalidOperation => e + ServiceResponse.error(message: e.message, reason: :illegal_operation) + rescue UnknownOperationError => e + ServiceResponse.error(message: e.message, reason: :illegal_operation) + rescue Feature::Target::UnknownTargetError => e ServiceResponse.error(message: e.message, reason: :actor_not_found) end private - attr_reader :name, :params + attr_reader :name, :params, :target, :force - def enable!(flag_target) - if flag_target.gate_specified? - flag_target.targets.each { |target| Feature.enable(name, target) } - else - Feature.enable(name) + # Note: the if expressions in `update_targets` and `update_global` are order dependant. + def update_targets + target.targets.each do |target| + if enable? + enable(target) + elsif disable? + Feature.disable(name, target) + elsif opt_out? + Feature.opt_out(name, target) + elsif remove_opt_out? + remove_opt_out(target) + else + raise UnknownOperationError, "Cannot set '#{name}' to #{value.inspect} for #{target}" + end end end - def disable!(flag_target) - if flag_target.gate_specified? - flag_target.targets.each { |target| Feature.disable(name, target) } - else + def update_global + if enable? + Feature.enable(name) + elsif disable? Feature.disable(name) + elsif percentage_of_actors? + Feature.enable_percentage_of_actors(name, percentage) + elsif percentage_of_time? + Feature.enable_percentage_of_time(name, percentage) + else + msg = if key.present? + "Cannot set '#{name}' (#{key.inspect}) to #{value.inspect}" + else + "Cannot set '#{name}' to #{value.inspect}" + end + + raise UnknownOperationError, msg end end - def enable_partially!(value, params) - if params[:key] == 'percentage_of_actors' - Feature.enable_percentage_of_actors(name, value) - else - Feature.enable_percentage_of_time(name, value) + def remove_opt_out(target) + raise Feature::InvalidOperation, "No opt-out exists for #{target}" unless Feature.opted_out?(name, target) + + Feature.remove_opt_out(name, target) + end + + def enable(target) + if Feature.opted_out?(name, target) + target_name = target.respond_to?(:to_reference) ? target.to_reference : target.to_s + raise Feature::InvalidOperation, "Opt-out exists for #{target_name} - remove opt-out before enabling" end + + Feature.enable(name, target) end - def validate_feature_flag_name - # overridden in EE + def value + params[:value] end - def gate_value(params) - case params[:value] - when 'true' - true - when '0', 'false' - false - else - # https://github.com/jnunemaker/flipper/blob/master/lib/flipper/typecast.rb#L47 - if params[:value].to_s.include?('.') - params[:value].to_f - else - params[:value].to_i - end - end + def key + params[:key] + end + + def numeric_value? + params[:value].match?(/^\d+(\.\d+)?$/) + end + + def percentage + raise UnknownOperationError, "Not a percentage" unless numeric_value? + + value.to_f + end + + def percentage_of_actors? + key == 'percentage_of_actors' + end + + def percentage_of_time? + return true if key == 'percentage_of_time' + return numeric_value? if key.nil? + + false + end + + # Note: `key` is NOT considered - setting to a percentage to 0 is the same as disabling. + def disable? + value.in?(%w[0 0.0 false]) + end + + # Note: `key` is NOT considered - setting to a percentage to 100 is the same + def enable? + value.in?(%w[100 100.0 true]) + end + + def opt_out? + value == 'opt_out' + end + + def remove_opt_out? + value == 'remove_opt_out' + end + + def validate_feature_flag_name + ## Overridden in EE end end end |