ruby-grafana-reporter 0.3.0 → 0.4.4

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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +337 -170
  3. data/bin/ruby-grafana-reporter +5 -5
  4. data/lib/VERSION.rb +3 -2
  5. data/lib/grafana/abstract_datasource.rb +149 -0
  6. data/lib/grafana/dashboard.rb +1 -3
  7. data/lib/grafana/errors.rb +20 -5
  8. data/lib/grafana/grafana.rb +52 -57
  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 +37 -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 +10 -4
  16. data/lib/grafana/prometheus_datasource.rb +67 -0
  17. data/lib/grafana/sql_datasource.rb +70 -0
  18. data/lib/grafana/unsupported_datasource.rb +7 -0
  19. data/lib/grafana/variable.rb +27 -21
  20. data/lib/grafana/webrequest.rb +71 -0
  21. data/lib/grafana_reporter/abstract_query.rb +478 -0
  22. data/lib/grafana_reporter/abstract_report.rb +152 -18
  23. data/lib/grafana_reporter/abstract_table_format_strategy.rb +34 -0
  24. data/lib/grafana_reporter/alerts_table_query.rb +43 -0
  25. data/lib/grafana_reporter/annotations_table_query.rb +42 -0
  26. data/lib/grafana_reporter/application/application.rb +28 -25
  27. data/lib/grafana_reporter/application/webservice.rb +80 -39
  28. data/lib/grafana_reporter/asciidoctor/adoc_plain_table_format_strategy.rb +25 -0
  29. data/lib/grafana_reporter/asciidoctor/alerts_table_include_processor.rb +92 -0
  30. data/lib/grafana_reporter/asciidoctor/annotations_table_include_processor.rb +91 -0
  31. data/lib/grafana_reporter/asciidoctor/help.rb +336 -313
  32. data/lib/grafana_reporter/asciidoctor/panel_image_block_macro.rb +78 -0
  33. data/lib/grafana_reporter/asciidoctor/panel_image_inline_macro.rb +80 -0
  34. data/lib/grafana_reporter/asciidoctor/panel_property_inline_macro.rb +74 -0
  35. data/lib/grafana_reporter/asciidoctor/panel_query_table_include_processor.rb +99 -0
  36. data/lib/grafana_reporter/asciidoctor/panel_query_value_inline_macro.rb +93 -0
  37. data/lib/grafana_reporter/asciidoctor/processor_mixin.rb +50 -0
  38. data/lib/grafana_reporter/asciidoctor/report.rb +41 -82
  39. data/lib/grafana_reporter/asciidoctor/show_environment_include_processor.rb +46 -0
  40. data/lib/grafana_reporter/asciidoctor/show_help_include_processor.rb +35 -0
  41. data/lib/grafana_reporter/asciidoctor/sql_table_include_processor.rb +94 -0
  42. data/lib/grafana_reporter/asciidoctor/sql_value_inline_macro.rb +90 -0
  43. data/lib/grafana_reporter/asciidoctor/value_as_variable_include_processor.rb +90 -0
  44. data/lib/grafana_reporter/configuration.rb +26 -8
  45. data/lib/grafana_reporter/console_configuration_wizard.rb +109 -67
  46. data/lib/grafana_reporter/csv_table_format_strategy.rb +23 -0
  47. data/lib/grafana_reporter/demo_report_wizard.rb +104 -0
  48. data/lib/grafana_reporter/erb/demo_report_builder.rb +46 -0
  49. data/lib/grafana_reporter/erb/report.rb +36 -0
  50. data/lib/grafana_reporter/erb/report_jail.rb +21 -0
  51. data/lib/grafana_reporter/errors.rb +57 -0
  52. data/lib/grafana_reporter/logger/{two_way_logger.rb → two_way_delegate_logger.rb} +1 -1
  53. data/lib/grafana_reporter/panel_image_query.rb +25 -0
  54. data/lib/grafana_reporter/panel_property_query.rb +22 -0
  55. data/lib/grafana_reporter/query_value_query.rb +61 -0
  56. data/lib/grafana_reporter/report_webhook.rb +39 -0
  57. data/lib/ruby_grafana_extension.rb +8 -0
  58. data/lib/{ruby-grafana-reporter.rb → ruby_grafana_reporter.rb} +1 -3
  59. metadata +49 -38
  60. data/lib/grafana/abstract_panel_query.rb +0 -22
  61. data/lib/grafana/abstract_query.rb +0 -132
  62. data/lib/grafana/abstract_sql_query.rb +0 -51
  63. data/lib/grafana/panel_image_query.rb +0 -52
  64. data/lib/grafana_reporter/asciidoctor/alerts_table_query.rb +0 -101
  65. data/lib/grafana_reporter/asciidoctor/annotations_table_query.rb +0 -96
  66. data/lib/grafana_reporter/asciidoctor/errors.rb +0 -40
  67. data/lib/grafana_reporter/asciidoctor/extensions/alerts_table_include_processor.rb +0 -92
  68. data/lib/grafana_reporter/asciidoctor/extensions/annotations_table_include_processor.rb +0 -91
  69. data/lib/grafana_reporter/asciidoctor/extensions/panel_image_block_macro.rb +0 -69
  70. data/lib/grafana_reporter/asciidoctor/extensions/panel_image_inline_macro.rb +0 -68
  71. data/lib/grafana_reporter/asciidoctor/extensions/panel_property_inline_macro.rb +0 -61
  72. data/lib/grafana_reporter/asciidoctor/extensions/panel_query_table_include_processor.rb +0 -78
  73. data/lib/grafana_reporter/asciidoctor/extensions/panel_query_value_inline_macro.rb +0 -73
  74. data/lib/grafana_reporter/asciidoctor/extensions/processor_mixin.rb +0 -20
  75. data/lib/grafana_reporter/asciidoctor/extensions/show_environment_include_processor.rb +0 -43
  76. data/lib/grafana_reporter/asciidoctor/extensions/show_help_include_processor.rb +0 -30
  77. data/lib/grafana_reporter/asciidoctor/extensions/sql_table_include_processor.rb +0 -70
  78. data/lib/grafana_reporter/asciidoctor/extensions/sql_value_inline_macro.rb +0 -66
  79. data/lib/grafana_reporter/asciidoctor/extensions/value_as_variable_include_processor.rb +0 -88
  80. data/lib/grafana_reporter/asciidoctor/panel_first_value_query.rb +0 -36
  81. data/lib/grafana_reporter/asciidoctor/panel_image_query.rb +0 -28
  82. data/lib/grafana_reporter/asciidoctor/panel_property_query.rb +0 -44
  83. data/lib/grafana_reporter/asciidoctor/panel_table_query.rb +0 -40
  84. data/lib/grafana_reporter/asciidoctor/query_mixin.rb +0 -312
  85. data/lib/grafana_reporter/asciidoctor/sql_first_value_query.rb +0 -42
  86. data/lib/grafana_reporter/asciidoctor/sql_table_query.rb +0 -44
@@ -1,27 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrafanaReporter
4
+ # This module contains all classes, which are necessary to use the reporter in conjunction with asciidoctor.
4
5
  module Asciidoctor
5
6
  # Implementation of a specific {AbstractReport}. It is used to
6
7
  # build reports specifically for asciidoctor results.
7
- class Report < GrafanaReporter::AbstractReport
8
- # (see AbstractReport#initialize)
9
- def initialize(config, template, destination_file_or_path = nil, custom_attributes = {})
8
+ class Report < ::GrafanaReporter::AbstractReport
9
+ # @see AbstractReport#initialize
10
+ def initialize(config)
10
11
  super
11
- @current_pos = 0
12
12
  @image_files = []
13
- @grafana_instances = {}
14
13
  end
15
14
 
16
- # Starts to create an asciidoctor report. It utilizes all {Extensions} to
17
- # realize the conversion.
18
- # @see AbstractReport#create_report
19
- # @return [void]
20
- def create_report
21
- @start_time = Time.now
15
+ # Starts to create an asciidoctor report. It utilizes all extensions in the {GrafanaReporter::Asciidoctor}
16
+ # namespace to realize the conversion.
17
+ # @see AbstractReport#build
18
+ def build
22
19
  attrs = { 'convert-backend' => 'pdf' }.merge(@config.default_document_attributes.merge(@custom_attributes))
23
- attrs['grafana-report-timestamp'] = @start_time.to_s
24
- logger.info("Report started at #{@start_time}")
25
20
  logger.debug("Document attributes: #{attrs}")
26
21
 
27
22
  initialize_step_counter
@@ -30,43 +25,30 @@ module GrafanaReporter
30
25
  ::Asciidoctor::LoggerManager.logger = logger
31
26
 
32
27
  registry = ::Asciidoctor::Extensions::Registry.new
33
- # TODO: dynamically register macros, which is also needed when supporting custom macros
34
- registry.inline_macro Extensions::PanelImageInlineMacro.new.current_report(self)
35
- registry.inline_macro Extensions::PanelQueryValueInlineMacro.new.current_report(self)
36
- registry.inline_macro Extensions::PanelPropertyInlineMacro.new.current_report(self)
37
- registry.inline_macro Extensions::SqlValueInlineMacro.new.current_report(self)
38
- registry.block_macro Extensions::PanelImageBlockMacro.new.current_report(self)
39
- registry.include_processor Extensions::ValueAsVariableIncludeProcessor.new.current_report(self)
40
- registry.include_processor Extensions::PanelQueryTableIncludeProcessor.new.current_report(self)
41
- registry.include_processor Extensions::SqlTableIncludeProcessor.new.current_report(self)
42
- registry.include_processor Extensions::ShowEnvironmentIncludeProcessor.new.current_report(self)
43
- registry.include_processor Extensions::ShowHelpIncludeProcessor.new.current_report(self)
44
- registry.include_processor Extensions::AnnotationsTableIncludeProcessor.new.current_report(self)
45
- registry.include_processor Extensions::AlertsTableIncludeProcessor.new.current_report(self)
28
+ registry.inline_macro PanelImageInlineMacro.new.current_report(self)
29
+ registry.inline_macro PanelQueryValueInlineMacro.new.current_report(self)
30
+ registry.inline_macro PanelPropertyInlineMacro.new.current_report(self)
31
+ registry.inline_macro SqlValueInlineMacro.new.current_report(self)
32
+ registry.block_macro PanelImageBlockMacro.new.current_report(self)
33
+ registry.include_processor ValueAsVariableIncludeProcessor.new.current_report(self)
34
+ registry.include_processor PanelQueryTableIncludeProcessor.new.current_report(self)
35
+ registry.include_processor SqlTableIncludeProcessor.new.current_report(self)
36
+ registry.include_processor ShowEnvironmentIncludeProcessor.new.current_report(self)
37
+ registry.include_processor ShowHelpIncludeProcessor.new.current_report(self)
38
+ registry.include_processor AnnotationsTableIncludeProcessor.new.current_report(self)
39
+ registry.include_processor AlertsTableIncludeProcessor.new.current_report(self)
46
40
 
47
41
  ::Asciidoctor.convert_file(@template, extension_registry: registry, backend: attrs['convert-backend'],
48
42
  to_file: path, attributes: attrs, header_footer: true)
49
43
 
50
- @destination_file_or_path.close if @destination_file_or_path.is_a?(File)
51
-
52
44
  # store report including als images as ZIP file, if the result is not a PDF
53
45
  if attrs['convert-backend'] != 'pdf'
54
- dest_path = nil
55
- case
56
- when @destination_file_or_path.is_a?(File)
57
- dest_path = @destination_file_or_path.path
58
- when @destination_file_or_path.is_a?(Tempfile)
59
- dest_path = @destination_file_or_path.path
60
- else
61
- dest_path = @destination_file_or_path
62
- end
63
-
64
46
  # build zip file
65
47
  zip_file = Tempfile.new('gf_zip')
66
48
  buffer = Zip::OutputStream.write_buffer do |zipfile|
67
49
  # add report file
68
- zipfile.put_next_entry("#{dest_path.gsub(@config.reports_folder, '')}.#{attrs['convert-backend']}")
69
- zipfile.write File.read(dest_path)
50
+ zipfile.put_next_entry("#{path.gsub(@config.reports_folder, '')}.#{attrs['convert-backend']}")
51
+ zipfile.write File.read(path)
70
52
 
71
53
  # add image files
72
54
  @image_files.each do |file|
@@ -81,9 +63,9 @@ module GrafanaReporter
81
63
  # replace original file with zip file
82
64
  zip_file.rewind
83
65
  begin
84
- File.write(dest_path, zip_file.read)
66
+ File.write(path, zip_file.read)
85
67
  rescue StandardError => e
86
- logger.fatal("Could not overwrite report file '#{dest_path}' with ZIP file. (#{e.message}).")
68
+ logger.fatal("Could not overwrite report file '#{path}' with ZIP file. (#{e.message}).")
87
69
  end
88
70
 
89
71
  # cleanup temporary zip file
@@ -92,39 +74,6 @@ module GrafanaReporter
92
74
  end
93
75
 
94
76
  clean_image_files
95
- @end_time = Time.now
96
- logger.info("Report finished after #{@end_time - @start_time} seconds.")
97
- @done = true
98
- rescue StandardError => e
99
- # catch all errors during execution
100
- died_with_error(e)
101
- raise e
102
- end
103
-
104
- # @see AbstractReport#progress
105
- # @return [Float] number between 0 and 1 reflecting the current progress.
106
- def progress
107
- return 0 if @total_steps.to_i.zero?
108
-
109
- @current_pos.to_f / @total_steps
110
- end
111
-
112
- # @param instance [String] requested grafana instance
113
- # @return [Grafana::Grafana] the requested grafana instance.
114
- def grafana(instance)
115
- unless @grafana_instances[instance]
116
- @grafana_instances[instance] = ::Grafana::Grafana.new(@config.grafana_host(instance),
117
- @config.grafana_api_key(instance),
118
- logger: @logger, ssl_cert: @config.ssl_cert)
119
- end
120
- @grafana_instances[instance]
121
- end
122
-
123
- # Increments the progress.
124
- # @return [Integer] number of the current progress position.
125
- def next_step
126
- @current_pos += 1
127
- @current_pos
128
77
  end
129
78
 
130
79
  # Called to save a temporary image file. After the final generation of the
@@ -133,6 +82,7 @@ module GrafanaReporter
133
82
  # @return [String] path to the temporary file.
134
83
  def save_image_file(img_data)
135
84
  file = Tempfile.new(['gf_image_', '.png'], @config.images_folder.to_s)
85
+ file.binmode
136
86
  file.write(img_data)
137
87
  path = file.path.gsub(/#{@config.images_folder}/, '')
138
88
 
@@ -142,13 +92,21 @@ module GrafanaReporter
142
92
  path
143
93
  end
144
94
 
145
- # Called, if the report generation has died with an error.
146
- # @param error [StandardError] occured error
147
- # @return [void]
148
- def died_with_error(error)
149
- @error = [error.message] << [error.backtrace]
150
- @end_time = Time.now
151
- @done = true
95
+ # @see AbstractReport#default_template_extension
96
+ def self.default_template_extension
97
+ 'adoc'
98
+ end
99
+
100
+ # @see AbstractReport#default_result_extension
101
+ def self.default_result_extension
102
+ 'pdf'
103
+ end
104
+
105
+ # @see AbstractReport#demo_report_classes
106
+ def self.demo_report_classes
107
+ [AlertsTableIncludeProcessor, AnnotationsTableIncludeProcessor, PanelImageBlockMacro, PanelImageInlineMacro,
108
+ PanelPropertyInlineMacro, PanelQueryTableIncludeProcessor, PanelQueryValueInlineMacro,
109
+ SqlTableIncludeProcessor, SqlValueInlineMacro, ShowHelpIncludeProcessor, ShowEnvironmentIncludeProcessor]
152
110
  end
153
111
 
154
112
  private
@@ -162,6 +120,7 @@ module GrafanaReporter
162
120
  @total_steps = 0
163
121
  File.readlines(@template).each do |line|
164
122
  begin
123
+ # TODO: move these calls to the specific processors to ensure all are counted properly
165
124
  @total_steps += line.gsub(%r{//.*}, '').scan(/(?:grafana_panel_image|grafana_panel_query_value|
166
125
  grafana_panel_query_table|grafana_sql_value|
167
126
  grafana_sql_table|grafana_environment|grafana_help|
@@ -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 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,94 @@
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
+ vars = { 'table_formatter' => 'adoc_plain' }.merge(build_attribute_hash(doc.attributes, attrs))
55
+ query = QueryValueQuery.new(@report.grafana(instance), variables: vars)
56
+ query.datasource = @report.grafana(instance).datasource_by_id(target.split(':')[1].to_i)
57
+ query.raw_query = attrs['sql']
58
+
59
+ reader.unshift_lines query.execute
60
+ rescue GrafanaError => e
61
+ @report.logger.error(e.message)
62
+ reader.unshift_line "|#{e.message}"
63
+ rescue GrafanaReporterError => e
64
+ @report.logger.error(e.message)
65
+ reader.unshift_line "|#{e.message}"
66
+ rescue StandardError => e
67
+ @report.logger.fatal(e.message)
68
+ reader.unshift_line "|#{e.message}"
69
+ end
70
+
71
+ reader
72
+ end
73
+
74
+ # @see ProcessorMixin#build_demo_entry
75
+ def build_demo_entry(panel)
76
+ return nil unless panel
77
+ return nil unless panel.model['type'].include?('table')
78
+
79
+ ref_id = nil
80
+ panel.model['targets'].each do |item|
81
+ if !item['hide'] && !panel.query(item['refId']).to_s.empty?
82
+ ref_id = item['refId']
83
+ break
84
+ end
85
+ end
86
+ return nil unless ref_id
87
+
88
+ "|===\ninclude::grafana_sql_table:#{panel.dashboard.grafana.datasource_by_name(panel.model['datasource']).id}"\
89
+ "[sql=\"#{panel.query(ref_id).gsub(/"/, '\"').gsub("\n", ' ').gsub(/\\/, '\\\\')}\",filter_columns=\"time\","\
90
+ "dashboard=\"#{panel.dashboard.id}\",from=\"now-1h\",to=\"now\"]\n|==="
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,90 @@
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
+ variables: build_attribute_hash(parent.document.attributes, attrs))
54
+ query.datasource = @report.grafana(instance).datasource_by_id(target)
55
+ query.raw_query = attrs['sql']
56
+
57
+ create_inline(parent, :quoted, query.execute)
58
+ rescue GrafanaError => e
59
+ @report.logger.error(e.message)
60
+ create_inline(parent, :quoted, e.message)
61
+ rescue GrafanaReporterError => e
62
+ @report.logger.error(e.message)
63
+ create_inline(parent, :quoted, e.message)
64
+ rescue StandardError => e
65
+ @report.logger.fatal(e.message)
66
+ create_inline(parent, :quoted, e.message)
67
+ end
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'] == 'singlestat'
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
+ "grafana_sql_value:#{panel.dashboard.grafana.datasource_by_name(panel.model['datasource']).id}"\
85
+ "[sql=\"#{panel.query(ref_id).gsub(/"/, '\"').gsub("\n", ' ').gsub(/\\/, '\\\\')}\",from=\"now-1h\","\
86
+ 'to="now"]'
87
+ end
88
+ end
89
+ end
90
+ end