gitlab-org--gitlab-foss/app/models/concerns/has_environment_scope.rb

87 lines
2.7 KiB
Ruby

# frozen_string_literal: true
module HasEnvironmentScope
extend ActiveSupport::Concern
prepended do
validates(
:environment_scope,
presence: true,
format: { with: ::Gitlab::Regex.environment_scope_regex,
message: ::Gitlab::Regex.environment_scope_regex_message }
)
##
# Select rows which have a scope that matches the given environment name.
# Rows are ordered by relevance, by default. The most relevant row is
# placed at the end of a list.
#
# options:
# - relevant_only: (boolean)
# You can get the most relevant row only. Other rows are not be
# selected even if its scope matches the environment name.
# This is equivalent to using `#last` from SQL standpoint.
#
scope :on_environment, -> (environment_name, relevant_only: false) do
order_direction = relevant_only ? 'DESC' : 'ASC'
where = <<~SQL
environment_scope IN (:wildcard, :environment_name) OR
:environment_name LIKE
#{::Gitlab::SQL::Glob.to_like('environment_scope')}
SQL
order = <<~SQL
CASE environment_scope
WHEN :wildcard THEN 0
WHEN :environment_name THEN 2
ELSE 1
END #{order_direction}
SQL
values = {
wildcard: '*',
environment_name: environment_name
}
sanitized_order_sql = sanitize_sql_array([order, values])
# The query is trying to find variables with scopes matching the
# current environment name. Suppose the environment name is
# 'review/app', and we have variables with environment scopes like:
# * variable A: review
# * variable B: review/app
# * variable C: review/*
# * variable D: *
# And the query should find variable B, C, and D, because it would
# try to convert the scope into a LIKE pattern for each variable:
# * A: review
# * B: review/app
# * C: review/%
# * D: %
# Note that we'll match % and _ literally therefore we'll escape them.
# In this case, B, C, and D would match. We also want to prioritize
# the exact matched name, and put * last, and everything else in the
# middle. So the order should be: D < C < B
relation = where(where, values)
.order(Arel.sql(sanitized_order_sql)) # `order` cannot escape for us!
relation = relation.limit(1) if relevant_only
relation
end
scope :for_environment, ->(environment) do
if environment
on_environment(environment)
else
where(environment_scope: '*')
end
end
end
def environment_scope=(new_environment_scope)
super(new_environment_scope.to_s.strip)
end
end