ruby-grafana-reporter 0.2.1 → 0.2.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f95b8d5f6221cb369bf0d11671823394683a60299dff0e688da9c5f0eab3246
4
- data.tar.gz: 245804ca1843422b29a8bf8866dfaf75ec916a329f2de05739436e580c32b6e8
3
+ metadata.gz: 0aff73969c807020f8c39fe5a9c4095a3a8ee093e5fe94982a12f0899c057d44
4
+ data.tar.gz: 179f0d922e2a2a477c04cb91d4fffc692c99fdf394f78fef8ff6bfed0a0b5817
5
5
  SHA512:
6
- metadata.gz: 84d7265803d6cfedec4f63b9a2938ed181bfdd9f9865bafbd3953c5a208de129e4029dd1bd8855c38ceefb9b215ea071a69242bb8ba6bfacd3f38c14e5a72e5e
7
- data.tar.gz: a28e456174baae970bbe0d2abc76675e6900ce3e821b6e35a215f5ee81ff760a61ccf757bc1c5c5d7b79e8e5ac8488f7edde5d1c7f3d708399a60dade839c9dc
6
+ metadata.gz: e2d4730cbac1a089b9bb23eff305f5e6f9b189c66129370ab16400ec0edf993b8f5350305eccce29d4ffb1472d3e80d548be640ef4ac529babb14b3a8066e957
7
+ data.tar.gz: 74dfd8961382f3a9e0e35db3557a46762df27843f67cb17be6d0a3b530ff05de4ea09384a4beebfc40716f07b357ffa6eab1fbe0868c63140eeb7de6e2d3c5a1
data/README.md CHANGED
@@ -35,6 +35,9 @@ The report may also be returned in any other format that asciidoctor supports.
35
35
  The reporter can run standalone or as a webservice. It is built to
36
36
  integrate without further dependencies with the asciidoctor docker image.
37
37
 
38
+ Can't wait to see, what functions the reporter provides within the asciidoctor
39
+ templates? Have a look at the [function documentation](FUNCTION_CALLS.md).
40
+
38
41
  The complete
39
42
  [API documentation](https://rubydoc.info/gems/ruby-grafana-reporter) can be
40
43
  found here.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Version information
4
- GRAFANA_REPORTER_VERSION = [0, 2, 1].freeze
5
- GRAFANA_REPORTER_RELEASE_DATE = '2020-12-20'
4
+ GRAFANA_REPORTER_VERSION = [0, 2, 2].freeze
5
+ GRAFANA_REPORTER_RELEASE_DATE = '2021-01-30'
@@ -13,8 +13,8 @@ module Grafana
13
13
  @grafana = grafana
14
14
  @model = model
15
15
 
16
- initialize_panels
17
- initialize_variables
16
+ init_panels
17
+ init_variables
18
18
  end
19
19
 
20
20
  # @return [String] +from+ time configured in the dashboard.
@@ -42,35 +42,35 @@ module Grafana
42
42
 
43
43
  panels.first
44
44
  end
45
- end
46
45
 
47
- private
46
+ private
48
47
 
49
- # store variables in array as objects of type Variable
50
- def initialize_variables
51
- @variables = []
52
- return unless @model.key?('templating')
48
+ # store variables in array as objects of type Variable
49
+ def init_variables
50
+ @variables = []
51
+ return unless @model.key?('templating')
53
52
 
54
- list = @model['templating']['list']
55
- return unless list.is_a? Array
53
+ list = @model['templating']['list']
54
+ return unless list.is_a? Array
56
55
 
57
- list.each do |item|
58
- @variables << Variable.new(item)
56
+ list.each do |item|
57
+ @variables << Variable.new(item)
58
+ end
59
59
  end
60
- end
61
60
 
62
- # read panels
63
- def initialize_panels
64
- @panels = []
65
- return unless @model.key?('panels')
61
+ # read panels
62
+ def init_panels
63
+ @panels = []
64
+ return unless @model.key?('panels')
66
65
 
67
- @model['panels'].each do |panel|
68
- if panel.key?('panels')
69
- panel['panels'].each do |subpanel|
70
- @panels << Panel.new(subpanel, self)
66
+ @model['panels'].each do |panel|
67
+ if panel.key?('panels')
68
+ panel['panels'].each do |subpanel|
69
+ @panels << Panel.new(subpanel, self)
70
+ end
71
+ else
72
+ @panels << Panel.new(panel, self)
71
73
  end
72
- else
73
- @panels << Panel.new(panel, self)
74
74
  end
75
75
  end
76
76
  end
@@ -46,8 +46,8 @@ module GrafanaReporter
46
46
  def pre_process(_grafana)
47
47
  raise MissingMandatoryAttributeError, 'columns' unless @variables['columns']
48
48
 
49
- @from = translate_date(@from, @variables['grafana-report-timestamp'], false)
50
- @to = translate_date(@to, @variables['grafana-report-timestamp'], true)
49
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
50
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
51
51
  end
52
52
 
53
53
  # Filter the query result for the given columns and sets the result in the preformatted SQL
@@ -43,8 +43,8 @@ module GrafanaReporter
43
43
  def pre_process(_grafana)
44
44
  raise MissingMandatoryAttributeError, 'columns' unless @variables['columns']
45
45
 
46
- @from = translate_date(@from, @variables['grafana-report-timestamp'], false)
47
- @to = translate_date(@to, @variables['grafana-report-timestamp'], true)
46
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
47
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
48
48
  end
49
49
 
50
50
  # Filters the query result for the given columns and sets the result
@@ -1,228 +1,30 @@
1
- module GrafanaReporter
2
- module Asciidoctor
3
- module Extensions
4
- # Implements the hook
5
- # include::grafana_help[]
6
- #
7
- # Shows all available options for the asciidoctor grafana reporter in a asciidoctor readable form.
8
- #
9
- # == Used document parameters
10
- # None
11
- class ShowHelpIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
12
- include ProcessorMixin
13
-
14
- # :nodoc:
15
- def handles?(target)
16
- target.start_with? 'grafana_help'
17
- end
18
-
19
- # :nodoc:
20
- def replaces_variables(where = nil)
21
- "https://grafana.com/docs/grafana/latest/variables/templates-and-variables/#variable-syntax[Grafana variables] will be replaced#{' ' + where.to_s if where}."
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 ShowHelpIncludeProcessor')
29
- exec_order = 'Execution is applied in the following order: `format`, `replace_values`, `filter_columns`, `transpose`.'
30
-
31
- param_instance = '| `instance="<instance_name>"` | can be used to override global grafana instance, set in the report with `grafana_default_instance`. If nothing is set, the configured grafana instance with name `default` will be used.'
32
- param_dashboard = '| `dashboard="<dashboard_uid>"` | Specifies the dashboard to be used. If `grafana_default_dashboard` is specified in the report template, this value can be overridden with this option.'
33
- param_from = '| `from="<from_timestamp>"` | can be used to override default `from` time'
34
- param_to = '| `to="<to_timestamp>"` | can be used to override default `to` time'
35
-
36
- param_format = '| `format="<format_col1>,<format_col2>,..."` | Specify format in which the results shall be returned, e.g. `%.2f` for only two digit decimals of a float. Several columns are separated by `,`. For details see https://ruby-doc.org/core-2.4.0/Kernel.html#method-i-sprintf[Ruby documentation].'
37
- param_replace_values = "| `replace_values=\"<replace_1>:<with_1>,<replace_2>:<with_2>,...\"` | Specify result values which shall be replaced, e.g. `2:OK` will replace query values `2` with value `OK`. Replacing several values is possible by separating by `,`. Matches with regular expressions are also supported, but must be full matches, i.e. have to start with `^` and end with `$`, e.g. `^[012]$:OK`. For details see https://ruby-doc.org/core-2.7.1/Regexp.html#class-Regexp-label-Character+Classes[Ruby Regexp class]. Number replacements can also be performed, e.g. `<8.2` or `<>3`. #{exec_order}"
38
- param_filter_columns = '| `filter_columns="<column_name_1>,<column_name_2>,..."` | Removes specified columns from result.'
39
- param_transpose = '| `transpose="true"` | Transposes the query result, i.e. columns become rows and rows become columnns.'
40
- param_column_divider = '| `column_divider="<divider>"` | Replace the default column divider with another one. Defaults to ` | ` for being interpreted as a asciidoctor column.'
41
- param_row_divider = '| `row_divider="<divider>"` | Replace the default row divider with another one. Defaults to `| ` for being interpreted as a asciidoctor row.'
42
- param_timeout = '| `timeout="<timeout_in_seconds>" | Set a timeout for the current query. If not overridden with `grafana-default-timeout` in the report template, this defaults to 60 seconds.'
43
-
44
- help = "
45
- == Grafana Reporter Functions
46
- === `grafana_help`
47
- [cols=\"~,80\"]
48
- |===
49
- | Call | `+include::grafana_help[]+`
50
- | Description | Shows this information.
51
- |===
52
-
53
- === `grafana_environment`
54
- [cols=\"~,80\"]
55
- |===
56
- | Call | `+include::grafana_environment[]+`
57
- | Description | Shows all available variables in the rendering context which can be used in the document.
58
- |===
59
-
60
- === `grafana_alerts`
61
- [cols=\"~,80\"]
62
- |===
63
- | Call | `+grafana_alerts[columns=\"<column_name_1>,<column_name_2>,...\",options]+`
64
- | Description | Returns a table of active alert states with the specified columns. Valid colums are `id`, `dashboardId`, `dashboardUId`, `dashboardSlug`, `panelId`, `name`, `state`, `newStateDate`, `evalDate`, `evalData` and `executionError` (for details see https://grafana.com/docs/grafana/latest/http_api/alerting/#get-alerts[Grafana Alerting API]).
65
- |===
66
- [%autowidth.stretch, options=\"header\"]
67
- |===
68
- | Option | Description
69
- #{param_column_divider}
70
- #{param_dashboard} If this option, or the global option `grafana_default_dashboard` is set, the resulting alerts will be limited to this dashboard. To show all alerts in this case, specify `dashboard=\"\"` as option.
71
- #{param_filter_columns} #{exec_order}
72
- #{param_format} #{exec_order}
73
- #{param_from}
74
- #{param_instance}
75
- | `panel=\"<panel_id>\"` | If specified, the resulting alerts are filtered for this panel. This option will only work, if a `dashboard` or `grafana_default_dashboard` is set.
76
- #{param_replace_values} #{exec_order}
77
- #{param_row_divider}
78
- #{param_timeout}
79
- #{param_to}
80
- #{param_transpose} #{exec_order}
81
- |===
82
- Additionally all query parameters from the https://grafana.com/docs/grafana/latest/http_api/alerting/#get-alerts[Grafana Alerting API], such as `query`, `state`, `limit`, `folderId` and others are supported.
83
-
84
- === `grafana_annotations`
85
- [cols=\"~,80\"]
86
- |===
87
- | Call | `+grafana_annotations[columns=\"<column_name_1>,<column_name_2>,...\",options]+`
88
- | Description | Returns a table of all annotations, matching the specified filter criteria and the specified columns. Valid colums are `id`, `alertId`, `dashboardId`, `panelId`, `userId`, `userName`, `newState`, `prevState, `time`, `timeEnd`, `text`, `metric` and `type` (for details see https://grafana.com/docs/grafana/latest/http_api/annotations/#find_annotations[Grafana Annotations API]).
89
- |===
90
- [%autowidth.stretch, options=\"header\"]
91
- |===
92
- | Option | Description
93
- #{param_column_divider}
94
- #{param_dashboard} If this option, or the global option `grafana_default_dashboard` is set, the resulting annotations will be limited to this dashboard. To show all annotations in this case, specify `dashboard=\"\"` as option.
95
- #{param_filter_columns} #{exec_order}
96
- #{param_format} #{exec_order}
97
- #{param_from}
98
- #{param_instance}
99
- | `panel=\"<panel_id>\"` | If specified, the resulting annotations are filtered for this panel. This option will only work, if a `dashboard` or `grafana_default_dashboard` is set.
100
- #{param_replace_values} #{exec_order}
101
- #{param_row_divider}
102
- #{param_timeout}
103
- #{param_to}
104
- #{param_transpose} #{exec_order}
105
- |===
106
- Additionally all quer parameters from the https://grafana.com/docs/grafana/latest/http_api/annotations/#find_annotations[Grafana Alerting API], such as `limit`, `alertId`, `panelId` and others are supported.
107
-
108
- === `grafana_panel_description`
109
- [cols=\"~,80\"]
110
- |===
111
- | Call | `+grafana_panel_description:<panel_id>[\"<type>\",options]+`
112
- | Description | Returns a description field for the specified panel. `+<type>+` can either be `title` or `description`. #{replaces_variables('in the returned value')}
113
- |===
114
- [%autowidth.stretch, options=\"header\"]
115
- |===
116
- | Option | Description
117
- #{param_dashboard}
118
- #{param_instance}
119
- |===
120
-
121
- === `grafana_panel_image`
122
- [cols=\"~,80\"]
123
- |===
124
- | Call Inline | `+grafana_panel_image:<panel_id>[options]+`
125
- | Call Block | `+grafana_panel_image::<panel_id>[options]+`
126
- | Description | Includes a panel image as an image in the document. Can be calles for inline-images as well as for blocks.
127
- |===
128
- [%autowidth.stretch, options=\"header\"]
129
- |===
130
- | Option | Description
131
- #{param_dashboard}
132
- #{param_from}
133
- #{param_instance}
134
- #{param_timeout}
135
- #{param_to}
136
- | `render-height=\"<height>\"` | can be used to override default `height` in which the panel shall be rendered
137
- | `render-width=\"<width>\"` | can be used to override default `width` in which the panel shall be rendered
138
- | `render-theme=\"<theme>\"` | can be used to override default `theme` in which the panel shall be rendered (`light` by default)
139
- | `render-timeout=\"<timeout>\"` | can be used to override default `timeout` in which the panel shall be rendered (60 seconds by default)
140
- |===
141
-
142
- === `grafana_panel_query_table`
143
- [cols=\"~,80\"]
144
- |===
145
- | Call | `+include:grafana_panel_query_table:<panel_id>[query=\"<query_letter>\",options]+`
146
- | Description | Returns the results of a query, which is configured in a grafana panel, as a table in asciidoc. `+<query_letter>+` needs to point to the grafana query which shall be evaluated, e.g. `A` or `B`. #{replaces_variables("in the panel's SQL statement")}
147
- |===
148
- [%autowidth.stretch, options=\"header\"]
149
- |===
150
- | Option | Description
151
- #{param_column_divider}
152
- #{param_dashboard}
153
- #{param_filter_columns} #{exec_order}
154
- #{param_format} #{exec_order}
155
- #{param_from}
156
- #{param_instance}
157
- #{param_replace_values} #{exec_order}
158
- #{param_row_divider}
159
- #{param_timeout}
160
- #{param_to}
161
- #{param_transpose} #{exec_order}
162
- |===
163
-
164
- === `grafana_panel_query_value`
165
- [cols=\"~,80\"]
166
- |===
167
- | Call | `+grafana_panel_query_value:<panel_id>[query=\"<query_letter>\",options]+`
168
- | Description | Returns the first returned value of in the first column of a query, which is configured in a grafana panel. `+<query_letter>+` needs to point to the grafana query which shall be evaluated, e.g. `A` or `B`. #{replaces_variables("in the panel's SQL statement")}
169
- |===
170
- [%autowidth.stretch, options=\"header\"]
171
- |===
172
- | Option | Description
173
- #{param_dashboard}
174
- #{param_filter_columns} #{exec_order}
175
- #{param_format} #{exec_order}
176
- #{param_from}
177
- #{param_instance}
178
- #{param_replace_values} #{exec_order}
179
- #{param_row_divider}
180
- #{param_timeout}
181
- #{param_to}
182
- |===
183
-
184
- === `grafana_sql_table`
185
- [cols=\"~,80\"]
186
- |===
187
- | Call | `+include::grafana_sql_table:<datasource_id>[sql=\"<sql_query>\",options]+`
188
- | Description | Returns a table with all results of the given query. #{replaces_variables('in the SQL statement')}
189
- |===
190
- [%autowidth.stretch, options=\"header\"]
191
- |===
192
- | Option | Description
193
- #{param_column_divider}
194
- #{param_filter_columns} #{exec_order}
195
- #{param_format} #{exec_order}
196
- #{param_from}
197
- #{param_instance}
198
- #{param_replace_values} #{exec_order}
199
- #{param_row_divider}
200
- #{param_timeout}
201
- #{param_to}
202
- #{param_transpose} #{exec_order}
203
- |===
204
-
205
- === `grafana_sql_value`
206
- [cols=\"~,80\"]
207
- |===
208
- | Call | `+grafana_sql_value:<datasource_id>[sql=\"<sql_query>\",options]+`
209
- | Description | Returns a table with all results of the given query. #{replaces_variables('in the SQL statement')}
210
- |===
211
- [%autowidth.stretch, options=\"header\"]
212
- |===
213
- | Option | Description
214
- #{param_filter_columns} #{exec_order}
215
- #{param_format} #{exec_order}
216
- #{param_from}
217
- #{param_instance}
218
- #{param_replace_values} #{exec_order}
219
- #{param_timeout}
220
- #{param_to}
221
- |==="
222
-
223
- reader.unshift_lines help.split("\n")
224
- end
225
- end
226
- end
227
- end
228
- end
1
+ module GrafanaReporter
2
+ module Asciidoctor
3
+ module Extensions
4
+ # Implements the hook
5
+ # include::grafana_help[]
6
+ #
7
+ # Shows all available options for the asciidoctor grafana reporter in a asciidoctor readable form.
8
+ #
9
+ # == Used document parameters
10
+ # None
11
+ class ShowHelpIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
12
+ include ProcessorMixin
13
+
14
+ # :nodoc:
15
+ def handles?(target)
16
+ target.start_with? 'grafana_help'
17
+ end
18
+
19
+ # :nodoc:
20
+ def process(_doc, reader, _target, _attrs)
21
+ # return if @report.cancel
22
+ @report.next_step
23
+ @report.logger.debug('Processing ShowHelpIncludeProcessor')
24
+
25
+ reader.unshift_lines Asciidoctor::Help.new.asciidoctor.split("\n")
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,435 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module GrafanaReporter
6
+ module Asciidoctor
7
+ class Help
8
+ def asciidoctor(headline_level = 2)
9
+ help_text(asciidoctor_options.merge(level: headline_level))
10
+ end
11
+
12
+ def github(headline_level = 2)
13
+ "#{toc}\n\n#{help_text(github_options.merge(level: headline_level))}"
14
+ end
15
+
16
+ private
17
+
18
+ def github_options
19
+ { headline_separator: '#', code_begin: '`', code_end: '`', table_begin: "\n", head_postfix_col: '| -- ' }
20
+ end
21
+
22
+ def asciidoctor_options
23
+ { headline_separator: '=', code_begin: '`+', code_end: '+`', table_begin: "\n[%autowidth.stretch, options=\"header\"]\n|===\n", table_end: "\n|===" }
24
+ end
25
+
26
+ def help_text(opts)
27
+ %(#{opts[:headline_separator] * opts[:level]} Global options
28
+ #{global_options_as_text(opts.merge(level: opts[:level] + 1))}
29
+ #{opts[:headline_separator] * opts[:level]} Functions
30
+ #{functions_as_text(opts.merge(level: opts[:level] + 1))})
31
+ end
32
+
33
+ def toc(opts = {})
34
+ result = []
35
+
36
+ result << "Table of contents"
37
+ result << "* [Global options](#global-options)"
38
+ prepared_help[:global_options].sort.map do |k, v|
39
+ result << " * [#{k}](##{k.downcase})"
40
+ end
41
+
42
+ result << "* [Functions](#functions)"
43
+ prepared_help[:functions].sort.map do |k, v|
44
+ result << " * [#{k}](##{k.downcase})"
45
+ end
46
+
47
+ result.join("\n")
48
+ end
49
+
50
+ def global_options_as_text(opts = {})
51
+ opts = { level: 3 }.merge(opts)
52
+ result = []
53
+
54
+ prepared_help[:global_options].sort.map do |k, v|
55
+ result << %(
56
+ #{opts[:headline_separator] * opts[:level]} #{opts[:code_begin]}#{k}#{opts[:code_end]}
57
+ Usage: #{opts[:code_begin]}#{v['call']}#{opts[:code_end]}
58
+
59
+ #{v['description']}
60
+ )
61
+ end
62
+
63
+ result.join
64
+ end
65
+
66
+ def functions_as_text(opts = {})
67
+ opts = { level: 3, headline_separator: '=' }.merge(opts)
68
+ result = []
69
+
70
+ prepared_help[:functions].sort.map do |k, v|
71
+ result << %(
72
+ #{opts[:headline_separator] * opts[:level]} #{opts[:code_begin]}#{k}#{opts[:code_end]}
73
+ Usage: #{opts[:code_begin]}#{v[:call]}#{opts[:code_end]}
74
+
75
+ #{v[:description]}#{"\n\nSee also: #{v[:see]}" if v[:see]}#{unless v[:options].empty?
76
+ %(
77
+ #{opts[:table_begin]}| Option | Description#{"\n#{opts[:head_postfix_col] * 2}" if opts[:head_postfix_col]}
78
+ #{v[:options].sort.map { |_opt_k, opt_v| "| #{opts[:code_begin]}#{opt_v[:call]}#{opts[:code_end]} | #{opt_v[:description].gsub('|', '\|')}" }.join("\n") }#{opts[:table_end]})
79
+ end}
80
+ )
81
+ end
82
+
83
+ result.join
84
+ end
85
+
86
+ def prepared_help
87
+ yaml = YAML.safe_load(raw_help_yaml)
88
+
89
+ result = {}
90
+ result[:functions] = {}
91
+ result[:global_options] = yaml['global_options']
92
+
93
+ functions = result[:functions]
94
+ std_opts = yaml['standard_options']
95
+ yaml.reject { |k, _v| k =~ /.*_options$/ }.each_key do |key|
96
+ functions[key] = {}
97
+ res_item = functions[key]
98
+ res_item[:options] = {}
99
+
100
+ item = yaml[key]
101
+ res_item[:call] = item['call']
102
+ res_item[:description] = item['description']
103
+ res_item[:see] = item['see']
104
+
105
+ opts = ((item['options'] ? item['options'].keys : []) + (item['standard_options'] ? item['standard_options'].keys : [])).sort
106
+ opts.each do |opt_key|
107
+ res_item[:options][opt_key] = {}
108
+
109
+ if item['standard_options'].key?(opt_key)
110
+ res_item[:options][opt_key][:call] = std_opts[opt_key]['call']
111
+ res_item[:options][opt_key][:description] = "#{std_opts[opt_key]['description']} #{item['standard_options'][opt_key]}".chop
112
+ res_item[:options][opt_key][:see] = std_opts[opt_key]['see'] if std_opts[opt_key]['see']
113
+ else
114
+ res_item[:options][opt_key][:call] = item['options'][opt_key]['call']
115
+ res_item[:options][opt_key][:description] = item['options'][opt_key]['description']
116
+ res_item[:options][opt_key][:see] = item['options'][opt_key]['see'] if item['options'][opt_key]['see']
117
+ end
118
+ end
119
+ end
120
+
121
+ result
122
+ end
123
+
124
+ def raw_help_yaml
125
+ return <<YAML_HELP
126
+ global_options:
127
+ grafana_default_instance:
128
+ call: ":grafana_default_instance: <instance_name>"
129
+ description: >-
130
+ Specifies which grafana instance shall be used. If not set, the grafana instance names `default`
131
+ will be used.
132
+
133
+ grafana_default_dashboard:
134
+ call: ":grafana_default_dashboard: <dashboard_uid>"
135
+ description: >-
136
+ Specifies to which dashboard the queries shall be targeted by default.
137
+
138
+ grafana_default_from_timezone:
139
+ call: ":grafana_default_from_timezone: <timezone>"
140
+ description: Specifies which timezone shall be used for the `from` time, e.g. `CET` or `CEST`.
141
+
142
+ grafana_default_to_timezone:
143
+ call: ":grafana_default_to_timezone: <timezone>"
144
+ description: Specifies which timezone shall be used for the `to` time, e.g. `CET` or `CEST`.
145
+
146
+ from:
147
+ call: ":from: <from_timestamp>"
148
+ description: >-
149
+ Overrides the time setting from grafana. It may contain dates as `now-1M/M`, which will be translated
150
+ properly to timestamps relative to the called time.
151
+
152
+ to:
153
+ call: ":to: <to_timestamp>"
154
+ description: >-
155
+ Overrides the time setting from grafana. It may contain dates as `now-1M/M`, which will be translated
156
+ properly to timestamps relative to the called time.
157
+
158
+ standard_options:
159
+ instance:
160
+ call: instance="<instance_name>"
161
+ description: >-
162
+ can be used to override global grafana instance, set in the report with `grafana_default_instance`.
163
+ If nothing is set, the configured grafana instance with name `default` will be used.
164
+
165
+ dashboard:
166
+ call: dashboard="<dashboard_uid>"
167
+ description: >-
168
+ Specifies the dashboard to be used. If `grafana_default_dashboard` is specified in the report template,
169
+ this value can be overridden with this option.
170
+
171
+ from:
172
+ call: from="<timestamp>"
173
+ description: can be used to override default `from` time
174
+
175
+ from_timezone:
176
+ call: from_timezone="<timezone>"
177
+ description: can be used to override system timezone for `from` time and will also override `grafana_default_from_timezone` option
178
+
179
+ to_timezone:
180
+ call: to_timezone="<timezone>"
181
+ description: can be used to override system timezone for `to` time and will also override `grafana_default_to_timezone` option
182
+
183
+ to:
184
+ call: to="<timestamp>"
185
+ description: can be used to override default `to` time
186
+
187
+ format:
188
+ call: format="<format_col1>,<format_col2>,..."
189
+ description: >-
190
+ Specify format in which the results shall be returned, e.g. `%.2f` for only two digit decimals of a
191
+ float. Several columns are separated by `,`. Execution is applied in the following order `format`,
192
+ `replace_values`, `filter_columns`, `transpose`.
193
+ see: 'https://ruby-doc.org/core-2.4.0/Kernel.html#method-i-sprintf'
194
+
195
+ replace_values:
196
+ call: replace_values="<replace_1>:<with_1>,<replace_2>:<with_2>,..."
197
+ description: >-
198
+ Specify result values which shall be replaced, e.g. `2:OK` will replace query values `2` with value `OK`.
199
+ Replacing several values is possible by separating by `,`. Matches with regular expressions are also
200
+ supported, but must be full matches, i.e. have to start with `^` and end with `$`, e.g. `^[012]$:OK`.
201
+ Number replacements can also be performed, e.g. `<8.2` or `<>3`. Execution is applied in the following order `format`,
202
+ `replace_values`, `filter_columns`, `transpose`.
203
+ see: https://ruby-doc.org/core-2.7.1/Regexp.html#class-Regexp-label-Character+Classes
204
+
205
+ filter_columns:
206
+ call: filter_columns="<column_name_1>,<column_name_2>,..."
207
+ description: >-
208
+ Removes specified columns from result. Execution is applied in the following order `format`, `replace_values`,
209
+ `filter_columns`, `transpose`.
210
+
211
+ transpose:
212
+ call: transpose="true"
213
+ description: >-
214
+ Transposes the query result, i.e. columns become rows and rows become columnns. Execution is applied in the
215
+ following order `format`, `replace_values`, `filter_columns`, `transpose`.
216
+
217
+ column_divider:
218
+ call: column_divider="<divider>"
219
+ description: >-
220
+ Replace the default column divider with another one. Defaults to ` | ` for being interpreted as a asciidoctor column.
221
+
222
+ row_divider:
223
+ call: row_divider="<divider>"
224
+ description: >-
225
+ Replace the default row divider with another one. Defaults to `| ` for being interpreted as a asciidoctor row.
226
+
227
+ timeout:
228
+ call: timeout="<timeout_in_seconds>"
229
+ description: >-
230
+ Set a timeout for the current query. If not overridden with `grafana-default-timeout` in the report template,
231
+ this defaults to 60 seconds.
232
+
233
+ # ----------------------------------
234
+ # FUNCTION DOCUMENTATION STARTS HERE
235
+ # ----------------------------------
236
+
237
+ grafana_help:
238
+ description: Show all available grafana calls within the asciidoctor templates, including available options.
239
+ call: 'include::grafana_help[]'
240
+
241
+ grafana_environment:
242
+ description: Shows all available variables in the rendering context which can be used in the asciidoctor template.
243
+ call: 'include::grafana_environment[]'
244
+
245
+ grafana_alerts:
246
+ description: >-
247
+ Returns a table of active alert states including the specified columns and the connected information. Supports
248
+ all query parameters from the Grafana Alerting API, such as `query`, `state`, `limit`, `folderId` and others.
249
+ call: grafana_alerts[columns="<column_name_1>,<column_name_2>,...",options]
250
+ see: https://grafana.com/docs/grafana/latest/http_api/alerting/#get-alerts
251
+ options:
252
+ columns:
253
+ description: >-
254
+ Specifies columns that shall be returned. Valid columns are `id`, `dashboardId`, `dashboardUId`, `dashboardSlug`,
255
+ `panelId`, `name`, `state`, `newStateDate`, `evalDate`, `evalData` and `executionError`.
256
+ call: columns="<column_name_1>,<columns_name_2>,..."
257
+ panel:
258
+ description: >-
259
+ If specified, the resulting alerts are filtered for this panel. This option will only work, if a `dashboard`
260
+ or `grafana_default_dashboard` is set.
261
+ call: panel="<panel_id>"
262
+ standard_options:
263
+ column_divider:
264
+ dashboard: >-
265
+ If this option, or the global option `grafana_default_dashboard` is set, the resulting alerts will be limited to
266
+ this dashboard. To show all alerts in this case, specify `dashboard=""` as option.
267
+ filter_columns:
268
+ format:
269
+ from:
270
+ instance:
271
+ replace_values:
272
+ row_divider:
273
+ timeout:
274
+ to:
275
+ transpose:
276
+ from_timezone:
277
+ to_timezone:
278
+
279
+ grafana_annotations:
280
+ description: >-
281
+ Returns a table of all annotations, matching the specified filter criteria and the specified columns. Supports all
282
+ query parameters from the Grafana Alerting API, such as `limit`, `alertId`, `panelId` and others.
283
+ call: grafana_annotations[columns="<column_name_1>,<column_name_2>,...",options]
284
+ see: https://grafana.com/docs/grafana/latest/http_api/annotations/#find_annotations
285
+ options:
286
+ columns:
287
+ description: >-
288
+ Specified the columns that shall be returned. Valid columns are `id`, `alertId`, `dashboardId`, `panelId`, `userId`,
289
+ `userName`, `newState`, `prevState, `time`, `timeEnd`, `text`, `metric` and `type`.
290
+ call: columns="<column_name_1>,<columns_name_2>,..."
291
+ panel:
292
+ description: >-
293
+ If specified, the resulting alerts are filtered for this panel. This option will only work, if a `dashboard` or
294
+ `grafana_default_dashboard` is set.
295
+ call: panel="<panel_id>"
296
+ standard_options:
297
+ column_divider:
298
+ dashboard: >-
299
+ If this option, or the global option `grafana_default_dashboard` is set, the resulting alerts will be limited to this
300
+ dashboard. To show all alerts in this case, specify `dashboard=""` as option.
301
+ filter_columns:
302
+ format:
303
+ from:
304
+ instance:
305
+ replace_values:
306
+ row_divider:
307
+ timeout:
308
+ to:
309
+ transpose:
310
+ from_timezone:
311
+ to_timezone:
312
+
313
+ grafana_panel_description:
314
+ description: >-
315
+ Returns a description field for the specified panel. `<type>` can either be `title` or `description`.
316
+ Grafana variables will be replaced in the returned value.
317
+ call: 'grafana_panel_description:<panel_id>["<type>",options]'
318
+ see: https://grafana.com/docs/grafana/latest/variables/templates-and-variables/#variable-syntax
319
+ standard_options:
320
+ dashboard:
321
+ instance:
322
+
323
+ grafana_panel_image:
324
+ description: Includes a panel image as an image in the document. Can be called for inline-images as well as for blocks.
325
+ call: 'grafana_panel_image:<panel_id>[options] or grafana_panel_image::<panel_id>[options]'
326
+ options:
327
+ render-height:
328
+ description: can be used to override default `height` in which the panel shall be rendered
329
+ call: render-height="<height>"
330
+ render-width:
331
+ description: can be used to override default `width` in which the panel shall be rendered
332
+ call: render-width="<width>"
333
+ render-theme:
334
+ description: can be used to override default `theme` in which the panel shall be rendered (light by default)
335
+ call: render-theme="<theme>"
336
+ render-timeout:
337
+ description: can be used to override default `timeout` in which the panel shall be rendered (60 seconds by default)
338
+ call: render-timeout="<timeout>"
339
+ standard_options:
340
+ dashboard:
341
+ from:
342
+ instance:
343
+ timeout:
344
+ to:
345
+ from_timezone:
346
+ to_timezone:
347
+
348
+ grafana_panel_query_table:
349
+ description: >-
350
+ Returns the results of a query, which is configured in a grafana panel, as a table in asciidoctor.
351
+ Grafana variables will be replaced in the panel's SQL statement.
352
+ call: 'include::grafana_panel_query_table:<panel_id>[query="<query_letter>",options]'
353
+ see: https://grafana.com/docs/grafana/latest/variables/templates-and-variables/#variable-syntax
354
+ options:
355
+ query:
356
+ call: query="<query_letter>"
357
+ description: +<query_letter>+ needs to point to the grafana query which shall be evaluated, e.g. +A+ or +B+.
358
+ standard_options:
359
+ column_divider:
360
+ dashboard:
361
+ filter_columns:
362
+ format:
363
+ from:
364
+ instance:
365
+ replace_values:
366
+ row_divider:
367
+ timeout:
368
+ to:
369
+ transpose:
370
+ from_timezone:
371
+ to_timezone:
372
+
373
+ grafana_panel_query_value:
374
+ call: 'grafana_panel_query_value:<panel_id>[query="<query_letter>",options]'
375
+ description: >-
376
+ Returns the value in the first column and the first row of a query, which is configured in a grafana panel.
377
+ Grafana variables will be replaced in the panel's SQL statement.
378
+ see: https://grafana.com/docs/grafana/latest/variables/templates-and-variables/#variable-syntax
379
+ options:
380
+ query:
381
+ call: query="<query_letter>"
382
+ description: +<query_letter>+ needs to point to the grafana query which shall be evaluated, e.g. +A+ or +B+.
383
+ standard_options:
384
+ dashboard:
385
+ filter_columns:
386
+ format:
387
+ from:
388
+ instance:
389
+ replace_values:
390
+ timeout:
391
+ to:
392
+ from_timezone:
393
+ to_timezone:
394
+
395
+ grafana_sql_table:
396
+ call: 'include::grafana_sql_table:<datasource_id>[sql="<sql_query>",options]'
397
+ description: >-
398
+ Returns a table with all results of the given query.
399
+ Grafana variables will be replaced in the SQL statement.
400
+ see: https://grafana.com/docs/grafana/latest/variables/templates-and-variables/#variable-syntax
401
+ standard_options:
402
+ column_divider:
403
+ filter_columns:
404
+ format:
405
+ from:
406
+ instance:
407
+ replace_values:
408
+ row_divider:
409
+ timeout:
410
+ to:
411
+ transpose:
412
+ from_timezone:
413
+ to_timezone:
414
+
415
+ grafana_sql_value:
416
+ call: 'grafana_sql_value:<datasource_id>[sql="<sql_query>",options]'
417
+ description: >-
418
+ Returns the value in the first column and the first row of the given query.
419
+ Grafana variables will be replaced in the SQL statement.
420
+ see: https://grafana.com/docs/grafana/latest/variables/templates-and-variables/#variable-syntax
421
+ standard_options:
422
+ filter_columns:
423
+ format:
424
+ from:
425
+ instance:
426
+ replace_values:
427
+ timeout:
428
+ to:
429
+ from_timezone:
430
+ to_timezone:
431
+ YAML_HELP
432
+ end
433
+ end
434
+ end
435
+ end
@@ -26,8 +26,8 @@ module GrafanaReporter
26
26
  @datasource = @panel.field('datasource')
27
27
  @datasource_id = grafana.datasource_id(@datasource)
28
28
  super(grafana)
29
- @from = translate_date(@from, @variables['grafana-report-timestamp'], false)
30
- @to = translate_date(@to, @variables['grafana-report-timestamp'], true)
29
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
30
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
31
31
  end
32
32
  end
33
33
  end
@@ -9,8 +9,9 @@ module GrafanaReporter
9
9
  # Sets the proper render variables.
10
10
  def pre_process(grafana)
11
11
  super
12
- @from = translate_date(@from, @variables['grafana-report-timestamp'], false)
13
- @to = translate_date(@to, @variables['grafana-report-timestamp'], true)
12
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
13
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
14
+ # TODO: ensure that in case of timezones are specified, that they are also forwarded to the image renderer as URL parameter
14
15
  # rename "render-" variables
15
16
  @variables = @variables.each_with_object({}) { |(k,v), h| h[k.gsub(/^render-/, '')] = v }
16
17
  end
@@ -30,8 +30,8 @@ module GrafanaReporter
30
30
  @datasource = @panel.field('datasource')
31
31
  @datasource_id = grafana.datasource_id(@datasource)
32
32
  super(grafana)
33
- @from = translate_date(@from, @variables['grafana-report-timestamp'], false)
34
- @to = translate_date(@to, @variables['grafana-report-timestamp'], true)
33
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
34
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
35
35
  end
36
36
  end
37
37
  end
@@ -8,8 +8,8 @@ module GrafanaReporter
8
8
  # @param item_hash [Hash] variables from item configuration level, i.e. specific call, which may override document
9
9
  # @return [void]
10
10
  def merge_hash_variables(document_hash, item_hash)
11
- merge_variables(document_hash.select { |k, _v| k =~ /^var-/ || k == 'grafana-report-timestamp' }.each_with_object({}) { |(k,v), h| h[k] = ::Grafana::Variable.new(v) })
12
- merge_variables(item_hash.select { |k, _v| k =~ /^var-/ || k =~ /^render-/ || k =~ /filter_columns|format|replace_values_.*|transpose|column_divider|row_divider/ }.each_with_object({}) { |(k,v), h| h[k] = ::Grafana::Variable.new(v) })
11
+ merge_variables(document_hash.select { |k, _v| k =~ /^var-/ || k == 'grafana-report-timestamp' || k =~ /grafana_default_(?:from|to)_timezone/ }.each_with_object({}) { |(k,v), h| h[k] = ::Grafana::Variable.new(v) })
12
+ merge_variables(item_hash.select { |k, _v| k =~ /^var-/ || k =~ /^render-/ || k =~ /filter_columns|format|replace_values_.*|transpose|column_divider|row_divider|from_timezone|to_timezone/ }.each_with_object({}) { |(k,v), h| h[k] = ::Grafana::Variable.new(v) })
13
13
  self.timeout = item_hash['timeout'] || document_hash['grafana-default-timeout'] || timeout
14
14
  self.from = item_hash['from'] || document_hash['from'] || from
15
15
  self.to = item_hash['to'] || document_hash['to'] || to
@@ -225,8 +225,9 @@ module GrafanaReporter
225
225
  # @param report_time [Grafana::Variable] report start time
226
226
  # @param is_to_time [Boolean] true, if the time should be calculated for +to+, false if it shall be
227
227
  # calculated for +from+
228
+ # @param timezone [Grafana::Variable] timezone to use, if not system timezone
228
229
  # @return [String] translated date as timestamp string
229
- def translate_date(orig_date, report_time, is_to_time)
230
+ def translate_date(orig_date, report_time, is_to_time, timezone=nil)
230
231
  report_time ||= Variable.new(Time.now.to_s)
231
232
  return (DateTime.parse(report_time.raw_value).to_time.to_i * 1000).to_s unless orig_date
232
233
  return orig_date if orig_date =~ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/
@@ -237,6 +238,9 @@ module GrafanaReporter
237
238
  raise TimeRangeUnknownError, orig_date unless date_splitted
238
239
 
239
240
  date = DateTime.parse(report_time.raw_value)
241
+ # TODO: allow from_translated or similar in ADOC template
242
+ date = date.new_offset(timezone.raw_value) if timezone
243
+
240
244
  # substract specified time
241
245
  count = 1
242
246
  count = date_splitted[:sub_count].to_i if date_splitted[:sub_count]
@@ -287,7 +291,10 @@ module GrafanaReporter
287
291
  date = date.next_year if is_to_time
288
292
  end
289
293
 
290
- (date.to_time.to_i * 1000).to_s
294
+ # step back one second, if this is the 'to' time
295
+ date = (date.to_time - 1).to_datetime if is_to_time
296
+
297
+ (Time.at(date.to_time.to_i).to_i * 1000).to_s
291
298
  end
292
299
  end
293
300
  end
@@ -63,17 +63,20 @@ module GrafanaReporter
63
63
 
64
64
  # build zip file
65
65
  zip_file = Tempfile.new('gf_zip')
66
- Zip::File.open(zip_file.path, Zip::File::CREATE) do |zipfile|
66
+ buffer = Zip::OutputStream.write_buffer do |zipfile|
67
67
  # add report file
68
- zipfile.get_output_stream("#{dest_path.gsub(@config.reports_folder, '')}.#{attrs['convert-backend']}") do |f|
69
- f.puts File.read(dest_path)
70
- end
68
+ zipfile.put_next_entry("#{dest_path.gsub(@config.reports_folder, '')}.#{attrs['convert-backend']}")
69
+ zipfile.write File.read(dest_path)
71
70
 
72
71
  # add image files
73
72
  @image_files.each do |file|
74
- zipfile.get_output_stream(file.path.gsub(@config.images_folder, '')) { |f| f.puts File.read(file.path) }
73
+ zipfile.put_next_entry(file.path.gsub(@config.images_folder, ''))
74
+ zipfile.write File.read(file.path)
75
75
  end
76
76
  end
77
+ File.open(zip_file, 'wb') do |f|
78
+ f.write buffer.string
79
+ end
77
80
 
78
81
  # replace original file with zip file
79
82
  zip_file.rewind
@@ -27,6 +27,16 @@ module GrafanaReporter
27
27
  end
28
28
  @result = ''
29
29
  end
30
+
31
+ # Translates the from and to times.
32
+ # @see Grafana::AbstractSqlQuery#pre_process
33
+ # @param grafana [Grafana::Grafana] grafana instance against which the query shall be executed
34
+ # @return [void]
35
+ def pre_process(grafana)
36
+ super(grafana)
37
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
38
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
39
+ end
30
40
  end
31
41
  end
32
42
  end
@@ -29,6 +29,16 @@ module GrafanaReporter
29
29
  end.join(column_divider)
30
30
  end
31
31
  end
32
+
33
+ # Translates the from and to times.
34
+ # @see Grafana::AbstractSqlQuery#pre_process
35
+ # @param grafana [Grafana::Grafana] grafana instance against which the query shall be executed
36
+ # @return [void]
37
+ def pre_process(grafana)
38
+ super(grafana)
39
+ @from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] || @variables['grafana_default_from_timezone'])
40
+ @to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] || @variables['grafana_default_to_timezone'])
41
+ end
32
42
  end
33
43
  end
34
44
  end
@@ -27,5 +27,3 @@ folders = [
27
27
  %w[grafana_reporter application]
28
28
  ]
29
29
  folders.each { |folder| Dir[File.join(__dir__, *folder, '*.rb')].sort.each { |file| require_relative file } }
30
-
31
- # TODO check if panel with ID exists before trying to render image
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-grafana-reporter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Kohlmeyer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-20 00:00:00.000000000 Z
11
+ date: 2021-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -100,10 +100,10 @@ dependencies:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
102
  version: '3.9'
103
- description: Provides a standalone and a webservice frontend for creating reportsbased
104
- on asciidoctor, including interfaces to integrate dynamic contentcaptured from grafana.By
105
- default the reports will be converted to PDF documents, whereas othertarget formats
106
- can be used as well.
103
+ description: Provides a standalone and a webservice frontend for creating reports
104
+ based on asciidoctor, including interfaces to integrate dynamic content captured
105
+ from grafana.By default the reports will be converted to PDF documents, whereas
106
+ other target formats can be used as well.
107
107
  email: kohly@gmx.de
108
108
  executables:
109
109
  - ruby-grafana-reporter
@@ -142,6 +142,7 @@ files:
142
142
  - "./lib/grafana_reporter/asciidoctor/extensions/sql_table_include_processor.rb"
143
143
  - "./lib/grafana_reporter/asciidoctor/extensions/sql_value_inline_macro.rb"
144
144
  - "./lib/grafana_reporter/asciidoctor/extensions/value_as_variable_include_processor.rb"
145
+ - "./lib/grafana_reporter/asciidoctor/help.rb"
145
146
  - "./lib/grafana_reporter/asciidoctor/panel_first_value_query.rb"
146
147
  - "./lib/grafana_reporter/asciidoctor/panel_image_query.rb"
147
148
  - "./lib/grafana_reporter/asciidoctor/panel_property_query.rb"