easol-canvas 0.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/canvas/check.rb +4 -1
- data/lib/canvas/checks/required_files_check.rb +3 -0
- data/lib/canvas/checks/valid_block_schemas_check.rb +72 -0
- data/lib/canvas/checks/valid_custom_types_check.rb +36 -0
- data/lib/canvas/checks/valid_footer_schema_check.rb +81 -0
- data/lib/canvas/checks/valid_html_check.rb +3 -0
- data/lib/canvas/checks/valid_json_check.rb +27 -0
- data/lib/canvas/checks/valid_liquid_check.rb +4 -1
- data/lib/canvas/checks/valid_menu_schema_check.rb +81 -0
- data/lib/canvas/checks/valid_sass_check.rb +30 -0
- data/lib/canvas/checks.rb +4 -1
- data/lib/canvas/cli.rb +10 -1
- data/lib/canvas/constants.rb +31 -0
- data/lib/canvas/lint.rb +7 -4
- data/lib/canvas/offense.rb +3 -0
- data/lib/canvas/services/expand_attributes.rb +40 -0
- data/lib/canvas/services/fetch_custom_types.rb +27 -0
- data/lib/canvas/services/front_matter_extractor.rb +1 -0
- data/lib/canvas/validators/block_schema.rb +86 -0
- data/lib/canvas/validators/custom_type.rb +141 -0
- data/lib/canvas/validators/footer_schema.rb +29 -0
- data/lib/canvas/validators/html.rb +5 -2
- data/lib/canvas/validators/json.rb +24 -0
- data/lib/canvas/validators/liquid.rb +4 -1
- data/lib/canvas/validators/menu_schema.rb +95 -0
- data/lib/canvas/validators/sass.rb +26 -0
- data/lib/canvas/validators/schema_attribute.rb +146 -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 +3 -1
- data/lib/canvas.rb +30 -12
- data/lib/easol/canvas.rb +4 -0
- metadata +62 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97ee44a992eb400c6fe2eedd35e6bb395ebc05e2f59f039ea9335194ff64fe83
|
4
|
+
data.tar.gz: 2684ff4ca3910fab9f0cd3bc36cb0c847f31565b1d3f8576964102a28773f247
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28c155af546e6898977757280894cba8ab26942aa1883c5b3ff752f3d7256b68c0631b9d18b6e29df8629d79e94a488739d2c9d0d1d1563dc8f4672f91d164ff
|
7
|
+
data.tar.gz: 3f743a067e8621d1b0893fe41afa8746b60dcc87a4398214b7d8b45ae66648088c22db84e2ed23b26df72c586f19b0547df3a6c8c354f36a005556262fef5a98
|
data/lib/canvas/check.rb
CHANGED
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
# :documented:
|
5
|
+
# This check will validate the schema defined in the front matter
|
6
|
+
# within each block template file.
|
7
|
+
#
|
8
|
+
# Example of block Liquid with valid front matter:
|
9
|
+
#
|
10
|
+
# ---
|
11
|
+
# my_title:
|
12
|
+
# type: string
|
13
|
+
# my_color:
|
14
|
+
# type: color
|
15
|
+
# label: My color
|
16
|
+
# hint: "Select your favourite color"
|
17
|
+
# ---
|
18
|
+
#
|
19
|
+
# <p>My block HTML</p>
|
20
|
+
#
|
21
|
+
class ValidBlockSchemasCheck < Check
|
22
|
+
def run
|
23
|
+
custom_types = Canvas::FetchCustomTypes.call
|
24
|
+
block_files.each do |filename|
|
25
|
+
file = File.read(filename)
|
26
|
+
front_matter = extract_front_matter(file)
|
27
|
+
validate_format(filename, front_matter) &&
|
28
|
+
validate_schema(filename, front_matter, custom_types)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def block_files
|
35
|
+
Dir.glob("blocks/**/*.{html,liquid}")
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_format(filename, front_matter)
|
39
|
+
return true if front_matter.is_a?(Hash) &&
|
40
|
+
front_matter.values.all? { |attr| attr.is_a?(Hash) }
|
41
|
+
|
42
|
+
@offenses << Offense.new(
|
43
|
+
message: "Invalid Block Schema: #{filename} - \nSchema is not in a valid format"
|
44
|
+
)
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate_schema(filename, front_matter, custom_types)
|
49
|
+
schema = extract_schema(front_matter)
|
50
|
+
validator = Validator::BlockSchema.new(schema: schema, custom_types: custom_types)
|
51
|
+
return if validator.validate
|
52
|
+
|
53
|
+
validator.errors.each do |message|
|
54
|
+
@offenses << Offense.new(
|
55
|
+
message: "Invalid Block Schema: #{filename} - \n#{message}"
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def extract_front_matter(file)
|
61
|
+
extractor = Canvas::FrontMatterExtractor.new(file)
|
62
|
+
front_matter = extractor.front_matter
|
63
|
+
front_matter.nil? ? {} : YAML.safe_load(front_matter)
|
64
|
+
end
|
65
|
+
|
66
|
+
def extract_schema(front_matter)
|
67
|
+
{
|
68
|
+
"attributes" => Canvas::ExpandAttributes.call(front_matter)
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
# :documented:
|
5
|
+
# This check will validate the JSON objects that represent the
|
6
|
+
# custom types that are defined in the /types directory.
|
7
|
+
class ValidCustomTypesCheck < Check
|
8
|
+
def run
|
9
|
+
custom_type_files.each do |filename|
|
10
|
+
schema = extract_json(filename)
|
11
|
+
validator = Validator::CustomType.new(schema: schema)
|
12
|
+
|
13
|
+
next if validator.validate
|
14
|
+
|
15
|
+
validator.errors.each do |message|
|
16
|
+
@offenses << Offense.new(
|
17
|
+
message: "Invalid Custom Type: #{filename} - \n#{message}"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def custom_type_files
|
26
|
+
Dir.glob("types/*.json")
|
27
|
+
end
|
28
|
+
|
29
|
+
def extract_json(filename)
|
30
|
+
file = File.read(filename)
|
31
|
+
JSON.parse(file)
|
32
|
+
rescue JSON::ParserError
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
# :documented:
|
5
|
+
# This check will validate the schema defined in the front matter
|
6
|
+
# within each footer template file.
|
7
|
+
#
|
8
|
+
# Example of footer Liquid with valid front matter:
|
9
|
+
#
|
10
|
+
# ---
|
11
|
+
# max_item_levels: 2
|
12
|
+
# supports_open_new_tab: true
|
13
|
+
# attributes:
|
14
|
+
# my_title:
|
15
|
+
# type: string
|
16
|
+
# my_color:
|
17
|
+
# type: color
|
18
|
+
# label: My color
|
19
|
+
# hint: "Select your favourite color"
|
20
|
+
# ---
|
21
|
+
#
|
22
|
+
# <p>My footer HTML</p>
|
23
|
+
#
|
24
|
+
class ValidFooterSchemaCheck < Check
|
25
|
+
def run
|
26
|
+
file = File.read(footer_filename)
|
27
|
+
front_matter = extract_front_matter(file)
|
28
|
+
validate_format(front_matter) &&
|
29
|
+
validate_schema(front_matter)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def footer_filename
|
35
|
+
Dir.glob("partials/footer/index.{html,liquid}").first
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_format(front_matter)
|
39
|
+
return true if front_matter.is_a?(Hash) && attributes_valid_format(front_matter)
|
40
|
+
|
41
|
+
@offenses << Offense.new(
|
42
|
+
message: "Invalid Footer Schema: #{footer_filename} - \nSchema is not in a valid format"
|
43
|
+
)
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def attributes_valid_format(front_matter)
|
48
|
+
return true unless front_matter.key?("attributes")
|
49
|
+
|
50
|
+
front_matter["attributes"].is_a?(Hash) &&
|
51
|
+
front_matter["attributes"].values.all? { |attr| attr.is_a?(Hash) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_schema(front_matter)
|
55
|
+
schema = extract_schema(front_matter)
|
56
|
+
validator = Validator::FooterSchema.new(
|
57
|
+
schema: schema,
|
58
|
+
custom_types: Canvas::FetchCustomTypes.call
|
59
|
+
)
|
60
|
+
return if validator.validate
|
61
|
+
|
62
|
+
validator.errors.each do |message|
|
63
|
+
@offenses << Offense.new(
|
64
|
+
message: "Invalid Footer Schema: #{footer_filename} - \n#{message}"
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def extract_front_matter(file)
|
70
|
+
extractor = Canvas::FrontMatterExtractor.new(file)
|
71
|
+
front_matter = extractor.front_matter
|
72
|
+
front_matter.nil? ? {} : YAML.safe_load(front_matter)
|
73
|
+
end
|
74
|
+
|
75
|
+
def extract_schema(front_matter)
|
76
|
+
front_matter.merge(
|
77
|
+
"attributes" => Canvas::ExpandAttributes.call(front_matter["attributes"])
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
# :documented:
|
5
|
+
class ValidJsonCheck < Check
|
6
|
+
def run
|
7
|
+
json_files.each do |filename|
|
8
|
+
file = File.read(filename)
|
9
|
+
validator = Validator::Json.new(file)
|
10
|
+
|
11
|
+
next if validator.validate
|
12
|
+
|
13
|
+
validator.errors.each do |message|
|
14
|
+
@offenses << Offense.new(
|
15
|
+
message: "Invalid JSON: #{filename} - \n#{message}"
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def json_files
|
24
|
+
Dir.glob("**/*.json")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Canvas
|
4
|
+
# :documented:
|
2
5
|
class ValidLiquidCheck < Check
|
3
6
|
def run
|
4
7
|
register_tags!
|
@@ -9,7 +12,7 @@ module Canvas
|
|
9
12
|
|
10
13
|
next if validator.validate
|
11
14
|
|
12
|
-
validator.errors.
|
15
|
+
validator.errors.each do |message|
|
13
16
|
@offenses << Offense.new(
|
14
17
|
message: "Invalid Liquid: #{filename} - \n#{message}"
|
15
18
|
)
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
# :documented:
|
5
|
+
# This check will validate the schema defined in the front matter
|
6
|
+
# within each menu template file.
|
7
|
+
#
|
8
|
+
# Example of menu Liquid with valid front matter:
|
9
|
+
#
|
10
|
+
# ---
|
11
|
+
# max_item_levels: 2
|
12
|
+
# supports_open_new_tab: true
|
13
|
+
# attributes:
|
14
|
+
# my_title:
|
15
|
+
# type: string
|
16
|
+
# my_color:
|
17
|
+
# type: color
|
18
|
+
# label: My color
|
19
|
+
# hint: "Select your favourite color"
|
20
|
+
# ---
|
21
|
+
#
|
22
|
+
# <p>My menu HTML</p>
|
23
|
+
#
|
24
|
+
class ValidMenuSchemaCheck < Check
|
25
|
+
def run
|
26
|
+
file = File.read(menu_filename)
|
27
|
+
front_matter = extract_front_matter(file)
|
28
|
+
validate_format(front_matter) &&
|
29
|
+
validate_schema(front_matter)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def menu_filename
|
35
|
+
Dir.glob("partials/menu/index.{html,liquid}").first
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_format(front_matter)
|
39
|
+
return true if front_matter.is_a?(Hash) && attributes_valid_format(front_matter)
|
40
|
+
|
41
|
+
@offenses << Offense.new(
|
42
|
+
message: "Invalid Menu Schema: #{menu_filename} - \nSchema is not in a valid format"
|
43
|
+
)
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def attributes_valid_format(front_matter)
|
48
|
+
return true unless front_matter.key?("attributes")
|
49
|
+
|
50
|
+
front_matter["attributes"].is_a?(Hash) &&
|
51
|
+
front_matter["attributes"].values.all? { |attr| attr.is_a?(Hash) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_schema(front_matter)
|
55
|
+
schema = extract_schema(front_matter)
|
56
|
+
validator = Validator::MenuSchema.new(
|
57
|
+
schema: schema,
|
58
|
+
custom_types: Canvas::FetchCustomTypes.call
|
59
|
+
)
|
60
|
+
return if validator.validate
|
61
|
+
|
62
|
+
validator.errors.each do |message|
|
63
|
+
@offenses << Offense.new(
|
64
|
+
message: "Invalid Menu Schema: #{menu_filename} - \n#{message}"
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def extract_front_matter(file)
|
70
|
+
extractor = Canvas::FrontMatterExtractor.new(file)
|
71
|
+
front_matter = extractor.front_matter
|
72
|
+
front_matter.nil? ? {} : YAML.safe_load(front_matter)
|
73
|
+
end
|
74
|
+
|
75
|
+
def extract_schema(front_matter)
|
76
|
+
front_matter.merge(
|
77
|
+
"attributes" => Canvas::ExpandAttributes.call(front_matter["attributes"])
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
# :documented:
|
5
|
+
#
|
6
|
+
# This check will find all files ending in .css, .scss or .sass
|
7
|
+
# and run them through the sass validator - {Canvas::Validator::Sass}.
|
8
|
+
class ValidSassCheck < Check
|
9
|
+
def run
|
10
|
+
sass_files.each do |filename|
|
11
|
+
file = File.read(filename)
|
12
|
+
validator = Validator::Sass.new(file)
|
13
|
+
|
14
|
+
next if validator.validate
|
15
|
+
|
16
|
+
validator.errors.each do |message|
|
17
|
+
@offenses << Offense.new(
|
18
|
+
message: "Invalid Sass: #{filename} - \n#{message}"
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def sass_files
|
27
|
+
Dir.glob("**/*.{css,scss,sass}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/canvas/checks.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Canvas
|
4
|
+
# :documented:
|
2
5
|
class Checks
|
3
6
|
class << self
|
4
7
|
def registered
|
@@ -10,7 +13,7 @@ module Canvas
|
|
10
13
|
return if @checks.include?(klass)
|
11
14
|
@checks << klass
|
12
15
|
end
|
13
|
-
|
16
|
+
|
14
17
|
def deregister_all!
|
15
18
|
@checks = []
|
16
19
|
end
|
data/lib/canvas/cli.rb
CHANGED
@@ -1,12 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "thor"
|
2
|
-
require
|
4
|
+
require "cli/ui"
|
3
5
|
|
4
6
|
module Canvas
|
7
|
+
# :documented:
|
5
8
|
class Cli < Thor
|
6
9
|
desc "lint", "Prints a hello world message"
|
7
10
|
def lint
|
8
11
|
CLI::UI::StdoutRouter.enable
|
9
12
|
Canvas::Lint.new.run
|
10
13
|
end
|
14
|
+
|
15
|
+
map %w[--version -v] => :__print_version
|
16
|
+
desc "--version, -v", "print the version"
|
17
|
+
def __print_version
|
18
|
+
puts Canvas::VERSION
|
19
|
+
end
|
11
20
|
end
|
12
21
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :documented:
|
4
|
+
# This file is used to define globally accessible constants.
|
5
|
+
module Canvas
|
6
|
+
module Constants
|
7
|
+
COLOR_PALETTE_VALUES = %w[
|
8
|
+
primary
|
9
|
+
secondary
|
10
|
+
body-bg
|
11
|
+
body-color
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
PRIMITIVE_TYPES = %w[
|
15
|
+
image
|
16
|
+
product
|
17
|
+
post
|
18
|
+
page
|
19
|
+
link
|
20
|
+
text
|
21
|
+
string
|
22
|
+
boolean
|
23
|
+
number
|
24
|
+
color
|
25
|
+
select
|
26
|
+
range
|
27
|
+
radio
|
28
|
+
variant
|
29
|
+
].freeze
|
30
|
+
end
|
31
|
+
end
|
data/lib/canvas/lint.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cli/ui"
|
2
4
|
|
3
5
|
module Canvas
|
6
|
+
#:documented:
|
4
7
|
class Lint
|
5
8
|
def run
|
6
9
|
output_context = CLI::UI::SpinGroup.new(auto_debrief: false)
|
@@ -29,13 +32,13 @@ module Canvas
|
|
29
32
|
end
|
30
33
|
|
31
34
|
def debrief_message
|
32
|
-
CLI::UI::Frame.open(
|
35
|
+
CLI::UI::Frame.open("Failures", color: :red) do
|
33
36
|
failed_checks = @checks.filter(&:failed?)
|
34
37
|
failed_checks.map do |check|
|
35
38
|
CLI::UI::Frame.open(check.class.name, color: :red) do
|
36
|
-
output = check.offenses.map
|
39
|
+
output = check.offenses.map { |offense|
|
37
40
|
CLI::UI.fmt "{{x}} #{offense.message}"
|
38
|
-
|
41
|
+
}
|
39
42
|
puts output.join("\n")
|
40
43
|
end
|
41
44
|
end
|
data/lib/canvas/offense.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Canvas
|
4
|
+
# :documented:
|
5
|
+
# This service will convert the attributes from a hash, as they are defined in the front matter,
|
6
|
+
# into an array of hashes that the {Canvas::Validator::SchemaAttribute} class expects.
|
7
|
+
# e.g. the following front matter:
|
8
|
+
#
|
9
|
+
# my_title:
|
10
|
+
# type: string
|
11
|
+
# my_color:
|
12
|
+
# type: color
|
13
|
+
#
|
14
|
+
# will get converted to:
|
15
|
+
#
|
16
|
+
# [
|
17
|
+
# {
|
18
|
+
# "name" => "my_title",
|
19
|
+
# "type" => "string"
|
20
|
+
# },
|
21
|
+
# {
|
22
|
+
# "name" => "my_color",
|
23
|
+
# "type" => "color"
|
24
|
+
# }
|
25
|
+
# ]
|
26
|
+
#
|
27
|
+
class ExpandAttributes
|
28
|
+
class << self
|
29
|
+
# @param attributes_hash [Hash] hash of attributes pulled from front matter
|
30
|
+
# @return [Array<Hash>] array of hashes that represent each attribute
|
31
|
+
def call(attributes_hash)
|
32
|
+
return [] if attributes_hash.nil?
|
33
|
+
|
34
|
+
attributes_hash.each_with_object([]) do |(name, attribute_hash), attrs|
|
35
|
+
attrs << attribute_hash.merge("name" => name)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module Canvas
|
6
|
+
# :documented:
|
7
|
+
# This service can be used to fetch the custom types from the /types directory.
|
8
|
+
class FetchCustomTypes
|
9
|
+
class << self
|
10
|
+
# @return [Array<Hash>] a list of all the custom types defined in the
|
11
|
+
# theme within the /types directory.
|
12
|
+
def call
|
13
|
+
filenames = Dir.glob("types/*.json")
|
14
|
+
filenames.map { |filename| extract_json(filename) }.compact
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def extract_json(filename)
|
20
|
+
file = File.read(filename)
|
21
|
+
JSON.parse(file)
|
22
|
+
rescue JSON::ParserError
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "nokogiri"
|
4
|
+
require "liquid"
|
5
|
+
|
6
|
+
module Canvas
|
7
|
+
module Validator
|
8
|
+
# :documented:
|
9
|
+
# This class is used to validate a schema for a block.
|
10
|
+
# Example of a valid block schema:
|
11
|
+
# {
|
12
|
+
# "attributes" => [
|
13
|
+
# {
|
14
|
+
# "name" => "my_title",
|
15
|
+
# "type" => "string"
|
16
|
+
# },
|
17
|
+
# {
|
18
|
+
# "name" => "my_color",
|
19
|
+
# "type" => "color",
|
20
|
+
# "label" => "My color",
|
21
|
+
# "hint" => "Select your favourite color"
|
22
|
+
# }
|
23
|
+
# ]
|
24
|
+
# }
|
25
|
+
class BlockSchema
|
26
|
+
PERMITTED_KEYS = %w[attributes].freeze
|
27
|
+
|
28
|
+
attr_reader :errors, :schema
|
29
|
+
|
30
|
+
# @param schema [Hash] the schema to be validated
|
31
|
+
# @param custom_types [Array<Hash>] a list of custom types
|
32
|
+
def initialize(schema:, custom_types: [])
|
33
|
+
@schema = schema
|
34
|
+
@custom_types = custom_types
|
35
|
+
@errors = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate
|
39
|
+
if ensure_valid_format
|
40
|
+
ensure_no_unrecognized_keys
|
41
|
+
ensure_attributes_are_valid
|
42
|
+
end
|
43
|
+
|
44
|
+
@errors.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def ensure_valid_format
|
50
|
+
return true if schema.is_a?(Hash) &&
|
51
|
+
(schema["attributes"].nil? || attributes_array_of_hashes?(schema))
|
52
|
+
|
53
|
+
@errors << "Schema is not in a valid format"
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
def ensure_no_unrecognized_keys
|
58
|
+
unrecognized_keys = schema.keys - PERMITTED_KEYS
|
59
|
+
return true if unrecognized_keys.empty?
|
60
|
+
|
61
|
+
@errors << "Unrecognized keys: #{unrecognized_keys.join(', ')}"
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
def ensure_attributes_are_valid
|
66
|
+
return true unless schema["attributes"]
|
67
|
+
|
68
|
+
schema["attributes"].each do |attribute_schema|
|
69
|
+
attr_validator = Validator::SchemaAttribute.new(
|
70
|
+
attribute: attribute_schema,
|
71
|
+
custom_types: @custom_types
|
72
|
+
)
|
73
|
+
next if attr_validator.validate
|
74
|
+
|
75
|
+
@errors << "Attribute \"#{attribute_schema['name']}\" is invalid "\
|
76
|
+
"- #{attr_validator.errors.join(', ')}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def attributes_array_of_hashes?(schema)
|
81
|
+
schema["attributes"].is_a?(Array) &&
|
82
|
+
schema["attributes"].all? { |attr| attr.is_a?(Hash) }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|