ruby-grafana-reporter 0.2.0 → 0.4.1

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +105 -86
  3. data/bin/ruby-grafana-reporter +5 -5
  4. data/lib/VERSION.rb +3 -2
  5. data/lib/grafana/abstract_datasource.rb +136 -0
  6. data/lib/grafana/dashboard.rb +21 -23
  7. data/lib/grafana/errors.rb +8 -1
  8. data/lib/grafana/grafana.rb +61 -65
  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 +25 -0
  12. data/lib/grafana/graphite_datasource.rb +50 -0
  13. data/lib/grafana/image_rendering_datasource.rb +44 -0
  14. data/lib/grafana/panel.rb +9 -3
  15. data/lib/grafana/prometheus_datasource.rb +45 -0
  16. data/lib/grafana/sql_datasource.rb +71 -0
  17. data/lib/grafana/unsupported_datasource.rb +7 -0
  18. data/lib/grafana/variable.rb +3 -2
  19. data/lib/grafana/webrequest.rb +71 -0
  20. data/lib/grafana_reporter/abstract_query.rb +359 -0
  21. data/lib/grafana_reporter/abstract_report.rb +119 -17
  22. data/lib/grafana_reporter/alerts_table_query.rb +44 -0
  23. data/lib/grafana_reporter/annotations_table_query.rb +43 -0
  24. data/lib/grafana_reporter/application/application.rb +49 -297
  25. data/lib/grafana_reporter/application/webservice.rb +49 -14
  26. data/lib/grafana_reporter/asciidoctor/alerts_table_include_processor.rb +90 -0
  27. data/lib/grafana_reporter/asciidoctor/annotations_table_include_processor.rb +89 -0
  28. data/lib/grafana_reporter/asciidoctor/panel_image_block_macro.rb +77 -0
  29. data/lib/grafana_reporter/asciidoctor/panel_image_inline_macro.rb +79 -0
  30. data/lib/grafana_reporter/asciidoctor/panel_property_inline_macro.rb +73 -0
  31. data/lib/grafana_reporter/asciidoctor/panel_query_table_include_processor.rb +99 -0
  32. data/lib/grafana_reporter/asciidoctor/panel_query_value_inline_macro.rb +93 -0
  33. data/lib/grafana_reporter/asciidoctor/processor_mixin.rb +64 -0
  34. data/lib/grafana_reporter/asciidoctor/report.rb +47 -76
  35. data/lib/grafana_reporter/asciidoctor/show_environment_include_processor.rb +46 -0
  36. data/lib/grafana_reporter/asciidoctor/show_help_include_processor.rb +35 -0
  37. data/lib/grafana_reporter/asciidoctor/sql_table_include_processor.rb +92 -0
  38. data/lib/grafana_reporter/asciidoctor/sql_value_inline_macro.rb +88 -0
  39. data/lib/grafana_reporter/asciidoctor/value_as_variable_include_processor.rb +90 -0
  40. data/lib/grafana_reporter/configuration.rb +108 -43
  41. data/lib/grafana_reporter/console_configuration_wizard.rb +311 -0
  42. data/lib/grafana_reporter/demo_report_wizard.rb +101 -0
  43. data/lib/grafana_reporter/erb/report.rb +43 -0
  44. data/lib/grafana_reporter/errors.rb +41 -0
  45. data/lib/grafana_reporter/help.rb +443 -0
  46. data/lib/grafana_reporter/logger/{two_way_logger.rb → two_way_delegate_logger.rb} +1 -1
  47. data/lib/grafana_reporter/panel_image_query.rb +29 -0
  48. data/lib/grafana_reporter/panel_property_query.rb +22 -0
  49. data/lib/grafana_reporter/query_value_query.rb +79 -0
  50. data/lib/grafana_reporter/report_webhook.rb +35 -0
  51. data/lib/ruby_grafana_extension.rb +8 -0
  52. data/lib/{ruby-grafana-reporter.rb → ruby_grafana_reporter.rb} +13 -0
  53. metadata +47 -43
  54. data/lib/grafana/abstract_panel_query.rb +0 -22
  55. data/lib/grafana/abstract_query.rb +0 -132
  56. data/lib/grafana/abstract_sql_query.rb +0 -51
  57. data/lib/grafana/panel_image_query.rb +0 -52
  58. data/lib/grafana_reporter/asciidoctor/alerts_table_query.rb +0 -104
  59. data/lib/grafana_reporter/asciidoctor/annotations_table_query.rb +0 -99
  60. data/lib/grafana_reporter/asciidoctor/errors.rb +0 -40
  61. data/lib/grafana_reporter/asciidoctor/extensions/alerts_table_include_processor.rb +0 -92
  62. data/lib/grafana_reporter/asciidoctor/extensions/annotations_table_include_processor.rb +0 -91
  63. data/lib/grafana_reporter/asciidoctor/extensions/panel_image_block_macro.rb +0 -69
  64. data/lib/grafana_reporter/asciidoctor/extensions/panel_image_inline_macro.rb +0 -68
  65. data/lib/grafana_reporter/asciidoctor/extensions/panel_property_inline_macro.rb +0 -61
  66. data/lib/grafana_reporter/asciidoctor/extensions/panel_query_table_include_processor.rb +0 -78
  67. data/lib/grafana_reporter/asciidoctor/extensions/panel_query_value_inline_macro.rb +0 -73
  68. data/lib/grafana_reporter/asciidoctor/extensions/processor_mixin.rb +0 -20
  69. data/lib/grafana_reporter/asciidoctor/extensions/show_environment_include_processor.rb +0 -43
  70. data/lib/grafana_reporter/asciidoctor/extensions/show_help_include_processor.rb +0 -202
  71. data/lib/grafana_reporter/asciidoctor/extensions/sql_table_include_processor.rb +0 -70
  72. data/lib/grafana_reporter/asciidoctor/extensions/sql_value_inline_macro.rb +0 -66
  73. data/lib/grafana_reporter/asciidoctor/extensions/value_as_variable_include_processor.rb +0 -61
  74. data/lib/grafana_reporter/asciidoctor/panel_first_value_query.rb +0 -34
  75. data/lib/grafana_reporter/asciidoctor/panel_image_query.rb +0 -25
  76. data/lib/grafana_reporter/asciidoctor/panel_property_query.rb +0 -44
  77. data/lib/grafana_reporter/asciidoctor/panel_table_query.rb +0 -38
  78. data/lib/grafana_reporter/asciidoctor/query_mixin.rb +0 -310
  79. data/lib/grafana_reporter/asciidoctor/sql_first_value_query.rb +0 -37
  80. data/lib/grafana_reporter/asciidoctor/sql_table_query.rb +0 -39
@@ -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,92 @@
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))
55
+ query.datasource = @report.grafana(instance).datasource_by_id(target.split(':')[1].to_i)
56
+ query.raw_query = attrs['sql']
57
+ assign_doc_and_item_variables(query, doc.attributes, attrs)
58
+ @report.logger.debug("from: #{query.from}, to: #{query.to}")
59
+
60
+ reader.unshift_lines query.execute
61
+ rescue GrafanaReporterError => e
62
+ @report.logger.error(e.message)
63
+ reader.unshift_line "|#{e.message}"
64
+ rescue StandardError => e
65
+ @report.logger.fatal(e.message)
66
+ reader.unshift_line "|#{e.message}"
67
+ end
68
+
69
+ reader
70
+ end
71
+
72
+ # @see ProcessorMixin#build_demo_entry
73
+ def build_demo_entry(panel)
74
+ return nil unless panel
75
+ return nil unless panel.model['type'].include?('table')
76
+
77
+ ref_id = nil
78
+ panel.model['targets'].each do |item|
79
+ if !item['hide'] && !panel.query(item['refId']).to_s.empty?
80
+ ref_id = item['refId']
81
+ break
82
+ end
83
+ end
84
+ return nil unless ref_id
85
+
86
+ "|===\ninclude::grafana_sql_table:#{panel.dashboard.grafana.datasource_by_name(panel.model['datasource']).id}"\
87
+ "[sql=\"#{panel.query(ref_id).gsub(/"/, '\"').gsub("\n", ' ').gsub(/\\/, '\\\\')}\",filter_columns=\"time\","\
88
+ "dashboard=\"#{panel.dashboard.id}\",from=\"now-1h\",to=\"now\"]\n|==="
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,88 @@
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))
53
+ query.datasource = @report.grafana(instance).datasource_by_id(target)
54
+ query.raw_query = attrs['sql']
55
+ assign_doc_and_item_variables(query, parent.document.attributes, attrs)
56
+ @report.logger.debug("from: #{query.from}, to: #{query.to}")
57
+
58
+ create_inline(parent, :quoted, query.execute)
59
+ rescue GrafanaReporterError => e
60
+ @report.logger.error(e.message)
61
+ create_inline(parent, :quoted, e.message)
62
+ rescue StandardError => e
63
+ @report.logger.fatal(e.message)
64
+ create_inline(parent, :quoted, e.message)
65
+ end
66
+ end
67
+
68
+ # @see ProcessorMixin#build_demo_entry
69
+ def build_demo_entry(panel)
70
+ return nil unless panel
71
+ return nil unless panel.model['type'] == 'singlestat'
72
+
73
+ ref_id = nil
74
+ panel.model['targets'].each do |item|
75
+ if !item['hide'] && !panel.query(item['refId']).to_s.empty?
76
+ ref_id = item['refId']
77
+ break
78
+ end
79
+ end
80
+ return nil unless ref_id
81
+
82
+ "grafana_sql_value:#{panel.dashboard.grafana.datasource_by_name(panel.model['datasource']).id}"\
83
+ "[sql=\"#{panel.query(ref_id).gsub(/"/, '\"').gsub("\n", ' ').gsub(/\\/, '\\\\')}\",from=\"now-1h\","\
84
+ 'to="now"]'
85
+ end
86
+ end
87
+ end
88
+ 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'
@@ -23,18 +27,28 @@ module GrafanaReporter
23
27
 
24
28
  # Used to access the configuration hash. To make sure, that the configuration is
25
29
  # valid, call {#validate}.
26
- #
27
- # NOTE: This function overwrites all existing configurations
28
- attr_accessor :config
30
+ attr_reader :config
29
31
 
30
32
  def initialize
31
33
  @config = {}
32
- @logger = ::Logger.new($stderr, level: :unknown)
33
- # TODO: set report class somewhere else, but make it known here
34
- self.report_class = Asciidoctor::Report
34
+ @logger = ::Logger.new($stderr, level: :info)
35
35
  end
36
36
 
37
- 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
46
+
47
+ # Used to overwrite the current configuration.
48
+ def config=(new_config)
49
+ @config = new_config
50
+ update_configuration
51
+ end
38
52
 
39
53
  # @return [String] mode, in which the reporting shall be executed. One of {MODE_CONNECTION_TEST},
40
54
  # {MODE_SINGLE_RENDER} and {MODE_SERVICE}.
@@ -50,7 +64,8 @@ module GrafanaReporter
50
64
  # @return [String] full path of configured report template. Only needed in {MODE_SINGLE_RENDER}.
51
65
  def template
52
66
  return nil if get_config('default-document-attributes:var-template').nil?
53
- "#{templates_folder}#{get_config('default-document-attributes:var-template')}.adoc"
67
+
68
+ "#{templates_folder}#{get_config('default-document-attributes:var-template')}"
54
69
  end
55
70
 
56
71
  # @return [String] destination filename for the report in {MODE_SINGLE_RENDER}.
@@ -81,16 +96,6 @@ module GrafanaReporter
81
96
  get_config("grafana:#{instance}:api_key")
82
97
  end
83
98
 
84
- # @param instance [String] grafana instance name, for which the value shall be retrieved.
85
- # @return [Hash<String,Integer>] configured datasources for the requested grafana instance. Name as key,
86
- # ID as value.
87
- def grafana_datasources(instance = 'default')
88
- hash = get_config("grafana:#{instance}:datasources")
89
- return nil if hash.nil?
90
-
91
- hash.map { |k, v| [k, v] }.to_h
92
- end
93
-
94
99
  # @return [String] configured folder, in which the report templates are stored including trailing slash.
95
100
  # By default: current folder.
96
101
  def templates_folder
@@ -150,9 +155,11 @@ module GrafanaReporter
150
155
  # This function shall be called, before the configuration object is used in the
151
156
  # {Application::Application#run}. It ensures, that everything is setup properly
152
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
153
159
  # @return [void]
154
- def validate
155
- validate_schema(schema, @config)
160
+ def validate(explicit = false)
161
+ check_deprecation
162
+ validate_schema(schema(explicit), @config)
156
163
 
157
164
  # check if set folders exist
158
165
  raise FolderDoesNotExistError.new(reports_folder, 'reports-folder') unless File.directory?(reports_folder)
@@ -160,8 +167,66 @@ module GrafanaReporter
160
167
  raise FolderDoesNotExistError.new(images_folder, 'images-folder') unless File.directory?(images_folder)
161
168
  end
162
169
 
170
+ # Can be used to configure or overwrite single parameters.
171
+ #
172
+ # @param path [String] path of the paramter to set, e.g. +grafana-reporter:webservice-port+
173
+ # @param value [Object] value to set
174
+ def set_param(path, value)
175
+ return if path.nil?
176
+
177
+ levels = path.split(':')
178
+ last_level = levels.pop
179
+
180
+ cur_pos = @config
181
+ levels.each do |subpath|
182
+ cur_pos[subpath] = {} unless cur_pos[subpath]
183
+ cur_pos = cur_pos[subpath]
184
+ end
185
+
186
+ cur_pos[last_level] = value
187
+ update_configuration
188
+ end
189
+
190
+ # Merge the given configuration object settings with the current config, i.e. overwrite and add all
191
+ # settings from the given config, but keep the not specified configs from the current object.
192
+ #
193
+ # param other_config [Configuration] other configuration object
194
+ def merge!(other_config)
195
+ config.merge!(other_config.config) { |_key, v1, v2| v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2) : v2 }
196
+ update_configuration
197
+ end
198
+
163
199
  private
164
200
 
201
+ def check_deprecation
202
+ return if report_class
203
+
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.')
209
+ set_param('grafana-reporter:report-class', 'GrafanaReporter::Asciidoctor::Report')
210
+ end
211
+
212
+ def update_configuration
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')
220
+
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))
227
+ end
228
+ end
229
+
165
230
  def get_config(path)
166
231
  return if path.nil?
167
232
 
@@ -182,20 +247,16 @@ module GrafanaReporter
182
247
 
183
248
  if key.nil?
184
249
  # apply to all on this level
185
- case
186
- when subject.is_a?(Hash)
187
- if subject.length < min_occurence
188
- raise ConfigurationDoesNotMatchSchemaError.new(key, 'occur', min_occurence, subject.length)
189
- end
190
-
191
- subject.each do |k, _v|
192
- sub_scheme = {}
193
- sub_scheme[k] = schema[nil]
194
- validate_schema(sub_scheme, subject)
195
- end
196
-
197
- else
198
- 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)
199
260
  end
200
261
 
201
262
  # apply to single item
@@ -221,7 +282,7 @@ module GrafanaReporter
221
282
  end
222
283
  end
223
284
 
224
- def schema
285
+ def schema(explicit)
225
286
  {
226
287
  'grafana' =>
227
288
  [
@@ -232,23 +293,27 @@ module GrafanaReporter
232
293
  Hash, 1,
233
294
  {
234
295
  'host' => [String, 1],
235
- 'api_key' => [String, 0],
236
- 'datasources' => [Hash, 0, { nil => [Integer, 1] }]
296
+ 'api_key' => [String, 0]
237
297
  }
238
298
  ]
239
299
  }
240
300
  ],
241
- 'default-document-attributes' => [Hash, 0],
301
+ 'default-document-attributes' => [Hash, explicit ? 1 : 0],
302
+ 'to_file' => [String, 0],
242
303
  'grafana-reporter' =>
243
304
  [
244
- Hash, 0,
305
+ Hash, 1,
245
306
  {
307
+ 'debug-level' => [String, 0],
246
308
  'run-mode' => [String, 0],
247
309
  'test-instance' => [String, 0],
248
- 'templates-folder' => [String, 0],
249
- 'reports-folder' => [String, 0],
250
- 'report-retention' => [Integer, 0],
251
- 'webservice-port' => [Integer, 0]
310
+ 'templates-folder' => [String, explicit ? 1 : 0],
311
+ 'report-class' => [String, 1],
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] }]
252
317
  }
253
318
  ]
254
319
  }