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
@@ -6,21 +6,48 @@ module GrafanaReporter
6
6
  # make use of `webrick` or similar, so that it can be used without futher dependencies
7
7
  # in conjunction with the standard asciidoctor docker container.
8
8
  class Webservice
9
- def initialize(config)
9
+ # Array of possible webservice running states
10
+ STATUS = %I[stopped running stopping].freeze
11
+
12
+ def initialize
10
13
  @reports = []
14
+ @status = :stopped
15
+ end
16
+
17
+ # Runs the webservice with the given {Configuration} object.
18
+ def run(config)
11
19
  @config = config
12
20
  @logger = config.logger
13
- end
14
21
 
15
- # Runs the webservice with the current set {Configuration} object.
16
- def run
17
22
  # start webserver
18
23
  @server = TCPServer.new(@config.webserver_port)
19
24
  @logger.info("Server listening on port #{@config.webserver_port}...")
20
25
 
21
26
  @progress_reporter = Thread.new {}
22
27
 
28
+ @status = :running
23
29
  accept_requests_loop
30
+ @status = :stopped
31
+ end
32
+
33
+ # @return True, if webservice is stopped, false otherwise
34
+ def stopped?
35
+ @status == :stopped
36
+ end
37
+
38
+ # @return True, if webservice is up and running, false otherwise
39
+ def running?
40
+ @status == :running
41
+ end
42
+
43
+ # Forces stopping the webservice.
44
+ def stop!
45
+ @status = :stopping
46
+
47
+ # invoke a new request, so that the webservice stops.
48
+ socket = TCPSocket.new('localhost', @config.webserver_port)
49
+ socket.send '', 0
50
+ socket.close
24
51
  end
25
52
 
26
53
  private
@@ -30,6 +57,14 @@ module GrafanaReporter
30
57
  # step 1) accept incoming connection
31
58
  socket = @server.accept
32
59
 
60
+ # TODO: shutdown properly on SIGINT/SIGHUB
61
+
62
+ # stop webservice properly, if shall be shutdown
63
+ if @status == :stopping
64
+ socket.close
65
+ break
66
+ end
67
+
33
68
  # step 2) print the request headers (separated by a blank line e.g. \r\n)
34
69
  request = ''
35
70
  line = ''
@@ -48,9 +83,6 @@ module GrafanaReporter
48
83
  rescue WebserviceUnknownPathError => e
49
84
  @logger.debug(e.message)
50
85
  socket.write http_response(404, '', e.message)
51
- rescue MissingTemplateError => e
52
- @logger.error(e.message)
53
- socket.write http_response(400, 'Bad Request', e.message)
54
86
  rescue WebserviceGeneralRenderingError => e
55
87
  @logger.fatal(e.message)
56
88
  socket.write http_response(400, 'Bad Request', e.message)
@@ -160,17 +192,17 @@ module GrafanaReporter
160
192
 
161
193
  # provide report
162
194
  @logger.debug("Returning PDF report at #{report.path}")
163
- content = File.read(report.path)
195
+ content = File.read(report.path, mode: 'rb')
164
196
  return http_response(200, 'OK', content, "Content-Type": 'application/pdf') if content.start_with?('%PDF')
165
197
 
166
- # TODO: properly provide file as zip
167
198
  http_response(200, 'OK', content, "Content-Type": 'application/octet-stream',
168
- "Content-Disposition": 'attachment; filename=report.zip')
199
+ "Content-Disposition": 'attachment; '\
200
+ "filename=report_#{attrs['report_id']}.zip")
169
201
  end
170
202
 
171
203
  def render_report(attrs)
172
204
  # build report
173
- template_file = "#{@config.templates_folder}#{attrs['var-template']}.adoc"
205
+ template_file = "#{@config.templates_folder}#{attrs['var-template']}"
174
206
 
175
207
  file = Tempfile.new('gf_pdf_', @config.reports_folder)
176
208
  begin
@@ -179,9 +211,10 @@ module GrafanaReporter
179
211
  @logger.debug("File permissions could not be set for #{file.path}: #{e.message}")
180
212
  end
181
213
 
182
- report = @config.report_class.new(@config, template_file, file, attrs)
214
+ report = @config.report_class.new(@config)
215
+ Thread.report_on_exception = false
183
216
  Thread.new do
184
- report.create_report
217
+ report.create_report(template_file, file, attrs)
185
218
  end
186
219
  @reports << report
187
220
 
@@ -211,7 +244,9 @@ module GrafanaReporter
211
244
  "<td>#{report.start_time}</td>"\
212
245
  "<td>#{report.end_time}</td>"\
213
246
  "<td>#{report.template}</td>"\
214
- "<td>#{report.execution_time.to_i} secs</td><td>#{report.status} (#{(report.progress * 100).to_i}%)</td><td>#{report.error.join('<br>')}</td>"\
247
+ "<td>#{report.execution_time.to_i} secs</td>"\
248
+ "<td>#{report.status} (#{(report.progress * 100).to_i}%)</td>"\
249
+ "<td>#{report.error.join('<br>')}</td>"\
215
250
  "<td>#{!report.done && !report.cancel ? "<a href=\"/cancel_report?report_id=#{report.object_id}\">Cancel</a>&nbsp;" : ''}"\
216
251
  "#{(report.status == 'finished') || (report.status == 'cancelled') ? "<a href=\"/view_report?report_id=#{report.object_id}\">View</a>&nbsp;" : '&nbsp;'}"\
217
252
  "<a href=\"/view_log?report_id=#{report.object_id}\">Log</a></td>"\
@@ -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_alerts[<options>]
9
+ #
10
+ # Returns the results of alerts query as a asciidoctor table.
11
+ #
12
+ # == Used document parameters
13
+ # +grafana_default_instance+ - name of grafana instance, 'default' if not specified
14
+ #
15
+ # +grafana_default_dashboard+ - uid of grafana default dashboard to use
16
+ #
17
+ # +from+ - 'from' time for the sql query
18
+ #
19
+ # +to+ - 'to' time for the sql query
20
+ #
21
+ # == Supported options
22
+ # +columns+ - see {AlertsTableQuery#pre_process} (*mandatory*)
23
+ #
24
+ # +instance+ - name of grafana instance, 'default' if not specified
25
+ #
26
+ # +dashboard+ - uid of grafana dashboard to query for, empty string if no filter is wanted
27
+ #
28
+ # +panel+ - id of the panel to query for
29
+ #
30
+ # +from+ - 'from' time for the sql query
31
+ #
32
+ # +to+ - 'to' time for the sql query
33
+ #
34
+ # +format+ - see {AbstractQuery#format_columns}
35
+ #
36
+ # +replace_values+ - see {AbstractQuery#replace_values}
37
+ #
38
+ # +filter_columns+ - see {AbstractQuery#filter_columns}
39
+ class AlertsTableIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
40
+ include ProcessorMixin
41
+
42
+ # :nodoc:
43
+ def handles?(target)
44
+ target.start_with? 'grafana_alerts'
45
+ end
46
+
47
+ # :nodoc:
48
+ def process(doc, reader, _target, attrs)
49
+ return if @report.cancel
50
+
51
+ @report.next_step
52
+ instance = attrs['instance'] || doc.attr('grafana_default_instance') || 'default'
53
+ dashboard_id = attrs['dashboard'] || doc.attr('grafana_default_dashboard')
54
+ panel_id = attrs['panel']
55
+ @report.logger.debug("Processing AlertsTableIncludeProcessor (instance: #{instance},"\
56
+ " dashboard: #{dashboard_id}, panel: #{panel_id})")
57
+
58
+ query = AlertsTableQuery.new(@report.grafana(instance))
59
+ assign_dashboard_defaults(query, @report.grafana(instance).dashboard(dashboard_id)) if dashboard_id
60
+ defaults = {}
61
+ defaults['dashboardId'] = dashboard_id if dashboard_id
62
+ defaults['panelId'] = panel_id if panel_id
63
+
64
+ assign_doc_and_item_variables(query, doc.attributes, attrs)
65
+ selected_attrs = attrs.select do |k, _v|
66
+ k =~ /(?:columns|limit|folderId|dashboardId|panelId|dahboardTag|dashboardQuery|state|query)/x
67
+ end
68
+ query.raw_query = defaults.merge(selected_attrs.each_with_object({}) { |(k, v), h| h[k] = v })
69
+ @report.logger.debug("from: #{query.from}, to: #{query.to}")
70
+
71
+ begin
72
+ reader.unshift_lines query.execute
73
+ rescue GrafanaReporterError => e
74
+ @report.logger.error(e.message)
75
+ reader.unshift_line "|#{e.message}"
76
+ rescue StandardError => e
77
+ @report.logger.fatal(e.message)
78
+ reader.unshift_line "|#{e.message}"
79
+ end
80
+
81
+ reader
82
+ end
83
+
84
+ # @see ProcessorMixin#build_demo_entry
85
+ def build_demo_entry(_panel)
86
+ "|===\ninclude::grafana_alerts[columns=\"panelId,name,state\"]\n|==="
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,89 @@
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_annotations[<options>]
9
+ #
10
+ # Returns the results of alerts query as a asciidoctor table.
11
+ #
12
+ # == Used document parameters
13
+ # +grafana_default_instance+ - name of grafana instance, 'default' if not specified
14
+ #
15
+ # +grafana_default_dashboard+ - uid of grafana default dashboard to use
16
+ #
17
+ # +from+ - 'from' time for the sql query
18
+ #
19
+ # +to+ - 'to' time for the sql query
20
+ #
21
+ # == Supported options
22
+ # +columns+ - see {AnnotationsTableQuery#pre_process} (*mandatory*)
23
+ #
24
+ # +instance+ - name of grafana instance, 'default' if not specified
25
+ #
26
+ # +dashboard+ - uid of grafana dashboard to query for, empty string if no filter is wanted
27
+ #
28
+ # +panel+ - id of the panel to query for
29
+ #
30
+ # +from+ - 'from' time for the sql query
31
+ #
32
+ # +to+ - 'to' time for the sql query
33
+ #
34
+ # +format+ - see {AbstractQuery#format_columns}
35
+ #
36
+ # +replace_values+ - see {AbstractQuery#replace_values}
37
+ #
38
+ # +filter_columns+ - see {AbstractQuery#filter_columns}
39
+ class AnnotationsTableIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
40
+ include ProcessorMixin
41
+
42
+ # :nodoc:
43
+ def handles?(target)
44
+ target.start_with? 'grafana_annotations'
45
+ end
46
+
47
+ # :nodoc:
48
+ def process(doc, reader, _target, attrs)
49
+ return if @report.cancel
50
+
51
+ @report.next_step
52
+ instance = attrs['instance'] || doc.attr('grafana_default_instance') || 'default'
53
+ dashboard_id = attrs['dashboard'] || doc.attr('grafana_default_dashboard')
54
+ panel_id = attrs['panel']
55
+ @report.logger.debug("Processing AnnotationsTableIncludeProcessor (instance: #{instance})")
56
+
57
+ query = AnnotationsTableQuery.new(@report.grafana(instance))
58
+ assign_dashboard_defaults(query, @report.grafana(instance).dashboard(dashboard_id)) if dashboard_id
59
+ defaults = {}
60
+ defaults['dashboardId'] = dashboard_id if dashboard_id
61
+ defaults['panelId'] = panel_id if panel_id
62
+
63
+ assign_doc_and_item_variables(query, doc.attributes, attrs)
64
+ selected_attrs = attrs.select do |k, _v|
65
+ k =~ /(?:columns|limit|alertId|dashboardId|panelId|userId|type|tags)/
66
+ end
67
+ query.raw_query = defaults.merge(selected_attrs.each_with_object({}) { |(k, v), h| h[k] = v })
68
+ @report.logger.debug("from: #{query.from}, to: #{query.to}")
69
+
70
+ begin
71
+ reader.unshift_lines query.execute
72
+ rescue GrafanaReporterError => e
73
+ @report.logger.error(e.message)
74
+ reader.unshift_line "|#{e.message}"
75
+ rescue StandardError => e
76
+ @report.logger.fatal(e.message)
77
+ reader.unshift_line "|#{e.message}"
78
+ end
79
+
80
+ reader
81
+ end
82
+
83
+ # @see ProcessorMixin#build_demo_entry
84
+ def build_demo_entry(_panel)
85
+ "|===\ninclude::grafana_annotations[columns=\"time,panelId,newState,prevState,text\"]\n|==="
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'processor_mixin'
4
+
5
+ module GrafanaReporter
6
+ module Asciidoctor
7
+ # Implements the hook
8
+ # grafana_panel_image::<panel_id>[<options>]
9
+ #
10
+ # Stores the queried panel as a temporary image file and returns an asciidoctor link
11
+ # to be included in the report.
12
+ #
13
+ # == Used document parameters
14
+ # +grafana_default_instance+ - name of grafana instance, 'default' if not specified
15
+ #
16
+ # +grafana_default_dashboard+ - uid of grafana default dashboard to use
17
+ #
18
+ # +from+ - 'from' time for the sql query
19
+ #
20
+ # +to+ - 'to' time for the sql query
21
+ #
22
+ # == Supported options
23
+ # +field+ - property to query for, e.g. +description+ or +title+ (*mandatory*)
24
+ #
25
+ # +instance+ - name of grafana instance, 'default' if not specified
26
+ #
27
+ # +dashboard+ - uid of grafana dashboard to use
28
+ #
29
+ # +from+ - 'from' time for the sql query
30
+ #
31
+ # +to+ - 'to' time for the sql query
32
+ class PanelImageBlockMacro < ::Asciidoctor::Extensions::BlockMacroProcessor
33
+ include ProcessorMixin
34
+ use_dsl
35
+
36
+ named :grafana_panel_image
37
+
38
+ # :nodoc:
39
+ def process(parent, target, attrs)
40
+ return if @report.cancel
41
+
42
+ @report.next_step
43
+ instance = attrs['instance'] || parent.document.attr('grafana_default_instance') || 'default'
44
+ dashboard = attrs['dashboard'] || parent.document.attr('grafana_default_dashboard')
45
+ @report.logger.debug("Processing PanelImageBlockMacro (instance: #{instance}, dashboard: #{dashboard},"\
46
+ " panel: #{target})")
47
+
48
+ begin
49
+ query = PanelImageQuery.new(@report.grafana(instance).dashboard(dashboard).panel(target))
50
+ assign_dashboard_defaults(query, @report.grafana(instance).dashboard(dashboard))
51
+ assign_doc_and_item_variables(query, parent.document.attributes, attrs)
52
+ @report.logger.debug("from: #{query.from}, to: #{query.to}")
53
+
54
+ image = query.execute
55
+ image_path = @report.save_image_file(image)
56
+ rescue GrafanaReporterError => e
57
+ @report.logger.error(e.message)
58
+ return create_paragraph(parent, e.message, attrs)
59
+ rescue StandardError => e
60
+ @report.logger.fatal(e.message)
61
+ return create_paragraph(parent, e.message, attrs)
62
+ end
63
+
64
+ attrs['target'] = image_path
65
+ create_image_block(parent, attrs)
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'] == 'graph'
72
+
73
+ "grafana_panel_image::#{panel.id}[dashboard=\"#{panel.dashboard.id}\",width=\"50%\"]"
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'processor_mixin'
4
+
5
+ module GrafanaReporter
6
+ module Asciidoctor
7
+ # Implements the hook
8
+ # grafana_panel_image:<panel_id>[<options>]
9
+ #
10
+ # Stores the queried panel as a temporary image file and returns an asciidoctor link
11
+ # to be included in the report.
12
+ #
13
+ # == Used document parameters
14
+ # +grafana_default_instance+ - name of grafana instance, 'default' if not specified
15
+ #
16
+ # +grafana_default_dashboard+ - uid of grafana default dashboard to use
17
+ #
18
+ # +from+ - 'from' time for the sql query
19
+ #
20
+ # +to+ - 'to' time for the sql query
21
+ #
22
+ # == Supported options
23
+ # +field+ - property to query for, e.g. +description+ or +title+ (*mandatory*)
24
+ #
25
+ # +instance+ - name of grafana instance, 'default' if not specified
26
+ #
27
+ # +dashboard+ - uid of grafana dashboard to use
28
+ #
29
+ # +from+ - 'from' time for the sql query
30
+ #
31
+ # +to+ - 'to' time for the sql query
32
+ class PanelImageInlineMacro < ::Asciidoctor::Extensions::InlineMacroProcessor
33
+ include ProcessorMixin
34
+ use_dsl
35
+
36
+ named :grafana_panel_image
37
+
38
+ # :nodoc:
39
+ def process(parent, target, attrs)
40
+ return if @report.cancel
41
+
42
+ @report.next_step
43
+ instance = attrs['instance'] || parent.document.attr('grafana_default_instance') || 'default'
44
+ dashboard = attrs['dashboard'] || parent.document.attr('grafana_default_dashboard')
45
+ @report.logger.debug("Processing PanelImageInlineMacro (instance: #{instance}, dashboard: #{dashboard},"\
46
+ " panel: #{target})")
47
+
48
+ begin
49
+ query = PanelImageQuery.new(@report.grafana(instance).dashboard(dashboard).panel(target))
50
+ # set alt text to a default, because otherwise asciidoctor fails
51
+ attrs['alt'] = '' unless attrs['alt']
52
+ assign_dashboard_defaults(query, @report.grafana(instance).dashboard(dashboard))
53
+ assign_doc_and_item_variables(query, parent.document.attributes, attrs)
54
+ @report.logger.debug("from: #{query.from}, to: #{query.to}")
55
+
56
+ image = query.execute
57
+ image_path = @report.save_image_file(image)
58
+ rescue GrafanaReporterError => e
59
+ @report.logger.error(e.message)
60
+ return create_inline(parent, :quoted, e.message)
61
+ rescue StandardError => e
62
+ @report.logger.fatal(e.message)
63
+ return create_inline(parent, :quoted, e.message)
64
+ end
65
+
66
+ create_inline(parent, :image, nil, { target: image_path, attributes: attrs })
67
+ end
68
+
69
+ # @see ProcessorMixin#build_demo_entry
70
+ def build_demo_entry(panel)
71
+ return nil unless panel
72
+ return nil unless panel.model['type'] == 'graph'
73
+
74
+ "see here: grafana_panel_image:#{panel.id}[dashboard=\"#{panel.dashboard.id}\","\
75
+ 'width="90%"] - a working inline image'
76
+ end
77
+ end
78
+ end
79
+ end