discourse/lib/theme_settings_validator.rb
Alan Guo Xiang Tan 7bcfe60a76
DEV: Validate default value for type: objects theme settings (#25833)
Why this change?

This change adds validation for the default value for `type: objects` theme
settings when a setting theme field is uploaded. This helps the theme
author to ensure that the objects which they specifc in the default
value adhere to the schema which they have declared.

When an error is encountered in one of the objects, the error
message will look something like:

`"The property at JSON Pointer '/0/title' must be at least 5 characters
long."`

We use a JSON Pointer to reference the property in the object which is
something most json-schema validator uses as well.

What does this change do?

1. This commit once again changes the shape of hash returned by
   `ThemeSettingsObjectValidator.validate`. Instead of using the
   property name as the key previously, we have decided to avoid
   multiple levels of nesting and instead use a JSON Pointer as the key
   which helps to simplify the implementation.

2 Introduces `ThemeSettingsObjectValidator.validate_objects` which
  returns an array of validation error messages for all the objects
  passed to the method.
2024-02-27 09:16:37 +08:00

90 lines
2.3 KiB
Ruby

# frozen_string_literal: true
# Service class that holds helper methods that can be used to validate theme settings.
class ThemeSettingsValidator
class << self
def is_value_present?(value)
!value.nil?
end
def is_valid_value_type?(value, type)
case type
when self.types[:integer]
value.is_a?(Integer)
when self.types[:float]
value.is_a?(Integer) || value.is_a?(Float)
when self.types[:bool]
value.is_a?(TrueClass) || value.is_a?(FalseClass)
when self.types[:list]
value.is_a?(String)
when self.types[:objects]
value.is_a?(Array) && value.all? { |v| v.is_a?(Hash) }
else
true
end
end
def validate_value(value, type, opts)
errors = []
case type
when types[:enum]
unless opts[:choices].include?(value) || opts[:choices].map(&:to_s).include?(value)
errors << I18n.t(
"themes.settings_errors.enum_value_not_valid",
choices: opts[:choices].join(", "),
)
end
when types[:integer], types[:float]
validate_value_in_range!(
value,
min: opts[:min],
max: opts[:max],
errors:,
translation_prefix: "number",
)
when types[:string]
validate_value_in_range!(
value.length,
min: opts[:min],
max: opts[:max],
errors:,
translation_prefix: "string",
)
when types[:objects]
errors.push(
ThemeSettingsObjectValidator.validate_objects(schema: opts[:schema], objects: value),
)
end
errors
end
private
def types
ThemeSetting.types
end
def validate_value_in_range!(value, min:, max:, errors:, translation_prefix:)
if min && max && max != Float::INFINITY && !(min..max).include?(value)
errors << I18n.t(
"themes.settings_errors.#{translation_prefix}_value_not_valid_min_max",
min: min,
max: max,
)
elsif min && value < min
errors << I18n.t(
"themes.settings_errors.#{translation_prefix}_value_not_valid_min",
min: min,
)
elsif max && value > max
errors << I18n.t(
"themes.settings_errors.#{translation_prefix}_value_not_valid_max",
max: max,
)
end
end
end
end