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
@@ -57,10 +57,17 @@ module Grafana
57
57
  end
58
58
  end
59
59
 
60
- # Raised if no SQL query is specified in a {AbstractSqlQuery} object.
60
+ # Raised if no SQL query is specified.
61
61
  class MissingSqlQueryError < GrafanaError
62
62
  def initialize
63
63
  super('No SQL statement has been specified.')
64
64
  end
65
65
  end
66
+
67
+ # Raised if a datasource shall be queried, which is not (yet) supported by the reporter
68
+ class InvalidDatasourceQueryProvidedError < GrafanaError
69
+ def initialize(query)
70
+ super("The datasource query provided, does not look like a grafana datasource target (received: #{query}).")
71
+ end
72
+ end
66
73
  end
@@ -9,20 +9,20 @@
9
9
  module Grafana
10
10
  # Main class for handling the interaction with one specific Grafana instance.
11
11
  class Grafana
12
+ attr_reader :logger
13
+
12
14
  # @param base_uri [String] full URI pointing to the specific grafana instance without
13
15
  # trailing slash, e.g. +https://localhost:3000+.
14
16
  # @param key [String] API key for the grafana instance, if required
15
17
  # @param opts [Hash] additional options.
16
- # Currently supporting +:logger+ and +:datasources+.
17
- # +:datasources+ need to be an Hash with datasource name as key and datasource id as value.
18
- # If not specified, the datasources will be queried from the grafana interface.
19
- # Specifying +:datasources+> here can be used, so that the interface can be used without grafana Admin privileges.
18
+ # Currently supporting +:logger+.
20
19
  def initialize(base_uri, key = nil, opts = {})
21
20
  @base_uri = base_uri
22
21
  @key = key
23
22
  @dashboards = {}
24
23
  @logger = opts[:logger] || ::Logger.new(nil)
25
- @datasources = opts[:datasources]
24
+
25
+ initialize_datasources unless @base_uri.empty?
26
26
  end
27
27
 
28
28
  # Used to test a connection to the grafana instance.
@@ -32,34 +32,52 @@ module Grafana
32
32
  #
33
33
  # @return [String] +Admin+, +NON-Admin+ or +Failed+ is returned, depending on the test results
34
34
  def test_connection
35
- if execute_http_request('/api/datasources').is_a?(Net::HTTPOK)
35
+ if prepare_request({ relative_url: '/api/datasources' }).execute.is_a?(Net::HTTPOK)
36
36
  # we have admin rights
37
- @logger.info('Reporter is running with Admin privileges on grafana.')
37
+ @logger.warn('Reporter is running with Admin privileges on grafana. This is a potential security risk.')
38
38
  return 'Admin'
39
39
  end
40
40
  # check if we have lower rights
41
- return 'Failed' unless execute_http_request('/api/dashboards/home').is_a?(Net::HTTPOK)
41
+ return 'Failed' unless prepare_request({ relative_url: '/api/dashboards/home' }).execute.is_a?(Net::HTTPOK)
42
42
 
43
- @logger.warn('Reporter is running with NON-Admin privileges on grafana. Make sure that necessary'\
44
- 'datasources are specified in CONFIG_FILE, otherwise operation will fail')
43
+ @logger.info('Reporter is running with NON-Admin privileges on grafana.')
45
44
  'NON-Admin'
46
45
  end
47
46
 
48
- # Returns the ID of a datasource, which has been queried by the datasource name.
47
+ # Returns the datasource, which has been queried by the datasource name.
49
48
  #
50
- # @return [Integer] ID for the specified datasource name
51
- def datasource_id(datasource_name)
52
- id = datasources[datasource_name]
53
- raise DatasourceDoesNotExistError.new('name', datasource_name) unless id
49
+ # @param datasource_name [String] name of the searched datasource
50
+ # @return [Datasource] Datasource for the specified datasource name
51
+ def datasource_by_name(datasource_name)
52
+ datasource_name = 'default' if datasource_name.to_s.empty?
53
+ raise DatasourceDoesNotExistError.new('name', datasource_name) unless @datasources[datasource_name]
54
54
 
55
- id
55
+ @datasources[datasource_name]
56
56
  end
57
57
 
58
- # Returns if the given datasource ID exists for the grafana instance.
58
+ # Returns the datasource, which has been queried by the datasource id.
59
59
  #
60
- # @return [Boolean] true if exists, false otherwise
61
- def datasource_id_exists?(datasource_id)
62
- datasources.value?(datasource_id)
60
+ # @param datasource_id [Integer] id of the searched datasource
61
+ # @return [Datasource] Datasource for the specified datasource id
62
+ def datasource_by_id(datasource_id)
63
+ datasource = @datasources.select { |_name, ds| ds.id == datasource_id.to_i }.values.first
64
+ raise DatasourceDoesNotExistError.new('id', datasource_id) unless datasource
65
+
66
+ datasource
67
+ end
68
+
69
+ # @return [Array] Array of dashboard uids within the current grafana object
70
+ def dashboard_ids
71
+ response = prepare_request({ relative_url: '/api/search' }).execute
72
+ return [] unless response.is_a?(Net::HTTPOK)
73
+
74
+ dashboards = JSON.parse(response.body)
75
+
76
+ dashboards.each do |dashboard|
77
+ @dashboards[dashboard['uid']] = nil unless @dashboards[dashboard['uid']]
78
+ end
79
+
80
+ @dashboards.keys
63
81
  end
64
82
 
65
83
  # @param dashboard_uid [String] UID of the searched {Dashboard}
@@ -67,64 +85,42 @@ module Grafana
67
85
  def dashboard(dashboard_uid)
68
86
  return @dashboards[dashboard_uid] unless @dashboards[dashboard_uid].nil?
69
87
 
70
- response = execute_http_request("/api/dashboards/uid/#{dashboard_uid}")
71
- model = JSON.parse(response.body)['dashboard']
72
-
73
- raise DashboardDoesNotExistError, dashboard_uid if model.nil?
88
+ response = prepare_request({ relative_url: "/api/dashboards/uid/#{dashboard_uid}" }).execute
89
+ raise DashboardDoesNotExistError, dashboard_uid unless response.is_a?(Net::HTTPOK)
74
90
 
75
91
  # cache dashboard for reuse
92
+ model = JSON.parse(response.body)['dashboard']
76
93
  @dashboards[dashboard_uid] = Dashboard.new(model, self)
77
94
 
78
95
  @dashboards[dashboard_uid]
79
96
  end
80
97
 
81
- # Runs a specific HTTP request against the current grafana instance.
98
+ # Prepares a {WebRequest} object for the current {Grafana} instance, which may be enriched
99
+ # with further properties and can then run {WebRequest#execute}.
82
100
  #
83
- # Default (can be overridden, by specifying the options Hash):
84
- # accept: 'application/json'
85
- # request: Net::HTTP::Get
86
- # content_type: 'application/json'
87
- #
88
- # @param relative_uri [String] relative URL with a leading slash, which shall be queried
89
- # @param options [Hash] options, which shall be merged to the request.
90
- # @param timeout [Integer] number of seconds to wait, before the http request is cancelled, defaults to 60 seconds
91
- def execute_http_request(relative_uri, options = {}, timeout = nil)
92
- uri = URI.parse(@base_uri + relative_uri)
93
- default_options = { accept: 'application/json', request: Net::HTTP::Get, content_type: 'application/json' }
94
- options = default_options.merge(options)
95
-
96
- http = Net::HTTP.new(uri.host, uri.port)
97
- if @base_uri =~ /^https/
98
- http.use_ssl = true
99
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
100
- end
101
- http.read_timeout = timeout.to_i if timeout
102
-
103
- request = options[:request].new(uri.request_uri)
104
- request['Accept'] = options[:accept]
105
- request['Content-Type'] = options[:content_type]
106
- request['Authorization'] = "Bearer #{@key}" unless @key.nil?
107
- request.body = options[:body]
108
-
109
- @logger.debug("Requesting #{relative_uri} with '#{options[:body]}' and timeout '#{http.read_timeout}'")
110
- http.request(request)
101
+ # @option options [Hash] :relative_url relative URL with a leading slash, which shall be queried
102
+ # @option options [Hash] :accept
103
+ # @option options [Hash] :body
104
+ # @option options [Hash] :content_type
105
+ # @return [WebRequest] webrequest prepared for execution
106
+ def prepare_request(options = {})
107
+ auth = @key ? { authorization: "Bearer #{@key}" } : {}
108
+ WebRequest.new(@base_uri, auth.merge({ logger: @logger }).merge(options))
111
109
  end
112
110
 
113
111
  private
114
112
 
115
- def datasources
116
- if @datasources.nil?
117
- # load datasources from grafana directly, if allowed
118
- response = execute_http_request('/api/datasources')
119
- if response['message'].nil?
120
- json = JSON.parse(response.body)
121
- # only store needed values
122
- @datasources = json.map { |item| [item['name'], item['id']] }.to_h
123
- else
124
- @datasources = {}
125
- end
113
+ def initialize_datasources
114
+ @datasources = {}
115
+
116
+ settings = prepare_request({ relative_url: '/api/frontend/settings' }).execute
117
+ return unless settings.is_a?(Net::HTTPOK)
118
+
119
+ json = JSON.parse(settings.body)
120
+ json['datasources'].select { |_k, v| v['id'].to_i.positive? }.each do |ds_name, ds_value|
121
+ @datasources[ds_name] = AbstractDatasource.build_instance(ds_value)
126
122
  end
127
- @datasources
123
+ @datasources['default'] = @datasources[json['defaultDatasource']]
128
124
  end
129
125
  end
130
126
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grafana
4
+ # Implements the datasource interface to grafana alerts.
5
+ class GrafanaAlertsDatasource < AbstractDatasource
6
+ # +:raw_query+ needs to contain a Hash with the following structure:
7
+ #
8
+ # {
9
+ # dashboardId: Dashboard ID as String or nil
10
+ # panelId: Panel ID as String or nil
11
+ # columns:
12
+ # limit:
13
+ # query:
14
+ # state:
15
+ # folderId:
16
+ # dashboardQuery:
17
+ # dashboardTag:
18
+ # }
19
+ # @see AbstractDatasource#request
20
+ def request(query_description)
21
+ webrequest = query_description[:prepared_request]
22
+ webrequest.relative_url = "/api/alerts#{url_parameters(query_description)}"
23
+
24
+ result = webrequest.execute(query_description[:timeout])
25
+
26
+ json = JSON.parse(result.body)
27
+
28
+ content = []
29
+ begin
30
+ json.each { |item| content << item.fetch_values(*query_description[:raw_query]['columns'].split(',')) }
31
+ rescue KeyError => e
32
+ raise MalformedAttributeContentError.new(e.message, 'columns', query_description[:raw_query]['columns'])
33
+ end
34
+
35
+ result = {}
36
+ result[:header] = [query_description[:raw_query]['columns'].split(',')]
37
+ result[:content] = content
38
+
39
+ result
40
+ end
41
+
42
+ private
43
+
44
+ def url_parameters(query_desc)
45
+ url_vars = {}
46
+ url_vars.merge!(query_desc[:raw_query].select do |k, _v|
47
+ k =~ /^(?:limit|dashboardId|panelId|query|state|folderId|dashboardQuery|dashboardTag)/
48
+ end)
49
+ url_vars['from'] = query_desc[:from] if query_desc[:from]
50
+ url_vars['to'] = query_desc[:to] if query_desc[:to]
51
+ url_params = URI.encode_www_form(url_vars.map { |k, v| [k, v.to_s] })
52
+ return '' if url_params.empty?
53
+
54
+ "?#{url_params}"
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grafana
4
+ # Implements the datasource interface to grafana annotations.
5
+ class GrafanaAnnotationsDatasource < AbstractDatasource
6
+ # +:raw_query+ needs to contain a Hash with the following structure:
7
+ #
8
+ # {
9
+ # dashboardId: Dashboard ID as String or nil
10
+ # panelId: Panel ID as String or nil
11
+ # columns:
12
+ # limit:
13
+ # alertId:
14
+ # userId:
15
+ # type:
16
+ # tags:
17
+ # }
18
+ # @see AbstractDatasource#request
19
+ def request(query_description)
20
+ webrequest = query_description[:prepared_request]
21
+ webrequest.relative_url = "/api/annotations#{url_parameters(query_description)}"
22
+
23
+ result = webrequest.execute(query_description[:timeout])
24
+
25
+ json = JSON.parse(result.body)
26
+
27
+ content = []
28
+ begin
29
+ json.each { |item| content << item.fetch_values(*query_description[:raw_query]['columns'].split(',')) }
30
+ rescue KeyError => e
31
+ raise MalformedAttributeContentError.new(e.message, 'columns', query_description[:raw_query]['columns'])
32
+ end
33
+
34
+ result = {}
35
+ result[:header] = [query_description[:raw_query]['columns'].split(',')]
36
+ result[:content] = content
37
+
38
+ result
39
+ end
40
+
41
+ private
42
+
43
+ def url_parameters(query_desc)
44
+ url_vars = {}
45
+ url_vars.merge!(query_desc[:raw_query].select do |k, _v|
46
+ k =~ /^(?:limit|alertId|dashboardId|panelId|userId|type|tags)/
47
+ end)
48
+ url_vars['from'] = query_desc[:from] if query_desc[:from]
49
+ url_vars['to'] = query_desc[:to] if query_desc[:to]
50
+ url_params = URI.encode_www_form(url_vars.map { |k, v| [k, v.to_s] })
51
+ return '' if url_params.empty?
52
+
53
+ "?#{url_params}"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grafana
4
+ # Implements the datasource interface to grafana model properties.
5
+ class GrafanaPropertyDatasource < AbstractDatasource
6
+ # +:raw_query+ needs to contain a Hash with the following structure:
7
+ #
8
+ # {
9
+ # property_name: Name of the queried property as String
10
+ # panel: {Panel} object to query
11
+ # }
12
+ # @see AbstractDatasource#request
13
+ def request(query_description)
14
+ raise MissingSqlQueryError if query_description[:raw_query].nil?
15
+
16
+ panel = query_description[:raw_query][:panel]
17
+ property_name = query_description[:raw_query][:property_name]
18
+
19
+ {
20
+ header: [query_description[:raw_query][:property_name]],
21
+ content: [replace_variables(panel.field(property_name), query_description[:variables])]
22
+ }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grafana
4
+ # Implements the interface to graphite datasources.
5
+ class GraphiteDatasource < AbstractDatasource
6
+ # @see AbstractDatasource#handles?
7
+ def self.handles?(model)
8
+ tmp = new(model)
9
+ tmp.type == 'graphite'
10
+ end
11
+
12
+ # +:raw_query+ needs to contain a Graphite query as String
13
+ # @see AbstractDatasource#request
14
+ def request(query_description)
15
+ raise MissingSqlQueryError if query_description[:raw_query].nil?
16
+
17
+ request = {
18
+ body: URI.encode_www_form('from': DateTime.strptime(query_description[:from], '%Q').strftime('%H:%M_%Y%m%d'),
19
+ 'until': DateTime.strptime(query_description[:to], '%Q').strftime('%H:%M_%Y%m%d'),
20
+ 'format': 'json',
21
+ 'target': replace_variables(query_description[:raw_query], query_description[:variables])),
22
+ content_type: 'application/x-www-form-urlencoded',
23
+ request: Net::HTTP::Post
24
+ }
25
+
26
+ webrequest = query_description[:prepared_request]
27
+ webrequest.relative_url = "/api/datasources/proxy/#{id}/render"
28
+ webrequest.options.merge!(request)
29
+
30
+ result = webrequest.execute(query_description[:timeout])
31
+ preformat_response(result.body)
32
+ end
33
+
34
+ # @see AbstractDatasource#raw_query_from_panel_model
35
+ def raw_query_from_panel_model(panel_query_target)
36
+ panel_query_target['target']
37
+ end
38
+
39
+ private
40
+
41
+ # @see AbstractDatasource#preformat_response
42
+ def preformat_response(response_body)
43
+ # TODO: support multiple metrics as return types
44
+ {
45
+ header: %w[value time],
46
+ content: JSON.parse(response_body).first['datapoints']
47
+ }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grafana
4
+ # Implements the interface to image rendering datasources.
5
+ class ImageRenderingDatasource < AbstractDatasource
6
+ # +:raw_query+ needs to contain a Hash with the following structure:
7
+ #
8
+ # {
9
+ # panel: {Panel} which shall be rendered
10
+ # }
11
+ # @see AbstractDatasource#request
12
+ def request(query_description)
13
+ webrequest = query_description[:prepared_request]
14
+ webrequest.relative_url = query_description[:raw_query][:panel].render_url + url_params(query_description)
15
+ webrequest.options.merge!({ accept: 'image/png' })
16
+
17
+ result = webrequest.execute
18
+
19
+ { header: ['image'], content: [result.body] }
20
+ end
21
+
22
+ private
23
+
24
+ def url_params(query_desc)
25
+ url_vars = query_desc[:variables].select { |k, _v| k =~ /^(?:timeout|height|width|theme|fullscreen|var-.+)$/ }
26
+ url_vars = default_vars.merge(url_vars)
27
+ url_vars['from'] = Variable.new(query_desc[:from])
28
+ url_vars['to'] = Variable.new(query_desc[:to])
29
+ result = URI.encode_www_form(url_vars.map { |k, v| [k, v.raw_value.to_s] })
30
+
31
+ return '' if result.empty?
32
+
33
+ "&#{result}"
34
+ end
35
+
36
+ def default_vars
37
+ {
38
+ 'fullscreen' => Variable.new(true),
39
+ 'theme' => Variable.new('light'),
40
+ 'timeout' => Variable.new(60)
41
+ }
42
+ end
43
+ end
44
+ end
data/lib/grafana/panel.rb CHANGED
@@ -5,6 +5,7 @@ module Grafana
5
5
  class Panel
6
6
  # @return [Dashboard] parent {Dashboard} object
7
7
  attr_reader :dashboard
8
+ attr_reader :model
8
9
 
9
10
  # @param model [Hash] converted JSON Hash of the panel
10
11
  # @param dashboard [Dashboard] parent {Dashboard} object
@@ -25,12 +26,17 @@ module Grafana
25
26
  @model['id']
26
27
  end
27
28
 
28
- # @return [String] SQL query string for the requested query letter
29
+ # @return [Datasource] datasource object specified for the current panel
30
+ def datasource
31
+ dashboard.grafana.datasource_by_name(@model['datasource'])
32
+ end
33
+
34
+ # @return [String] query string for the requested query letter
29
35
  def query(query_letter)
30
36
  query_item = @model['targets'].select { |item| item['refId'].to_s == query_letter.to_s }.first
31
- raise QueryLetterDoesNotExistError.new(query_letter, self) if query_item.nil?
37
+ raise QueryLetterDoesNotExistError.new(query_letter, self) unless query_item
32
38
 
33
- query_item['rawSql']
39
+ datasource.raw_query_from_panel_model(query_item)
34
40
  end
35
41
 
36
42
  # @return [String] relative rendering URL for the panel, to create an image out of it