ruby-grafana-reporter 0.2.2 → 0.3.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.
@@ -160,12 +160,11 @@ module GrafanaReporter
160
160
 
161
161
  # provide report
162
162
  @logger.debug("Returning PDF report at #{report.path}")
163
- content = File.read(report.path)
163
+ content = File.read(report.path, mode: 'rb')
164
164
  return http_response(200, 'OK', content, "Content-Type": 'application/pdf') if content.start_with?('%PDF')
165
165
 
166
- # TODO: properly provide file as zip
167
166
  http_response(200, 'OK', content, "Content-Type": 'application/octet-stream',
168
- "Content-Disposition": 'attachment; filename=report.zip')
167
+ "Content-Disposition": "attachment; filename=report_#{attrs['report_id']}.zip")
169
168
  end
170
169
 
171
170
  def render_report(attrs)
@@ -46,8 +46,10 @@ module GrafanaReporter
46
46
  def pre_process(_grafana)
47
47
  raise MissingMandatoryAttributeError, 'columns' unless @variables['columns']
48
48
 
49
- @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
50
- @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
49
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] ||
50
+ @variables['grafana_default_from_timezone'])
51
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] ||
52
+ @variables['grafana_default_to_timezone'])
51
53
  end
52
54
 
53
55
  # Filter the query result for the given columns and sets the result in the preformatted SQL
@@ -73,11 +75,6 @@ module GrafanaReporter
73
75
  result = format_columns(result, @variables['format'])
74
76
  result = replace_values(result, @variables.select { |k, _v| k =~ /^replace_values_\d+/ })
75
77
  result = filter_columns(result, @variables['filter_columns'])
76
- if @variables['filter_column']
77
- @report.logger.warn("DEPRECATED: Call of no longer supported function 'filter_column' has been found."\
78
- " Rename to 'filter_columns'")
79
- result = filter_columns(result, @variables['filter_column'])
80
- end
81
78
 
82
79
  @result = result[:content].map { |row| "| #{row.map { |item| item.to_s.gsub('|', '\\|') }.join(' | ')}" }
83
80
  end
@@ -43,8 +43,10 @@ module GrafanaReporter
43
43
  def pre_process(_grafana)
44
44
  raise MissingMandatoryAttributeError, 'columns' unless @variables['columns']
45
45
 
46
- @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
47
- @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
46
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] ||
47
+ @variables['grafana_default_from_timezone'])
48
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] ||
49
+ @variables['grafana_default_to_timezone'])
48
50
  end
49
51
 
50
52
  # Filters the query result for the given columns and sets the result
@@ -70,11 +72,6 @@ module GrafanaReporter
70
72
  result = format_columns(result, @variables['format'])
71
73
  result = replace_values(result, @variables.select { |k, _v| k =~ /^replace_values_\d+/ })
72
74
  result = filter_columns(result, @variables['filter_columns'])
73
- if @variables['filter_column']
74
- @report.logger.warn("DEPRECATED: Call of no longer supported function 'filter_column' has been found."\
75
- " Rename to 'filter_columns'")
76
- result = filter_columns(result, @variables['filter_column'])
77
- end
78
75
 
79
76
  @result = result[:content].map { |row| "| #{row.map { |item| item.to_s.gsub('|', '\\|') }.join(' | ')}" }
80
77
  end
@@ -70,7 +70,7 @@ module GrafanaReporter
70
70
  selected_attrs = attrs.select do |k, _v|
71
71
  k =~ /(?:columns|limit|alertId|dashboardId|panelId|userId|type|tags)/
72
72
  end
73
- query.merge_variables(selected_attrs.each_with_object({}) { |(k,v), h| h[k] = ::Grafana::Variable.new(v) })
73
+ query.merge_variables(selected_attrs.each_with_object({}) { |(k, v), h| h[k] = ::Grafana::Variable.new(v) })
74
74
  @report.logger.debug("from: #{query.from}, to: #{query.to}")
75
75
 
76
76
  begin
@@ -31,7 +31,7 @@ module GrafanaReporter
31
31
  # +call+ - regular call to the reporter hook (*mandatory*)
32
32
  #
33
33
  # +variable_name+ - name of the variable, to which the result shall be assigned (*mandatory*)
34
- class ValueAsVariableIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
34
+ class ValueAsVariableIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
35
35
  include ProcessorMixin
36
36
 
37
37
  # :nodoc:
@@ -52,7 +52,8 @@ module GrafanaReporter
52
52
  @report.logger.debug("Processing ValueAsVariableIncludeProcessor (call: #{call}, target: #{target},"\
53
53
  " variable_name: #{attribute}, attrs: #{attrs})")
54
54
  if !call || !attribute
55
- @report.logger.error("Missing mandatory attribute 'call' or 'variable_name'.")
55
+ @report.logger.error('ValueAsVariableIncludeProcessor: Missing mandatory attribute \'call\' or '\
56
+ '\'variable_name\'.')
56
57
  return reader
57
58
  end
58
59
 
@@ -64,7 +65,8 @@ module GrafanaReporter
64
65
  # TODO: properly show error messages also in document
65
66
  ext = doc.extensions.find_inline_macro_extension(call) if doc.extensions.inline_macros?
66
67
  if !ext
67
- @report.logger.error("Could not find inline macro extension for '#{call}'.")
68
+ @report.logger.error('ValueAsVariableIncludeProcessor: Could not find inline macro extension for '\
69
+ "'#{call}'.")
68
70
  else
69
71
  @report.logger.debug('ValueAsVariableIncludeProcessor: Calling sub-method.')
70
72
  item = ext.process_method.call(doc, target, attrs)
@@ -73,7 +75,7 @@ module GrafanaReporter
73
75
  @report.logger.debug("ValueAsVariableIncludeProcessor: Adding '#{result}' to document.")
74
76
  reader.unshift_line(result)
75
77
  else
76
- @report.logger.debug("ValueAsVariableIncludeProcessor: Not adding variable '#{attribute}',"\
78
+ @report.logger.debug("ValueAsVariableIncludeProcessor: Not adding variable '#{attribute}'"\
77
79
  ' as query result was empty.')
78
80
  end
79
81
  end
@@ -30,17 +30,17 @@ module GrafanaReporter
30
30
  #{functions_as_text(opts.merge(level: opts[:level] + 1))})
31
31
  end
32
32
 
33
- def toc(opts = {})
33
+ def toc
34
34
  result = []
35
35
 
36
36
  result << "Table of contents"
37
37
  result << "* [Global options](#global-options)"
38
- prepared_help[:global_options].sort.map do |k, v|
38
+ prepared_help[:global_options].sort.map do |k, _v|
39
39
  result << " * [#{k}](##{k.downcase})"
40
40
  end
41
41
 
42
42
  result << "* [Functions](#functions)"
43
- prepared_help[:functions].sort.map do |k, v|
43
+ prepared_help[:functions].sort.map do |k, _v|
44
44
  result << " * [#{k}](##{k.downcase})"
45
45
  end
46
46
 
@@ -122,7 +122,7 @@ end}
122
122
  end
123
123
 
124
124
  def raw_help_yaml
125
- return <<YAML_HELP
125
+ <<YAML_HELP
126
126
  global_options:
127
127
  grafana_default_instance:
128
128
  call: ":grafana_default_instance: <instance_name>"
@@ -26,8 +26,10 @@ module GrafanaReporter
26
26
  @datasource = @panel.field('datasource')
27
27
  @datasource_id = grafana.datasource_id(@datasource)
28
28
  super(grafana)
29
- @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
30
- @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
29
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] ||
30
+ @variables['grafana_default_from_timezone'])
31
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] ||
32
+ @variables['grafana_default_to_timezone'])
31
33
  end
32
34
  end
33
35
  end
@@ -9,11 +9,13 @@ module GrafanaReporter
9
9
  # Sets the proper render variables.
10
10
  def pre_process(grafana)
11
11
  super
12
- @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
13
- @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
14
- # TODO: ensure that in case of timezones are specified, that they are also forwarded to the image renderer as URL parameter
12
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] ||
13
+ @variables['grafana_default_from_timezone'])
14
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] ||
15
+ @variables['grafana_default_to_timezone'])
16
+ # TODO: ensure that in case of timezones are specified, that they are also forwarded to the image renderer
15
17
  # rename "render-" variables
16
- @variables = @variables.each_with_object({}) { |(k,v), h| h[k.gsub(/^render-/, '')] = v }
18
+ @variables = @variables.each_with_object({}) { |(k, v), h| h[k.gsub(/^render-/, '')] = v }
17
19
  end
18
20
 
19
21
  # Returns the body of the http query, which contains the raw image.
@@ -30,8 +30,10 @@ module GrafanaReporter
30
30
  @datasource = @panel.field('datasource')
31
31
  @datasource_id = grafana.datasource_id(@datasource)
32
32
  super(grafana)
33
- @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
34
- @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
33
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] ||
34
+ @variables['grafana_default_from_timezone'])
35
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] ||
36
+ @variables['grafana_default_to_timezone'])
35
37
  end
36
38
  end
37
39
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GrafanaReporter
2
4
  module Asciidoctor
3
5
  # This mixin contains several common methods, which can be used within the queries.
@@ -8,8 +10,17 @@ module GrafanaReporter
8
10
  # @param item_hash [Hash] variables from item configuration level, i.e. specific call, which may override document
9
11
  # @return [void]
10
12
  def merge_hash_variables(document_hash, item_hash)
11
- merge_variables(document_hash.select { |k, _v| k =~ /^var-/ || k == 'grafana-report-timestamp' || k =~ /grafana_default_(?:from|to)_timezone/ }.each_with_object({}) { |(k,v), h| h[k] = ::Grafana::Variable.new(v) })
12
- merge_variables(item_hash.select { |k, _v| k =~ /^var-/ || k =~ /^render-/ || k =~ /filter_columns|format|replace_values_.*|transpose|column_divider|row_divider|from_timezone|to_timezone/ }.each_with_object({}) { |(k,v), h| h[k] = ::Grafana::Variable.new(v) })
13
+ sel_doc_items = document_hash.select do |k, _v|
14
+ k =~ /^var-/ || k == 'grafana-report-timestamp' || k =~ /grafana_default_(?:from|to)_timezone/
15
+ end
16
+ merge_variables(sel_doc_items.each_with_object({}) { |(k, v), h| h[k] = ::Grafana::Variable.new(v) })
17
+
18
+ sel_items = item_hash.select do |k, _v|
19
+ k =~ /^var-/ || k =~ /^render-/ || k =~ /filter_columns|format|replace_values_.*|transpose|column_divider|
20
+ row_divider|from_timezone|to_timezone/x
21
+ end
22
+ merge_variables(sel_items.each_with_object({}) { |(k, v), h| h[k] = ::Grafana::Variable.new(v) })
23
+
13
24
  self.timeout = item_hash['timeout'] || document_hash['grafana-default-timeout'] || timeout
14
25
  self.from = item_hash['from'] || document_hash['from'] || from
15
26
  self.to = item_hash['to'] || document_hash['to'] || to
@@ -227,7 +238,7 @@ module GrafanaReporter
227
238
  # calculated for +from+
228
239
  # @param timezone [Grafana::Variable] timezone to use, if not system timezone
229
240
  # @return [String] translated date as timestamp string
230
- def translate_date(orig_date, report_time, is_to_time, timezone=nil)
241
+ def translate_date(orig_date, report_time, is_to_time, timezone = nil)
231
242
  report_time ||= Variable.new(Time.now.to_s)
232
243
  return (DateTime.parse(report_time.raw_value).to_time.to_i * 1000).to_s unless orig_date
233
244
  return orig_date if orig_date =~ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/
@@ -238,7 +249,7 @@ module GrafanaReporter
238
249
  raise TimeRangeUnknownError, orig_date unless date_splitted
239
250
 
240
251
  date = DateTime.parse(report_time.raw_value)
241
- # TODO: allow from_translated or similar in ADOC template
252
+ # TODO: allow from_translated or similar in ADOC template
242
253
  date = date.new_offset(timezone.raw_value) if timezone
243
254
 
244
255
  # substract specified time
@@ -115,8 +115,7 @@ module GrafanaReporter
115
115
  unless @grafana_instances[instance]
116
116
  @grafana_instances[instance] = ::Grafana::Grafana.new(@config.grafana_host(instance),
117
117
  @config.grafana_api_key(instance),
118
- logger: @logger,
119
- datasources: @config.grafana_datasources(instance))
118
+ logger: @logger, ssl_cert: @config.ssl_cert)
120
119
  end
121
120
  @grafana_instances[instance]
122
121
  end
@@ -19,13 +19,11 @@ module GrafanaReporter
19
19
  results = replace_values(results, @variables.select { |k, _v| k =~ /^replace_values_\d+/ })
20
20
  results = filter_columns(results, @variables['filter_columns'])
21
21
 
22
- unless results[:content].empty?
23
- unless results[:content][0].empty?
24
- @result = results[:content][0][0]
25
- return
26
- end
27
- end
28
22
  @result = ''
23
+ return if results[:content].empty?
24
+ return if results[:content][0].empty?
25
+
26
+ @result = results[:content][0][0]
29
27
  end
30
28
 
31
29
  # Translates the from and to times.
@@ -34,8 +32,10 @@ module GrafanaReporter
34
32
  # @return [void]
35
33
  def pre_process(grafana)
36
34
  super(grafana)
37
- @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
38
- @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
35
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] ||
36
+ @variables['grafana_default_from_timezone'])
37
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] ||
38
+ @variables['grafana_default_to_timezone'])
39
39
  end
40
40
  end
41
41
  end
@@ -18,15 +18,13 @@ module GrafanaReporter
18
18
  results = replace_values(results, @variables.select { |k, _v| k =~ /^replace_values_\d+/ })
19
19
  results = filter_columns(results, @variables['filter_columns'])
20
20
  results = transpose(results, @variables['transpose'])
21
- row_divider = '| '
22
- row_divider = @variables['row_divider'].raw_value if @variables['row_divider'].is_a?(Grafana::Variable)
23
- column_divider = ' | '
24
- column_divider = @variables['column_divider'].raw_value if @variables['column_divider'].is_a?(Grafana::Variable)
21
+ row_div = @variables['row_divider'].is_a?(Grafana::Variable) ? @variables['row_divider'].raw_value : '| '
22
+ col_div = @variables['column_divider'].is_a?(Grafana::Variable) ? @variables['column_divider'].raw_value : ' | '
25
23
 
26
24
  @result = results[:content].map do |row|
27
- row_divider + row.map do |item|
28
- column_divider == ' | ' ? item.to_s.gsub('|', '\\|') : item.to_s
29
- end.join(column_divider)
25
+ row_div + row.map do |item|
26
+ col_div == ' | ' ? item.to_s.gsub('|', '\\|') : item.to_s
27
+ end.join(col_div)
30
28
  end
31
29
  end
32
30
 
@@ -36,8 +34,10 @@ module GrafanaReporter
36
34
  # @return [void]
37
35
  def pre_process(grafana)
38
36
  super(grafana)
39
- @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
40
- @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
37
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] ||
38
+ @variables['grafana_default_from_timezone'])
39
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] ||
40
+ @variables['grafana_default_to_timezone'])
41
41
  end
42
42
  end
43
43
  end
@@ -27,7 +27,7 @@ module GrafanaReporter
27
27
 
28
28
  def initialize
29
29
  @config = {}
30
- @logger = ::Logger.new($stderr, level: :unknown)
30
+ @logger = ::Logger.new($stderr, level: :info)
31
31
  end
32
32
 
33
33
  attr_accessor :logger
@@ -56,6 +56,11 @@ module GrafanaReporter
56
56
  "#{templates_folder}#{get_config('default-document-attributes:var-template')}.adoc"
57
57
  end
58
58
 
59
+ # @return [String] path to ssl certificate file, if manually specified, or nil
60
+ def ssl_cert
61
+ get_config('grafana-reporter:ssl-cert')
62
+ end
63
+
59
64
  # @return [String] destination filename for the report in {MODE_SINGLE_RENDER}.
60
65
  def to_file
61
66
  return get_config('to_file') || true if mode == MODE_SINGLE_RENDER
@@ -84,16 +89,6 @@ module GrafanaReporter
84
89
  get_config("grafana:#{instance}:api_key")
85
90
  end
86
91
 
87
- # @param instance [String] grafana instance name, for which the value shall be retrieved.
88
- # @return [Hash<String,Integer>] configured datasources for the requested grafana instance. Name as key,
89
- # ID as value.
90
- def grafana_datasources(instance = 'default')
91
- hash = get_config("grafana:#{instance}:datasources")
92
- return nil if hash.nil?
93
-
94
- hash.map { |k, v| [k, v] }.to_h
95
- end
96
-
97
92
  # @return [String] configured folder, in which the report templates are stored including trailing slash.
98
93
  # By default: current folder.
99
94
  def templates_folder
@@ -153,10 +148,11 @@ module GrafanaReporter
153
148
  # This function shall be called, before the configuration object is used in the
154
149
  # {Application::Application#run}. It ensures, that everything is setup properly
155
150
  # and all necessary folders exist. Appropriate errors are raised in case of errors.
151
+ # @param explicit [Boolean] true, if validation shall expect explicit (wizard) configuration file
156
152
  # @return [void]
157
- def validate
153
+ def validate(explicit = false)
158
154
  check_deprecation
159
- validate_schema(schema, @config)
155
+ validate_schema(schema(explicit), @config)
160
156
 
161
157
  # check if set folders exist
162
158
  raise FolderDoesNotExistError.new(reports_folder, 'reports-folder') unless File.directory?(reports_folder)
@@ -176,12 +172,8 @@ module GrafanaReporter
176
172
 
177
173
  cur_pos = @config
178
174
  levels.each do |subpath|
179
- if cur_pos[subpath]
180
- cur_pos = cur_pos[subpath]
181
- else
182
- cur_pos[subpath] = {}
183
- cur_pos = cur_pos[subpath]
184
- end
175
+ cur_pos[subpath] = {} unless cur_pos[subpath]
176
+ cur_pos = cur_pos[subpath]
185
177
  end
186
178
 
187
179
  cur_pos[last_level] = value
@@ -193,7 +185,7 @@ module GrafanaReporter
193
185
  #
194
186
  # param other_config [Configuration] other configuration object
195
187
  def merge!(other_config)
196
- self.config.merge!(other_config.config) { |_key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2) : v2 }
188
+ config.merge!(other_config.config) { |_key, v1, v2| v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2) : v2 }
197
189
  update_configuration
198
190
  end
199
191
 
@@ -202,20 +194,21 @@ module GrafanaReporter
202
194
  def check_deprecation
203
195
  return if report_class
204
196
 
205
- logger.warn('DEPRECATION WARNING: Your configuration explicitly needs to specify the \'grafana-reporter:report-class\' value. '\
206
- 'Currently this defaults to \'GrafanaReporter::Asciidoctor::Report\'. You can get rid of this warning, if you explicitly '\
207
- 'set this configuration in your configuration file. Setting this default will be removed in a future version.')
197
+ logger.warn('DEPRECATION WARNING: Your configuration explicitly needs to specify the '\
198
+ '\'grafana-reporter:report-class\' value. Currently this defaults to '\
199
+ '\'GrafanaReporter::Asciidoctor::Report\'. You can get rid of this warning, if you '\
200
+ 'explicitly set this configuration in your configuration file. Setting this default will be '\
201
+ 'removed in a future version.')
208
202
  set_param('grafana-reporter:report-class', 'GrafanaReporter::Asciidoctor::Report')
209
203
  end
210
204
 
211
205
  def update_configuration
212
- if get_config('grafana-reporter:debug-level') =~ /DEBUG|INFO|WARN|ERROR|FATAL|UNKNOWN/
213
- @logger.level = Object.const_get("::Logger::Severity::#{get_config('grafana-reporter:debug-level')}")
214
- end
206
+ debug_level = get_config('grafana-reporter:debug-level')
207
+ rep_class = get_config('grafana-reporter:report-class')
215
208
 
216
- if get_config('grafana-reporter:report-class')
217
- self.report_class = Object.const_get(get_config('grafana-reporter:report-class'))
218
- end
209
+ @logger.level = Object.const_get("::Logger::Severity::#{debug_level}") if debug_level =~ /DEBUG|INFO|WARN|
210
+ ERROR|FATAL|UNKNOWN/x
211
+ self.report_class = Object.const_get(rep_class) if rep_class
219
212
  end
220
213
 
221
214
  def get_config(path)
@@ -238,20 +231,16 @@ module GrafanaReporter
238
231
 
239
232
  if key.nil?
240
233
  # apply to all on this level
241
- case
242
- when subject.is_a?(Hash)
243
- if subject.length < min_occurence
244
- raise ConfigurationDoesNotMatchSchemaError.new(key, 'occur', min_occurence, subject.length)
245
- end
246
-
247
- subject.each do |k, _v|
248
- sub_scheme = {}
249
- sub_scheme[k] = schema[nil]
250
- validate_schema(sub_scheme, subject)
251
- end
252
-
253
- else
254
- raise ConfigurationError, "Unhandled configuration data type '#{subject.class}'."
234
+ raise ConfigurationError, "Unhandled configuration data type '#{subject.class}'." unless subject.is_a?(Hash)
235
+
236
+ if subject.length < min_occurence
237
+ raise ConfigurationDoesNotMatchSchemaError.new(key, 'occur', min_occurence, subject.length)
238
+ end
239
+
240
+ subject.each do |k, _v|
241
+ sub_scheme = {}
242
+ sub_scheme[k] = schema[nil]
243
+ validate_schema(sub_scheme, subject)
255
244
  end
256
245
 
257
246
  # apply to single item
@@ -277,7 +266,7 @@ module GrafanaReporter
277
266
  end
278
267
  end
279
268
 
280
- def schema
269
+ def schema(explicit)
281
270
  {
282
271
  'grafana' =>
283
272
  [
@@ -288,13 +277,12 @@ module GrafanaReporter
288
277
  Hash, 1,
289
278
  {
290
279
  'host' => [String, 1],
291
- 'api_key' => [String, 0],
292
- 'datasources' => [Hash, 0, { nil => [Integer, 1] }]
280
+ 'api_key' => [String, 0]
293
281
  }
294
282
  ]
295
283
  }
296
284
  ],
297
- 'default-document-attributes' => [Hash, 0],
285
+ 'default-document-attributes' => [Hash, explicit ? 1 : 0],
298
286
  'grafana-reporter' =>
299
287
  [
300
288
  Hash, 1,
@@ -302,11 +290,12 @@ module GrafanaReporter
302
290
  'debug-level' => [String, 0],
303
291
  'run-mode' => [String, 0],
304
292
  'test-instance' => [String, 0],
305
- 'templates-folder' => [String, 0],
293
+ 'templates-folder' => [String, explicit ? 1 : 0],
306
294
  'report-class' => [String, 1],
307
- 'reports-folder' => [String, 0],
308
- 'report-retention' => [Integer, 0],
309
- 'webservice-port' => [Integer, 0]
295
+ 'reports-folder' => [String, explicit ? 1 : 0],
296
+ 'report-retention' => [Integer, explicit ? 1 : 0],
297
+ 'ssl-cert' => [String, 0],
298
+ 'webservice-port' => [Integer, explicit ? 1 : 0]
310
299
  }
311
300
  ]
312
301
  }