mirror of
https://github.com/discourse/discourse.git
synced 2025-01-18 17:12:45 +08:00
3e54351355
Why this change? The current shape of errors returns the error messages after it has been translated but there are cases where we want to customize the error messages and the current way return only translated error messages is making customization of error messages difficult. If we wish to have the error messages in complete sentences like "`some_property` property must be present in #link 1", this is not possible at the moment with the current shape of the errors we return. What does this change do? This change introduces the `ThemeSettingsObjectValidator::ThemeSettingsObjectErrors` and `ThemeSettingsObjectValidator::ThemeSettingsObjectError` classes to hold the relevant error key and i18n translation options.
186 lines
4.7 KiB
Ruby
186 lines
4.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class ThemeSettingsObjectValidator
|
|
class ThemeSettingsObjectErrors
|
|
def initialize
|
|
@errors = []
|
|
end
|
|
|
|
def add_error(key, i18n_opts = {})
|
|
@errors << ThemeSettingsObjectError.new(key, i18n_opts)
|
|
end
|
|
|
|
def full_messages
|
|
@errors.map(&:error_message)
|
|
end
|
|
end
|
|
|
|
class ThemeSettingsObjectError
|
|
def initialize(key, i18n_opts = {})
|
|
@key = key
|
|
@i18n_opts = i18n_opts
|
|
end
|
|
|
|
def error_message
|
|
I18n.t("themes.settings_errors.objects.#{@key}", @i18n_opts)
|
|
end
|
|
end
|
|
|
|
def initialize(schema:, object:, valid_category_ids: nil)
|
|
@object = object
|
|
@schema_name = schema[:name]
|
|
@properties = schema[:properties]
|
|
@errors = {}
|
|
@valid_category_ids = valid_category_ids
|
|
end
|
|
|
|
def validate
|
|
validate_properties
|
|
|
|
@properties.each do |property_name, property_attributes|
|
|
if property_attributes[:type] == "objects"
|
|
@object[property_name]&.each do |child_object|
|
|
@errors[property_name] ||= []
|
|
|
|
@errors[property_name].push(
|
|
self
|
|
.class
|
|
.new(schema: property_attributes[:schema], object: child_object, valid_category_ids:)
|
|
.validate,
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
@errors
|
|
end
|
|
|
|
private
|
|
|
|
def validate_properties
|
|
@properties.each do |property_name, property_attributes|
|
|
next if property_attributes[:type] == "objects"
|
|
next if property_attributes[:required] && !is_property_present?(property_name)
|
|
next if !has_valid_property_value_type?(property_attributes, property_name)
|
|
next if !has_valid_property_value?(property_attributes, property_name)
|
|
end
|
|
end
|
|
|
|
def has_valid_property_value_type?(property_attributes, property_name)
|
|
value = @object[property_name]
|
|
type = property_attributes[:type]
|
|
|
|
return true if (value.nil? && type != "enum")
|
|
|
|
is_value_valid =
|
|
case type
|
|
when "string"
|
|
value.is_a?(String)
|
|
when "integer", "category"
|
|
value.is_a?(Integer)
|
|
when "float"
|
|
value.is_a?(Float) || value.is_a?(Integer)
|
|
when "boolean"
|
|
[true, false].include?(value)
|
|
when "enum"
|
|
property_attributes[:choices].include?(value)
|
|
else
|
|
add_error(property_name, :invalid_type, type:)
|
|
return false
|
|
end
|
|
|
|
if is_value_valid
|
|
true
|
|
else
|
|
add_error(property_name, "not_valid_#{type}_value", property_attributes)
|
|
false
|
|
end
|
|
end
|
|
|
|
def has_valid_property_value?(property_attributes, property_name)
|
|
validations = property_attributes[:validations]
|
|
type = property_attributes[:type]
|
|
value = @object[property_name]
|
|
|
|
case type
|
|
when "category"
|
|
if !valid_category_ids.include?(value)
|
|
add_error(property_name, :not_valid_category_value)
|
|
return false
|
|
end
|
|
when "string"
|
|
if (min = validations&.dig(:min_length)) && value.length < min
|
|
add_error(property_name, :string_value_not_valid_min, min:)
|
|
return false
|
|
end
|
|
|
|
if (max = validations&.dig(:max_length)) && value.length > max
|
|
add_error(property_name, :string_value_not_valid_max, max:)
|
|
return false
|
|
end
|
|
|
|
if validations&.dig(:url) && !value.match?(URI.regexp)
|
|
add_error(property_name, :string_value_not_valid_url)
|
|
return false
|
|
end
|
|
when "integer", "float"
|
|
if (min = validations&.dig(:min)) && value < min
|
|
add_error(property_name, :number_value_not_valid_min, min:)
|
|
return false
|
|
end
|
|
|
|
if (max = validations&.dig(:max)) && value > max
|
|
add_error(property_name, :number_value_not_valid_max, max:)
|
|
return false
|
|
end
|
|
end
|
|
|
|
true
|
|
end
|
|
|
|
def is_property_present?(property_name)
|
|
if @object[property_name].nil?
|
|
add_error(property_name, :required)
|
|
false
|
|
else
|
|
true
|
|
end
|
|
end
|
|
|
|
def add_error(property_name, key, i18n_opts = {})
|
|
@errors[property_name] ||= ThemeSettingsObjectErrors.new
|
|
@errors[property_name].add_error(key, i18n_opts)
|
|
end
|
|
|
|
def valid_category_ids
|
|
@valid_category_ids ||=
|
|
Set.new(
|
|
Category.where(id: fetch_property_values_of_type(@properties, @object, "category")).pluck(
|
|
:id,
|
|
),
|
|
)
|
|
end
|
|
|
|
def fetch_property_values_of_type(properties, object, type)
|
|
values = Set.new
|
|
|
|
properties.each do |property_name, property_attributes|
|
|
if property_attributes[:type] == type
|
|
values << object[property_name]
|
|
elsif property_attributes[:type] == "objects"
|
|
object[property_name]&.each do |child_object|
|
|
values.merge(
|
|
fetch_property_values_of_type(
|
|
property_attributes[:schema][:properties],
|
|
child_object,
|
|
type,
|
|
),
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
values
|
|
end
|
|
end
|