diff options
Diffstat (limited to 'lib/feature')
-rw-r--r-- | lib/feature/definition.rb | 35 | ||||
-rw-r--r-- | lib/feature/logger.rb | 9 | ||||
-rw-r--r-- | lib/feature/shared.rb | 16 |
3 files changed, 49 insertions, 11 deletions
diff --git a/lib/feature/definition.rb b/lib/feature/definition.rb index 0ba1bdc4799..8d9b2fa5234 100644 --- a/lib/feature/definition.rb +++ b/lib/feature/definition.rb @@ -13,6 +13,12 @@ class Feature end end + TYPES.each do |type, _| + define_method("#{type}?") do + attributes[:type].to_sym == type + end + end + def initialize(path, opts = {}) @path = path @attributes = {} @@ -65,9 +71,7 @@ class Feature "a valid syntax: #{TYPES.dig(type, :example)}" end - # We accept an array of defaults as some features are undefined - # and have `default_enabled: true/false` - unless Array(default_enabled).include?(default_enabled_in_code) + unless default_enabled_in_code == :yaml || default_enabled == default_enabled_in_code # Raise exception in test and dev raise Feature::InvalidFeatureFlagError, "The `default_enabled:` of `#{key}` is not equal to config: " \ "#{default_enabled_in_code} vs #{default_enabled}. Ensure to update #{path}" @@ -90,12 +94,20 @@ class Feature @definitions ||= load_all! end + def get(key) + definitions[key.to_sym] + end + def reload! @definitions = load_all! end + def has_definition?(key) + definitions.has_key?(key.to_sym) + end + def valid_usage!(key, type:, default_enabled:) - if definition = definitions[key.to_sym] + if definition = get(key) definition.valid_usage!(type_in_code: type, default_enabled_in_code: default_enabled) elsif type_definition = self::TYPES[type] raise InvalidFeatureFlagError, "Missing feature definition for `#{key}`" unless type_definition[:optional] @@ -104,6 +116,17 @@ class Feature end end + def default_enabled?(key) + if definition = get(key) + definition.default_enabled + else + Gitlab::ErrorTracking.track_and_raise_for_dev_exception( + InvalidFeatureFlagError.new("The feature flag YAML definition for '#{key}' does not exist")) + + false + end + end + def register_hot_reloader! # Reload feature flags on change of this file or any `.yml` file_watcher = Rails.configuration.file_watcher.new(reload_files, reload_directories) do @@ -119,10 +142,6 @@ class Feature private def load_all! - # We currently do not load feature flag definitions - # in production environments - return [] unless Gitlab.dev_or_test_env? - paths.each_with_object({}) do |glob_path, definitions| load_all_from_path!(definitions, glob_path) end diff --git a/lib/feature/logger.rb b/lib/feature/logger.rb new file mode 100644 index 00000000000..784a619e182 --- /dev/null +++ b/lib/feature/logger.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class Feature + class Logger < ::Gitlab::JsonLogger + def self.file_name_noext + 'features_json' + end + end +end diff --git a/lib/feature/shared.rb b/lib/feature/shared.rb index 1fcbc8fa173..17dfe26bd82 100644 --- a/lib/feature/shared.rb +++ b/lib/feature/shared.rb @@ -23,7 +23,7 @@ class Feature example: <<-EOS Feature.enabled?(:my_feature_flag, project) Feature.enabled?(:my_feature_flag, project, type: :development) - push_frontend_feature_flag?(:my_feature_flag, project) + push_frontend_feature_flag(:my_feature_flag, project) EOS }, ops: { @@ -33,8 +33,8 @@ class Feature ee_only: false, default_enabled: false, example: <<-EOS - Feature.enabled?(:my_ops_flag, type: ops) - push_frontend_feature_flag?(:my_ops_flag, project, type: :ops) + Feature.enabled?(:my_ops_flag, type: :ops) + push_frontend_feature_flag(:my_ops_flag, project, type: :ops) EOS }, licensed: { @@ -48,6 +48,16 @@ class Feature project.feature_available?(:my_licensed_feature) namespace.feature_available?(:my_licensed_feature) EOS + }, + experiment: { + description: 'Short lived, used specifically to run A/B/n experiments.', + optional: true, + rollout_issue: true, + ee_only: true, + default_enabled: false, + example: <<-EOS + experiment(:my_experiment, project: project, actor: current_user) { ...variant code... } + EOS } }.freeze |