mirror of
https://github.com/discourse/discourse.git
synced 2025-02-16 23:12:45 +08:00
DEV: Support validations options for string and numeral types (#25719)
Why this change? This commit updates `ThemeSettingsObjectValidator` to validate a property's value against the validations listed in the schema. For string types, `min_length`, `max_length` and `url` are supported. For integer and float types, `min` and `max` are supported.
This commit is contained in:
parent
3894ee6cb6
commit
bf3c4b634a
|
@ -142,6 +142,11 @@ en:
|
||||||
not_valid_float_value: "must be a float"
|
not_valid_float_value: "must be a float"
|
||||||
not_valid_boolean_value: "must be a boolean"
|
not_valid_boolean_value: "must be a boolean"
|
||||||
not_valid_enum_value: "must be one of the following: %{choices}"
|
not_valid_enum_value: "must be one of the following: %{choices}"
|
||||||
|
string_value_not_valid_min: "must be at least %{min} characters long"
|
||||||
|
string_value_not_valid_max: "must be at most %{max} characters long"
|
||||||
|
number_value_not_valid_min: "must be larger than or equal to %{min}"
|
||||||
|
number_value_not_valid_max: "must be smaller than or equal to %{max}"
|
||||||
|
string_value_not_valid_url: "must be a valid URL"
|
||||||
locale_errors:
|
locale_errors:
|
||||||
top_level_locale: "The top level key in a locale file must match the locale name"
|
top_level_locale: "The top level key in a locale file must match the locale name"
|
||||||
invalid_yaml: "Translation YAML invalid"
|
invalid_yaml: "Translation YAML invalid"
|
||||||
|
|
|
@ -30,17 +30,18 @@ class ThemeSettingsObjectValidator
|
||||||
|
|
||||||
def validate_properties
|
def validate_properties
|
||||||
@properties.each do |property_name, property_attributes|
|
@properties.each do |property_name, property_attributes|
|
||||||
next if property_attributes[:required] && validate_required_property(property_name)
|
next if property_attributes[:required] && !is_property_present?(property_name)
|
||||||
validate_property_type(property_attributes, 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
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_property_type(property_attributes, property_name)
|
def has_valid_property_value_type?(property_attributes, property_name)
|
||||||
value = @object[property_name]
|
value = @object[property_name]
|
||||||
type = property_attributes[:type]
|
type = property_attributes[:type]
|
||||||
|
|
||||||
return if (value.nil? && type != "enum")
|
return true if (value.nil? && type != "enum")
|
||||||
return if type == "objects"
|
return true if type == "objects"
|
||||||
|
|
||||||
is_value_valid =
|
is_value_valid =
|
||||||
case type
|
case type
|
||||||
|
@ -56,23 +57,97 @@ class ThemeSettingsObjectValidator
|
||||||
property_attributes[:choices].include?(value)
|
property_attributes[:choices].include?(value)
|
||||||
else
|
else
|
||||||
add_error(property_name, I18n.t("themes.settings_errors.objects.invalid_type", type:))
|
add_error(property_name, I18n.t("themes.settings_errors.objects.invalid_type", type:))
|
||||||
return
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if !is_value_valid
|
if is_value_valid
|
||||||
|
true
|
||||||
|
else
|
||||||
add_error(
|
add_error(
|
||||||
property_name,
|
property_name,
|
||||||
I18n.t("themes.settings_errors.objects.not_valid_#{type}_value", property_attributes),
|
I18n.t("themes.settings_errors.objects.not_valid_#{type}_value", property_attributes),
|
||||||
)
|
)
|
||||||
|
false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_required_property(property_name)
|
def has_valid_property_value?(property_attributes, property_name)
|
||||||
|
validations = property_attributes[:validations]
|
||||||
|
|
||||||
|
return true if validations.blank?
|
||||||
|
|
||||||
|
type = property_attributes[:type]
|
||||||
|
value = @object[property_name]
|
||||||
|
|
||||||
|
case type
|
||||||
|
when "string"
|
||||||
|
if validations[:min_length] && value.length < validations[:min_length]
|
||||||
|
add_error(
|
||||||
|
property_name,
|
||||||
|
I18n.t(
|
||||||
|
"themes.settings_errors.objects.string_value_not_valid_min",
|
||||||
|
min: validations[:min_length],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if validations[:max_length] && value.length > validations[:max_length]
|
||||||
|
add_error(
|
||||||
|
property_name,
|
||||||
|
I18n.t(
|
||||||
|
"themes.settings_errors.objects.string_value_not_valid_max",
|
||||||
|
max: validations[:max_length],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if validations[:url] && !value.match?(URI.regexp)
|
||||||
|
add_error(
|
||||||
|
property_name,
|
||||||
|
I18n.t("themes.settings_errors.objects.string_value_not_valid_url"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
when "integer", "float"
|
||||||
|
if validations[:min] && value < validations[:min]
|
||||||
|
add_error(
|
||||||
|
property_name,
|
||||||
|
I18n.t(
|
||||||
|
"themes.settings_errors.objects.number_value_not_valid_min",
|
||||||
|
min: validations[:min],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if validations[:max] && value > validations[:max]
|
||||||
|
add_error(
|
||||||
|
property_name,
|
||||||
|
I18n.t(
|
||||||
|
"themes.settings_errors.objects.number_value_not_valid_max",
|
||||||
|
max: validations[:max],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_property_present?(property_name)
|
||||||
if @object[property_name].nil?
|
if @object[property_name].nil?
|
||||||
add_error(property_name, I18n.t("themes.settings_errors.objects.required"))
|
add_error(property_name, I18n.t("themes.settings_errors.objects.required"))
|
||||||
true
|
|
||||||
else
|
|
||||||
false
|
false
|
||||||
|
else
|
||||||
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,29 @@ RSpec.describe ThemeSettingsObjectValidator do
|
||||||
described_class.new(schema: schema, object: { float_property: "string" }).validate,
|
described_class.new(schema: schema, object: { float_property: "string" }).validate,
|
||||||
).to eq(float_property: ["must be a float"])
|
).to eq(float_property: ["must be a float"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should return the right hash of error messages when integer property does not satisfy min or max validations" do
|
||||||
|
schema = {
|
||||||
|
name: "section",
|
||||||
|
properties: {
|
||||||
|
float_property: {
|
||||||
|
type: "float",
|
||||||
|
validations: {
|
||||||
|
min: 5.5,
|
||||||
|
max: 11.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(described_class.new(schema: schema, object: { float_property: 4.5 }).validate).to eq(
|
||||||
|
float_property: ["must be larger than or equal to 5.5"],
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
described_class.new(schema: schema, object: { float_property: 12.5 }).validate,
|
||||||
|
).to eq(float_property: ["must be smaller than or equal to 11.5"])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "for integer properties" do
|
context "for integer properties" do
|
||||||
|
@ -159,6 +182,48 @@ RSpec.describe ThemeSettingsObjectValidator do
|
||||||
described_class.new(schema: schema, object: { integer_property: 1.0 }).validate,
|
described_class.new(schema: schema, object: { integer_property: 1.0 }).validate,
|
||||||
).to eq(integer_property: ["must be an integer"])
|
).to eq(integer_property: ["must be an integer"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should not return any error messages when the value of the integer property satisfies min and max validations" do
|
||||||
|
schema = {
|
||||||
|
name: "section",
|
||||||
|
properties: {
|
||||||
|
integer_property: {
|
||||||
|
type: "integer",
|
||||||
|
validations: {
|
||||||
|
min: 5,
|
||||||
|
max: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(described_class.new(schema: schema, object: { integer_property: 6 }).validate).to eq(
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return the right hash of error messages when integer property does not satisfy min or max validations" do
|
||||||
|
schema = {
|
||||||
|
name: "section",
|
||||||
|
properties: {
|
||||||
|
integer_property: {
|
||||||
|
type: "integer",
|
||||||
|
validations: {
|
||||||
|
min: 5,
|
||||||
|
max: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(described_class.new(schema: schema, object: { integer_property: 4 }).validate).to eq(
|
||||||
|
integer_property: ["must be larger than or equal to 5"],
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
described_class.new(schema: schema, object: { integer_property: 11 }).validate,
|
||||||
|
).to eq(integer_property: ["must be smaller than or equal to 10"])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "for string properties" do
|
context "for string properties" do
|
||||||
|
@ -171,10 +236,72 @@ RSpec.describe ThemeSettingsObjectValidator do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return the right hash of error messages when value of property is not of type string" do
|
it "should return the right hash of error messages when value of property is not of type string" do
|
||||||
|
schema = { name: "section", properties: { string_property: { type: "string" } } }
|
||||||
|
|
||||||
expect(described_class.new(schema: schema, object: { string_property: 1 }).validate).to eq(
|
expect(described_class.new(schema: schema, object: { string_property: 1 }).validate).to eq(
|
||||||
string_property: ["must be a string"],
|
string_property: ["must be a string"],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should return the right hash of error messages when string property does not statisfy url validation" do
|
||||||
|
schema = {
|
||||||
|
name: "section",
|
||||||
|
properties: {
|
||||||
|
string_property: {
|
||||||
|
type: "string",
|
||||||
|
validations: {
|
||||||
|
url: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(
|
||||||
|
described_class.new(schema: schema, object: { string_property: "not a url" }).validate,
|
||||||
|
).to eq(string_property: ["must be a valid URL"])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not return any error messages when the value of the string property satisfies min_length and max_length validations" do
|
||||||
|
schema = {
|
||||||
|
name: "section",
|
||||||
|
properties: {
|
||||||
|
string_property: {
|
||||||
|
type: "string",
|
||||||
|
validations: {
|
||||||
|
min_length: 5,
|
||||||
|
max_length: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(
|
||||||
|
described_class.new(schema: schema, object: { string_property: "123456" }).validate,
|
||||||
|
).to eq({})
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return the right hash of error messages when string property does not satisfy min_length or max_length validations" do
|
||||||
|
schema = {
|
||||||
|
name: "section",
|
||||||
|
properties: {
|
||||||
|
string_property: {
|
||||||
|
type: "string",
|
||||||
|
validations: {
|
||||||
|
min_length: 5,
|
||||||
|
max_length: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(
|
||||||
|
described_class.new(schema: schema, object: { string_property: "1234" }).validate,
|
||||||
|
).to eq(string_property: ["must be at least 5 characters long"])
|
||||||
|
|
||||||
|
expect(
|
||||||
|
described_class.new(schema: schema, object: { string_property: "12345678910" }).validate,
|
||||||
|
).to eq(string_property: ["must be at most 10 characters long"])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user