easol-canvas 0.1.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/canvas/checks/valid_block_schemas_check.rb +72 -0
- data/lib/canvas/checks/valid_footer_schema_check.rb +81 -0
- data/lib/canvas/checks/valid_json_check.rb +24 -0
- data/lib/canvas/checks/valid_liquid_check.rb +1 -1
- data/lib/canvas/checks/valid_menu_schema_check.rb +81 -0
- data/lib/canvas/constants.rb +12 -0
- data/lib/canvas/services/expand_attributes.rb +40 -0
- data/lib/canvas/services/fetch_custom_types.rb +27 -0
- data/lib/canvas/validators/block_schema.rb +86 -0
- data/lib/canvas/validators/footer_schema.rb +29 -0
- data/lib/canvas/validators/html.rb +2 -2
- data/lib/canvas/validators/json.rb +21 -0
- data/lib/canvas/validators/liquid.rb +1 -1
- data/lib/canvas/validators/menu_schema.rb +95 -0
- data/lib/canvas/validators/schema_attribute.rb +147 -0
- data/lib/canvas/validators/schema_attributes/base.rb +104 -0
- data/lib/canvas/validators/schema_attributes/color.rb +81 -0
- data/lib/canvas/validators/schema_attributes/image.rb +45 -0
- data/lib/canvas/validators/schema_attributes/link.rb +51 -0
- data/lib/canvas/validators/schema_attributes/number.rb +17 -0
- data/lib/canvas/validators/schema_attributes/page.rb +49 -0
- data/lib/canvas/validators/schema_attributes/post.rb +49 -0
- data/lib/canvas/validators/schema_attributes/product.rb +49 -0
- data/lib/canvas/validators/schema_attributes/radio.rb +55 -0
- data/lib/canvas/validators/schema_attributes/range.rb +24 -0
- data/lib/canvas/validators/schema_attributes/select.rb +59 -0
- data/lib/canvas/validators/schema_attributes/variant.rb +49 -0
- data/lib/canvas/version.rb +1 -1
- data/lib/canvas.rb +14 -3
- metadata +43 -19
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "nokogiri"
|
4
|
+
require "liquid"
|
5
|
+
|
6
|
+
module Canvas
|
7
|
+
module Validator
|
8
|
+
# :documented:
|
9
|
+
# This class can be used to validate the format of an attribute that is used
|
10
|
+
# within a schema.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# {
|
14
|
+
# "name" => "headings",
|
15
|
+
# "type" => "string",
|
16
|
+
# "default" => "My Heading",
|
17
|
+
# "array" => true
|
18
|
+
# }
|
19
|
+
#
|
20
|
+
class SchemaAttribute
|
21
|
+
VALIDATORS = {
|
22
|
+
"image" => SchemaAttribute::Image,
|
23
|
+
"product" => SchemaAttribute::Product,
|
24
|
+
"post" => SchemaAttribute::Post,
|
25
|
+
"page" => SchemaAttribute::Page,
|
26
|
+
"link" => SchemaAttribute::Link,
|
27
|
+
"text" => SchemaAttribute::Base,
|
28
|
+
"string" => SchemaAttribute::Base,
|
29
|
+
"boolean" => SchemaAttribute::Base,
|
30
|
+
"number" => SchemaAttribute::Number,
|
31
|
+
"color" => SchemaAttribute::Color,
|
32
|
+
"select" => SchemaAttribute::Select,
|
33
|
+
"range" => SchemaAttribute::Range,
|
34
|
+
"radio" => SchemaAttribute::Radio,
|
35
|
+
"variant" => SchemaAttribute::Variant,
|
36
|
+
}.freeze
|
37
|
+
PRIMITIVE_TYPES = VALIDATORS.keys
|
38
|
+
RESERVED_NAMES = %w[
|
39
|
+
page
|
40
|
+
company
|
41
|
+
cart
|
42
|
+
flash
|
43
|
+
block
|
44
|
+
].freeze
|
45
|
+
|
46
|
+
attr_reader :attribute, :custom_types, :errors, :additional_reserved_names
|
47
|
+
|
48
|
+
def initialize(attribute:, custom_types: [], additional_reserved_names: [])
|
49
|
+
@attribute = attribute
|
50
|
+
@custom_types = custom_types
|
51
|
+
@errors = []
|
52
|
+
@additional_reserved_names = additional_reserved_names
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate
|
56
|
+
ensure_attribute_is_hash &&
|
57
|
+
ensure_not_reserved_name &&
|
58
|
+
ensure_not_boolean_array &&
|
59
|
+
ensure_not_radio_array &&
|
60
|
+
ensure_type_key_is_present &&
|
61
|
+
ensure_valid_for_type &&
|
62
|
+
ensure_composite_array_without_non_array_attributes
|
63
|
+
|
64
|
+
errors.empty?
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def attribute_type
|
70
|
+
attribute["type"]&.downcase
|
71
|
+
end
|
72
|
+
|
73
|
+
def valid_types
|
74
|
+
PRIMITIVE_TYPES + custom_type_keys
|
75
|
+
end
|
76
|
+
|
77
|
+
def validator_for_type
|
78
|
+
@_validator ||= VALIDATORS[attribute_type].new(attribute) if VALIDATORS[attribute_type]
|
79
|
+
end
|
80
|
+
|
81
|
+
def custom_type_keys
|
82
|
+
custom_types.map { |type| type["key"]&.downcase }.compact
|
83
|
+
end
|
84
|
+
|
85
|
+
def ensure_type_key_is_present
|
86
|
+
return true if attribute.key?("type")
|
87
|
+
|
88
|
+
@errors << "Missing required keys: type"
|
89
|
+
false
|
90
|
+
end
|
91
|
+
|
92
|
+
def ensure_valid_for_type
|
93
|
+
return true if custom_type_keys.include?(attribute_type)
|
94
|
+
|
95
|
+
if validator_for_type.nil?
|
96
|
+
@errors << "\"type\" must be one of: #{valid_types.join(', ')}"
|
97
|
+
false
|
98
|
+
else
|
99
|
+
validator_for_type.validate
|
100
|
+
errors.concat(validator_for_type.errors)
|
101
|
+
validator_for_type.errors.empty?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def ensure_attribute_is_hash
|
106
|
+
return true if attribute.is_a? Hash
|
107
|
+
|
108
|
+
@errors << "Must be valid JSON"
|
109
|
+
false
|
110
|
+
end
|
111
|
+
|
112
|
+
def ensure_not_reserved_name
|
113
|
+
all_reserved_names = RESERVED_NAMES + additional_reserved_names
|
114
|
+
return true unless all_reserved_names.include?(attribute["name"])
|
115
|
+
|
116
|
+
@errors << "\"name\" can't be one of these reserved words: #{all_reserved_names.join(', ')}"
|
117
|
+
false
|
118
|
+
end
|
119
|
+
|
120
|
+
def ensure_not_boolean_array
|
121
|
+
return true unless attribute["type"] == "boolean" && attribute["array"] == true
|
122
|
+
|
123
|
+
@errors << "Boolean attributes cannot be arrays"
|
124
|
+
false
|
125
|
+
end
|
126
|
+
|
127
|
+
def ensure_not_radio_array
|
128
|
+
return true unless attribute["type"] == "radio" && attribute["array"] == true
|
129
|
+
|
130
|
+
@errors << "Radio attributes cannot be arrays"
|
131
|
+
false
|
132
|
+
end
|
133
|
+
|
134
|
+
def ensure_composite_array_without_non_array_attributes
|
135
|
+
custom_type = custom_types.find { |type| type["key"]&.downcase == attribute_type }
|
136
|
+
|
137
|
+
return true if !custom_type || attribute["array"] != true
|
138
|
+
|
139
|
+
sub_attributes = custom_type.fetch("attributes", [])
|
140
|
+
return true unless sub_attributes.any? { |attribute| attribute["type"] == "radio" }
|
141
|
+
|
142
|
+
@errors << "Cannot be an array because \"#{custom_type['key']}\" type includes nonarray types"
|
143
|
+
false
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
module Validator
|
5
|
+
class SchemaAttribute
|
6
|
+
# :documented:
|
7
|
+
#
|
8
|
+
# Validations to run against an attribute's schema that are shared
|
9
|
+
# across types. This class acts as the base class for type-specific
|
10
|
+
# validators.
|
11
|
+
class Base
|
12
|
+
attr_reader :attribute, :errors
|
13
|
+
|
14
|
+
def initialize(attribute)
|
15
|
+
@attribute = attribute
|
16
|
+
@errors = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate
|
20
|
+
ensure_has_required_keys &&
|
21
|
+
ensure_no_unrecognized_keys &&
|
22
|
+
ensure_keys_are_correct_types
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# The base keys required for this attribute to be valid and their
|
28
|
+
# expected types. Types can also be specified as an array of discrete
|
29
|
+
# expected values.
|
30
|
+
# This can be overwritten in sub-validator class.
|
31
|
+
#
|
32
|
+
# @return [Hash]
|
33
|
+
def required_keys
|
34
|
+
{
|
35
|
+
"name" => String,
|
36
|
+
"type" => String
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Either a class or array of values we expect the default to be.
|
41
|
+
# This can be overwritten in sub-validator class.
|
42
|
+
def permitted_values_for_default_key
|
43
|
+
Object
|
44
|
+
end
|
45
|
+
|
46
|
+
# The optional keys that can be supplied for this attribute and their
|
47
|
+
# expected types. Types can also be specified as an array of discrete
|
48
|
+
# expected values.
|
49
|
+
# This can be overwritten in sub-validator class.
|
50
|
+
#
|
51
|
+
# @return [Hash]
|
52
|
+
def optional_keys
|
53
|
+
{
|
54
|
+
"default" => permitted_values_for_default_key,
|
55
|
+
"array" => [true, false],
|
56
|
+
"label" => String,
|
57
|
+
"hint" => String,
|
58
|
+
"group" => %w[content layout design mobile]
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def all_permitted_keys
|
63
|
+
required_keys.merge(optional_keys)
|
64
|
+
end
|
65
|
+
|
66
|
+
def ensure_has_required_keys
|
67
|
+
missing_keys = required_keys.keys - attribute.keys
|
68
|
+
return true if missing_keys.empty?
|
69
|
+
|
70
|
+
@errors << "Missing required keys: #{missing_keys.join(', ')}"
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
def ensure_no_unrecognized_keys
|
75
|
+
unrecognized_keys = attribute.keys - all_permitted_keys.keys
|
76
|
+
return true if unrecognized_keys.empty?
|
77
|
+
|
78
|
+
@errors << "Unrecognized keys: #{unrecognized_keys.join(', ')}"
|
79
|
+
false
|
80
|
+
end
|
81
|
+
|
82
|
+
def ensure_keys_are_correct_types
|
83
|
+
all_permitted_keys.each do |key, expected|
|
84
|
+
if expected.is_a?(Class)
|
85
|
+
if attribute.key?(key) && !attribute[key].is_a?(expected)
|
86
|
+
actual = attribute[key].class.name
|
87
|
+
@errors << "\"#{key}\" is a #{actual}, expected #{expected}"
|
88
|
+
return false
|
89
|
+
end
|
90
|
+
else
|
91
|
+
if attribute.key?(key) && !expected.include?(attribute[key])
|
92
|
+
actual = attribute[key].to_s
|
93
|
+
@errors << "\"#{key}\" is '#{actual}', expected one of: #{[*expected].join(', ')}"
|
94
|
+
return false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
module Validator
|
5
|
+
class SchemaAttribute
|
6
|
+
# :documented:
|
7
|
+
# Attribute validations specific to color-type variables.
|
8
|
+
class Color < Base
|
9
|
+
def validate
|
10
|
+
super &&
|
11
|
+
ensure_default_keys_are_valid &&
|
12
|
+
ensure_default_values_are_valid
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def permitted_values_for_default_key
|
18
|
+
Hash
|
19
|
+
end
|
20
|
+
|
21
|
+
def ensure_default_keys_are_valid
|
22
|
+
return true unless attribute.key?("default")
|
23
|
+
return true if attribute["default"].key?("palette")
|
24
|
+
|
25
|
+
key_diff = %w[r g b] - attribute["default"].keys
|
26
|
+
if key_diff.any? && key_diff != ["a"]
|
27
|
+
@errors << "\"default\" for color-type variables must include palette or rgba values"
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def ensure_default_values_are_valid
|
35
|
+
return true unless attribute.key?("default")
|
36
|
+
|
37
|
+
if attribute["default"].key?("palette")
|
38
|
+
ensure_palette_value_is_valid
|
39
|
+
else
|
40
|
+
ensure_rgb_value_is_valid
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def ensure_palette_value_is_valid
|
45
|
+
return true if Constants::COLOR_PALETTE_VALUES.include?(attribute["default"]["palette"])
|
46
|
+
|
47
|
+
@errors << "\"default\" value for palette color-type must be one of "\
|
48
|
+
"the following values: #{Constants::COLOR_PALETTE_VALUES.join(', ')}"
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
def ensure_rgb_value_is_valid
|
53
|
+
invalid_rgb_error = "\"default\" values for color-type variables must be "\
|
54
|
+
"between 0 and 255 for rgb, and between 0 and 1 for a"
|
55
|
+
|
56
|
+
attribute["default"].each do |key, value|
|
57
|
+
if %w[r g b].include?(key) && !valid_rgb_value?(value)
|
58
|
+
@errors << invalid_rgb_error
|
59
|
+
return false
|
60
|
+
end
|
61
|
+
|
62
|
+
if key == "a" && !valid_alpha_value?(value)
|
63
|
+
@errors << invalid_rgb_error
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
71
|
+
def valid_rgb_value?(value)
|
72
|
+
value.to_s.match?(/\A\d+\z/) && value.to_i.between?(0, 255)
|
73
|
+
end
|
74
|
+
|
75
|
+
def valid_alpha_value?(value)
|
76
|
+
value.to_s.match?(/([0-9]*[.])?[0-9]+/) && value.to_f.between?(0, 1)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
module Validator
|
5
|
+
class SchemaAttribute
|
6
|
+
# :documented:
|
7
|
+
# Attribute validations specific to link-type variables.
|
8
|
+
class Image < Base
|
9
|
+
ALLOWED_DEFAULT_KEYS = %w[url asset].freeze
|
10
|
+
|
11
|
+
def validate
|
12
|
+
super && ensure_default_key_is_valid
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def ensure_default_key_is_valid
|
18
|
+
return true unless attribute.key?("default")
|
19
|
+
|
20
|
+
if attribute["array"]
|
21
|
+
attribute["default"].all? { |value| default_value_is_validate(value) }
|
22
|
+
else
|
23
|
+
default_value_is_validate(attribute["default"])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# The default value can be one of 3 different formats:
|
28
|
+
# - A string (for backwards compatibility) e.g. "http://my-image.jpg"
|
29
|
+
# - A hash with "url" key" e.g. { "url": "http://my-image.jpg" }
|
30
|
+
# - A hash with "asset" key" e.g. { "asset": "http://my-image.jpg" }
|
31
|
+
def default_value_is_validate(value)
|
32
|
+
return true if value.is_a?(String)
|
33
|
+
|
34
|
+
return true if value.is_a?(Hash) &&
|
35
|
+
value.keys.size == 1 &&
|
36
|
+
ALLOWED_DEFAULT_KEYS.include?(value.keys.first) &&
|
37
|
+
value.values.first.is_a?(String)
|
38
|
+
|
39
|
+
@errors << "\"default\" for image-type variables must include a single url or asset value"
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
module Validator
|
5
|
+
class SchemaAttribute
|
6
|
+
# :documented:
|
7
|
+
# Attribute validations specific to link-type variables.
|
8
|
+
class Link < Base
|
9
|
+
ALLOWED_DEFAULT_KEYS = %w[url page post product].freeze
|
10
|
+
INVALID_DEFAULT_ERROR = "\"default\" for link-type variables must include "\
|
11
|
+
"a single url, page, post or product value"
|
12
|
+
|
13
|
+
def validate
|
14
|
+
super &&
|
15
|
+
ensure_single_default_provided &&
|
16
|
+
ensure_default_key_is_valid
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def permitted_values_for_default_key
|
22
|
+
Hash
|
23
|
+
end
|
24
|
+
|
25
|
+
def ensure_default_key_is_valid
|
26
|
+
return true unless attribute.key?("default")
|
27
|
+
|
28
|
+
key = attribute["default"].keys.first
|
29
|
+
|
30
|
+
unless ALLOWED_DEFAULT_KEYS.include?(key)
|
31
|
+
@errors << INVALID_DEFAULT_ERROR
|
32
|
+
return false
|
33
|
+
end
|
34
|
+
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def ensure_single_default_provided
|
39
|
+
return true unless attribute.key?("default")
|
40
|
+
|
41
|
+
if attribute["default"].count != 1
|
42
|
+
@errors << INVALID_DEFAULT_ERROR
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
|
46
|
+
true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
module Validator
|
5
|
+
class SchemaAttribute
|
6
|
+
# :documented:
|
7
|
+
# Attribute validations specific to number-type variables.
|
8
|
+
class Number < Base
|
9
|
+
private
|
10
|
+
|
11
|
+
def optional_keys
|
12
|
+
super.merge("unit" => String)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
module Validator
|
5
|
+
class SchemaAttribute
|
6
|
+
# :documented:
|
7
|
+
# Attribute validations specific to page-type variables.
|
8
|
+
class Page < Base
|
9
|
+
ALLOWED_DEFAULT_VALUES = %w[ random ].freeze
|
10
|
+
|
11
|
+
def validate
|
12
|
+
super &&
|
13
|
+
ensure_default_values_are_valid
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def permitted_values_for_default_key
|
19
|
+
if attribute["array"]
|
20
|
+
Array
|
21
|
+
else
|
22
|
+
String
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def ensure_default_values_are_valid
|
27
|
+
return true unless attribute.key?("default")
|
28
|
+
|
29
|
+
if attribute["array"]
|
30
|
+
attribute["default"].all? { |value| default_value_is_validate(value) }
|
31
|
+
else
|
32
|
+
default_value_is_validate(attribute["default"])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def default_value_is_validate(value)
|
37
|
+
value = value.downcase
|
38
|
+
if !ALLOWED_DEFAULT_VALUES.include?(value)
|
39
|
+
@errors << "\"default\" for page-type variables must be "\
|
40
|
+
"one of: #{ALLOWED_DEFAULT_VALUES.join(', ')}"
|
41
|
+
false
|
42
|
+
else
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
module Validator
|
5
|
+
class SchemaAttribute
|
6
|
+
# :documented:
|
7
|
+
# Attribute validations specific to post-type variables.
|
8
|
+
class Post < Base
|
9
|
+
ALLOWED_DEFAULT_VALUES = %w[random].freeze
|
10
|
+
|
11
|
+
def validate
|
12
|
+
super &&
|
13
|
+
ensure_default_values_are_valid
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def permitted_values_for_default_key
|
19
|
+
if attribute["array"]
|
20
|
+
Array
|
21
|
+
else
|
22
|
+
String
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def ensure_default_values_are_valid
|
27
|
+
return true unless attribute.key?("default")
|
28
|
+
|
29
|
+
if attribute["array"]
|
30
|
+
attribute["default"].all? { |value| default_value_is_valid?(value) }
|
31
|
+
else
|
32
|
+
default_value_is_valid?(attribute["default"])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def default_value_is_valid?(value)
|
37
|
+
value = value.downcase
|
38
|
+
if !ALLOWED_DEFAULT_VALUES.include?(value)
|
39
|
+
@errors << "\"default\" for post-type variables must be "\
|
40
|
+
"one of: #{ALLOWED_DEFAULT_VALUES.join(', ')}"
|
41
|
+
false
|
42
|
+
else
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
module Validator
|
5
|
+
class SchemaAttribute
|
6
|
+
# :documented:
|
7
|
+
# Attribute validations specific to product-type variables.
|
8
|
+
class Product < Base
|
9
|
+
ALLOWED_DEFAULT_VALUES = %w[random].freeze
|
10
|
+
|
11
|
+
def validate
|
12
|
+
super &&
|
13
|
+
ensure_default_values_are_valid
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def permitted_values_for_default_key
|
19
|
+
if attribute["array"]
|
20
|
+
Array
|
21
|
+
else
|
22
|
+
String
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def ensure_default_values_are_valid
|
27
|
+
return true unless attribute.key?("default")
|
28
|
+
|
29
|
+
if attribute["array"]
|
30
|
+
attribute["default"].all? { |value| default_value_is_valid?(value) }
|
31
|
+
else
|
32
|
+
default_value_is_valid?(attribute["default"])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def default_value_is_valid?(value)
|
37
|
+
value = value.downcase
|
38
|
+
if !ALLOWED_DEFAULT_VALUES.include?(value)
|
39
|
+
@errors << "\"default\" for product-type variables must be "\
|
40
|
+
"one of: #{ALLOWED_DEFAULT_VALUES.join(', ')}"
|
41
|
+
false
|
42
|
+
else
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
module Validator
|
5
|
+
class SchemaAttribute
|
6
|
+
# :documented:
|
7
|
+
# Attribute validations specific to radio-type variables.
|
8
|
+
class Radio < Base
|
9
|
+
def validate
|
10
|
+
super &&
|
11
|
+
ensure_at_least_one_option &&
|
12
|
+
ensure_options_are_valid
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def permitted_values_for_default_key
|
18
|
+
if attribute["options"].is_a?(Array)
|
19
|
+
attribute["options"].map { |option| option["value"] }
|
20
|
+
else
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def required_keys
|
26
|
+
super.merge(
|
27
|
+
"options" => Array
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def ensure_at_least_one_option
|
32
|
+
return true if attribute["options"].length.positive?
|
33
|
+
|
34
|
+
@errors << "Must provide at least 1 option for radio type variable"
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def ensure_options_are_valid
|
39
|
+
return true if attribute["options"].all? { |option|
|
40
|
+
select_option_valid?(option)
|
41
|
+
}
|
42
|
+
|
43
|
+
@errors << "All options for radio type variable must specify a label and value"
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def select_option_valid?(option)
|
48
|
+
option.is_a?(Hash) &&
|
49
|
+
option.key?("value") &&
|
50
|
+
option.key?("label")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
module Canvas
|
6
|
+
module Validator
|
7
|
+
class SchemaAttribute
|
8
|
+
# :documented:
|
9
|
+
# Attribute validations specific to range-type variables.
|
10
|
+
class Range < Base
|
11
|
+
private
|
12
|
+
|
13
|
+
def optional_keys
|
14
|
+
super.merge(
|
15
|
+
"min" => Object,
|
16
|
+
"max" => Object,
|
17
|
+
"step" => Object,
|
18
|
+
"unit" => String
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|