blufin-lib 1.7.6 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/blufin-lib.rb +5 -0
- data/lib/core/arrays.rb +21 -0
- data/lib/core/aws.rb +12 -3
- data/lib/core/config.rb +1 -1
- data/lib/core/git.rb +274 -0
- data/lib/core/projects.rb +285 -32
- data/lib/core/routes.rb +20 -1
- data/lib/core/strings.rb +16 -0
- data/lib/core/terminal.rb +25 -15
- data/lib/core/update.rb +15 -0
- data/lib/core/yml.rb +43 -0
- data/lib/generate/generate_base.rb +25 -0
- data/lib/generate/generate_ui_routes.rb +165 -0
- data/lib/version.rb +1 -1
- data/opt/schema/projects.yml +15 -2
- data/opt/schema/routes.yml +44 -0
- data/opt/shell/ec2-check +24 -0
- metadata +9 -2
data/lib/core/routes.rb
CHANGED
@@ -67,11 +67,30 @@ module Blufin
|
|
67
67
|
flags_set = 0
|
68
68
|
possible_flags.each { |possible_flag|
|
69
69
|
raise RuntimeError, "@opts does not contain flag: #{possible_flag}" unless opts.has_key?(possible_flag)
|
70
|
-
flags_set += 1 if opts[possible_flag]
|
70
|
+
flags_set += 1 if opts[possible_flag] && !opts[possible_flag].nil?
|
71
71
|
}
|
72
72
|
Blufin::Terminal::error("Cannot set #{Blufin::Terminal::format_highlight('more than one')} of the following flags: \x1B[38;5;172m#{possible_flags.join(', ')}\x1B[0m") if flags_set > 1
|
73
73
|
end
|
74
74
|
|
75
|
+
# Prevents code from running twice.
|
76
|
+
# @return boolean
|
77
|
+
def self.code_already_ran(tmp_key, timeout = 1000)
|
78
|
+
t = Time.new
|
79
|
+
epoch = t.strftime('%s%3N')
|
80
|
+
epoch_previous = nil
|
81
|
+
tmp_file = "/tmp/#{tmp_key}-last-run-epoch.txt"
|
82
|
+
if Blufin::Files::file_exists(tmp_file)
|
83
|
+
Blufin::Files::read_file(tmp_file).each do |line|
|
84
|
+
epoch_previous = line
|
85
|
+
break
|
86
|
+
end
|
87
|
+
end
|
88
|
+
Blufin::Files::write_file(tmp_file, [epoch])
|
89
|
+
return if epoch_previous.nil?
|
90
|
+
diff = (epoch.to_i - epoch_previous.to_i).abs
|
91
|
+
diff < timeout
|
92
|
+
end
|
93
|
+
|
75
94
|
private
|
76
95
|
|
77
96
|
# Does exactly what it says on the Tin.
|
data/lib/core/strings.rb
CHANGED
@@ -97,6 +97,22 @@ module Blufin
|
|
97
97
|
rs
|
98
98
|
end
|
99
99
|
|
100
|
+
# Strips all newline character(s) -- IE: "abc\n" -> "abc"
|
101
|
+
# Be careful, if multiple newlines are in string, they will all get stripped and you might end up with weird output.
|
102
|
+
# @return string
|
103
|
+
def self.strip_newline(string)
|
104
|
+
raise RuntimeError, "Expected String, instead got: #{string.class}" unless string.is_a?(String)
|
105
|
+
string.gsub(/[\r\n]+/m, '').strip
|
106
|
+
end
|
107
|
+
|
108
|
+
# Removes all colors from a string.
|
109
|
+
# https://stackoverflow.com/questions/16032726/removing-color-decorations-from-strings-before-writing-them-to-logfile
|
110
|
+
# @return string
|
111
|
+
def self.strip_ansi_colors(string)
|
112
|
+
raise RuntimeError, "Expected String, instead got: #{string.class}" unless string.is_a?(String)
|
113
|
+
string.gsub(/\e\[([;\d]+)?m/, '').strip
|
114
|
+
end
|
115
|
+
|
100
116
|
private
|
101
117
|
|
102
118
|
# Internal string validation.
|
data/lib/core/terminal.rb
CHANGED
@@ -22,6 +22,7 @@ module Blufin
|
|
22
22
|
MSG_PROGRESS = 'progress'
|
23
23
|
MSG_CUSTOM = 'custom'
|
24
24
|
MSG_CUSTOM_AUTO_PAD = 'custom_auto_pad'
|
25
|
+
ERR_OUTPUT = '/tmp/execute-output'
|
25
26
|
|
26
27
|
# Runs a series of commands in the terminal.
|
27
28
|
# @return void
|
@@ -77,30 +78,39 @@ module Blufin
|
|
77
78
|
# If capture: is false, returns TRUE:FALSE whether command was successful or not.
|
78
79
|
# If capture: is true, returns raw output of command.
|
79
80
|
# See: https://github.com/piotrmurach/tty-spinner/blob/master/lib/tty/spinner/formats.rb (for spinner options).
|
80
|
-
# @return
|
81
|
-
|
81
|
+
# @return idx-1 => Command was successful (exit code 0) will output TRUE if capture: FALSE or otherwise STDOUT will be returned, IE: 'On branch master'
|
82
|
+
# @return idx-2 => STDERR (if any), IE: anything written to /tmp/execute-output
|
83
|
+
def self.execute(command, path = nil, capture: false, verbose: true, text: nil, error_output: ERR_OUTPUT, display_error: true, ignore_error: false)
|
82
84
|
text = text.is_a?(String) ? text : command
|
83
85
|
t1 = Time.now
|
84
86
|
spinner = TTY::Spinner.new("[:spinner] \x1B[38;5;208m#{text}#{!path.nil? ? " \x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;240m#{path}" : nil}\x1B[0m", format: :dots) if verbose
|
85
87
|
spinner.auto_spin if verbose
|
86
88
|
path = File.expand_path('~/') if path.nil?
|
89
|
+
path = File.expand_path(path) unless path.nil?
|
90
|
+
`echo '' > #{ERR_OUTPUT}`
|
87
91
|
if capture
|
88
|
-
res = `cd #{path} && #{command}`
|
92
|
+
res = `cd #{path} && #{command} 2>#{error_output}`
|
89
93
|
else
|
90
|
-
res = system("cd #{path} && #{command}
|
94
|
+
res = system("cd #{path} && #{command} 1>/dev/null 2>#{error_output}")
|
91
95
|
end
|
92
96
|
t2 = Time.now
|
93
97
|
delta = "#{'%.3f' % (t2 - t1).abs}s"
|
94
|
-
|
98
|
+
error = `cat #{ERR_OUTPUT}`.to_s.strip
|
99
|
+
if (res && error.length == 0) || ignore_error
|
95
100
|
spinner.success("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;46mComplete \x1B[38;5;240m(#{delta})\x1B[0m\x1B[0m") if verbose
|
101
|
+
res = true unless capture
|
96
102
|
else
|
97
103
|
spinner.error("\x1B[38;5;246m\xe2\x86\x92 \x1B[38;5;196mFailed (#{delta})\x1B[0m") if verbose
|
98
104
|
# If there is an error, output it (even if verbose == false).
|
99
|
-
|
100
|
-
|
101
|
-
|
105
|
+
if error.length > 0 && display_error
|
106
|
+
puts "\x1B[38;5;240m"
|
107
|
+
system("cat #{ERR_OUTPUT}")
|
108
|
+
puts "\x1B[0m"
|
109
|
+
end
|
110
|
+
res = false unless capture
|
102
111
|
end
|
103
|
-
|
112
|
+
error = nil if error.nil? || error.strip == ''
|
113
|
+
return res, error
|
104
114
|
end
|
105
115
|
|
106
116
|
# Same as above but with a proc/lambda instead of a terminal command.
|
@@ -124,11 +134,9 @@ module Blufin
|
|
124
134
|
# If CUSTOM, title must be 11 characters to match existing scheme.
|
125
135
|
# @return void
|
126
136
|
def self.output(message = 'No message', type = MSG_INFO, title = nil, bg_color = nil)
|
127
|
-
|
128
137
|
raise RuntimeError, "'title' cannot be nil." if title.nil? && [MSG_CUSTOM, MSG_CUSTOM_AUTO_PAD].include?(type)
|
129
138
|
raise RuntimeError, "'bg_color' cannot be nil." if bg_color.nil? && [MSG_CUSTOM, MSG_CUSTOM_AUTO_PAD].include?(type)
|
130
139
|
raise RuntimeError, "'title' cannot be more that 9 characerts when MSG_CUSTOM_AUTO_PAD is the message type." if type == MSG_CUSTOM_AUTO_PAD && title.length > 9
|
131
|
-
|
132
140
|
case type
|
133
141
|
when MSG_INFO
|
134
142
|
puts "\x1B[38;5;231m\x1B[48;5;22m Executing \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
|
@@ -151,8 +159,7 @@ module Blufin
|
|
151
159
|
when MSG_CUSTOM_AUTO_PAD
|
152
160
|
puts "\x1B[38;5;231m#{''.rjust((9 - title.length), ' ')}\x1B[48;5;#{bg_color}m #{title} \x1B[0m \x1B[0m\xe2\x86\x92\x1B[0m \x1B[0m#{message}\x1B[0m\n"
|
153
161
|
else
|
154
|
-
|
155
|
-
exit
|
162
|
+
raise RuntimeError, "Invalid Terminal::output type: #{type}"
|
156
163
|
end
|
157
164
|
end
|
158
165
|
|
@@ -170,7 +177,7 @@ module Blufin
|
|
170
177
|
puts if preceding_blank_line
|
171
178
|
puts " \x1B[38;5;231m\x1B[48;5;124m #{error_text} \x1B[0m #{title.nil? ? '' : "\xe2\x86\x92 "}#{title}\n"
|
172
179
|
parse_messages(message)
|
173
|
-
exit if exit_script
|
180
|
+
exit 1 if exit_script
|
174
181
|
end
|
175
182
|
|
176
183
|
# Displays automatic message.
|
@@ -607,7 +614,10 @@ module Blufin
|
|
607
614
|
type = type.downcase
|
608
615
|
types = {
|
609
616
|
'yml' => Rouge::Lexers::YAML.new,
|
610
|
-
'json' => Rouge::Lexers::JSON.new
|
617
|
+
'json' => Rouge::Lexers::JSON.new,
|
618
|
+
'js' => Rouge::Lexers::Javascript.new,
|
619
|
+
'java' => Rouge::Lexers::Javascript.new,
|
620
|
+
'sass' => Rouge::Lexers::Sass.new
|
611
621
|
}
|
612
622
|
raise RuntimeError, "Lexer not defined for type: #{type}" unless types.has_key?(type)
|
613
623
|
repeat = ' ' * indent
|
data/lib/core/update.rb
ADDED
data/lib/core/yml.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Blufin
|
2
|
+
|
3
|
+
class Yml
|
4
|
+
|
5
|
+
# Validate and initialize a YML file.
|
6
|
+
# @return Hash
|
7
|
+
def self.read_file(content_file, schema_file)
|
8
|
+
content_file = File.expand_path(content_file)
|
9
|
+
schema_file = File.expand_path(schema_file)
|
10
|
+
# Check file(s) exist.
|
11
|
+
[content_file, schema_file].each { |file| Blufin::Terminal::error("File not found: #{schema_file}") unless Blufin::Files::file_exists(file) }
|
12
|
+
# Parse the schema file.
|
13
|
+
begin
|
14
|
+
schema_file_parsed = YAML.load_file(schema_file)
|
15
|
+
rescue => e
|
16
|
+
Blufin::Terminal::error("Failed to parse schema file: #{Blufin::Terminal::format_directory(schema_file)}", e.message)
|
17
|
+
end
|
18
|
+
# Initialize the validator.
|
19
|
+
validator = Kwalify::Validator.new(schema_file_parsed)
|
20
|
+
# Validate the content file.
|
21
|
+
begin
|
22
|
+
document = YAML.load_file(content_file)
|
23
|
+
# noinspection RubyArgCount
|
24
|
+
errors = validator.validate(document)
|
25
|
+
rescue => e
|
26
|
+
Blufin::Terminal::error("Failed to parse content file: #{Blufin::Terminal::format_directory(content_file)}", e.message)
|
27
|
+
end
|
28
|
+
# If errors occurred, display and bomb-out.
|
29
|
+
if errors && !errors.empty?
|
30
|
+
errors_output = []
|
31
|
+
errors.each { |e|
|
32
|
+
errors_output << "[#{e.path}] #{e.message}"
|
33
|
+
}
|
34
|
+
Blufin::Terminal::error("File had errors: #{Blufin::Terminal::format_directory(content_file)}", errors_output)
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
# Otherwise, return valid YML.
|
38
|
+
document
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Blufin
|
2
|
+
|
3
|
+
class GenerateBase
|
4
|
+
|
5
|
+
OUTPUT_GENERATE = 'generated'
|
6
|
+
OUTPUT_SKIP = 'exists'
|
7
|
+
|
8
|
+
# Standardized output for code-generation.
|
9
|
+
# @return void
|
10
|
+
def self.output(message, type)
|
11
|
+
|
12
|
+
case type
|
13
|
+
when OUTPUT_GENERATE
|
14
|
+
puts "\x1B[38;5;231m\x1B[48;5;55m Generated \x1B[0m \xe2\x86\x92 \x1B[38;5;182m#{message}\x1B[0m"
|
15
|
+
when OUTPUT_SKIP
|
16
|
+
puts " \x1B[38;5;246m\x1B[48;5;234m Skipping \x1B[0m \xe2\x86\x92 \x1B[38;5;240m#{message}\x1B[0m"
|
17
|
+
else
|
18
|
+
raise "Unrecognized output type: #{type}"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module Blufin
|
2
|
+
|
3
|
+
class GenerateUiRoutes
|
4
|
+
|
5
|
+
SCHEMA_FILE = "#{Blufin::Base::get_base_path}#{Blufin::Base::OPT_PATH}/schema/routes.yml"
|
6
|
+
HOME_TERM = 'dashboard'
|
7
|
+
|
8
|
+
# Generates routes in the UI.
|
9
|
+
# @return void
|
10
|
+
def self.run(project)
|
11
|
+
raise RuntimeError, "Expected project type to be: #{Blufin::Projects::TYPE_UI}, instead got: #{project[Blufin::Projects::TYPE]}" unless project[Blufin::Projects::TYPE] == Blufin::Projects::TYPE_UI
|
12
|
+
@errors = []
|
13
|
+
@warnings = []
|
14
|
+
@files = []
|
15
|
+
@files_to_create = []
|
16
|
+
begin
|
17
|
+
@output = <<TEMPLATE
|
18
|
+
const routes = [
|
19
|
+
{
|
20
|
+
TEMPLATE
|
21
|
+
project_path = Blufin::Projects::get_project_path(project[Blufin::Projects::PROJECT_ID], true)
|
22
|
+
routes_file = "#{project_path}/#{project[Blufin::Projects::UI][Blufin::Projects::ROUTES_FILE]}"
|
23
|
+
routes_yml = Blufin::Yml::read_file(routes_file, SCHEMA_FILE)
|
24
|
+
target_file = "#{project_path}/src/#{Blufin::Strings::remove_surrounding_slashes(routes_yml['RoutesFile'])}"
|
25
|
+
layout = routes_yml['Layout']
|
26
|
+
pages_root = routes_yml['PagesRoot']
|
27
|
+
pages_not_found = routes_yml['Pages404']
|
28
|
+
path_pages_ignore = []
|
29
|
+
path_to_pages = "#{project_path}/src/#{pages_root}"
|
30
|
+
# Add ignore path(s) -- if exists.
|
31
|
+
routes_yml['Ignore'].each { |ignore_path| path_pages_ignore << "#{Blufin::Strings::remove_surrounding_slashes(ignore_path)}" } if routes_yml.has_key?('Ignore')
|
32
|
+
routes = routes_yml['Routes']
|
33
|
+
routes.each_with_index do |route, idx|
|
34
|
+
comma = route.has_key?('Children') ? ',' : nil
|
35
|
+
parent_path = route['Parent']['Path']
|
36
|
+
@output += <<TEMPLATE
|
37
|
+
path: '/#{route['Parent'].has_key?('RootPath') && route['Parent']['RootPath'] ? nil : parent_path}',
|
38
|
+
sectionName: '#{route['Parent']['Name']}',
|
39
|
+
sectionIcon: '#{route['Parent']['Icon']}',
|
40
|
+
component: () => import('./../#{layout}')#{comma}
|
41
|
+
TEMPLATE
|
42
|
+
file = parent_path == HOME_TERM ? "#{pages_root}/#{parent_path}/#{HOME_TERM}.vue" : "#{pages_root}/#{parent_path}/#{parent_path}-#{HOME_TERM}.vue"
|
43
|
+
prefix = parent_path == HOME_TERM ? 'dashboard/' : nil
|
44
|
+
process_file(file, project_path)
|
45
|
+
if route.has_key?('Children')
|
46
|
+
@output += <<TEMPLATE
|
47
|
+
children: [
|
48
|
+
{
|
49
|
+
path: '',
|
50
|
+
component: () => import('./../#{file}')
|
51
|
+
},
|
52
|
+
TEMPLATE
|
53
|
+
children = route['Children']
|
54
|
+
children.each_with_index do |child, idx|
|
55
|
+
comma = idx == children.length - 1 ? nil : ','
|
56
|
+
file = "#{pages_root}/#{parent_path}/#{child['Path']}.vue"
|
57
|
+
disabled = child.has_key?('Disabled') && child['Disabled'] ? "\n disabled: true," : nil
|
58
|
+
process_file(file, project_path)
|
59
|
+
@output += <<TEMPLATE
|
60
|
+
{
|
61
|
+
path: '#{prefix}#{child['Path']}',
|
62
|
+
name: '#{child['Name']}',#{disabled}
|
63
|
+
component: () => import('./../#{file}')
|
64
|
+
}#{comma}
|
65
|
+
TEMPLATE
|
66
|
+
end
|
67
|
+
@output += " ]\n"
|
68
|
+
end
|
69
|
+
|
70
|
+
if idx == routes.length - 1
|
71
|
+
@output += <<TEMPLATE
|
72
|
+
}
|
73
|
+
TEMPLATE
|
74
|
+
else
|
75
|
+
@output += <<TEMPLATE
|
76
|
+
},
|
77
|
+
{
|
78
|
+
TEMPLATE
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
@output += <<TEMPLATE
|
83
|
+
];
|
84
|
+
|
85
|
+
if (process.env.MODE !== 'ssr') {
|
86
|
+
routes.push({
|
87
|
+
path: '*',
|
88
|
+
component: () => import('./../#{pages_not_found}')
|
89
|
+
});
|
90
|
+
}
|
91
|
+
|
92
|
+
export default routes;
|
93
|
+
TEMPLATE
|
94
|
+
rescue => e
|
95
|
+
Blufin::Terminal::print_exception(e)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Write the output to the file.
|
99
|
+
Blufin::Files::write_file(target_file, Blufin::Arrays::convert_string_to_line_array(@output))
|
100
|
+
|
101
|
+
# Output message.
|
102
|
+
Blufin::Terminal::info('Generating route file(s):') if @files_to_create.any?
|
103
|
+
|
104
|
+
# Write all the blank route files.
|
105
|
+
@files_to_create.each do |ftc|
|
106
|
+
if Blufin::Files::file_exists(ftc)
|
107
|
+
Blufin::GenerateBase::output(ftc, Blufin::GenerateBase::OUTPUT_SKIP)
|
108
|
+
else
|
109
|
+
Blufin::Files::write_file(ftc, Blufin::Arrays::convert_string_to_line_array(get_blank_file_content))
|
110
|
+
Blufin::GenerateBase::output(ftc, Blufin::GenerateBase::OUTPUT_GENERATE)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
puts if @files_to_create.any?
|
115
|
+
|
116
|
+
# Check for rogue files.
|
117
|
+
Blufin::Files::get_files_in_dir(path_to_pages).each do |file|
|
118
|
+
next if file =~ /#{path_to_pages}\/(#{path_pages_ignore.join('|')})/
|
119
|
+
unless @files_to_create.include?(file)
|
120
|
+
@warnings << "Found rogue file: #{Blufin::Terminal::format_invalid(file)}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
return @errors, @warnings
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
# Ensures that we're not defining a path twice.
|
131
|
+
# Creates file if not exists
|
132
|
+
# @return void
|
133
|
+
def self.process_file(file, project_path)
|
134
|
+
Blufin::Terminal::error("Duplicate file: #{Blufin::Terminal::format_invalid(file)}", 'Your configuration would produce a duplicate file. Please fix.') if @files.include?(file)
|
135
|
+
@files << file
|
136
|
+
@files_to_create << "#{project_path}/src/#{file}"
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns the contents of a blank file.
|
140
|
+
# @return string
|
141
|
+
def self.get_blank_file_content
|
142
|
+
<<TEMPLATE
|
143
|
+
<template>
|
144
|
+
<div>
|
145
|
+
<base-title h="5" :nudge-right="30" :nudge-up="10">{{ $route.name }}</base-title>
|
146
|
+
</div>
|
147
|
+
</template>
|
148
|
+
|
149
|
+
<script type="text/ecmascript-6">
|
150
|
+
export default {
|
151
|
+
data() {
|
152
|
+
return {
|
153
|
+
test: this.$route.path
|
154
|
+
};
|
155
|
+
}
|
156
|
+
};
|
157
|
+
</script>
|
158
|
+
|
159
|
+
<style scoped type="text/scss" lang="scss"></style>
|
160
|
+
TEMPLATE
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
data/lib/version.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
BLUFIN_LIB_VERSION = '1.
|
1
|
+
BLUFIN_LIB_VERSION = '1.8.0'
|
data/opt/schema/projects.yml
CHANGED
@@ -56,7 +56,15 @@ mapping:
|
|
56
56
|
Remote:
|
57
57
|
required: yes
|
58
58
|
ProjectRoot:
|
59
|
-
required:
|
59
|
+
required: no
|
60
|
+
Upstream:
|
61
|
+
type: seq
|
62
|
+
sequence:
|
63
|
+
- type: str
|
64
|
+
Downstream:
|
65
|
+
type: seq
|
66
|
+
sequence:
|
67
|
+
- type: str
|
60
68
|
Run:
|
61
69
|
type: map
|
62
70
|
mapping:
|
@@ -137,4 +145,9 @@ mapping:
|
|
137
145
|
type: seq
|
138
146
|
required: yes
|
139
147
|
sequence:
|
140
|
-
- type: str
|
148
|
+
- type: str
|
149
|
+
UI:
|
150
|
+
type: map
|
151
|
+
mapping:
|
152
|
+
RoutesFile:
|
153
|
+
required: yes
|
@@ -0,0 +1,44 @@
|
|
1
|
+
type: map
|
2
|
+
mapping:
|
3
|
+
Layout:
|
4
|
+
required: true
|
5
|
+
PagesRoot:
|
6
|
+
required: true
|
7
|
+
Pages404:
|
8
|
+
required: true
|
9
|
+
RoutesFile:
|
10
|
+
required: true
|
11
|
+
Ignore:
|
12
|
+
type: seq
|
13
|
+
sequence:
|
14
|
+
- type: str
|
15
|
+
Routes:
|
16
|
+
type: seq
|
17
|
+
required: true
|
18
|
+
sequence:
|
19
|
+
- type: map
|
20
|
+
mapping:
|
21
|
+
Parent:
|
22
|
+
type: map
|
23
|
+
required: true
|
24
|
+
mapping:
|
25
|
+
Path:
|
26
|
+
required: true
|
27
|
+
Name:
|
28
|
+
required: true
|
29
|
+
Icon:
|
30
|
+
required: true
|
31
|
+
RootPath:
|
32
|
+
type: bool
|
33
|
+
Children:
|
34
|
+
type: seq
|
35
|
+
sequence:
|
36
|
+
- type: map
|
37
|
+
required: true
|
38
|
+
mapping:
|
39
|
+
Path:
|
40
|
+
required: true
|
41
|
+
Name:
|
42
|
+
required: true
|
43
|
+
Disabled:
|
44
|
+
type: bool
|