79 lines
2.5 KiB
Ruby
79 lines
2.5 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
|
||
|
end
|
||
|
|
||
|
def environment_scope=(new_environment_scope)
|
||
|
super(new_environment_scope.to_s.strip)
|
||
|
end
|
||
|
end
|