diff options
Diffstat (limited to 'app/models/operations/feature_flag.rb')
-rw-r--r-- | app/models/operations/feature_flag.rb | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/app/models/operations/feature_flag.rb b/app/models/operations/feature_flag.rb new file mode 100644 index 00000000000..586e9d689a1 --- /dev/null +++ b/app/models/operations/feature_flag.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +module Operations + class FeatureFlag < ApplicationRecord + include AtomicInternalId + include IidRoutes + + self.table_name = 'operations_feature_flags' + + belongs_to :project + + has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.operations_feature_flags&.maximum(:iid) } + + default_value_for :active, true + + # scopes exists only for the first version + has_many :scopes, class_name: 'Operations::FeatureFlagScope' + # strategies exists only for the second version + has_many :strategies, class_name: 'Operations::FeatureFlags::Strategy' + has_many :feature_flag_issues + has_many :issues, through: :feature_flag_issues + has_one :default_scope, -> { where(environment_scope: '*') }, class_name: 'Operations::FeatureFlagScope' + + validates :project, presence: true + validates :name, + presence: true, + length: 2..63, + format: { + with: Gitlab::Regex.feature_flag_regex, + message: Gitlab::Regex.feature_flag_regex_message + } + validates :name, uniqueness: { scope: :project_id } + validates :description, allow_blank: true, length: 0..255 + validate :first_default_scope, on: :create, if: :has_scopes? + validate :version_associations + + before_create :build_default_scope, if: -> { legacy_flag? && scopes.none? } + + accepts_nested_attributes_for :scopes, allow_destroy: true + accepts_nested_attributes_for :strategies, allow_destroy: true + + scope :ordered, -> { order(:name) } + + scope :enabled, -> { where(active: true) } + scope :disabled, -> { where(active: false) } + + enum version: { + legacy_flag: 1, + new_version_flag: 2 + } + + class << self + def preload_relations + preload(:scopes, strategies: :scopes) + end + + def for_unleash_client(project, environment) + includes(strategies: [:scopes, :user_list]) + .where(project: project) + .merge(Operations::FeatureFlags::Scope.on_environment(environment)) + .reorder(:id) + .references(:operations_scopes) + end + end + + def related_issues(current_user, preload:) + issues = ::Issue + .select('issues.*, operations_feature_flags_issues.id AS link_id') + .joins(:feature_flag_issues) + .where('operations_feature_flags_issues.feature_flag_id = ?', id) + .order('operations_feature_flags_issues.id ASC') + .includes(preload) + + Ability.issues_readable_by_user(issues, current_user) + end + + private + + def version_associations + if new_version_flag? && scopes.any? + errors.add(:version_associations, 'version 2 feature flags may not have scopes') + elsif legacy_flag? && strategies.any? + errors.add(:version_associations, 'version 1 feature flags may not have strategies') + end + end + + def first_default_scope + unless scopes.first.environment_scope == '*' + errors.add(:default_scope, 'has to be the first element') + end + end + + def build_default_scope + scopes.build(environment_scope: '*', active: self.active) + end + + def has_scopes? + scopes.any? + end + end +end |