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.
This commit is contained in:
Alan Guo Xiang Tan
2024-02-27 09:16:37 +08:00
committed by GitHub
parent 4df4584396
commit 7bcfe60a76
6 changed files with 251 additions and 47 deletions
+61 -19
View File
@@ -1,37 +1,72 @@
# frozen_string_literal: true
class ThemeSettingsObjectValidator
class << self
def validate_objects(schema:, objects:)
error_messages = []
objects.each_with_index do |object, index|
humanize_error_messages(
self.new(schema: schema, object: object).validate,
index:,
error_messages:,
)
end
error_messages
end
private
def humanize_error_messages(errors, index:, error_messages:)
errors.each do |property_json_pointer, error_details|
error_messages.push(*error_details.humanize_messages("/#{index}#{property_json_pointer}"))
end
end
end
class ThemeSettingsObjectErrors
def initialize
@errors = []
end
def add_error(key, i18n_opts = {})
@errors << ThemeSettingsObjectError.new(key, i18n_opts)
def add_error(error, i18n_opts = {})
@errors << ThemeSettingsObjectError.new(error, i18n_opts)
end
def humanize_messages(property_json_pointer)
@errors.map { |error| error.humanize_messages(property_json_pointer) }
end
def full_messages
@errors.map(&:error_message)
end
end
class ThemeSettingsObjectError
def initialize(key, i18n_opts = {})
@key = key
def initialize(error, i18n_opts = {})
@error = error
@i18n_opts = i18n_opts
end
def humanize_messages(property_json_pointer)
I18n.t(
"themes.settings_errors.objects.humanize_#{@error}",
@i18n_opts.merge(property_json_pointer:),
)
end
def error_message
I18n.t("themes.settings_errors.objects.#{@key}", @i18n_opts)
I18n.t("themes.settings_errors.objects.#{@error}", @i18n_opts)
end
end
def initialize(schema:, object:, valid_category_ids: nil)
def initialize(schema:, object:, valid_category_ids: nil, json_pointer_prefix: "", errors: {})
@object = object
@schema_name = schema[:name]
@properties = schema[:properties]
@errors = {}
@errors = errors
@valid_category_ids = valid_category_ids
@json_pointer_prefix = json_pointer_prefix
end
def validate
@@ -39,15 +74,17 @@ class ThemeSettingsObjectValidator
@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,
)
@object[property_name]&.each_with_index do |child_object, index|
self
.class
.new(
schema: property_attributes[:schema],
object: child_object,
valid_category_ids:,
json_pointer_prefix: "#{@json_pointer_prefix}#{property_name}/#{index}/",
errors: @errors,
)
.validate
end
end
end
@@ -148,8 +185,13 @@ class ThemeSettingsObjectValidator
end
def add_error(property_name, key, i18n_opts = {})
@errors[property_name] ||= ThemeSettingsObjectErrors.new
@errors[property_name].add_error(key, i18n_opts)
pointer = json_pointer(property_name)
@errors[pointer] ||= ThemeSettingsObjectErrors.new
@errors[pointer].add_error(key, i18n_opts)
end
def json_pointer(property_name)
"/#{@json_pointer_prefix}#{property_name}"
end
def valid_category_ids
+4
View File
@@ -51,6 +51,10 @@ class ThemeSettingsValidator
errors:,
translation_prefix: "string",
)
when types[:objects]
errors.push(
ThemeSettingsObjectValidator.validate_objects(schema: opts[:schema], objects: value),
)
end
errors