ruby-grafana-reporter 0.2.2 → 0.4.3

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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +126 -88
  3. data/bin/ruby-grafana-reporter +2 -2
  4. data/lib/VERSION.rb +3 -2
  5. data/lib/grafana/abstract_datasource.rb +146 -0
  6. data/lib/grafana/dashboard.rb +1 -3
  7. data/lib/grafana/errors.rb +18 -3
  8. data/lib/grafana/grafana.rb +64 -66
  9. data/lib/grafana/grafana_alerts_datasource.rb +57 -0
  10. data/lib/grafana/grafana_annotations_datasource.rb +56 -0
  11. data/lib/grafana/grafana_property_datasource.rb +30 -0
  12. data/lib/grafana/graphite_datasource.rb +72 -0
  13. data/lib/grafana/image_rendering_datasource.rb +44 -0
  14. data/lib/grafana/influxdb_datasource.rb +70 -0
  15. data/lib/grafana/panel.rb +9 -3
  16. data/lib/grafana/prometheus_datasource.rb +67 -0
  17. data/lib/grafana/sql_datasource.rb +78 -0
  18. data/lib/grafana/unsupported_datasource.rb +7 -0
  19. data/lib/grafana/variable.rb +1 -1
  20. data/lib/grafana/webrequest.rb +71 -0
  21. data/lib/grafana_reporter/abstract_query.rb +460 -0
  22. data/lib/grafana_reporter/abstract_report.rb +139 -18
  23. data/lib/grafana_reporter/alerts_table_query.rb +39 -0
  24. data/lib/grafana_reporter/annotations_table_query.rb +38 -0
  25. data/lib/grafana_reporter/application/application.rb +34 -286
  26. data/lib/grafana_reporter/application/webservice.rb +50 -15
  27. data/lib/grafana_reporter/asciidoctor/alerts_table_include_processor.rb +91 -0
  28. data/lib/grafana_reporter/asciidoctor/annotations_table_include_processor.rb +90 -0
  29. data/lib/grafana_reporter/asciidoctor/panel_image_block_macro.rb +74 -0
  30. data/lib/grafana_reporter/asciidoctor/panel_image_inline_macro.rb +76 -0
  31. data/lib/grafana_reporter/asciidoctor/panel_property_inline_macro.rb +70 -0
  32. data/lib/grafana_reporter/asciidoctor/panel_query_table_include_processor.rb +95 -0
  33. data/lib/grafana_reporter/asciidoctor/panel_query_value_inline_macro.rb +90 -0
  34. data/lib/grafana_reporter/asciidoctor/processor_mixin.rb +49 -0
  35. data/lib/grafana_reporter/asciidoctor/report.rb +32 -76
  36. data/lib/grafana_reporter/asciidoctor/show_environment_include_processor.rb +46 -0
  37. data/lib/grafana_reporter/asciidoctor/show_help_include_processor.rb +35 -0
  38. data/lib/grafana_reporter/asciidoctor/sql_table_include_processor.rb +90 -0
  39. data/lib/grafana_reporter/asciidoctor/sql_value_inline_macro.rb +86 -0
  40. data/lib/grafana_reporter/asciidoctor/value_as_variable_include_processor.rb +90 -0
  41. data/lib/grafana_reporter/configuration.rb +59 -52
  42. data/lib/grafana_reporter/console_configuration_wizard.rb +311 -0
  43. data/lib/grafana_reporter/demo_report_wizard.rb +105 -0
  44. data/lib/grafana_reporter/erb/report.rb +30 -0
  45. data/lib/grafana_reporter/erb/report_jail.rb +21 -0
  46. data/lib/grafana_reporter/errors.rb +55 -0
  47. data/lib/grafana_reporter/help.rb +443 -0
  48. data/lib/grafana_reporter/logger/{two_way_logger.rb → two_way_delegate_logger.rb} +1 -1
  49. data/lib/grafana_reporter/panel_image_query.rb +25 -0
  50. data/lib/grafana_reporter/panel_property_query.rb +22 -0
  51. data/lib/grafana_reporter/query_value_query.rb +61 -0
  52. data/lib/grafana_reporter/report_webhook.rb +35 -0
  53. data/lib/ruby_grafana_extension.rb +8 -0
  54. data/lib/{ruby-grafana-reporter.rb → ruby_grafana_reporter.rb} +1 -0
  55. metadata +47 -39
  56. data/lib/grafana/abstract_panel_query.rb +0 -22
  57. data/lib/grafana/abstract_query.rb +0 -132
  58. data/lib/grafana/abstract_sql_query.rb +0 -51
  59. data/lib/grafana/panel_image_query.rb +0 -52
  60. data/lib/grafana_reporter/asciidoctor/alerts_table_query.rb +0 -104
  61. data/lib/grafana_reporter/asciidoctor/annotations_table_query.rb +0 -99
  62. data/lib/grafana_reporter/asciidoctor/errors.rb +0 -40
  63. data/lib/grafana_reporter/asciidoctor/extensions/alerts_table_include_processor.rb +0 -92
  64. data/lib/grafana_reporter/asciidoctor/extensions/annotations_table_include_processor.rb +0 -91
  65. data/lib/grafana_reporter/asciidoctor/extensions/panel_image_block_macro.rb +0 -69
  66. data/lib/grafana_reporter/asciidoctor/extensions/panel_image_inline_macro.rb +0 -68
  67. data/lib/grafana_reporter/asciidoctor/extensions/panel_property_inline_macro.rb +0 -61
  68. data/lib/grafana_reporter/asciidoctor/extensions/panel_query_table_include_processor.rb +0 -78
  69. data/lib/grafana_reporter/asciidoctor/extensions/panel_query_value_inline_macro.rb +0 -73
  70. data/lib/grafana_reporter/asciidoctor/extensions/processor_mixin.rb +0 -20
  71. data/lib/grafana_reporter/asciidoctor/extensions/show_environment_include_processor.rb +0 -43
  72. data/lib/grafana_reporter/asciidoctor/extensions/show_help_include_processor.rb +0 -30
  73. data/lib/grafana_reporter/asciidoctor/extensions/sql_table_include_processor.rb +0 -70
  74. data/lib/grafana_reporter/asciidoctor/extensions/sql_value_inline_macro.rb +0 -66
  75. data/lib/grafana_reporter/asciidoctor/extensions/value_as_variable_include_processor.rb +0 -86
  76. data/lib/grafana_reporter/asciidoctor/help.rb +0 -435
  77. data/lib/grafana_reporter/asciidoctor/panel_first_value_query.rb +0 -34
  78. data/lib/grafana_reporter/asciidoctor/panel_image_query.rb +0 -26
  79. data/lib/grafana_reporter/asciidoctor/panel_property_query.rb +0 -44
  80. data/lib/grafana_reporter/asciidoctor/panel_table_query.rb +0 -38
  81. data/lib/grafana_reporter/asciidoctor/query_mixin.rb +0 -301
  82. data/lib/grafana_reporter/asciidoctor/sql_first_value_query.rb +0 -42
  83. data/lib/grafana_reporter/asciidoctor/sql_table_query.rb +0 -44
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrafanaReporter
4
+ module Asciidoctor
5
+ # Implements the hook
6
+ # include::grafana_environment[]
7
+ #
8
+ # Shows all available variables, which are accessible during this run of the asciidoctor
9
+ # grafana reporter in a asciidoctor readable form.
10
+ #
11
+ # This processor is very helpful during report template design, to find out the available
12
+ # variables, that can be accessed.
13
+ #
14
+ # == Used document parameters
15
+ # All, to be listed as the available environment.
16
+ class ShowEnvironmentIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
17
+ include ProcessorMixin
18
+
19
+ # :nodoc:
20
+ def handles?(target)
21
+ target.start_with? 'grafana_environment'
22
+ end
23
+
24
+ # :nodoc:
25
+ def process(doc, reader, _target, _attrs)
26
+ # return if @report.cancel
27
+ @report.next_step
28
+ @report.logger.debug('Processing ShowEnvironmentIncludeProcessor')
29
+
30
+ vars = ['== Accessible Variables',
31
+ '|===']
32
+ doc.attributes.sort.each do |k, v|
33
+ vars << "| `+{#{k}}+` | #{v}"
34
+ end
35
+ vars << '|==='
36
+
37
+ reader.unshift_lines vars
38
+ end
39
+
40
+ # @see ProcessorMixin#build_demo_entry
41
+ def build_demo_entry(_panel)
42
+ 'include::grafana_environment[]'
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrafanaReporter
4
+ module Asciidoctor
5
+ # Implements the hook
6
+ # include::grafana_help[]
7
+ #
8
+ # Shows all available options for the asciidoctor grafana reporter in a asciidoctor readable form.
9
+ #
10
+ # == Used document parameters
11
+ # None
12
+ class ShowHelpIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
13
+ include ProcessorMixin
14
+
15
+ # :nodoc:
16
+ def handles?(target)
17
+ target.start_with? 'grafana_help'
18
+ end
19
+
20
+ # :nodoc:
21
+ def process(_doc, reader, _target, _attrs)
22
+ # return if @report.cancel
23
+ @report.next_step
24
+ @report.logger.debug('Processing ShowHelpIncludeProcessor')
25
+
26
+ reader.unshift_lines GrafanaReporter::Help.new.asciidoctor.split("\n")
27
+ end
28
+
29
+ # @see ProcessorMixin#build_demo_entry
30
+ def build_demo_entry(_panel)
31
+ 'include::grafana_help[]'
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrafanaReporter
4
+ module Asciidoctor
5
+ # Implements the hook
6
+ # include::grafana_sql_table:<datasource_id>[<options>]
7
+ #
8
+ # Returns the results of the SQL query as a asciidoctor table.
9
+ #
10
+ # == Used document parameters
11
+ # +grafana_default_instance+ - name of grafana instance, 'default' if not specified
12
+ #
13
+ # +from+ - 'from' time for the sql query
14
+ #
15
+ # +to+ - 'to' time for the sql query
16
+ #
17
+ # All other variables starting with +var-+ will be used to replace grafana templating strings
18
+ # in the given SQL query.
19
+ #
20
+ # == Supported options
21
+ # +sql+ - sql statement (*mandatory*)
22
+ #
23
+ # +instance+ - name of grafana instance, 'default' if not specified
24
+ #
25
+ # +from+ - 'from' time for the sql query
26
+ #
27
+ # +to+ - 'to' time for the sql query
28
+ #
29
+ # +format+ - see {AbstractQuery#format_columns}
30
+ #
31
+ # +replace_values+ - see {AbstractQuery#replace_values}
32
+ #
33
+ # +filter_columns+ - see {AbstractQuery#filter_columns}
34
+ class SqlTableIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
35
+ include ProcessorMixin
36
+
37
+ # :nodoc:
38
+ def handles?(target)
39
+ target.start_with? 'grafana_sql_table:'
40
+ end
41
+
42
+ # :nodoc:
43
+ def process(doc, reader, target, attrs)
44
+ return if @report.cancel
45
+
46
+ @report.next_step
47
+ instance = attrs['instance'] || doc.attr('grafana_default_instance') || 'default'
48
+ attrs['result_type'] = 'sql_table'
49
+ @report.logger.debug("Processing SqlTableIncludeProcessor (instance: #{instance},"\
50
+ " datasource: #{target.split(':')[1]}, sql: #{attrs['sql']})")
51
+
52
+ begin
53
+ # catch properly if datasource could not be identified
54
+ query = QueryValueQuery.new(@report.grafana(instance), variables: build_attribute_hash(doc.attributes, attrs))
55
+ query.datasource = @report.grafana(instance).datasource_by_id(target.split(':')[1].to_i)
56
+ query.raw_query = attrs['sql']
57
+
58
+ reader.unshift_lines query.execute
59
+ rescue GrafanaReporterError => e
60
+ @report.logger.error(e.message)
61
+ reader.unshift_line "|#{e.message}"
62
+ rescue StandardError => e
63
+ @report.logger.fatal(e.message)
64
+ reader.unshift_line "|#{e.message}"
65
+ end
66
+
67
+ reader
68
+ end
69
+
70
+ # @see ProcessorMixin#build_demo_entry
71
+ def build_demo_entry(panel)
72
+ return nil unless panel
73
+ return nil unless panel.model['type'].include?('table')
74
+
75
+ ref_id = nil
76
+ panel.model['targets'].each do |item|
77
+ if !item['hide'] && !panel.query(item['refId']).to_s.empty?
78
+ ref_id = item['refId']
79
+ break
80
+ end
81
+ end
82
+ return nil unless ref_id
83
+
84
+ "|===\ninclude::grafana_sql_table:#{panel.dashboard.grafana.datasource_by_name(panel.model['datasource']).id}"\
85
+ "[sql=\"#{panel.query(ref_id).gsub(/"/, '\"').gsub("\n", ' ').gsub(/\\/, '\\\\')}\",filter_columns=\"time\","\
86
+ "dashboard=\"#{panel.dashboard.id}\",from=\"now-1h\",to=\"now\"]\n|==="
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GrafanaReporter
4
+ module Asciidoctor
5
+ # Implements the hook
6
+ # grafana_sql_value:<datasource_id>[<options>]
7
+ #
8
+ # Returns the first value of the resulting SQL query.
9
+ #
10
+ # == Used document parameters
11
+ # +grafana_default_instance+ - name of grafana instance, 'default' if not specified
12
+ #
13
+ # +from+ - 'from' time for the sql query
14
+ #
15
+ # +to+ - 'to' time for the sql query
16
+ #
17
+ # All other variables starting with +var-+ will be used to replace grafana templating strings
18
+ # in the given SQL query.
19
+ #
20
+ # == Supported options
21
+ # +sql+ - sql statement (*mandatory*)
22
+ #
23
+ # +instance+ - name of grafana instance, 'default' if not specified
24
+ #
25
+ # +from+ - 'from' time for the sql query
26
+ #
27
+ # +to+ - 'to' time for the sql query
28
+ #
29
+ # +format+ - see {AbstractQuery#format_columns}
30
+ #
31
+ # +replace_values+ - see {AbstractQuery#replace_values}
32
+ #
33
+ # +filter_columns+ - see {AbstractQuery#filter_columns}
34
+ class SqlValueInlineMacro < ::Asciidoctor::Extensions::InlineMacroProcessor
35
+ include ProcessorMixin
36
+ use_dsl
37
+
38
+ named :grafana_sql_value
39
+
40
+ # @see GrafanaReporter::Asciidoctor::SqlFirstValueQuery
41
+ def process(parent, target, attrs)
42
+ return if @report.cancel
43
+
44
+ @report.next_step
45
+ instance = attrs['instance'] || parent.document.attr('grafana_default_instance') || 'default'
46
+ attrs['result_type'] = 'sql_value'
47
+ @report.logger.debug("Processing SqlValueInlineMacro (instance: #{instance}, datasource: #{target},"\
48
+ " sql: #{attrs['sql']})")
49
+
50
+ begin
51
+ # catch properly if datasource could not be identified
52
+ query = QueryValueQuery.new(@report.grafana(instance), variables: build_attribute_hash(parent.document.attributes, attrs))
53
+ query.datasource = @report.grafana(instance).datasource_by_id(target)
54
+ query.raw_query = attrs['sql']
55
+
56
+ create_inline(parent, :quoted, query.execute)
57
+ rescue GrafanaReporterError => e
58
+ @report.logger.error(e.message)
59
+ create_inline(parent, :quoted, e.message)
60
+ rescue StandardError => e
61
+ @report.logger.fatal(e.message)
62
+ create_inline(parent, :quoted, e.message)
63
+ end
64
+ end
65
+
66
+ # @see ProcessorMixin#build_demo_entry
67
+ def build_demo_entry(panel)
68
+ return nil unless panel
69
+ return nil unless panel.model['type'] == 'singlestat'
70
+
71
+ ref_id = nil
72
+ panel.model['targets'].each do |item|
73
+ if !item['hide'] && !panel.query(item['refId']).to_s.empty?
74
+ ref_id = item['refId']
75
+ break
76
+ end
77
+ end
78
+ return nil unless ref_id
79
+
80
+ "grafana_sql_value:#{panel.dashboard.grafana.datasource_by_name(panel.model['datasource']).id}"\
81
+ "[sql=\"#{panel.query(ref_id).gsub(/"/, '\"').gsub("\n", ' ').gsub(/\\/, '\\\\')}\",from=\"now-1h\","\
82
+ 'to="now"]'
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'processor_mixin'
4
+
5
+ module GrafanaReporter
6
+ module Asciidoctor
7
+ # Implements the hook
8
+ # include::grafana_value_as_variable[<options>]
9
+ #
10
+ # Returns an attribute definition in asciidoctor format. This is needed if you want to refer to values of
11
+ # a grafana query within a variable in asciidoctor. As this works without this function for the
12
+ # `IncludeProcessor`s values, it will not work for all the other processors.
13
+ #
14
+ # This method is just a proxy for all other hooks and will forward parameters accordingly.
15
+ #
16
+ # Example:
17
+ #
18
+ # include:grafana_value_as_variable[call="grafana_sql_value:1",variable_name="my_variable",sql="SELECT 'looks good'",<any_other_option>]
19
+ #
20
+ # This will call the {SqlValueInlineMacro} with `datasource_id` set to `1` and store the result in the
21
+ # variable. The resulting asciidoctor variable definition will be created as:
22
+ #
23
+ # :my_variable: looks good
24
+ #
25
+ # and can be refered to in your document easily as
26
+ #
27
+ # {my_variable}
28
+ #
29
+ # == Supported options
30
+ # +call+ - regular call to the reporter hook (*mandatory*)
31
+ #
32
+ # +variable_name+ - name of the variable, to which the result shall be assigned (*mandatory*)
33
+ class ValueAsVariableIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
34
+ include ProcessorMixin
35
+
36
+ # :nodoc:
37
+ def handles?(target)
38
+ target.start_with? 'grafana_value_as_variable'
39
+ end
40
+
41
+ # :nodoc:
42
+ def process(doc, reader, target, attrs)
43
+ return if @report.cancel
44
+
45
+ # increase step for this processor as well as it is also counted in the step counter
46
+ @report.next_step
47
+
48
+ call_attr = attrs.delete('call')
49
+ call, target = call_attr.split(':') if call_attr
50
+ attribute = attrs.delete('variable_name')
51
+ @report.logger.debug("Processing ValueAsVariableIncludeProcessor (call: #{call}, target: #{target},"\
52
+ " variable_name: #{attribute}, attrs: #{attrs})")
53
+ if !call || !attribute
54
+ @report.logger.error('ValueAsVariableIncludeProcessor: Missing mandatory attribute \'call\' or '\
55
+ '\'variable_name\'.')
56
+ # increase counter, as error occured and no sub call is being processed
57
+ @report.next_step
58
+ return reader
59
+ end
60
+
61
+ # TODO: remove dirty hack to allow the document as parameter for other processors
62
+ def doc.document
63
+ self
64
+ end
65
+
66
+ # TODO: properly show error messages also in document
67
+ ext = doc.extensions.find_inline_macro_extension(call) if doc.extensions.inline_macros?
68
+ if !ext
69
+ @report.logger.error('ValueAsVariableIncludeProcessor: Could not find inline macro extension for '\
70
+ "'#{call}'.")
71
+ # increase counter, as error occured and no sub call is being processed
72
+ @report.next_step
73
+ else
74
+ @report.logger.debug('ValueAsVariableIncludeProcessor: Calling sub-method.')
75
+ item = ext.process_method.call(doc, target, attrs)
76
+ if !item.text.to_s.empty?
77
+ result = ":#{attribute}: #{item.text}"
78
+ @report.logger.debug("ValueAsVariableIncludeProcessor: Adding '#{result}' to document.")
79
+ reader.unshift_line(result)
80
+ else
81
+ @report.logger.debug("ValueAsVariableIncludeProcessor: Not adding variable '#{attribute}'"\
82
+ ' as query result was empty.')
83
+ end
84
+ end
85
+
86
+ reader
87
+ end
88
+ end
89
+ end
90
+ end
@@ -13,6 +13,10 @@ module GrafanaReporter
13
13
  class Configuration
14
14
  # @return [AbstractReport] specific report class, which should be used.
15
15
  attr_accessor :report_class
16
+ attr_accessor :logger
17
+
18
+ # Default file name for grafana reporter configuration file
19
+ DEFAULT_CONFIG_FILE_NAME = 'grafana_reporter.config'
16
20
 
17
21
  # Returned by {#mode} if only a connection test shall be executed.
18
22
  MODE_CONNECTION_TEST = 'test'
@@ -27,10 +31,18 @@ module GrafanaReporter
27
31
 
28
32
  def initialize
29
33
  @config = {}
30
- @logger = ::Logger.new($stderr, level: :unknown)
34
+ @logger = ::Logger.new($stderr, level: :info)
31
35
  end
32
36
 
33
- attr_accessor :logger
37
+ # Reads a given configuration file.
38
+ # @param config_file [String] path to configuration file, defaults to DEFAULT_CONFIG_FILE_NAME
39
+ # @return [Hash] configuration hash to be set as {Configuration#config}
40
+ def load_config_from_file(config_file = nil)
41
+ config_file ||= DEFAULT_CONFIG_FILE_NAME
42
+ self.config = YAML.load_file(config_file)
43
+ rescue StandardError => e
44
+ raise ConfigurationError, "Could not read config file '#{config_file}' (Error: #{e.message})"
45
+ end
34
46
 
35
47
  # Used to overwrite the current configuration.
36
48
  def config=(new_config)
@@ -53,7 +65,7 @@ module GrafanaReporter
53
65
  def template
54
66
  return nil if get_config('default-document-attributes:var-template').nil?
55
67
 
56
- "#{templates_folder}#{get_config('default-document-attributes:var-template')}.adoc"
68
+ "#{templates_folder}#{get_config('default-document-attributes:var-template')}"
57
69
  end
58
70
 
59
71
  # @return [String] destination filename for the report in {MODE_SINGLE_RENDER}.
@@ -84,16 +96,6 @@ module GrafanaReporter
84
96
  get_config("grafana:#{instance}:api_key")
85
97
  end
86
98
 
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
99
  # @return [String] configured folder, in which the report templates are stored including trailing slash.
98
100
  # By default: current folder.
99
101
  def templates_folder
@@ -153,10 +155,11 @@ module GrafanaReporter
153
155
  # This function shall be called, before the configuration object is used in the
154
156
  # {Application::Application#run}. It ensures, that everything is setup properly
155
157
  # and all necessary folders exist. Appropriate errors are raised in case of errors.
158
+ # @param explicit [Boolean] true, if validation shall expect explicit (wizard) configuration file
156
159
  # @return [void]
157
- def validate
160
+ def validate(explicit = false)
158
161
  check_deprecation
159
- validate_schema(schema, @config)
162
+ validate_schema(schema(explicit), @config)
160
163
 
161
164
  # check if set folders exist
162
165
  raise FolderDoesNotExistError.new(reports_folder, 'reports-folder') unless File.directory?(reports_folder)
@@ -176,12 +179,8 @@ module GrafanaReporter
176
179
 
177
180
  cur_pos = @config
178
181
  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
182
+ cur_pos[subpath] = {} unless cur_pos[subpath]
183
+ cur_pos = cur_pos[subpath]
185
184
  end
186
185
 
187
186
  cur_pos[last_level] = value
@@ -193,7 +192,7 @@ module GrafanaReporter
193
192
  #
194
193
  # param other_config [Configuration] other configuration object
195
194
  def merge!(other_config)
196
- self.config.merge!(other_config.config) { |_key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2) : v2 }
195
+ config.merge!(other_config.config) { |_key, v1, v2| v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2) : v2 }
197
196
  update_configuration
198
197
  end
199
198
 
@@ -202,19 +201,29 @@ module GrafanaReporter
202
201
  def check_deprecation
203
202
  return if report_class
204
203
 
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.')
204
+ logger.warn('DEPRECATION WARNING: Your configuration explicitly needs to specify the '\
205
+ '\'grafana-reporter:report-class\' value. Currently this defaults to '\
206
+ '\'GrafanaReporter::Asciidoctor::Report\'. You can get rid of this warning, if you '\
207
+ 'explicitly set this configuration in your configuration file. Setting this default will be '\
208
+ 'removed in a future version.')
208
209
  set_param('grafana-reporter:report-class', 'GrafanaReporter::Asciidoctor::Report')
209
210
  end
210
211
 
211
212
  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
213
+ debug_level = get_config('grafana-reporter:debug-level')
214
+ rep_class = get_config('grafana-reporter:report-class')
215
+
216
+ @logger.level = Object.const_get("::Logger::Severity::#{debug_level}") if debug_level =~ /DEBUG|INFO|WARN|
217
+ ERROR|FATAL|UNKNOWN/x
218
+ self.report_class = Object.const_get(rep_class) if rep_class
219
+ ::Grafana::WebRequest.ssl_cert = get_config('grafana-reporter:ssl-cert')
215
220
 
216
- if get_config('grafana-reporter:report-class')
217
- self.report_class = Object.const_get(get_config('grafana-reporter:report-class'))
221
+ # register callbacks
222
+ callbacks = get_config('grafana-reporter:callbacks')
223
+ return unless callbacks
224
+
225
+ callbacks.each do |url, event|
226
+ AbstractReport.add_event_listener(event.to_sym, ReportWebhook.new(url))
218
227
  end
219
228
  end
220
229
 
@@ -238,20 +247,16 @@ module GrafanaReporter
238
247
 
239
248
  if key.nil?
240
249
  # 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}'."
250
+ raise ConfigurationError, "Unhandled configuration data type '#{subject.class}'." unless subject.is_a?(Hash)
251
+
252
+ if subject.length < min_occurence
253
+ raise ConfigurationDoesNotMatchSchemaError.new(key, 'occur', min_occurence, subject.length)
254
+ end
255
+
256
+ subject.each do |k, _v|
257
+ sub_scheme = {}
258
+ sub_scheme[k] = schema[nil]
259
+ validate_schema(sub_scheme, subject)
255
260
  end
256
261
 
257
262
  # apply to single item
@@ -277,7 +282,7 @@ module GrafanaReporter
277
282
  end
278
283
  end
279
284
 
280
- def schema
285
+ def schema(explicit)
281
286
  {
282
287
  'grafana' =>
283
288
  [
@@ -288,13 +293,13 @@ module GrafanaReporter
288
293
  Hash, 1,
289
294
  {
290
295
  'host' => [String, 1],
291
- 'api_key' => [String, 0],
292
- 'datasources' => [Hash, 0, { nil => [Integer, 1] }]
296
+ 'api_key' => [String, 0]
293
297
  }
294
298
  ]
295
299
  }
296
300
  ],
297
- 'default-document-attributes' => [Hash, 0],
301
+ 'default-document-attributes' => [Hash, explicit ? 1 : 0],
302
+ 'to_file' => [String, 0],
298
303
  'grafana-reporter' =>
299
304
  [
300
305
  Hash, 1,
@@ -302,11 +307,13 @@ module GrafanaReporter
302
307
  'debug-level' => [String, 0],
303
308
  'run-mode' => [String, 0],
304
309
  'test-instance' => [String, 0],
305
- 'templates-folder' => [String, 0],
310
+ 'templates-folder' => [String, explicit ? 1 : 0],
306
311
  'report-class' => [String, 1],
307
- 'reports-folder' => [String, 0],
308
- 'report-retention' => [Integer, 0],
309
- 'webservice-port' => [Integer, 0]
312
+ 'reports-folder' => [String, explicit ? 1 : 0],
313
+ 'report-retention' => [Integer, explicit ? 1 : 0],
314
+ 'ssl-cert' => [String, 0],
315
+ 'webservice-port' => [Integer, explicit ? 1 : 0],
316
+ 'callbacks' => [Hash, 0, { nil => [String, 1] }]
310
317
  }
311
318
  ]
312
319
  }