ruby-grafana-reporter 0.1.7 → 0.2.0
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.
- checksums.yaml +4 -4
- data/README.md +166 -339
- data/bin/ruby-grafana-reporter +5 -4
- data/lib/VERSION.rb +5 -3
- data/lib/grafana/abstract_panel_query.rb +22 -20
- data/lib/grafana/abstract_query.rb +132 -127
- data/lib/grafana/abstract_sql_query.rb +51 -42
- data/lib/grafana/dashboard.rb +77 -66
- data/lib/grafana/errors.rb +66 -61
- data/lib/grafana/grafana.rb +130 -131
- data/lib/grafana/panel.rb +41 -39
- data/lib/grafana/panel_image_query.rb +52 -49
- data/lib/grafana/variable.rb +217 -259
- data/lib/grafana_reporter/abstract_report.rb +112 -109
- data/lib/grafana_reporter/application/application.rb +404 -229
- data/lib/grafana_reporter/application/errors.rb +33 -30
- data/lib/grafana_reporter/application/webservice.rb +231 -0
- data/lib/grafana_reporter/asciidoctor/alerts_table_query.rb +104 -99
- data/lib/grafana_reporter/asciidoctor/annotations_table_query.rb +99 -96
- data/lib/grafana_reporter/asciidoctor/errors.rb +40 -37
- data/lib/grafana_reporter/asciidoctor/extensions/alerts_table_include_processor.rb +92 -86
- data/lib/grafana_reporter/asciidoctor/extensions/annotations_table_include_processor.rb +91 -86
- data/lib/grafana_reporter/asciidoctor/extensions/panel_image_block_macro.rb +69 -67
- data/lib/grafana_reporter/asciidoctor/extensions/panel_image_inline_macro.rb +68 -65
- data/lib/grafana_reporter/asciidoctor/extensions/panel_property_inline_macro.rb +61 -58
- data/lib/grafana_reporter/asciidoctor/extensions/panel_query_table_include_processor.rb +78 -75
- data/lib/grafana_reporter/asciidoctor/extensions/panel_query_value_inline_macro.rb +73 -70
- data/lib/grafana_reporter/asciidoctor/extensions/processor_mixin.rb +20 -18
- data/lib/grafana_reporter/asciidoctor/extensions/show_environment_include_processor.rb +43 -41
- data/lib/grafana_reporter/asciidoctor/extensions/sql_table_include_processor.rb +70 -67
- data/lib/grafana_reporter/asciidoctor/extensions/sql_value_inline_macro.rb +66 -65
- data/lib/grafana_reporter/asciidoctor/extensions/value_as_variable_include_processor.rb +61 -57
- data/lib/grafana_reporter/asciidoctor/panel_first_value_query.rb +34 -32
- data/lib/grafana_reporter/asciidoctor/panel_image_query.rb +25 -23
- data/lib/grafana_reporter/asciidoctor/panel_property_query.rb +44 -43
- data/lib/grafana_reporter/asciidoctor/panel_table_query.rb +38 -36
- data/lib/grafana_reporter/asciidoctor/query_mixin.rb +310 -309
- data/lib/grafana_reporter/asciidoctor/report.rb +177 -159
- data/lib/grafana_reporter/asciidoctor/sql_first_value_query.rb +37 -34
- data/lib/grafana_reporter/asciidoctor/sql_table_query.rb +39 -32
- data/lib/grafana_reporter/configuration.rb +257 -326
- data/lib/grafana_reporter/errors.rb +48 -38
- data/lib/grafana_reporter/logger/two_way_logger.rb +58 -52
- data/lib/ruby-grafana-reporter.rb +29 -27
- metadata +10 -23
@@ -1,49 +1,52 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@variables['
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grafana
|
4
|
+
# Query, which allows to render a {Panel} as a PNG image.
|
5
|
+
class PanelImageQuery < AbstractPanelQuery
|
6
|
+
# Returns the URL for rendering the panel. Uses {Panel#render_url} and sets additional url
|
7
|
+
# parameters according {https://grafana.com/docs/grafana/latest/reference/share_panel Grafana Share Panel}.
|
8
|
+
#
|
9
|
+
# @see AbstractQuery#url
|
10
|
+
# @return [String] string for rendering the panel
|
11
|
+
def url
|
12
|
+
@panel.render_url + url_parameters
|
13
|
+
end
|
14
|
+
|
15
|
+
# Changes the result of the request to be of type +image/png+.
|
16
|
+
#
|
17
|
+
# @see AbstractQuery#request
|
18
|
+
def request
|
19
|
+
{ accept: 'image/png' }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Adds default variables for querying the image.
|
23
|
+
#
|
24
|
+
# @see AbstractQuery#pre_process
|
25
|
+
def pre_process(_grafana)
|
26
|
+
@variables['fullscreen'] = Variable.new(true)
|
27
|
+
@variables['theme'] = Variable.new('light')
|
28
|
+
@variables['timeout'] = Variable.new(timeout) if timeout
|
29
|
+
@variables['timeout'] ||= Variable.new(60)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Checks if the rendering has been performed properly.
|
33
|
+
# If so, the resulting image is stored in the @result variable, otherwise an error is raised.
|
34
|
+
#
|
35
|
+
# @see AbstractQuery#post_process
|
36
|
+
def post_process
|
37
|
+
raise ImageCouldNotBeRenderedError, @panel if @result.body.include?('<html')
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def url_parameters
|
43
|
+
url_vars = variables.select { |k, _v| k =~ /^(?:timeout|height|width|theme|fullscreen)/ || k =~ /^var-.+/ }
|
44
|
+
url_vars['from'] = Variable.new(@from) if @from
|
45
|
+
url_vars['to'] = Variable.new(@to) if @to
|
46
|
+
url_params = URI.encode_www_form(url_vars.map { |k, v| [k, v.raw_value.to_s] })
|
47
|
+
return '' if url_params.empty?
|
48
|
+
|
49
|
+
"&#{url_params}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/grafana/variable.rb
CHANGED
@@ -1,259 +1,217 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
#
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
value
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
when '
|
107
|
-
if multi?
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
return
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
'%-U'
|
219
|
-
when 'ww'
|
220
|
-
'%U'
|
221
|
-
when 'W'
|
222
|
-
'%-V'
|
223
|
-
when 'WW'
|
224
|
-
'%V'
|
225
|
-
when 'YY'
|
226
|
-
'%y'
|
227
|
-
when 'YYYY'
|
228
|
-
'%Y'
|
229
|
-
when 'A'
|
230
|
-
'%p'
|
231
|
-
when 'a'
|
232
|
-
'%P'
|
233
|
-
when 'H'
|
234
|
-
'%-H'
|
235
|
-
when 'HH'
|
236
|
-
'%H'
|
237
|
-
when 'h'
|
238
|
-
'%-I'
|
239
|
-
when 'hh'
|
240
|
-
'%I'
|
241
|
-
when 'm'
|
242
|
-
'%-M'
|
243
|
-
when 'mm'
|
244
|
-
'%M'
|
245
|
-
when 's'
|
246
|
-
'%-S'
|
247
|
-
when 'ss'
|
248
|
-
'%S'
|
249
|
-
when 'X'
|
250
|
-
'%s'
|
251
|
-
else
|
252
|
-
match
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
Time.at((Float(value) / 1000).to_i).strftime(format_string)
|
257
|
-
end
|
258
|
-
end
|
259
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grafana
|
4
|
+
# This class contains a representation of
|
5
|
+
# {https://grafana.com/docs/grafana/latest/variables/templates-and-variables grafana variables},
|
6
|
+
# aka grafana templates.
|
7
|
+
#
|
8
|
+
# The main need therefore rises in order to replace variables properly in different
|
9
|
+
# texts, e.g. SQL statements or results.
|
10
|
+
class Variable
|
11
|
+
attr_reader :name, :text, :raw_value
|
12
|
+
|
13
|
+
DATE_MATCHES = { 'M' => '%-m', 'MM' => '%m', 'MMM' => '%b', 'MMMM' => '%B',
|
14
|
+
'D' => '%-d', 'DD' => '%d', 'DDD' => '%-j', 'DDDD' => '%j',
|
15
|
+
'd' => '%w', 'ddd' => '%a', 'dddd' => '%A',
|
16
|
+
'YY' => '%y', 'YYYY' => '%Y',
|
17
|
+
'h' => '%-I', 'hh' => '%I',
|
18
|
+
'H' => '%-H', 'HH' => '%H',
|
19
|
+
'm' => '%-M', 'mm' => '%M',
|
20
|
+
's' => '%-S', 'ss' => '%S',
|
21
|
+
'w' => '%-U', 'ww' => '%U',
|
22
|
+
'W' => '%-V', 'WW' => '%V',
|
23
|
+
'a' => '%P',
|
24
|
+
'A' => '%p',
|
25
|
+
'e' => '%w',
|
26
|
+
'E' => '%u',
|
27
|
+
'X' => '%s' }.freeze
|
28
|
+
|
29
|
+
# @param config_or_value [Hash, Object] configuration hash of a variable out of an {Dashboard} instance
|
30
|
+
# or a value of any kind.
|
31
|
+
def initialize(config_or_value)
|
32
|
+
if config_or_value.is_a? Hash
|
33
|
+
@config = config_or_value
|
34
|
+
@name = @config['name']
|
35
|
+
unless @config['current'].nil?
|
36
|
+
@raw_value = @config['current']['value']
|
37
|
+
@text = @config['current']['text']
|
38
|
+
end
|
39
|
+
else
|
40
|
+
@config = {}
|
41
|
+
@raw_value = config_or_value
|
42
|
+
@text = config_or_value.to_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the stored value formatted according the given format.
|
47
|
+
#
|
48
|
+
# Supported formats are: +csv+, +distributed+, +doublequote+, +json+, +percentencode+, +pipe+, +raw+,
|
49
|
+
# +regex+, +singlequote+, +sqlstring+, +lucene+, +date+ or +glob+ (default)
|
50
|
+
#
|
51
|
+
# For details see {https://grafana.com/docs/grafana/latest/variables/advanced-variable-format-options
|
52
|
+
# Grafana Advanced variable format options}.
|
53
|
+
#
|
54
|
+
# For details of +date+ format, see
|
55
|
+
# {https://grafana.com/docs/grafana/latest/variables/variable-types/global-variables/#__from-and-__to
|
56
|
+
# Grafana global variables $__from and $__to}.
|
57
|
+
# Please note that input for +date+ format is unixtime in milliseconds.
|
58
|
+
#
|
59
|
+
# @param format [String] desired format
|
60
|
+
# @return [String] value of stored variable according the specified format
|
61
|
+
def value_formatted(format = '')
|
62
|
+
value = @raw_value
|
63
|
+
|
64
|
+
# handle value 'All' properly
|
65
|
+
# TODO: fix check for selection of All properly
|
66
|
+
if (value == 'All') || (@text == 'All')
|
67
|
+
if !@config['options'].empty?
|
68
|
+
value = @config['options'].map { |item| item['value'] }
|
69
|
+
elsif !@config['query'].empty?
|
70
|
+
# TODO: replace variables in this query, too
|
71
|
+
return @config['query']
|
72
|
+
# TODO: handle 'All' value properly for query attributes
|
73
|
+
else
|
74
|
+
# TODO: how to handle All selection properly at this point?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
case format
|
79
|
+
when 'csv'
|
80
|
+
return value.join(',').to_s if multi?
|
81
|
+
|
82
|
+
value.to_s
|
83
|
+
|
84
|
+
when 'distributed'
|
85
|
+
return value.join(",#{name}=") if multi?
|
86
|
+
|
87
|
+
value
|
88
|
+
when 'doublequote'
|
89
|
+
if multi?
|
90
|
+
value = value.map { |item| "\"#{item.gsub(/\\/, '\\\\').gsub(/"/, '\\"')}\"" }
|
91
|
+
return value.join(',')
|
92
|
+
end
|
93
|
+
"\"#{value.gsub(/"/, '\\"')}\""
|
94
|
+
|
95
|
+
when 'json'
|
96
|
+
if multi?
|
97
|
+
value = value.map { |item| "\"#{item.gsub(/["\\]/, '\\\\\0')}\"" }
|
98
|
+
return "[#{value.join(',')}]"
|
99
|
+
end
|
100
|
+
"\"#{value.gsub(/"/, '\\"')}\""
|
101
|
+
|
102
|
+
when 'percentencode'
|
103
|
+
value = "{#{value.join(',')}}" if multi?
|
104
|
+
ERB::Util.url_encode(value)
|
105
|
+
|
106
|
+
when 'pipe'
|
107
|
+
return value.join('|') if multi?
|
108
|
+
|
109
|
+
value
|
110
|
+
|
111
|
+
when 'raw'
|
112
|
+
return "{#{value.join(',')}}" if multi?
|
113
|
+
|
114
|
+
value
|
115
|
+
|
116
|
+
when 'regex'
|
117
|
+
if multi?
|
118
|
+
value = value.map { |item| item.gsub(%r{[/$.|\\]}, '\\\\\0') }
|
119
|
+
return "(#{value.join('|')})"
|
120
|
+
end
|
121
|
+
value.gsub(%r{[/$.|\\]}, '\\\\\0')
|
122
|
+
|
123
|
+
when 'singlequote'
|
124
|
+
if multi?
|
125
|
+
value = value.map { |item| "'#{item.gsub(/'/, '\\\\\0')}'" }
|
126
|
+
return value.join(',')
|
127
|
+
end
|
128
|
+
"'#{value.gsub(/'/, '\\\\\0')}'"
|
129
|
+
|
130
|
+
when 'sqlstring'
|
131
|
+
if multi?
|
132
|
+
value = value.map { |item| "'#{item.gsub(/'/, "''")}'" }
|
133
|
+
return value.join(',')
|
134
|
+
end
|
135
|
+
"'#{value.gsub(/'/, "''")}'"
|
136
|
+
|
137
|
+
when 'lucene'
|
138
|
+
if multi?
|
139
|
+
value = value.map { |item| "\"#{item.gsub(%r{[" |=/\\]}, '\\\\\0')}\"" }
|
140
|
+
return "(#{value.join(' OR ')})"
|
141
|
+
end
|
142
|
+
value.gsub(%r{[" |=/\\]}, '\\\\\0')
|
143
|
+
|
144
|
+
when /^date(?::(?<format>.*))?$/
|
145
|
+
# TODO: validate how grafana handles multivariables with date format
|
146
|
+
get_date_formatted(value, Regexp.last_match(1))
|
147
|
+
|
148
|
+
when ''
|
149
|
+
# default
|
150
|
+
if multi?
|
151
|
+
value = value.map { |item| "'#{item.gsub(/'/, "''")}'" }
|
152
|
+
return value.join(',')
|
153
|
+
end
|
154
|
+
value.gsub(/'/, "''")
|
155
|
+
|
156
|
+
else
|
157
|
+
# glob and all unknown
|
158
|
+
# TODO add check for array value properly for all cases
|
159
|
+
return "{#{value.join(',')}}" if multi? && value.is_a?(Array)
|
160
|
+
|
161
|
+
value
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# @return [Boolean] true, if the value can contain multiple selections, i.e. is an Array
|
166
|
+
def multi?
|
167
|
+
return @config['multi'] unless @config['multi'].nil?
|
168
|
+
|
169
|
+
@raw_value.is_a? Array
|
170
|
+
end
|
171
|
+
|
172
|
+
# @return [Object] raw value of the variable
|
173
|
+
def raw_value=(new_val)
|
174
|
+
@raw_value = new_val
|
175
|
+
@raw_value = @raw_value.to_s unless @raw_value.is_a?(Array)
|
176
|
+
new_text = @raw_value
|
177
|
+
if @config['options']
|
178
|
+
val = @config['options'].select { |item| item['value'] == @raw_value }
|
179
|
+
new_text = val.first['text'] unless val.empty?
|
180
|
+
end
|
181
|
+
@text = new_text
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
# Realize time formatting according
|
187
|
+
# {https://grafana.com/docs/grafana/latest/variables/variable-types/global-variables/#__from-and-__to}
|
188
|
+
# and {https://momentjs.com/docs/#/displaying/}.
|
189
|
+
def get_date_formatted(value, format)
|
190
|
+
return (Float(value) / 1000).to_i.to_s if format == 'seconds'
|
191
|
+
return Time.at((Float(value) / 1000).to_i).utc.iso8601(3) if !format || (format == 'iso')
|
192
|
+
|
193
|
+
# build array of known matches
|
194
|
+
matches = []
|
195
|
+
work_string = format
|
196
|
+
until work_string.empty?
|
197
|
+
tmp = work_string.scan(/^(?:M{1,4}|D{1,4}|d{1,4}|e|E|w{1,2}|W{1,2}|Y{4}|Y{2}|A|a|H{1,2}|
|
198
|
+
h{1,2}|k{1,2}|m{1,2}|s{1,2}|S+|X)/x)
|
199
|
+
if tmp.empty?
|
200
|
+
matches << work_string[0]
|
201
|
+
work_string.delete_prefix!(work_string[0])
|
202
|
+
else
|
203
|
+
matches << tmp[0]
|
204
|
+
work_string.delete_prefix!(tmp[0])
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
format_string = ''.dup
|
209
|
+
matches.each do |match|
|
210
|
+
replacement = DATE_MATCHES[match]
|
211
|
+
format_string << (replacement || match)
|
212
|
+
end
|
213
|
+
|
214
|
+
Time.at((Float(value) / 1000).to_i).strftime(format_string)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|