debian-mirror-gitlab/app/services/admin/set_feature_flag_service.rb
2023-03-05 14:24:40 +05:30

146 lines
3.7 KiB
Ruby

# frozen_string_literal: true
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 force
error = validate_feature_flag_name
return ServiceResponse.error(message: error, reason: :invalid_feature_flag) if error
end
if target.gate_specified?
update_targets
else
update_global
end
feature_flag = Feature.get(name) # rubocop:disable Gitlab/AvoidFeatureGet
ServiceResponse.success(payload: { feature_flag: feature_flag })
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, :target, :force
# 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 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 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 value
params[:value]
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
Admin::SetFeatureFlagService.prepend_mod