ruby-grafana-reporter 0.8.0 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3d762755e84f775f00d04fe0333389ae011fbf0ed1b13942cd006599e60e0e2
4
- data.tar.gz: 29c72d42855e2de7ecd43cbced9d6339c36312deebe05fc5210b80928e0d7894
3
+ metadata.gz: c90ce3f15b0a5fa90cb7569a54467e10a251fce77d3642b75053b6792288d72d
4
+ data.tar.gz: 3855618dd7c3a721340516b7949777f2efe045a0c093f0d1b7d3457eb1bcfc01
5
5
  SHA512:
6
- metadata.gz: 2efa68036513d9d7c73ad13296e369bd0c945bf97dd5101f288b3c646443bbc95af0d96f876ab1c23d1424d2f450c1618d24e58705ecbfd406b31adfdd1c7871
7
- data.tar.gz: b37f3d569f59a2ae22bea51894cde019ca395c253a4c62d83cbbfd9cd692010e327996c98fb1f0711f8f4ba396cd1eda35b203ebec7c92d95a855218aeb09fdd
6
+ metadata.gz: a7d90bac8d4dee595da22401d53f27380d4119ccca5df73020ae6abd12eb71ad4cee577f9b8bfc4200ca73e90a4467060aab6e3ffcb6db52af4682c1926f2feb
7
+ data.tar.gz: 6851b6fc6714cf4376499dc585449040ed2f341536c80856c012eaf58bacdaca2f71813f66e07ce6c18926c973f3586cb364eedbb8896c66fea61f9674c0a17e
data/README.md CHANGED
@@ -17,26 +17,17 @@ Reporting Service for Grafana
17
17
  * [Installation](#installation)
18
18
  * [Grafana integration](#grafana-integration)
19
19
  * [Advanced information](#advanced-information)
20
- * [Webservice](#webservice)
20
+ * [Use grafana variables in templates](#use-grafana-variables-in-templates)
21
+ * [Webservice endpoints](#webservice-endpoints)
22
+ * [API endpoints](#api-endpoints)
21
23
  * [Using ERB templates](#using-erb-templates)
22
24
  * [Using webhooks](#using-webhooks)
23
- * [Developing your own plugin](#developing-your-own-plugin)
25
+ * [Developing your own datasource plugin](#developing-your-own-datasource-plugin)
24
26
  * [Roadmap](#roadmap)
25
27
  * [Contributing](#contributing)
26
28
  * [Licensing](#licensing)
27
29
  * [Acknowledgements](#acknowledgements)
28
30
 
29
- ## Your support is appreciated!
30
-
31
- Hey there! I provide you this software free of charge. I have already
32
- spend a lot of my private time in developing, maintaining and supporting it.
33
-
34
- If you enjoy my work, feel free to
35
-
36
- [![buymeacoffee](https://az743702.vo.msecnd.net/cdn/kofi3.png?v=0)](https://ko-fi.com/divinity666)
37
-
38
- Thanks for your support and keeping this project alive!
39
-
40
31
  ## About the project
41
32
 
42
33
  [Grafana](https://github.com/grafana/grafana) is a great tool for monitoring and
@@ -62,11 +53,12 @@ By default (an extended version of) Asciidoctor is enabled as template language.
62
53
 
63
54
  ### Features
64
55
 
65
- * Supports creation of reports for multiple [grafana](https://github.com/grafana/grafana)
66
- dashboards (and also multiple grafana installations!) in one resulting report
67
- * PDF (default), HTML and many other report formats are supported
68
- * Easy-to-use configuration wizard, including fully automated functionality to create a
69
- demo report for your dashboard
56
+ * Use Asciidoctor or ERB template language for defining templates
57
+ * Render your templates as PDF (default), HTML, CSV, text files and any other format
58
+ you can think of
59
+ * Create reports containing data from multiple [grafana](https://github.com/grafana/grafana)
60
+ dashboards or even multiple grafana installations
61
+ * Easy-to-use configuration wizard including automated demo report creation
70
62
  * Include dynamic content from grafana (find here a reference for all
71
63
  [asciidcotor reporter calls](FUNCTION_CALLS.md)):
72
64
  * panels as images
@@ -77,8 +69,7 @@ database queries
77
69
  * webservice to be called directly from grafana
78
70
  * standalone command line tool, e.g. to be automated with `cron` or `bash` scrips
79
71
  * microservice from standard asciidoctor docker container without any dependencies
80
- * Use webhook callbacks on before, on cancel and on finishing a report (see
81
- configuration file) to combine them with your services
72
+ * Integrate it in your toolchain with webhooks and API calls
82
73
  * Solid as a rock - no matter if you do mistakes in your configuration or grafana does no
83
74
  longer match templates: the ruby-grafana-reporter webservice will always return properly.
84
75
  * Full [API documentation](https://rubydoc.info/gems/ruby-grafana-reporter) available
@@ -234,6 +225,21 @@ The main endpoint to call for report generation is configured in the previous ch
234
225
 
235
226
  However, if you would like to see, currently running report generations and previously generated reports, you may want to call the endpoint `/overview`.
236
227
 
228
+ ### API endpoints
229
+
230
+ If you want to call the reporter programatically, you might want to get machine
231
+ usable endpoints. Therefore the following API endpoints are available:
232
+
233
+ POST /api/v1/render
234
+ GET /api/v1/status
235
+ DELETE /api/v1/cancel
236
+
237
+ Those endpoints have to be called with the same URL parameters, as the webservice
238
+ endpoints.
239
+
240
+ If you want to receive the report itself, you'll have to use the `/view_report`
241
+ call from the webservice endpoints.
242
+
237
243
  ### Using ERB templates
238
244
 
239
245
  By default the configuration wizard will setup the reporter with the asciidoctor
@@ -280,6 +286,12 @@ grafana-reporter:
280
286
  callbacks:
281
287
  all:
282
288
  - http://<<your_callback_url>>
289
+ on_before_create:
290
+ - http://<<your_callback_url>>
291
+ on_after_cancel:
292
+ - http://<<your_callback_url>>
293
+ on_after_finish:
294
+ - http://<<your_callback_url>>
283
295
  ````
284
296
 
285
297
  Remember to restart the reporter, if it is running as a webservice.
@@ -288,7 +300,7 @@ After having done so, your callback url will be called for each event with
288
300
  a JSON body including all necessary information of the report. For details see
289
301
  [callback](https://rubydoc.info/gems/ruby-grafana-reporter/GrafanaReporter/ReportWebhook#callback-instance_method).
290
302
 
291
- ### Developing your own plugin
303
+ ### Developing your own datasource plugin
292
304
 
293
305
  The reporter is designed to allow easy integration of your own plugins,
294
306
  without having to modify the reporter base source on github (or anywhere
@@ -372,6 +384,12 @@ This is just a collection of things, I am heading for in future, without a sched
372
384
  If you'd like to contribute, please fork the repository and use a feature
373
385
  branch. Pull requests are warmly welcome.
374
386
 
387
+ If you enjoy my work, and can't support with code contributions, feel free to
388
+
389
+ [![buymeacoffee](https://az743702.vo.msecnd.net/cdn/kofi3.png?v=0)](https://ko-fi.com/divinity666)
390
+
391
+ Thanks for your support and keeping this project alive!
392
+
375
393
  ## Licensing
376
394
 
377
395
  The code in this project is licensed under MIT license.
data/lib/VERSION.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Version information
4
- GRAFANA_REPORTER_VERSION = [0, 8, 0].freeze
4
+ GRAFANA_REPORTER_VERSION = [0, 9, 3].freeze
5
5
  # Release date
6
- GRAFANA_REPORTER_RELEASE_DATE = '2024-06-08'
6
+ GRAFANA_REPORTER_RELEASE_DATE = '2024-07-13'
@@ -144,27 +144,56 @@ module GrafanaReporter
144
144
  attrs[k] = v.length == 1 ? v[0] : v
145
145
  end
146
146
 
147
- case request.split("\r\n")[0]
148
- when %r{^GET /render[? ]}
149
- return render_report(attrs)
147
+ parsed_url = request.split("\r\n")[0].match(%r{(?<verb>GET|POST|DELETE) (?<subpath>/.*?)(?:api/v1/(?<api_action>render|status|cancel))?(?<html_action>render|overview|view_report|cancel_report|view_log)?[ ?]})
148
+ parsed_url = {} if not parsed_url
150
149
 
151
- when %r{^GET /overview[? ]}
150
+ case parsed_url
151
+ # API calls
152
+ when -> (h) { h['verb'] == 'POST' && h['api_action'] == 'render' }
153
+ return render_report(attrs, parsed_url['subpath'], true)
154
+
155
+ when -> (h) { h['verb'] == 'GET' && h['api_action'] == 'status' }
156
+ return report_status(attrs)
157
+
158
+ when -> (h) { h['verb'] == 'DELETE' && h['api_action'] == 'cancel' }
159
+ return cancel_report(attrs, parsed_url['subpath'], true)
160
+
161
+ # HTML calls
162
+ when -> (h) { h['verb'] == 'GET' && h['html_action'] == 'render' }
163
+ return render_report(attrs, parsed_url['subpath'])
164
+
165
+ when -> (h) { h['verb'] == 'GET' && h['html_action'] == 'overview' }
152
166
  # show overview for current reports
153
- return get_reports_status_as_html(@reports)
167
+ return get_reports_status_as_html(@reports, parsed_url['subpath'])
154
168
 
155
- when %r{^GET /view_report[? ]}
156
- return view_report(attrs)
169
+ when -> (h) { h['verb'] == 'GET' && h['html_action'] == 'view_report' }
170
+ return view_report(attrs, parsed_url['subpath'])
157
171
 
158
- when %r{^GET /cancel_report[? ]}
159
- return cancel_report(attrs)
172
+ when -> (h) { h['verb'] == 'GET' && h['html_action'] == 'cancel_report' }
173
+ return cancel_report(attrs, parsed_url['subpath'])
160
174
 
161
- when %r{^GET /view_log[? ]}
175
+ when -> (h) { h['verb'] == 'GET' && h['html_action'] == 'view_log' }
162
176
  return view_log(attrs)
163
177
  end
164
178
 
165
179
  raise WebserviceUnknownPathError, request.split("\r\n")[0]
166
180
  end
167
181
 
182
+ def report_status(attrs)
183
+ report = @reports.select { |r| r.object_id.to_s == attrs['report_id'].to_s }.first
184
+ raise WebserviceGeneralRenderingError, 'report_status has been called without valid id' if report.nil?
185
+
186
+ response = {
187
+ report_id: report.object_id,
188
+ progress: report.progress,
189
+ state: report.status,
190
+ done: report.done,
191
+ execution_time: report.execution_time
192
+ }
193
+
194
+ http_response(200, 'OK', JSON.generate(response), "Content-Type": "application/json")
195
+ end
196
+
168
197
  def view_log(attrs)
169
198
  # view report if already available, or show status view
170
199
  report = @reports.select { |r| r.object_id.to_s == attrs['report_id'].to_s }.first
@@ -175,7 +204,7 @@ module GrafanaReporter
175
204
  http_response(200, 'OK', content, "Content-Type": 'text/plain')
176
205
  end
177
206
 
178
- def cancel_report(attrs)
207
+ def cancel_report(attrs, subpath, as_json=false)
179
208
  # view report if already available, or show status view
180
209
  report = @reports.select { |r| r.object_id.to_s == attrs['report_id'].to_s }.first
181
210
  raise WebserviceGeneralRenderingError, 'cancel_report has been called without valid id' if report.nil?
@@ -183,28 +212,34 @@ module GrafanaReporter
183
212
  report.cancel! unless report.done
184
213
 
185
214
  # redirect to view_report page
186
- http_response(302, 'Found', nil, Location: "/view_report?report_id=#{report.object_id}")
215
+ return http_response(302, 'Found', nil, Location: "#{subpath}view_report?report_id=#{report.object_id}") if not as_json
216
+
217
+ http_response(200, 'OK', nil)
187
218
  end
188
219
 
189
- def view_report(attrs)
220
+ def view_report(attrs, subpath)
190
221
  # view report if already available, or show status view
191
222
  report = @reports.select { |r| r.object_id.to_s == attrs['report_id'].to_s }.first
192
223
  raise WebserviceGeneralRenderingError, 'view_report has been called without valid id' if report.nil?
193
224
 
194
225
  # show report status
195
- return get_reports_status_as_html([report]) if !report.done || !report.error.empty?
226
+ return get_reports_status_as_html([report], subpath) if !report.done || !report.error.empty?
196
227
 
197
228
  # provide report
198
- @logger.debug("Returning PDF report at #{report.path}")
229
+ @logger.debug("Returning report file #{report.path}")
199
230
  content = File.read(report.path, mode: 'rb')
200
231
  return http_response(200, 'OK', content, "Content-Type": 'application/pdf') if content.start_with?('%PDF')
201
232
 
233
+ return http_response(200, 'OK', content, "Content-Type": 'application/octet-stream',
234
+ "Content-Disposition": 'attachment; '\
235
+ "filename=report_#{attrs['report_id']}.zip") if content.start_with?('PK')
236
+
202
237
  http_response(200, 'OK', content, "Content-Type": 'application/octet-stream',
203
238
  "Content-Disposition": 'attachment; '\
204
- "filename=report_#{attrs['report_id']}.zip")
239
+ "filename=report_#{attrs['report_id']}.#{report.class.default_result_extension}")
205
240
  end
206
241
 
207
- def render_report(attrs)
242
+ def render_report(attrs, subpath, as_json=false)
208
243
  # build report
209
244
  template_file = "#{@config.templates_folder}#{attrs['var-template']}"
210
245
 
@@ -222,10 +257,13 @@ module GrafanaReporter
222
257
  end
223
258
  @reports << report
224
259
 
225
- http_response(302, 'Found', nil, Location: "/view_report?report_id=#{report.object_id}")
260
+ return http_response(302, 'Found', nil, Location: "#{subpath}view_report?report_id=#{report.object_id}") if not as_json
261
+
262
+ response = {report_id: report.object_id}
263
+ return http_response(200, 'OK', JSON.generate(response))
226
264
  end
227
265
 
228
- def get_reports_status_as_html(reports)
266
+ def get_reports_status_as_html(reports, subpath)
229
267
  i = reports.length
230
268
 
231
269
  # TODO: make reporter HTML results customizable
@@ -245,14 +283,14 @@ module GrafanaReporter
245
283
  <td><%= report.status %> (<%= (report.progress * 100).to_i %>%)</td>
246
284
  <td><%= report.error.join('<br>') %></td>
247
285
  <td><% if !report.done && !report.cancel %>
248
- <a href="/cancel_report?report_id=<%= report.object_id %>">Cancel</a>
286
+ <a href="<%= subpath %>cancel_report?report_id=<%= report.object_id %>">Cancel</a>
249
287
  <% end %>
250
288
  &nbsp;
251
289
  <% if (report.status == 'finished') || (report.status == 'cancelled') %>
252
- <a href="/view_report?report_id=<%= report.object_id %>">View</a>
290
+ <a href="<%= subpath %>view_report?report_id=<%= report.object_id %>">View</a>
253
291
  <% end %>
254
292
  &nbsp;
255
- <a href="/view_log?report_id=<%= report.object_id %>">Log</a></td></tr>
293
+ <a href="<%= subpath %>view_log?report_id=<%= report.object_id %>">Log</a></td></tr>
256
294
  <% end.join('') %>
257
295
  <tbody>
258
296
  </table>
@@ -267,8 +305,8 @@ module GrafanaReporter
267
305
  end
268
306
 
269
307
  def http_response(code, text, body, opts = {})
270
- "HTTP/1.1 #{code} #{text}\r\n#{opts.map { |k, v| "#{k}: #{v}" }.join("\r\n")}"\
271
- "#{body ? "\r\nContent-Length: #{body.to_s.bytesize}" : ''}\r\n\r\n#{body}"
308
+ "HTTP/1.1 #{code} #{text}\r\n#{opts.map { |k, v| "#{k}: #{v}" }.join("\r\n")}#{opts.length > 0 ? "\r\n" : ""}"\
309
+ "#{body ? "Content-Length: #{body.to_s.bytesize}" : ''}\r\n\r\n#{body}"
272
310
  end
273
311
  end
274
312
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'rubygems/name_tuple'
5
+ require 'rubygems/specification_policy'
5
6
  require 'rubygems/ext'
6
7
  require 'net/http'
7
8
  require 'fileutils'
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.8.0
4
+ version: 0.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Kohlmeyer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-08 00:00:00.000000000 Z
11
+ date: 2024-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor