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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3ffea90aebd213a65781274b5825d3f47d2a68e804821dd4a887ebdaebd67ae
4
- data.tar.gz: 931c47a54cebe300a6f0b3e3dffb470ac6ce6f4faddc8558e90bd18d4273b1d5
3
+ metadata.gz: 799071f1a4ed0795aeb9dbdc7fe175d3e023f4d0de3844f0ae57e85343ab47b4
4
+ data.tar.gz: 6524d4a07cd49c6622d69507e0ee6c3a37c18c358f8a3dc63b56848255c38e4c
5
5
  SHA512:
6
- metadata.gz: d0c187b97ec27c6ac374069d9253215d78c2b04d6e8ff10cc1bd660486d0f998c742cfd65a4d415cdb2b8045fed1c6800ccf2c64cbe21fc530b82527b4183f69
7
- data.tar.gz: b7b22e087b05c9c1bb6c0c92526755c1883ac9a57ca53c64e809de18e213421a2c65329618a42a75aa80b2ebdc0a38f48b28ac15eb2b9dd02c7c2250c838953c
6
+ metadata.gz: f52466eb19bc813271c4f67818414f578750f2a7d9254244cb5213be47ffa50e8a1849788935211391b606cc944a84b9bef6b46aec583185bbe94958d2e080a5
7
+ data.tar.gz: 4eae953b1ad50b9d188d66f3f7bf0c9c6184ff3009da077f2ed07c0d082d7b3d7be75c362143033b27b8256482886e797c78ab750bdde5a85cf198ea8b4fe540
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  [![MIT License](https://img.shields.io/github/license/divinity666/ruby-grafana-reporter.svg?style=flat-square)](https://github.com/divinity666/ruby-grafana-reporter/blob/master/LICENSE)
2
2
  [![Build Status](https://travis-ci.org/divinity666/ruby-grafana-reporter.svg?branch=master)](https://travis-ci.org/github/divinity666/ruby-grafana-reporter?branch=master)
3
3
  [![Coverage Status](https://coveralls.io/repos/github/divinity666/ruby-grafana-reporter/badge.svg?branch=master)](https://coveralls.io/github/divinity666/ruby-grafana-reporter?branch=master)
4
+ [![Gem Version](https://badge.fury.io/rb/ruby-grafana-reporter.svg)](https://badge.fury.io/rb/ruby-grafana-reporter)
4
5
 
5
6
  # Ruby Grafana Reporter
6
7
  Reporting Service for Grafana
@@ -8,14 +9,11 @@ Reporting Service for Grafana
8
9
  ## Table of Contents
9
10
 
10
11
  * [About the project](#about-the-project)
11
- * [Getting started](#getting-started)
12
- * [Grafana integration](#grafana-integration)
13
- * [Webservice overview](#webservice-overview)
14
12
  * [Features](#features)
13
+ * [Quick Start](#quick-start)
14
+ * [Grafana integration](#grafana-integration)
15
+ * [Webservice overview](#webservice-overview)
15
16
  * [Roadmap](#roadmap)
16
- * [Contributing](#contributing)
17
- * [Licensing](#licensing)
18
- * [Acknowledgements](#acknowledgements)
19
17
  * [Donations](#donations)
20
18
 
21
19
  ## About the project
@@ -24,55 +22,93 @@ Did you ever want to create (professional) reports based on Grafana dashboards?
24
22
  I did so in order to being able to automatically get monthly reports of my
25
23
  home's energy usage. That's how it started.
26
24
 
27
- The reporter provides reporting capabilities for Grafana. It is based on
28
- (but not limited to) [asciidoctor](https://github.com/asciidoctor/asciidoctor)
29
- report templates, which can dynamically integrate Grafana panels, queries,
30
- images etc. to create dynamic PDF reports on the fly.
31
-
32
- The report may also be returned in any other format that asciidoctor supports.
33
-
34
- The reporter can run standalone or as a webservice. It is built to
35
- integrate without further dependencies with the asciidoctor docker image.
36
-
37
- The complete
38
- [API documentation](https://rubydoc.info/gems/ruby-grafana-reporter) can be
39
- found here.
40
-
41
- ## Getting started
42
-
43
- There exist several ways of installing the reporter. Here I cover the easiest
44
- setup by using ruby gems. If you need further installation help, or want to use
45
- a "baremetal" ruby setup or a docker integration, please have a look at the more
46
- extended [installation documentation](INSTALL.md).
47
-
48
- To install the reporter as a gem, simply run:
49
-
50
- gem install ruby-grafana-reporter
51
-
52
- If no configuration file is in place, you might want to use the configuration
53
- wizard, which leads you through all necessary steps:
54
-
55
- ruby-grafana-reporter -w
56
-
57
- Now you're ready to go! Let's check it out!
58
-
59
- ruby-grafana-reporter -t demo_report -o my_first_render.pdf
60
-
61
- If everything works as expected, you should find a file named `my_first_render.pdf`
62
- in the current folder, which contains a detailed explanation of all available
63
- commands as well as your available configuration options.
64
-
65
- To run the reporter as a service, you only need to call it like this:
66
-
67
- ruby-grafana-reporter
68
-
69
- Neat, isn't it?
70
-
71
- ### Grafana integration
25
+ ## Features
72
26
 
73
- The key feature of the report is, that it can easily be integrated with grafana
74
- (I've not even been talking about the features it is providing for that, but
75
- you'll find them having a look in the example results above).
27
+ * Build reports based on [grafana](https://github.com/grafana/grafana) dashboards, PDF
28
+ (default) and many other formats supported
29
+ * Easy-to-use configuration wizard, including fully automated functionality to create a
30
+ demo report
31
+ * Include dynamic content from grafana (find here a reference for all
32
+ [asciidcotor reporter calls](FUNCTION_CALLS.md)):
33
+ * panels as images
34
+ * tables based on grafana panel queries or custom database queries (no images!)
35
+ * single values to be integrated in text, based on grafana panel queries or custom
36
+ database queries
37
+ * Multi purpose use of the reporter
38
+ * webservice to be called directly from grafana
39
+ * standalone command line tool, e.g. to be automated with `cron` or `bash` scrips
40
+ * seemlessly runs from asciidocotor docker container without further dependencies
41
+ * Webhook callbacks before, on cancel and on finishing callbacks (see configuration file)
42
+ * Solid as a rock, also in case of template errors and whatever else may happen
43
+ * Full [API documentation](https://rubydoc.info/gems/ruby-grafana-reporter) available
44
+
45
+ Functionalities are provided as shown here:
46
+
47
+ Database | Image rendering | Panel-based rendering | Query-based rendering
48
+ ------------------------- | :-------: | :-----------: | :------------:
49
+ all SQL based datasources | supported | supported | supported
50
+ Graphite | supported | supported | supported
51
+ Prometheus | supported | supported | supported
52
+ other datasources | supported | not-supported | not-supported
53
+
54
+ ## Quick Start
55
+
56
+ You don't have a grafana setup runnning already? No worries, just configure
57
+ `https://play.grafana.org` in the configuration wizard and see the magic
58
+ happen!
59
+
60
+ If your grafana setup requires a login, you'll have to setup an api key for
61
+ the reporter. Please follow the steps
62
+ [described here](https://github.com/divinity666/ruby-grafana-reporter/issues/2#issuecomment-811836757)
63
+ first.
64
+
65
+ **Windows:**
66
+
67
+ * [Download latest Windows executable](https://github.com/divinity666/ruby-grafana-reporter/releases/latest)
68
+ * `ruby-grafana-reporter -w`
69
+
70
+ **Raspberry Pi:**
71
+
72
+ * `sudo apt-get install ruby`
73
+ * `gem install ruby-grafana-reporter`
74
+ * `ruby-grafana-reporter -w`
75
+
76
+ **Ruby environment:**
77
+
78
+ * `gem install ruby-grafana-reporter`
79
+ * `ruby-grafana-reporter -w`
80
+
81
+ **Docker environment** (advanced users):
82
+
83
+ * [Download latest single-rb file](https://github.com/divinity666/ruby-grafana-reporter/releases/latest)
84
+ to an empty folder
85
+ * create a configuration file by calling `ruby ruby-grafana-reporter -w` (if in doubt,
86
+ run the command within your docker container)
87
+ * create file `/<<path-to-single-rb-file-folder>>/startup.sh` with the following
88
+ content:
89
+
90
+ ```
91
+ cd /documents
92
+ ruby bin/ruby-grafana-reporter
93
+ ```
94
+ * add asciidoctor your compose yaml:
95
+
96
+ ```
97
+ asciidoctor:
98
+ image: asciidoctor/docker-asciidoctor
99
+ container_name: asciidoctor
100
+ hostname: asciidoctor
101
+ volumes:
102
+ - /<<path-to-single-rb-file-folder>>:/documents
103
+ command:
104
+ sh /documents/startup.sh
105
+ restart: unless-stopped
106
+ ```
107
+ * start/restart the asciidoctor docker container
108
+
109
+ ## Grafana integration
110
+
111
+ The key feature of the report is, that it can easily be integrated with grafana.
76
112
 
77
113
  For accessing the reporter from grafana, you need to simply add a link to your
78
114
  grafana dashboard:
@@ -82,27 +118,27 @@ grafana dashboard:
82
118
  * Select `Add`
83
119
  * Fill out as following:
84
120
  * Type: `link`
85
- * Url: `http://<<your-server-url>>:<<your-webservice-port>>/render?var-template=myfirsttemplate`
86
- * Title: `MyFirstReport`
121
+ * Url: `http://<<your-server-url>>:<<your-webservice-port>>/render?var-template=demo_report`
122
+ * Title: `Demo Report`
87
123
  * Select `Time range`
88
124
  * Select `Variable values`
89
125
  * Select `Add`
90
126
 
91
- Now go back to your dashboard and click the newly generated 'MyFirstReport'
127
+ Now go back to your dashboard and click the newly generated `Demo Report`
92
128
  link on it. Now the renderer should start it's task and show you the expected
93
129
  results.
94
130
 
95
- But now the fun just starts! Try out the functions stated in the
96
- 'MyFirstReport' PDF file, to include the dynamic content in your asciidoctor
97
- template.
131
+ Please note, that the reporter won't automatically refresh your screen to update
132
+ the progress. Simply hit `F5` to refresh your browser. After the report has been
133
+ successfully built, it will show the PDF after the next refresh automatically.
98
134
 
99
- Additionally you might want to make the selection of the template variable.
135
+ You want to select a template in grafana, which shall then be rendered?
100
136
  Piece of cake: Just add a dashboard variable to your grafana dashboard named
101
137
  `template` and let the user select or enter a template name. To make use of it,
102
- you should change the link of the 'MyFirstReport' link to
103
- `http://<<your-server-url>>:<<your-webservice-port>>/render?`
104
-
105
- That's it. Let me know your feedback!
138
+ you should change the link of the `Demo Report` link to
139
+ `http://<<your-server-url>>:<<your-webservice-port>>/render?`. On
140
+ hitting the new link in the dashboard, grafana will add the selected template as
141
+ a variable and forward it to the reporter.
106
142
 
107
143
  ## Webservice overview
108
144
 
@@ -113,23 +149,15 @@ Running the reporter as a webservice provides the following URLs
113
149
  /view_report - for viewing the status or receving the result of a specific rendering, is automatically called after a successfull /render call
114
150
  /cancel_report - for cancelling the rendering of a specific report, normally not called manually, but on user interaction in the /view_report or /overview URL
115
151
 
116
- ## Features
152
+ The main endpoint to call for report generation is configured in the previous chapter [Grafana integration](#grafana-integration).
117
153
 
118
- * Build report template including all imaginable grafana content:
119
- * panels as images
120
- * panel table query or custom query results as real document tables (not images!)
121
- * single panel value or custom query single value result integrated in texts
122
- * Solid as a rock, also in case of template errors (at least it aims to be)
123
- * Runs standalone or as a webservice
124
- * Seamlessly integrates with asciidoctor docker container
125
- * Developed for being able to support other tools than asciidoctor as well
154
+ However, if you would like to see, currently running report generations and previously generated reports, you may want to call the endpoint `/overview`.
126
155
 
127
156
  ## Roadmap
128
157
 
129
158
  This is just a collection of things, I am heading for in future, without a schedule.
130
159
 
131
- * Add documentation of possible asciidoctor calls to grafana
132
- * Add a simple plugin system to support specific asciidoctor modifications
160
+ * Support all grafana datasources
133
161
  * Solve code TODOs
134
162
  * Become [rubocop](https://rubocop.org/) ready
135
163
 
@@ -138,14 +166,6 @@ This is just a collection of things, I am heading for in future, without a sched
138
166
  If you'd like to contribute, please fork the repository and use a feature
139
167
  branch. Pull requests are warmly welcome.
140
168
 
141
- Though not yet valid for my code, I'd like to see the project become
142
- [rubocop](https://rubocop.org/) ready :-)
143
-
144
- Definitely open spots from my side are:
145
-
146
- * This README
147
- * Clean and properly setup test cases
148
-
149
169
  ## Licensing
150
170
 
151
171
  The code in this project is licensed under MIT license.
@@ -160,7 +180,6 @@ Inspired by [Izak Marai's grafana reporter](https://github.com/IzakMarais/report
160
180
  ## Donations
161
181
 
162
182
  If this project saves you as much time as I hope it does, and if you'd like to
163
- support my work, feel free donate, even a cup of coffee is appreciated :)
183
+ support my work, feel free donate. :)
164
184
 
165
185
  [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate?hosted_button_id=35LH6JNLPHPHQ)
166
-
@@ -1,5 +1,5 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require_relative '../lib/ruby-grafana-reporter'
5
- GrafanaReporter::Application::Application.new.configure_and_run(ARGV)
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/ruby_grafana_reporter'
5
+ GrafanaReporter::Application::Application.new.configure_and_run(ARGV) unless defined?(Ocra)
data/lib/VERSION.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Version information
4
- GRAFANA_REPORTER_VERSION = [0, 2, 0].freeze
5
- GRAFANA_REPORTER_RELEASE_DATE = '2020-11-30'
4
+ GRAFANA_REPORTER_VERSION = [0, 4, 1].freeze
5
+ # Release date
6
+ GRAFANA_REPORTER_RELEASE_DATE = '2021-05-17'
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grafana
4
+ # This abstract class defines the base functionalities for the common datasource implementations.
5
+ # Additionally it provides a factory method to build a real datasource from a given specification.
6
+ class AbstractDatasource
7
+ attr_reader :model
8
+
9
+ @@subclasses = []
10
+
11
+ # Registers the subclass as datasource, which is asked by {#accepts?}, if it can handle a datasource
12
+ # model.
13
+ # @param subclass [Class] class inheriting from this abstract class
14
+ def self.inherited(subclass)
15
+ @@subclasses << subclass
16
+ end
17
+
18
+ # Overwrite this method, to specify if the current datasource implementation handles the given model.
19
+ # This method is called by {#build_instance} to determine, if the current datasource implementation
20
+ # can handle the given grafana model. By default this method returns false.
21
+ # @param model [Hash] grafana specification of the datasource to check
22
+ # @return [Boolean] True if fits, false otherwise
23
+ def self.handles?(_model)
24
+ false
25
+ end
26
+
27
+ # Factory method to build a datasource from a given datasource Hash description.
28
+ # @param ds_model [Hash] grafana specification of a single datasource
29
+ # @return [AbstractDatasource] instance of a fitting datasource implementation
30
+ def self.build_instance(ds_model)
31
+ raise InvalidDatasourceQueryProvidedError, ds_model unless ds_model.is_a?(Hash)
32
+
33
+ raise InvalidDatasourceQueryProvidedError, ds_model unless ds_model['meta'].is_a?(Hash)
34
+
35
+ @@subclasses.each do |datasource_class|
36
+ return datasource_class.new(ds_model) if datasource_class.handles?(ds_model)
37
+ end
38
+
39
+ UnsupportedDatasource.new(ds_model)
40
+ end
41
+
42
+ def initialize(model)
43
+ @model = model
44
+ end
45
+
46
+ # @return [String] category of the datasource, e.g. `tsdb` or `sql`
47
+ def category
48
+ @model['meta']['category']
49
+ end
50
+
51
+ # @return [String] type of the datasource, e.g. `mysql`
52
+ def type
53
+ @model['type'] || @model['meta']['id']
54
+ end
55
+
56
+ # @return [String] name of the datasource
57
+ def name
58
+ @model['name']
59
+ end
60
+
61
+ # @return [Integer] ID of the datasource
62
+ def id
63
+ @model['id'].to_i
64
+ end
65
+
66
+ # @abstract
67
+ #
68
+ # Executes a request for the current database with the given options.
69
+ #
70
+ # Used format of the response will always be the following:
71
+ #
72
+ # {
73
+ # :header => [column_title_1, column_title_2],
74
+ # :content => [
75
+ # [row_1_column_1, row_1_column_2],
76
+ # [row_2_column_1, row_2_column_2]
77
+ # ]
78
+ # }
79
+ #
80
+ # @param query_description [Hash] query description, which will requested:
81
+ # @option [String] :from +from+ timestamp
82
+ # @option [String] :to +to+ timestamp
83
+ # @option [Integer] :timeout expected timeout for the request
84
+ # @option [WebRequest] :prepared_request prepared web request for relevant {Grafana} instance, if this is needed by datasource
85
+ # @option [String] :raw_query raw query, which shall be executed. May include variables, which will be replaced before execution
86
+ # @option [Hash<Variable>] :variables hash of variables, which can potentially be replaced in the given +:raw_query+
87
+ # @return [Hash] sql result formatted as stated above
88
+ def request(query_description)
89
+ raise NotImplementedError
90
+ end
91
+
92
+ # @abstract
93
+ #
94
+ # The different datasources supported by grafana use different ways to store the query in the
95
+ # panel's JSON model. This method extracts a query from that description, that can be used
96
+ # by the {AbstractDatasource} implementation of the datasource.
97
+ #
98
+ # @param panel_query_target [Hash] grafana panel target, which contains the query description
99
+ # @return [String] query string, which can be used as +raw_query+ in a {#request}
100
+ def raw_query_from_panel_model(panel_query_target)
101
+ raise NotImplementedError
102
+ end
103
+
104
+ private
105
+
106
+ # Replaces the grafana variables in the given string with their replacement value.
107
+ #
108
+ # @param string [String] string in which the variables shall be replaced
109
+ # @param variables [Hash<String,Variable>] Hash containing the variables, which shall be replaced in the
110
+ # given string
111
+ # @return [String] string in which all variables are properly replaced
112
+ def replace_variables(string, variables = {})
113
+ res = string
114
+ repeat = true
115
+ repeat_count = 0
116
+
117
+ # TODO: find a proper way to replace variables recursively instead of over and over again
118
+ # TODO: add tests for recursive replacement of variable
119
+ while repeat && (repeat_count < 3)
120
+ repeat = false
121
+ repeat_count += 1
122
+ variables.each do |var_name, obj|
123
+ # only set ticks if value is string
124
+ variable = var_name.gsub(/^var-/, '')
125
+ res = res.gsub(/(?:\$\{#{variable}(?::(?<format>\w+))?\}|\$#{variable})/) do
126
+ # TODO: respect datasource requirements for formatting here
127
+ obj.value_formatted($LAST_MATCH_INFO ? $LAST_MATCH_INFO[:format] : nil)
128
+ end
129
+ end
130
+ repeat = true if res.include?('$')
131
+ end
132
+
133
+ res
134
+ end
135
+ end
136
+ end
@@ -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,33 @@ 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 { |item| @variables << Variable.new(item) }
59
57
  end
60
- end
61
58
 
62
- # read panels
63
- def initialize_panels
64
- @panels = []
65
- return unless @model.key?('panels')
59
+ # read panels
60
+ def init_panels
61
+ @panels = []
62
+ return unless @model.key?('panels')
66
63
 
67
- @model['panels'].each do |panel|
68
- if panel.key?('panels')
69
- panel['panels'].each do |subpanel|
70
- @panels << Panel.new(subpanel, self)
64
+ @model['panels'].each do |panel|
65
+ if panel.key?('panels')
66
+ panel['panels'].each do |subpanel|
67
+ @panels << Panel.new(subpanel, self)
68
+ end
69
+ else
70
+ @panels << Panel.new(panel, self)
71
71
  end
72
- else
73
- @panels << Panel.new(panel, self)
74
72
  end
75
73
  end
76
74
  end