├── .gitignore
├── .jscs.json
├── Gruntfile.js
├── LICENSE
├── MIGRATION.md
├── README.md
├── dist
├── README.md
├── css
│ └── query-editor.css
├── datasource.js
├── datasource.js.map
├── img
│ └── simpleJson_logo.svg
├── module.js
├── module.js.map
├── partials
│ ├── annotations.editor.html
│ ├── config.html
│ ├── query.editor.html
│ └── query.options.html
├── plugin.json
├── query_ctrl.js
└── query_ctrl.js.map
├── docker-compose.yaml
├── package.json
├── provisioning
├── dashboards-actual
│ └── migration.json
├── dashboards
│ └── default.yaml
└── datasources
│ └── default.yaml
├── server
└── Dockerfile
├── spec
├── datasource_spec.js
└── test-main.js
├── src
├── css
│ └── query-editor.css
├── datasource.js
├── img
│ └── simpleJson_logo.svg
├── module.js
├── partials
│ ├── annotations.editor.html
│ ├── config.html
│ ├── query.editor.html
│ └── query.options.html
├── plugin.json
└── query_ctrl.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | coverage/
4 | .aws-config.json
5 | awsconfig
6 | /emails/dist
7 | /public_gen
8 | /tmp
9 | vendor/phantomjs/phantomjs
10 |
11 | docs/AWS_S3_BUCKET
12 | docs/GIT_BRANCH
13 | docs/VERSION
14 | docs/GITCOMMIT
15 | docs/changed-files
16 | docs/changed-files
17 |
18 | # locally required config files
19 | public/css/*.min.css
20 |
21 | # Editor junk
22 | *.sublime-workspace
23 | *.swp
24 | .idea/
25 | *.iml
26 |
27 | /data/*
28 | /bin/*
29 |
30 | conf/custom.ini
31 | fig.yml
32 | profile.cov
33 | grafana
34 | .notouch
35 |
36 | # Test artifacts
37 | /dist/test/
38 |
--------------------------------------------------------------------------------
/.jscs.json:
--------------------------------------------------------------------------------
1 | {
2 | "esnext": true,
3 | "disallowImplicitTypeConversion": ["string"],
4 | "disallowKeywords": ["with"],
5 | "disallowMultipleLineBreaks": true,
6 | "disallowMixedSpacesAndTabs": true,
7 | "disallowTrailingWhitespace": true,
8 | "requireSpacesInFunctionExpression": {
9 | "beforeOpeningCurlyBrace": true
10 | },
11 | "disallowSpacesInsideArrayBrackets": true,
12 | "disallowSpacesInsideParentheses": true,
13 | "validateIndentation": 2
14 | }
15 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | require('load-grunt-tasks')(grunt);
4 |
5 | grunt.loadNpmTasks('grunt-execute');
6 | grunt.loadNpmTasks('grunt-contrib-clean');
7 |
8 | grunt.initConfig({
9 |
10 | clean: ["dist"],
11 |
12 | copy: {
13 | src_to_dist: {
14 | cwd: 'src',
15 | expand: true,
16 | src: ['**/*', '!**/*.js', '!**/*.scss'],
17 | dest: 'dist'
18 | },
19 | pluginDef: {
20 | expand: true,
21 | src: ['README.md'],
22 | dest: 'dist'
23 | }
24 | },
25 |
26 | watch: {
27 | rebuild_all: {
28 | files: ['src/**/*'],
29 | tasks: ['default'],
30 | options: {spawn: false}
31 | }
32 | },
33 |
34 | babel: {
35 | options: {
36 | sourceMap: true,
37 | presets: ['env'],
38 | plugins: ['transform-object-rest-spread']
39 | },
40 | dist: {
41 | files: [{
42 | cwd: 'src',
43 | expand: true,
44 | src: ['**/*.js'],
45 | dest: 'dist',
46 | ext:'.js'
47 | }]
48 | },
49 | distTestNoSystemJs: {
50 | files: [{
51 | cwd: 'src',
52 | expand: true,
53 | src: ['**/*.js'],
54 | dest: 'dist/test',
55 | ext:'.js'
56 | }]
57 | },
58 | distTestsSpecsNoSystemJs: {
59 | files: [{
60 | expand: true,
61 | cwd: 'spec',
62 | src: ['**/*.js'],
63 | dest: 'dist/test/spec',
64 | ext:'.js'
65 | }]
66 | }
67 | },
68 |
69 | mochaTest: {
70 | test: {
71 | options: {
72 | reporter: 'spec'
73 | },
74 | src: ['dist/test/spec/test-main.js', 'dist/test/spec/*_spec.js']
75 | }
76 | }
77 | });
78 |
79 | grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel', 'mochaTest']);
80 | };
81 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Grafana
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MIGRATION.md:
--------------------------------------------------------------------------------
1 | # Migration Guide
2 |
3 | This plugin is now deprecated and **no longer maintained** by the Grafana team. This deprecation means that this plugin won't receive any feature updates or bug fixes. After 6 months (End of June 2024), the plugin will reach EOL and the repository will be archived. You can use [Grafana Infinity data source plugin](https://grafana.com/grafana/plugins/yesoreyeram-infinity-datasource/) as an alternative for connecting to JSON/CSV/XML/GraphQL endpoints. Refer the migration guide [here](https://github.com/grafana/grafana-infinity-datasource/discussions/740), if you still prefer connecting to existing grafana simple JSON backend server implementation. Alternatively, you can also build your own plugin.
4 |
5 | ## Building your own plugin
6 |
7 | If you are looking for building your own plugin, consider the examples [here](https://github.com/grafana/grafana-plugin-examples) and documentation [here](https://grafana.com/developers).
8 |
9 | ## Migrating to Infinity data source plugin
10 |
11 |
12 |
=>
13 |
14 |
15 |
16 | > [!IMPORTANT]
17 | > Infinity is not a drop-in replacement for simple JSON datasource plugin. This require manual migration efforts. But migrating to infinity is strongly recommended approach if you are using simple JSON API datasource plugin.
18 |
19 | ### Approach 1: Direct API connection / Simple approach / Recommended approach
20 |
21 | This is much easier and **recommended approach** to connect JSON api endpoints directly. Infinity allows you to connect directly to your JSON endpoints instead of requiring you to write your server implementation comparing to grafana simple json server approach. Refer [Infinity plugin website](https://grafana.com/grafana/plugins/yesoreyeram-infinity-datasource/) for more details about connecting your APIs directly.
22 |
23 | With this approach, you can get rid of your custom json server and directly connect your API endpoints via Infinity plugin. If this approach is not possible for any reason, use the below alternate migration approach.
24 |
25 | ### Approach 2: Migrating using Grafana simple json server approach
26 |
27 | With this approach, Instead of connecting your API endpoints directly, you will be connecting via grafana simple JSON server (legacy server). Also in grafana, instead of using simple json datasource plugin, you will be using Infinity plugin.
28 |
29 | #### Migrating the configuration
30 |
31 | In terms of configuration editor, there is no much change. The URL used in simple json datasource goes into infinity config misc/url section. Other options such as proxy, tls/ca certificates, headers goes into network and headers section.
32 |
33 | | Before | After |
34 | |--|--|
35 | |
|
|
36 |
37 | #### Provisioning
38 |
39 | Provisioning of simple json and infinity is almost similar only the plugin id differs. Refer the infinity plugin [provisioning documentation](https://grafana.com/docs/plugins/yesoreyeram-infinity-datasource/latest/setup/provisioning/) for more examples.
40 |
41 | | Before | After |
42 | |--|--|
43 | |
|
|
44 |
45 |
46 | #### Migrating the queries ( quick migration / frontend parser )
47 |
48 | If you are using one or more queries using Simple JSON as shown below, you can migrate to infinity using the steps provided below. This approach is simple but less powerful. Doesn't support grafana backend features such as alerting.
49 |
50 | | Before | After |
51 | |---|---|
52 | |
| 
|
53 | | Select the target in the query editor. Example `upper_25`| Select `JSON` as query type.
Select `Default`/`Frontend` as your parser.
Source: `URL`.
Format: `As IS`/`Legacy`.
HTTP Method: `POST`.
URL : `/query`
HTTP Body: In your http body you need to specify the targets: `{ "targets": [{ "target":"upper_25" }] }`|
54 |
55 |
56 | #### Migrating the queries ( recommended migration / backend parser )
57 |
58 | Use this approach to migrate to get support for features such as alerting, public dashboards, query caching etc.
59 |
60 | | Before | After |
61 | |---|--|
62 | |
|
|
63 | |Select the target in the query editor. Example `upper_25`|Query Type: `URL`
Parser: `Backend`
Source: `URL`
Format: `Time series`
HTTP method: `POST`
URL: `/query`
BODY: `{ "targets": [{ "target":"upper_25" }]}`
Parsing options/Root: `datapoints`
Column 1: `0` as selector. `upper_25` as alias. `Number` as format.
Column 2: `1` as selector. `Time` as alias. `Unix (ms)` as format.
|
64 |
65 | ### Migrating the annotations
66 |
67 | Migrating the annotations is similar to query. For example, below screenoshots show different annotation creations
68 |
69 | ###### Using Simple JSON
70 |
71 |
72 | ###### Using Infinity
73 |
74 |
75 |
76 | #### Troubleshooting
77 |
78 | > [!NOTE]
79 | > Infinity doesn't support direct browser connection to your API endpoints. All the requests will be proxied from grafana server.
80 |
81 | #### Sample migration dashboard
82 |
83 |
84 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Deprecated plugin. Use [Infinity data source plugin](https://grafana.com/grafana/plugins/yesoreyeram-infinity-datasource/) instead
2 |
3 | ## Project status
4 |
5 | > [!CAUTION]
6 | > This plugin is now **DEPRECATED** and **no longer maintained** by the Grafana team. You can use [Grafana Infinity data source plugin](https://grafana.com/grafana/plugins/yesoreyeram-infinity-datasource/) as an alternative for connecting to JSON/CSV/XML/GraphQL endpoints. Refer the migration guide [here](https://github.com/grafana/grafana-infinity-datasource/discussions/740), if you still prefer connecting to existing grafana simple JSON backend server implementation. This deprecation means that this plugin won't receive any feature updates or bug fixes. After 6 months (End of June 2024), the plugin will reach EOL and the repository will be archived. If you are looking for building your own plugin, consider the examples [here](https://github.com/grafana/grafana-plugin-examples) and documentation [here](https://grafana.com/developers).
7 |
8 | ## Simple JSON Datasource - a generic backend datasource
9 |
10 | You can find more documentation about datasource plugins in Grafana's [Docs](https://grafana.com/docs/grafana/latest/developers/plugins/).
11 |
12 | This also serves as a living example implementation of a datasource.
13 |
14 | Your backend needs to implement 4 urls:
15 |
16 | * `/` should return 200 ok. Used for "Test connection" on the datasource config page.
17 | * `/search` used by the find metric options on the query tab in panels.
18 | * `/query` should return metrics based on input.
19 | * `/annotations` should return annotations.
20 |
21 | Those two urls are optional:
22 |
23 | * `/tag-keys` should return tag keys for ad hoc filters.
24 | * `/tag-values` should return tag values for ad hoc filters.
25 |
26 | ## Installation
27 |
28 | To install this plugin using the `grafana-cli` tool:
29 | ```
30 | sudo grafana-cli plugins install grafana-simple-json-datasource
31 | sudo service grafana-server restart
32 | ```
33 | See [here](https://grafana.com/plugins/grafana-simple-json-datasource/installation) for more
34 | information.
35 |
36 | ### Example backend implementations
37 | - https://github.com/bergquist/fake-simple-json-datasource
38 | - https://github.com/smcquay/jsonds
39 | - https://github.com/ContextLogic/eventmaster
40 | - https://gist.github.com/linar-jether/95ff412f9d19fdf5e51293eb0c09b850 (Python/pandas backend)
41 |
42 | ### Query API
43 |
44 | Example `timeserie` request
45 | ``` javascript
46 | {
47 | "panelId": 1,
48 | "range": {
49 | "from": "2016-10-31T06:33:44.866Z",
50 | "to": "2016-10-31T12:33:44.866Z",
51 | "raw": {
52 | "from": "now-6h",
53 | "to": "now"
54 | }
55 | },
56 | "rangeRaw": {
57 | "from": "now-6h",
58 | "to": "now"
59 | },
60 | "interval": "30s",
61 | "intervalMs": 30000,
62 | "targets": [
63 | { "target": "upper_50", "refId": "A", "type": "timeserie" },
64 | { "target": "upper_75", "refId": "B", "type": "timeserie" }
65 | ],
66 | "adhocFilters": [{
67 | "key": "City",
68 | "operator": "=",
69 | "value": "Berlin"
70 | }],
71 | "format": "json",
72 | "maxDataPoints": 550
73 | }
74 | ```
75 |
76 | Example `timeserie` response
77 | ``` javascript
78 | [
79 | {
80 | "target":"upper_75", // The field being queried for
81 | "datapoints":[
82 | [622,1450754160000], // Metric value as a float , unixtimestamp in milliseconds
83 | [365,1450754220000]
84 | ]
85 | },
86 | {
87 | "target":"upper_90",
88 | "datapoints":[
89 | [861,1450754160000],
90 | [767,1450754220000]
91 | ]
92 | }
93 | ]
94 | ```
95 |
96 | If the metric selected is `"type": "table"`, an example `table` response:
97 | ``` json
98 | [
99 | {
100 | "columns":[
101 | {"text":"Time","type":"time"},
102 | {"text":"Country","type":"string"},
103 | {"text":"Number","type":"number"}
104 | ],
105 | "rows":[
106 | [1234567,"SE",123],
107 | [1234567,"DE",231],
108 | [1234567,"US",321]
109 | ],
110 | "type":"table"
111 | }
112 | ]
113 | ```
114 |
115 | ### Annotation API
116 |
117 | The annotation request from the Simple JSON Datasource is a POST request to
118 | the `/annotations` endpoint in your datasource. The JSON request body looks like this:
119 | ``` javascript
120 | {
121 | "range": {
122 | "from": "2016-04-15T13:44:39.070Z",
123 | "to": "2016-04-15T14:44:39.070Z"
124 | },
125 | "rangeRaw": {
126 | "from": "now-1h",
127 | "to": "now"
128 | },
129 | "annotation": {
130 | "name": "deploy",
131 | "datasource": "Simple JSON Datasource",
132 | "iconColor": "rgba(255, 96, 96, 1)",
133 | "enable": true,
134 | "query": "#deploy"
135 | }
136 | }
137 | ```
138 |
139 | Grafana expects a response containing an array of annotation objects in the
140 | following format:
141 |
142 | ``` javascript
143 | [
144 | {
145 | annotation: annotation, // The original annotation sent from Grafana.
146 | time: time, // Time since UNIX Epoch in milliseconds. (required)
147 | title: title, // The title for the annotation tooltip. (required)
148 | tags: tags, // Tags for the annotation. (optional)
149 | text: text // Text for the annotation. (optional)
150 | }
151 | ]
152 | ```
153 |
154 | Note: If the datasource is configured to connect directly to the backend, you
155 | also need to implement an OPTIONS endpoint at `/annotations` that responds
156 | with the correct CORS headers:
157 |
158 | ```
159 | Access-Control-Allow-Headers:accept, content-type
160 | Access-Control-Allow-Methods:POST
161 | Access-Control-Allow-Origin:*
162 | ```
163 |
164 | ### Search API
165 |
166 | Example request
167 | ``` javascript
168 | { target: 'upper_50' }
169 | ```
170 |
171 | The search api can either return an array or map.
172 |
173 | Example array response
174 | ``` javascript
175 | ["upper_25","upper_50","upper_75","upper_90","upper_95"]
176 | ```
177 |
178 | Example map response
179 | ``` javascript
180 | [ { "text" :"upper_25", "value": 1}, { "text" :"upper_75", "value": 2} ]
181 | ```
182 |
183 | ### Tag Keys API
184 |
185 | Example request
186 | ``` javascript
187 | { }
188 | ```
189 |
190 | The tag keys api returns:
191 | ```javascript
192 | [
193 | {"type":"string","text":"City"},
194 | {"type":"string","text":"Country"}
195 | ]
196 | ```
197 |
198 | ### Tag Values API
199 |
200 | Example request
201 | ``` javascript
202 | {"key": "City"}
203 | ```
204 |
205 | The tag values api returns:
206 | ```javascript
207 | [
208 | {'text': 'Eins!'},
209 | {'text': 'Zwei'},
210 | {'text': 'Drei!'}
211 | ]
212 | ```
213 |
214 | ### Dev setup
215 |
216 | This plugin requires node 6.10.0
217 |
218 | ```
219 | npm install -g yarn
220 | yarn install
221 | npm run build
222 | ```
223 |
224 | ### Changelog
225 |
226 | 1.4.1
227 | - Fix for query editor to be compatible with Grafana 7+ (broke due to change in Grafana).
228 |
229 | 1.4.0
230 | - Support for adhoc filters:
231 | - added tag-keys + tag-values api
232 | - added adHocFilters parameter to query body
233 |
234 | 1.3.5
235 | - Fix for dropdowns in query editor to allow writing template variables (broke due to change in Grafana).
236 |
237 | 1.3.4
238 | - Adds support for With Credentials (sends grafana cookies with request) when using Direct mode
239 | - Fix for the typeahead component for metrics dropdown (`/search` endpoint).
240 |
241 | 1.3.3
242 | - Adds support for basic authentication
243 |
244 | 1.2.4
245 | - Add support returning sets in the search endpoint
246 |
247 | 1.2.3
248 | - Allow nested templates in find metric query. #23
249 |
250 | 1.2.2
251 | - Dont execute hidden queries
252 | - Template support for metrics queries
253 | - Template support for annotation queries
254 |
255 | ### If using Grafana 2.6
256 | NOTE!
257 | for grafana 2.6 please use [this version](https://github.com/grafana/simple-json-datasource/commit/b78720f6e00c115203d8f4c0e81ccd3c16001f94)
258 |
259 | Copy the data source you want to `/public/app/plugins/datasource/`. Then restart grafana-server. The new data source should now be available in the data source type dropdown in the Add Data Source View.
260 |
--------------------------------------------------------------------------------
/dist/README.md:
--------------------------------------------------------------------------------
1 | # Deprecated plugin. Use [Infinity data source plugin](https://grafana.com/grafana/plugins/yesoreyeram-infinity-datasource/) instead
2 |
3 | ## Project status
4 |
5 | > [!CAUTION]
6 | > This plugin is now **DEPRECATED** and **no longer maintained** by the Grafana team. You can use [Grafana Infinity data source plugin](https://grafana.com/grafana/plugins/yesoreyeram-infinity-datasource/) as an alternative for connecting to JSON/CSV/XML/GraphQL endpoints. Refer the migration guide [here](https://github.com/grafana/grafana-infinity-datasource/discussions/740), if you still prefer connecting to existing grafana simple JSON backend server implementation. This deprecation means that this plugin won't receive any feature updates or bug fixes. After 6 months (End of June 2024), the plugin will reach EOL and the repository will be archived. If you are looking for building your own plugin, consider the examples [here](https://github.com/grafana/grafana-plugin-examples) and documentation [here](https://grafana.com/developers).
7 |
8 | ## Simple JSON Datasource - a generic backend datasource
9 |
10 | You can find more documentation about datasource plugins in Grafana's [Docs](https://grafana.com/docs/grafana/latest/developers/plugins/).
11 |
12 | This also serves as a living example implementation of a datasource.
13 |
14 | Your backend needs to implement 4 urls:
15 |
16 | * `/` should return 200 ok. Used for "Test connection" on the datasource config page.
17 | * `/search` used by the find metric options on the query tab in panels.
18 | * `/query` should return metrics based on input.
19 | * `/annotations` should return annotations.
20 |
21 | Those two urls are optional:
22 |
23 | * `/tag-keys` should return tag keys for ad hoc filters.
24 | * `/tag-values` should return tag values for ad hoc filters.
25 |
26 | ## Installation
27 |
28 | To install this plugin using the `grafana-cli` tool:
29 | ```
30 | sudo grafana-cli plugins install grafana-simple-json-datasource
31 | sudo service grafana-server restart
32 | ```
33 | See [here](https://grafana.com/plugins/grafana-simple-json-datasource/installation) for more
34 | information.
35 |
36 | ### Example backend implementations
37 | - https://github.com/bergquist/fake-simple-json-datasource
38 | - https://github.com/smcquay/jsonds
39 | - https://github.com/ContextLogic/eventmaster
40 | - https://gist.github.com/linar-jether/95ff412f9d19fdf5e51293eb0c09b850 (Python/pandas backend)
41 |
42 | ### Query API
43 |
44 | Example `timeserie` request
45 | ``` javascript
46 | {
47 | "panelId": 1,
48 | "range": {
49 | "from": "2016-10-31T06:33:44.866Z",
50 | "to": "2016-10-31T12:33:44.866Z",
51 | "raw": {
52 | "from": "now-6h",
53 | "to": "now"
54 | }
55 | },
56 | "rangeRaw": {
57 | "from": "now-6h",
58 | "to": "now"
59 | },
60 | "interval": "30s",
61 | "intervalMs": 30000,
62 | "targets": [
63 | { "target": "upper_50", "refId": "A", "type": "timeserie" },
64 | { "target": "upper_75", "refId": "B", "type": "timeserie" }
65 | ],
66 | "adhocFilters": [{
67 | "key": "City",
68 | "operator": "=",
69 | "value": "Berlin"
70 | }],
71 | "format": "json",
72 | "maxDataPoints": 550
73 | }
74 | ```
75 |
76 | Example `timeserie` response
77 | ``` javascript
78 | [
79 | {
80 | "target":"upper_75", // The field being queried for
81 | "datapoints":[
82 | [622,1450754160000], // Metric value as a float , unixtimestamp in milliseconds
83 | [365,1450754220000]
84 | ]
85 | },
86 | {
87 | "target":"upper_90",
88 | "datapoints":[
89 | [861,1450754160000],
90 | [767,1450754220000]
91 | ]
92 | }
93 | ]
94 | ```
95 |
96 | If the metric selected is `"type": "table"`, an example `table` response:
97 | ``` json
98 | [
99 | {
100 | "columns":[
101 | {"text":"Time","type":"time"},
102 | {"text":"Country","type":"string"},
103 | {"text":"Number","type":"number"}
104 | ],
105 | "rows":[
106 | [1234567,"SE",123],
107 | [1234567,"DE",231],
108 | [1234567,"US",321]
109 | ],
110 | "type":"table"
111 | }
112 | ]
113 | ```
114 |
115 | ### Annotation API
116 |
117 | The annotation request from the Simple JSON Datasource is a POST request to
118 | the `/annotations` endpoint in your datasource. The JSON request body looks like this:
119 | ``` javascript
120 | {
121 | "range": {
122 | "from": "2016-04-15T13:44:39.070Z",
123 | "to": "2016-04-15T14:44:39.070Z"
124 | },
125 | "rangeRaw": {
126 | "from": "now-1h",
127 | "to": "now"
128 | },
129 | "annotation": {
130 | "name": "deploy",
131 | "datasource": "Simple JSON Datasource",
132 | "iconColor": "rgba(255, 96, 96, 1)",
133 | "enable": true,
134 | "query": "#deploy"
135 | }
136 | }
137 | ```
138 |
139 | Grafana expects a response containing an array of annotation objects in the
140 | following format:
141 |
142 | ``` javascript
143 | [
144 | {
145 | annotation: annotation, // The original annotation sent from Grafana.
146 | time: time, // Time since UNIX Epoch in milliseconds. (required)
147 | title: title, // The title for the annotation tooltip. (required)
148 | tags: tags, // Tags for the annotation. (optional)
149 | text: text // Text for the annotation. (optional)
150 | }
151 | ]
152 | ```
153 |
154 | Note: If the datasource is configured to connect directly to the backend, you
155 | also need to implement an OPTIONS endpoint at `/annotations` that responds
156 | with the correct CORS headers:
157 |
158 | ```
159 | Access-Control-Allow-Headers:accept, content-type
160 | Access-Control-Allow-Methods:POST
161 | Access-Control-Allow-Origin:*
162 | ```
163 |
164 | ### Search API
165 |
166 | Example request
167 | ``` javascript
168 | { target: 'upper_50' }
169 | ```
170 |
171 | The search api can either return an array or map.
172 |
173 | Example array response
174 | ``` javascript
175 | ["upper_25","upper_50","upper_75","upper_90","upper_95"]
176 | ```
177 |
178 | Example map response
179 | ``` javascript
180 | [ { "text" :"upper_25", "value": 1}, { "text" :"upper_75", "value": 2} ]
181 | ```
182 |
183 | ### Tag Keys API
184 |
185 | Example request
186 | ``` javascript
187 | { }
188 | ```
189 |
190 | The tag keys api returns:
191 | ```javascript
192 | [
193 | {"type":"string","text":"City"},
194 | {"type":"string","text":"Country"}
195 | ]
196 | ```
197 |
198 | ### Tag Values API
199 |
200 | Example request
201 | ``` javascript
202 | {"key": "City"}
203 | ```
204 |
205 | The tag values api returns:
206 | ```javascript
207 | [
208 | {'text': 'Eins!'},
209 | {'text': 'Zwei'},
210 | {'text': 'Drei!'}
211 | ]
212 | ```
213 |
214 | ### Dev setup
215 |
216 | This plugin requires node 6.10.0
217 |
218 | ```
219 | npm install -g yarn
220 | yarn install
221 | npm run build
222 | ```
223 |
224 | ### Changelog
225 |
226 | 1.4.1
227 | - Fix for query editor to be compatible with Grafana 7+ (broke due to change in Grafana).
228 |
229 | 1.4.0
230 | - Support for adhoc filters:
231 | - added tag-keys + tag-values api
232 | - added adHocFilters parameter to query body
233 |
234 | 1.3.5
235 | - Fix for dropdowns in query editor to allow writing template variables (broke due to change in Grafana).
236 |
237 | 1.3.4
238 | - Adds support for With Credentials (sends grafana cookies with request) when using Direct mode
239 | - Fix for the typeahead component for metrics dropdown (`/search` endpoint).
240 |
241 | 1.3.3
242 | - Adds support for basic authentication
243 |
244 | 1.2.4
245 | - Add support returning sets in the search endpoint
246 |
247 | 1.2.3
248 | - Allow nested templates in find metric query. #23
249 |
250 | 1.2.2
251 | - Dont execute hidden queries
252 | - Template support for metrics queries
253 | - Template support for annotation queries
254 |
255 | ### If using Grafana 2.6
256 | NOTE!
257 | for grafana 2.6 please use [this version](https://github.com/grafana/simple-json-datasource/commit/b78720f6e00c115203d8f4c0e81ccd3c16001f94)
258 |
259 | Copy the data source you want to `/public/app/plugins/datasource/`. Then restart grafana-server. The new data source should now be available in the data source type dropdown in the Add Data Source View.
260 |
--------------------------------------------------------------------------------
/dist/css/query-editor.css:
--------------------------------------------------------------------------------
1 | .generic-datasource-query-row .query-keyword {
2 | width: 75px;
3 | }
--------------------------------------------------------------------------------
/dist/datasource.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.GenericDatasource = undefined;
7 |
8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
9 |
10 | var _lodash = require('lodash');
11 |
12 | var _lodash2 = _interopRequireDefault(_lodash);
13 |
14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15 |
16 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
17 |
18 | var GenericDatasource = exports.GenericDatasource = function () {
19 | function GenericDatasource(instanceSettings, $q, backendSrv, templateSrv) {
20 | _classCallCheck(this, GenericDatasource);
21 |
22 | this.type = instanceSettings.type;
23 | this.url = instanceSettings.url;
24 | this.name = instanceSettings.name;
25 | this.q = $q;
26 | this.backendSrv = backendSrv;
27 | this.templateSrv = templateSrv;
28 | this.withCredentials = instanceSettings.withCredentials;
29 | this.headers = { 'Content-Type': 'application/json' };
30 | if (typeof instanceSettings.basicAuth === 'string' && instanceSettings.basicAuth.length > 0) {
31 | this.headers['Authorization'] = instanceSettings.basicAuth;
32 | }
33 | }
34 |
35 | _createClass(GenericDatasource, [{
36 | key: 'query',
37 | value: function query(options) {
38 | var query = this.buildQueryParameters(options);
39 | query.targets = query.targets.filter(function (t) {
40 | return !t.hide;
41 | });
42 |
43 | if (query.targets.length <= 0) {
44 | return this.q.when({ data: [] });
45 | }
46 |
47 | if (this.templateSrv.getAdhocFilters) {
48 | query.adhocFilters = this.templateSrv.getAdhocFilters(this.name);
49 | } else {
50 | query.adhocFilters = [];
51 | }
52 |
53 | return this.doRequest({
54 | url: this.url + '/query',
55 | data: query,
56 | method: 'POST'
57 | });
58 | }
59 | }, {
60 | key: 'testDatasource',
61 | value: function testDatasource() {
62 | return this.doRequest({
63 | url: this.url + '/',
64 | method: 'GET'
65 | }).then(function (response) {
66 | if (response.status === 200) {
67 | return { status: "success", message: "Data source is working", title: "Success" };
68 | }
69 | });
70 | }
71 | }, {
72 | key: 'annotationQuery',
73 | value: function annotationQuery(options) {
74 | var query = this.templateSrv.replace(options.annotation.query, {}, 'glob');
75 | var annotationQuery = {
76 | range: options.range,
77 | annotation: {
78 | name: options.annotation.name,
79 | datasource: options.annotation.datasource,
80 | enable: options.annotation.enable,
81 | iconColor: options.annotation.iconColor,
82 | query: query
83 | },
84 | rangeRaw: options.rangeRaw
85 | };
86 |
87 | return this.doRequest({
88 | url: this.url + '/annotations',
89 | method: 'POST',
90 | data: annotationQuery
91 | }).then(function (result) {
92 | return result.data;
93 | });
94 | }
95 | }, {
96 | key: 'metricFindQuery',
97 | value: function metricFindQuery(query) {
98 | var interpolated = {
99 | target: this.templateSrv.replace(query, null, 'regex')
100 | };
101 |
102 | return this.doRequest({
103 | url: this.url + '/search',
104 | data: interpolated,
105 | method: 'POST'
106 | }).then(this.mapToTextValue);
107 | }
108 | }, {
109 | key: 'mapToTextValue',
110 | value: function mapToTextValue(result) {
111 | return _lodash2.default.map(result.data, function (d, i) {
112 | if (d && d.text && d.value) {
113 | return { text: d.text, value: d.value };
114 | } else if (_lodash2.default.isObject(d)) {
115 | return { text: d, value: i };
116 | }
117 | return { text: d, value: d };
118 | });
119 | }
120 | }, {
121 | key: 'doRequest',
122 | value: function doRequest(options) {
123 | options.withCredentials = this.withCredentials;
124 | options.headers = this.headers;
125 |
126 | return this.backendSrv.datasourceRequest(options);
127 | }
128 | }, {
129 | key: 'buildQueryParameters',
130 | value: function buildQueryParameters(options) {
131 | var _this = this;
132 |
133 | //remove placeholder targets
134 | options.targets = _lodash2.default.filter(options.targets, function (target) {
135 | return target.target !== 'select metric';
136 | });
137 |
138 | var targets = _lodash2.default.map(options.targets, function (target) {
139 | return {
140 | target: _this.templateSrv.replace(target.target, options.scopedVars, 'regex'),
141 | refId: target.refId,
142 | hide: target.hide,
143 | type: target.type || 'timeserie'
144 | };
145 | });
146 |
147 | options.targets = targets;
148 |
149 | return options;
150 | }
151 | }, {
152 | key: 'getTagKeys',
153 | value: function getTagKeys(options) {
154 | var _this2 = this;
155 |
156 | return new Promise(function (resolve, reject) {
157 | _this2.doRequest({
158 | url: _this2.url + '/tag-keys',
159 | method: 'POST',
160 | data: options
161 | }).then(function (result) {
162 | return resolve(result.data);
163 | });
164 | });
165 | }
166 | }, {
167 | key: 'getTagValues',
168 | value: function getTagValues(options) {
169 | var _this3 = this;
170 |
171 | return new Promise(function (resolve, reject) {
172 | _this3.doRequest({
173 | url: _this3.url + '/tag-values',
174 | method: 'POST',
175 | data: options
176 | }).then(function (result) {
177 | return resolve(result.data);
178 | });
179 | });
180 | }
181 | }]);
182 |
183 | return GenericDatasource;
184 | }();
185 | //# sourceMappingURL=datasource.js.map
186 |
--------------------------------------------------------------------------------
/dist/datasource.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/datasource.js"],"names":["GenericDatasource","instanceSettings","$q","backendSrv","templateSrv","type","url","name","q","withCredentials","headers","basicAuth","length","options","query","buildQueryParameters","targets","filter","t","hide","when","data","getAdhocFilters","adhocFilters","doRequest","method","then","response","status","message","title","replace","annotation","annotationQuery","range","datasource","enable","iconColor","rangeRaw","result","interpolated","target","mapToTextValue","_","map","d","i","text","value","isObject","datasourceRequest","scopedVars","refId","Promise","resolve","reject"],"mappings":";;;;;;;;;AAAA;;;;;;;;IAEaA,iB,WAAAA,iB;AAEX,6BAAYC,gBAAZ,EAA8BC,EAA9B,EAAkCC,UAAlC,EAA8CC,WAA9C,EAA2D;AAAA;;AACzD,SAAKC,IAAL,GAAYJ,iBAAiBI,IAA7B;AACA,SAAKC,GAAL,GAAWL,iBAAiBK,GAA5B;AACA,SAAKC,IAAL,GAAYN,iBAAiBM,IAA7B;AACA,SAAKC,CAAL,GAASN,EAAT;AACA,SAAKC,UAAL,GAAkBA,UAAlB;AACA,SAAKC,WAAL,GAAmBA,WAAnB;AACA,SAAKK,eAAL,GAAuBR,iBAAiBQ,eAAxC;AACA,SAAKC,OAAL,GAAe,EAAC,gBAAgB,kBAAjB,EAAf;AACA,QAAI,OAAOT,iBAAiBU,SAAxB,KAAsC,QAAtC,IAAkDV,iBAAiBU,SAAjB,CAA2BC,MAA3B,GAAoC,CAA1F,EAA6F;AAC3F,WAAKF,OAAL,CAAa,eAAb,IAAgCT,iBAAiBU,SAAjD;AACD;AACF;;;;0BAEKE,O,EAAS;AACb,UAAIC,QAAQ,KAAKC,oBAAL,CAA0BF,OAA1B,CAAZ;AACAC,YAAME,OAAN,GAAgBF,MAAME,OAAN,CAAcC,MAAd,CAAqB;AAAA,eAAK,CAACC,EAAEC,IAAR;AAAA,OAArB,CAAhB;;AAEA,UAAIL,MAAME,OAAN,CAAcJ,MAAd,IAAwB,CAA5B,EAA+B;AAC7B,eAAO,KAAKJ,CAAL,CAAOY,IAAP,CAAY,EAACC,MAAM,EAAP,EAAZ,CAAP;AACD;;AAED,UAAI,KAAKjB,WAAL,CAAiBkB,eAArB,EAAsC;AACpCR,cAAMS,YAAN,GAAqB,KAAKnB,WAAL,CAAiBkB,eAAjB,CAAiC,KAAKf,IAAtC,CAArB;AACD,OAFD,MAEO;AACLO,cAAMS,YAAN,GAAqB,EAArB;AACD;;AAED,aAAO,KAAKC,SAAL,CAAe;AACpBlB,aAAK,KAAKA,GAAL,GAAW,QADI;AAEpBe,cAAMP,KAFc;AAGpBW,gBAAQ;AAHY,OAAf,CAAP;AAKD;;;qCAEgB;AACf,aAAO,KAAKD,SAAL,CAAe;AACpBlB,aAAK,KAAKA,GAAL,GAAW,GADI;AAEpBmB,gBAAQ;AAFY,OAAf,EAGJC,IAHI,CAGC,oBAAY;AAClB,YAAIC,SAASC,MAAT,KAAoB,GAAxB,EAA6B;AAC3B,iBAAO,EAAEA,QAAQ,SAAV,EAAqBC,SAAS,wBAA9B,EAAwDC,OAAO,SAA/D,EAAP;AACD;AACF,OAPM,CAAP;AAQD;;;oCAEejB,O,EAAS;AACvB,UAAIC,QAAQ,KAAKV,WAAL,CAAiB2B,OAAjB,CAAyBlB,QAAQmB,UAAR,CAAmBlB,KAA5C,EAAmD,EAAnD,EAAuD,MAAvD,CAAZ;AACA,UAAImB,kBAAkB;AACpBC,eAAOrB,QAAQqB,KADK;AAEpBF,oBAAY;AACVzB,gBAAMM,QAAQmB,UAAR,CAAmBzB,IADf;AAEV4B,sBAAYtB,QAAQmB,UAAR,CAAmBG,UAFrB;AAGVC,kBAAQvB,QAAQmB,UAAR,CAAmBI,MAHjB;AAIVC,qBAAWxB,QAAQmB,UAAR,CAAmBK,SAJpB;AAKVvB,iBAAOA;AALG,SAFQ;AASpBwB,kBAAUzB,QAAQyB;AATE,OAAtB;;AAYA,aAAO,KAAKd,SAAL,CAAe;AACpBlB,aAAK,KAAKA,GAAL,GAAW,cADI;AAEpBmB,gBAAQ,MAFY;AAGpBJ,cAAMY;AAHc,OAAf,EAIJP,IAJI,CAIC,kBAAU;AAChB,eAAOa,OAAOlB,IAAd;AACD,OANM,CAAP;AAOD;;;oCAEeP,K,EAAO;AACrB,UAAI0B,eAAe;AACfC,gBAAQ,KAAKrC,WAAL,CAAiB2B,OAAjB,CAAyBjB,KAAzB,EAAgC,IAAhC,EAAsC,OAAtC;AADO,OAAnB;;AAIA,aAAO,KAAKU,SAAL,CAAe;AACpBlB,aAAK,KAAKA,GAAL,GAAW,SADI;AAEpBe,cAAMmB,YAFc;AAGpBf,gBAAQ;AAHY,OAAf,EAIJC,IAJI,CAIC,KAAKgB,cAJN,CAAP;AAKD;;;mCAEcH,M,EAAQ;AACrB,aAAOI,iBAAEC,GAAF,CAAML,OAAOlB,IAAb,EAAmB,UAACwB,CAAD,EAAIC,CAAJ,EAAU;AAClC,YAAID,KAAKA,EAAEE,IAAP,IAAeF,EAAEG,KAArB,EAA4B;AAC1B,iBAAO,EAAED,MAAMF,EAAEE,IAAV,EAAgBC,OAAOH,EAAEG,KAAzB,EAAP;AACD,SAFD,MAEO,IAAIL,iBAAEM,QAAF,CAAWJ,CAAX,CAAJ,EAAmB;AACxB,iBAAO,EAAEE,MAAMF,CAAR,EAAWG,OAAOF,CAAlB,EAAP;AACD;AACD,eAAO,EAAEC,MAAMF,CAAR,EAAWG,OAAOH,CAAlB,EAAP;AACD,OAPM,CAAP;AAQD;;;8BAEShC,O,EAAS;AACjBA,cAAQJ,eAAR,GAA0B,KAAKA,eAA/B;AACAI,cAAQH,OAAR,GAAkB,KAAKA,OAAvB;;AAEA,aAAO,KAAKP,UAAL,CAAgB+C,iBAAhB,CAAkCrC,OAAlC,CAAP;AACD;;;yCAEoBA,O,EAAS;AAAA;;AAC5B;AACAA,cAAQG,OAAR,GAAkB2B,iBAAE1B,MAAF,CAASJ,QAAQG,OAAjB,EAA0B,kBAAU;AACpD,eAAOyB,OAAOA,MAAP,KAAkB,eAAzB;AACD,OAFiB,CAAlB;;AAIA,UAAIzB,UAAU2B,iBAAEC,GAAF,CAAM/B,QAAQG,OAAd,EAAuB,kBAAU;AAC7C,eAAO;AACLyB,kBAAQ,MAAKrC,WAAL,CAAiB2B,OAAjB,CAAyBU,OAAOA,MAAhC,EAAwC5B,QAAQsC,UAAhD,EAA4D,OAA5D,CADH;AAELC,iBAAOX,OAAOW,KAFT;AAGLjC,gBAAMsB,OAAOtB,IAHR;AAILd,gBAAMoC,OAAOpC,IAAP,IAAe;AAJhB,SAAP;AAMD,OAPa,CAAd;;AASAQ,cAAQG,OAAR,GAAkBA,OAAlB;;AAEA,aAAOH,OAAP;AACD;;;+BAEUA,O,EAAS;AAAA;;AAClB,aAAO,IAAIwC,OAAJ,CAAY,UAACC,OAAD,EAAUC,MAAV,EAAqB;AACtC,eAAK/B,SAAL,CAAe;AACblB,eAAK,OAAKA,GAAL,GAAW,WADH;AAEbmB,kBAAQ,MAFK;AAGbJ,gBAAMR;AAHO,SAAf,EAIGa,IAJH,CAIQ,kBAAU;AAChB,iBAAO4B,QAAQf,OAAOlB,IAAf,CAAP;AACD,SAND;AAOD,OARM,CAAP;AASD;;;iCAEYR,O,EAAS;AAAA;;AACpB,aAAO,IAAIwC,OAAJ,CAAY,UAACC,OAAD,EAAUC,MAAV,EAAqB;AACtC,eAAK/B,SAAL,CAAe;AACblB,eAAK,OAAKA,GAAL,GAAW,aADH;AAEbmB,kBAAQ,MAFK;AAGbJ,gBAAMR;AAHO,SAAf,EAIGa,IAJH,CAIQ,kBAAU;AAChB,iBAAO4B,QAAQf,OAAOlB,IAAf,CAAP;AACD,SAND;AAOD,OARM,CAAP;AASD","file":"datasource.js","sourcesContent":["import _ from \"lodash\";\n\nexport class GenericDatasource {\n\n constructor(instanceSettings, $q, backendSrv, templateSrv) {\n this.type = instanceSettings.type;\n this.url = instanceSettings.url;\n this.name = instanceSettings.name;\n this.q = $q;\n this.backendSrv = backendSrv;\n this.templateSrv = templateSrv;\n this.withCredentials = instanceSettings.withCredentials;\n this.headers = {'Content-Type': 'application/json'};\n if (typeof instanceSettings.basicAuth === 'string' && instanceSettings.basicAuth.length > 0) {\n this.headers['Authorization'] = instanceSettings.basicAuth;\n }\n }\n\n query(options) {\n var query = this.buildQueryParameters(options);\n query.targets = query.targets.filter(t => !t.hide);\n\n if (query.targets.length <= 0) {\n return this.q.when({data: []});\n }\n\n if (this.templateSrv.getAdhocFilters) {\n query.adhocFilters = this.templateSrv.getAdhocFilters(this.name);\n } else {\n query.adhocFilters = [];\n }\n\n return this.doRequest({\n url: this.url + '/query',\n data: query,\n method: 'POST'\n });\n }\n\n testDatasource() {\n return this.doRequest({\n url: this.url + '/',\n method: 'GET',\n }).then(response => {\n if (response.status === 200) {\n return { status: \"success\", message: \"Data source is working\", title: \"Success\" };\n }\n });\n }\n\n annotationQuery(options) {\n var query = this.templateSrv.replace(options.annotation.query, {}, 'glob');\n var annotationQuery = {\n range: options.range,\n annotation: {\n name: options.annotation.name,\n datasource: options.annotation.datasource,\n enable: options.annotation.enable,\n iconColor: options.annotation.iconColor,\n query: query\n },\n rangeRaw: options.rangeRaw\n };\n\n return this.doRequest({\n url: this.url + '/annotations',\n method: 'POST',\n data: annotationQuery\n }).then(result => {\n return result.data;\n });\n }\n\n metricFindQuery(query) {\n var interpolated = {\n target: this.templateSrv.replace(query, null, 'regex')\n };\n\n return this.doRequest({\n url: this.url + '/search',\n data: interpolated,\n method: 'POST',\n }).then(this.mapToTextValue);\n }\n\n mapToTextValue(result) {\n return _.map(result.data, (d, i) => {\n if (d && d.text && d.value) {\n return { text: d.text, value: d.value };\n } else if (_.isObject(d)) {\n return { text: d, value: i};\n }\n return { text: d, value: d };\n });\n }\n\n doRequest(options) {\n options.withCredentials = this.withCredentials;\n options.headers = this.headers;\n\n return this.backendSrv.datasourceRequest(options);\n }\n\n buildQueryParameters(options) {\n //remove placeholder targets\n options.targets = _.filter(options.targets, target => {\n return target.target !== 'select metric';\n });\n\n var targets = _.map(options.targets, target => {\n return {\n target: this.templateSrv.replace(target.target, options.scopedVars, 'regex'),\n refId: target.refId,\n hide: target.hide,\n type: target.type || 'timeserie'\n };\n });\n\n options.targets = targets;\n\n return options;\n }\n\n getTagKeys(options) {\n return new Promise((resolve, reject) => {\n this.doRequest({\n url: this.url + '/tag-keys',\n method: 'POST',\n data: options\n }).then(result => {\n return resolve(result.data);\n });\n });\n }\n\n getTagValues(options) {\n return new Promise((resolve, reject) => {\n this.doRequest({\n url: this.url + '/tag-values',\n method: 'POST',\n data: options\n }).then(result => {\n return resolve(result.data);\n });\n });\n }\n\n}\n"]}
--------------------------------------------------------------------------------
/dist/img/simpleJson_logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
636 |
--------------------------------------------------------------------------------
/dist/module.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.AnnotationsQueryCtrl = exports.QueryOptionsCtrl = exports.ConfigCtrl = exports.QueryCtrl = exports.Datasource = undefined;
7 |
8 | var _datasource = require('./datasource');
9 |
10 | var _query_ctrl = require('./query_ctrl');
11 |
12 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
13 |
14 | var GenericConfigCtrl = function GenericConfigCtrl() {
15 | _classCallCheck(this, GenericConfigCtrl);
16 | };
17 |
18 | GenericConfigCtrl.templateUrl = 'partials/config.html';
19 |
20 | var GenericQueryOptionsCtrl = function GenericQueryOptionsCtrl() {
21 | _classCallCheck(this, GenericQueryOptionsCtrl);
22 | };
23 |
24 | GenericQueryOptionsCtrl.templateUrl = 'partials/query.options.html';
25 |
26 | var GenericAnnotationsQueryCtrl = function GenericAnnotationsQueryCtrl() {
27 | _classCallCheck(this, GenericAnnotationsQueryCtrl);
28 | };
29 |
30 | GenericAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';
31 |
32 | exports.Datasource = _datasource.GenericDatasource;
33 | exports.QueryCtrl = _query_ctrl.GenericDatasourceQueryCtrl;
34 | exports.ConfigCtrl = GenericConfigCtrl;
35 | exports.QueryOptionsCtrl = GenericQueryOptionsCtrl;
36 | exports.AnnotationsQueryCtrl = GenericAnnotationsQueryCtrl;
37 | //# sourceMappingURL=module.js.map
38 |
--------------------------------------------------------------------------------
/dist/module.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/module.js"],"names":["GenericConfigCtrl","templateUrl","GenericQueryOptionsCtrl","GenericAnnotationsQueryCtrl","Datasource","GenericDatasource","QueryCtrl","GenericDatasourceQueryCtrl","ConfigCtrl","QueryOptionsCtrl","AnnotationsQueryCtrl"],"mappings":";;;;;;;AAAA;;AACA;;;;IAEMA,iB;;;;AACNA,kBAAkBC,WAAlB,GAAgC,sBAAhC;;IAEMC,uB;;;;AACNA,wBAAwBD,WAAxB,GAAsC,6BAAtC;;IAEME,2B;;;;AACNA,4BAA4BF,WAA5B,GAA0C,kCAA1C;;QAGuBG,U,GAArBC,6B;QAC8BC,S,GAA9BC,sC;QACqBC,U,GAArBR,iB;QAC2BS,gB,GAA3BP,uB;QAC+BQ,oB,GAA/BP,2B","file":"module.js","sourcesContent":["import {GenericDatasource} from './datasource';\nimport {GenericDatasourceQueryCtrl} from './query_ctrl';\n\nclass GenericConfigCtrl {}\nGenericConfigCtrl.templateUrl = 'partials/config.html';\n\nclass GenericQueryOptionsCtrl {}\nGenericQueryOptionsCtrl.templateUrl = 'partials/query.options.html';\n\nclass GenericAnnotationsQueryCtrl {}\nGenericAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html'\n\nexport {\n GenericDatasource as Datasource,\n GenericDatasourceQueryCtrl as QueryCtrl,\n GenericConfigCtrl as ConfigCtrl,\n GenericQueryOptionsCtrl as QueryOptionsCtrl,\n GenericAnnotationsQueryCtrl as AnnotationsQueryCtrl\n};\n"]}
--------------------------------------------------------------------------------
/dist/partials/annotations.editor.html:
--------------------------------------------------------------------------------
1 |
2 | Query
3 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/dist/partials/config.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/dist/partials/query.editor.html:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
--------------------------------------------------------------------------------
/dist/partials/query.options.html:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/dist/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SimpleJson",
3 | "id": "grafana-simple-json-datasource",
4 | "type": "datasource",
5 |
6 | "partials": {
7 | "config": "public/app/plugins/datasource/simplejson/partials/config.html"
8 | },
9 |
10 | "metrics": true,
11 | "annotations": true,
12 |
13 | "info": {
14 | "description": "simple json datasource",
15 | "author": {
16 | "name": "Grafana Labs",
17 | "url": "https://grafana.com"
18 | },
19 | "logos": {
20 | "small": "img/simpleJson_logo.svg",
21 | "large": "img/simpleJson_logo.svg"
22 | },
23 | "links": [
24 | {"name": "GitHub", "url": "https://github.com/grafana/simple-json-datasource"},
25 | {"name": "MIT License", "url": "https://github.com/grafana/simple-json-datasource/blob/master/LICENSE"}
26 | ],
27 | "version": "1.4.3",
28 | "updated": "2024-01-15"
29 | },
30 |
31 | "dependencies": {
32 | "grafanaVersion": "3.x.x",
33 | "plugins": [ ]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/dist/query_ctrl.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.GenericDatasourceQueryCtrl = undefined;
7 |
8 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
9 |
10 | var _sdk = require('app/plugins/sdk');
11 |
12 | require('./css/query-editor.css!');
13 |
14 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
15 |
16 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
17 |
18 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
19 |
20 | var GenericDatasourceQueryCtrl = exports.GenericDatasourceQueryCtrl = function (_QueryCtrl) {
21 | _inherits(GenericDatasourceQueryCtrl, _QueryCtrl);
22 |
23 | function GenericDatasourceQueryCtrl($scope, $injector) {
24 | _classCallCheck(this, GenericDatasourceQueryCtrl);
25 |
26 | var _this = _possibleConstructorReturn(this, (GenericDatasourceQueryCtrl.__proto__ || Object.getPrototypeOf(GenericDatasourceQueryCtrl)).call(this, $scope, $injector));
27 |
28 | _this.scope = $scope;
29 | _this.target.target = _this.target.target || 'select metric';
30 | _this.target.type = _this.target.type || 'timeserie';
31 | return _this;
32 | }
33 |
34 | _createClass(GenericDatasourceQueryCtrl, [{
35 | key: 'getOptions',
36 | value: function getOptions(query) {
37 | return this.datasource.metricFindQuery(query || '');
38 | }
39 | }, {
40 | key: 'toggleEditorMode',
41 | value: function toggleEditorMode() {
42 | this.target.rawQuery = !this.target.rawQuery;
43 | }
44 | }, {
45 | key: 'onChangeInternal',
46 | value: function onChangeInternal() {
47 | this.panelCtrl.refresh(); // Asks the panel to refresh data.
48 | }
49 | }]);
50 |
51 | return GenericDatasourceQueryCtrl;
52 | }(_sdk.QueryCtrl);
53 |
54 | GenericDatasourceQueryCtrl.templateUrl = 'partials/query.editor.html';
55 | //# sourceMappingURL=query_ctrl.js.map
56 |
--------------------------------------------------------------------------------
/dist/query_ctrl.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/query_ctrl.js"],"names":["GenericDatasourceQueryCtrl","$scope","$injector","scope","target","type","query","datasource","metricFindQuery","rawQuery","panelCtrl","refresh","QueryCtrl","templateUrl"],"mappings":";;;;;;;;;AAAA;;AACA;;;;;;;;IAEaA,0B,WAAAA,0B;;;AAEX,sCAAYC,MAAZ,EAAoBC,SAApB,EAAgC;AAAA;;AAAA,wJACxBD,MADwB,EAChBC,SADgB;;AAG9B,UAAKC,KAAL,GAAaF,MAAb;AACA,UAAKG,MAAL,CAAYA,MAAZ,GAAqB,MAAKA,MAAL,CAAYA,MAAZ,IAAsB,eAA3C;AACA,UAAKA,MAAL,CAAYC,IAAZ,GAAmB,MAAKD,MAAL,CAAYC,IAAZ,IAAoB,WAAvC;AAL8B;AAM/B;;;;+BAEUC,K,EAAO;AAChB,aAAO,KAAKC,UAAL,CAAgBC,eAAhB,CAAgCF,SAAS,EAAzC,CAAP;AACD;;;uCAEkB;AACjB,WAAKF,MAAL,CAAYK,QAAZ,GAAuB,CAAC,KAAKL,MAAL,CAAYK,QAApC;AACD;;;uCAEkB;AACjB,WAAKC,SAAL,CAAeC,OAAf,GADiB,CACS;AAC3B;;;;EApB6CC,c;;AAuBhDZ,2BAA2Ba,WAA3B,GAAyC,4BAAzC","file":"query_ctrl.js","sourcesContent":["import {QueryCtrl} from 'app/plugins/sdk';\nimport './css/query-editor.css!'\n\nexport class GenericDatasourceQueryCtrl extends QueryCtrl {\n\n constructor($scope, $injector) {\n super($scope, $injector);\n\n this.scope = $scope;\n this.target.target = this.target.target || 'select metric';\n this.target.type = this.target.type || 'timeserie';\n }\n\n getOptions(query) {\n return this.datasource.metricFindQuery(query || '');\n }\n\n toggleEditorMode() {\n this.target.rawQuery = !this.target.rawQuery;\n }\n\n onChangeInternal() {\n this.panelCtrl.refresh(); // Asks the panel to refresh data.\n }\n}\n\nGenericDatasourceQueryCtrl.templateUrl = 'partials/query.editor.html';\n\n"]}
--------------------------------------------------------------------------------
/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: '3.7'
2 |
3 | services:
4 | server:
5 | build: ./server
6 | container_name: server
7 | ports:
8 | - 3333:3333
9 | grafana:
10 | container_name: grafana-simple-json-datasource
11 | platform: linux/amd64
12 | image: grafana/grafana-enterprise:${GF_VERSION:-main}
13 | ports:
14 | - 3000:3000/tcp
15 | volumes:
16 | - ./provisioning/dashboards-actual/:/dashboards/
17 | - ./provisioning:/etc/grafana/provisioning
18 | environment:
19 | - TERM=linux
20 | - GF_DEFAULT_APP_MODE=development
21 | - GF_AUTH_ANONYMOUS_ENABLED=true
22 | - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
23 | - GF_ENTERPRISE_LICENSE_TEXT=$GF_ENTERPRISE_LICENSE_TEXT
24 | - GF_INSTALL_PLUGINS=yesoreyeram-infinity-datasource, grafana-simple-json-datasource
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "grafana-simple-json",
3 | "private": true,
4 | "version": "1.0.0",
5 | "description": "",
6 | "main": "index.js",
7 | "scripts": {
8 | "build": "./node_modules/grunt-cli/bin/grunt",
9 | "test": "./node_modules/grunt-cli/bin/grunt mochaTest"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/grafana/simple-json-datasource.git"
14 | },
15 | "author": "",
16 | "license": "MIT",
17 | "bugs": {
18 | "url": "https://github.com/grafana/simple-json-datasource/issues"
19 | },
20 | "engineStrict": true,
21 | "devDependencies": {
22 | "babel": "^6.23.0",
23 | "babel-preset-env": "^1.7.0",
24 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
25 | "chai": "~3.5.0",
26 | "grunt": "^1.0.1",
27 | "grunt-babel": "~6.0.0",
28 | "grunt-cli": "^1.2.0",
29 | "grunt-contrib-clean": "^1.1.0",
30 | "grunt-contrib-copy": "^1.0.0",
31 | "grunt-contrib-uglify": "^2.3.0",
32 | "grunt-contrib-watch": "^1.0.0",
33 | "grunt-execute": "~0.2.2",
34 | "grunt-mocha-test": "^0.13.2",
35 | "grunt-systemjs-builder": "^1.0.0",
36 | "jsdom": "~9.12.0",
37 | "load-grunt-tasks": "^3.5.2",
38 | "mocha": "^3.2.0",
39 | "prunk": "^1.3.0",
40 | "q": "^1.5.0"
41 | },
42 | "dependencies": {
43 | "lodash": "^4.17.4"
44 | },
45 | "homepage": "https://github.com/grafana/simple-json-datasource#readme"
46 | }
47 |
--------------------------------------------------------------------------------
/provisioning/dashboards-actual/migration.json:
--------------------------------------------------------------------------------
1 | {
2 | "__inputs": [
3 | {
4 | "name": "DS_SIMPLE_JSON - FAKE SERVICE",
5 | "label": "Simple JSON - Fake service",
6 | "description": "",
7 | "type": "datasource",
8 | "pluginId": "grafana-simple-json-datasource",
9 | "pluginName": "SimpleJson"
10 | },
11 | {
12 | "name": "DS_INFINITY_- FAKE SERVICE",
13 | "label": "Infinity - Fake service",
14 | "description": "",
15 | "type": "datasource",
16 | "pluginId": "yesoreyeram-infinity-datasource",
17 | "pluginName": "Infinity"
18 | }
19 | ],
20 | "__elements": {},
21 | "__requires": [
22 | {
23 | "type": "grafana",
24 | "id": "grafana",
25 | "name": "Grafana",
26 | "version": "10.3.0-64589"
27 | },
28 | {
29 | "type": "datasource",
30 | "id": "grafana-simple-json-datasource",
31 | "name": "SimpleJson",
32 | "version": "1.4.2"
33 | },
34 | {
35 | "type": "panel",
36 | "id": "table",
37 | "name": "Table",
38 | "version": ""
39 | },
40 | {
41 | "type": "panel",
42 | "id": "timeseries",
43 | "name": "Time series",
44 | "version": ""
45 | },
46 | {
47 | "type": "datasource",
48 | "id": "yesoreyeram-infinity-datasource",
49 | "name": "Infinity",
50 | "version": "2.3.1"
51 | }
52 | ],
53 | "annotations": {
54 | "list": [
55 | {
56 | "builtIn": 1,
57 | "datasource": {
58 | "type": "grafana",
59 | "uid": "-- Grafana --"
60 | },
61 | "enable": true,
62 | "hide": true,
63 | "iconColor": "rgba(0, 211, 255, 1)",
64 | "name": "Annotations & Alerts",
65 | "type": "dashboard"
66 | },
67 | {
68 | "datasource": {
69 | "type": "grafana-simple-json-datasource",
70 | "uid": "simple-json"
71 | },
72 | "enable": false,
73 | "iconColor": "red",
74 | "name": "Annotation using simple JSON",
75 | "query": "foo"
76 | },
77 | {
78 | "datasource": {
79 | "type": "yesoreyeram-infinity-datasource",
80 | "uid": "infinity"
81 | },
82 | "enable": false,
83 | "iconColor": "green",
84 | "mappings": {
85 | "time": {
86 | "source": "field",
87 | "value": "time"
88 | }
89 | },
90 | "name": "Annotation using Infinity ( frontend parser )",
91 | "target": {
92 | "columns": [],
93 | "filters": [],
94 | "format": "table",
95 | "global_query_id": "",
96 | "refId": "Anno",
97 | "root_selector": "",
98 | "source": "url",
99 | "type": "json",
100 | "url": "/annotations",
101 | "url_options": {
102 | "data": "",
103 | "method": "GET"
104 | }
105 | }
106 | },
107 | {
108 | "datasource": {
109 | "type": "yesoreyeram-infinity-datasource",
110 | "uid": "infinity"
111 | },
112 | "enable": false,
113 | "iconColor": "blue",
114 | "name": "Annotation using Infinity ( backend parser )",
115 | "target": {
116 | "columns": [
117 | {
118 | "selector": "time",
119 | "text": "",
120 | "type": "timestamp_epoch"
121 | },
122 | {
123 | "selector": "title",
124 | "text": "",
125 | "type": "string"
126 | },
127 | {
128 | "selector": "text",
129 | "text": "",
130 | "type": "string"
131 | },
132 | {
133 | "selector": "tags",
134 | "text": "",
135 | "type": "string"
136 | }
137 | ],
138 | "filters": [],
139 | "format": "table",
140 | "global_query_id": "",
141 | "parser": "backend",
142 | "refId": "Anno",
143 | "root_selector": "",
144 | "source": "url",
145 | "type": "json",
146 | "url": "/annotations",
147 | "url_options": {
148 | "data": "",
149 | "method": "GET"
150 | }
151 | }
152 | }
153 | ]
154 | },
155 | "editable": true,
156 | "fiscalYearStartMonth": 0,
157 | "graphTooltip": 0,
158 | "id": null,
159 | "links": [],
160 | "liveNow": false,
161 | "panels": [
162 | {
163 | "collapsed": false,
164 | "gridPos": {
165 | "h": 1,
166 | "w": 24,
167 | "x": 0,
168 | "y": 0
169 | },
170 | "id": 4,
171 | "panels": [],
172 | "title": "Single time series",
173 | "type": "row"
174 | },
175 | {
176 | "datasource": {
177 | "type": "grafana-simple-json-datasource",
178 | "uid": "simple-json"
179 | },
180 | "fieldConfig": {
181 | "defaults": {
182 | "color": {
183 | "mode": "palette-classic"
184 | },
185 | "custom": {
186 | "axisBorderShow": false,
187 | "axisCenteredZero": false,
188 | "axisColorMode": "text",
189 | "axisLabel": "",
190 | "axisPlacement": "auto",
191 | "barAlignment": 0,
192 | "drawStyle": "line",
193 | "fillOpacity": 0,
194 | "gradientMode": "none",
195 | "hideFrom": {
196 | "legend": false,
197 | "tooltip": false,
198 | "viz": false
199 | },
200 | "insertNulls": false,
201 | "lineInterpolation": "linear",
202 | "lineWidth": 1,
203 | "pointSize": 5,
204 | "scaleDistribution": {
205 | "type": "linear"
206 | },
207 | "showPoints": "auto",
208 | "spanNulls": false,
209 | "stacking": {
210 | "group": "A",
211 | "mode": "none"
212 | },
213 | "thresholdsStyle": {
214 | "mode": "off"
215 | }
216 | },
217 | "mappings": [],
218 | "thresholds": {
219 | "mode": "absolute",
220 | "steps": [
221 | {
222 | "color": "green",
223 | "value": null
224 | },
225 | {
226 | "color": "red",
227 | "value": 80
228 | }
229 | ]
230 | }
231 | },
232 | "overrides": []
233 | },
234 | "gridPos": {
235 | "h": 8,
236 | "w": 8,
237 | "x": 0,
238 | "y": 1
239 | },
240 | "id": 1,
241 | "options": {
242 | "legend": {
243 | "calcs": [],
244 | "displayMode": "list",
245 | "placement": "bottom",
246 | "showLegend": true
247 | },
248 | "tooltip": {
249 | "mode": "single",
250 | "sort": "none"
251 | }
252 | },
253 | "targets": [
254 | {
255 | "datasource": {
256 | "type": "grafana-simple-json-datasource",
257 | "uid": "simple-json"
258 | },
259 | "refId": "A",
260 | "target": "upper_25",
261 | "type": "timeserie"
262 | }
263 | ],
264 | "title": "Single time series - ( Simple JSON datasource plugin )",
265 | "type": "timeseries"
266 | },
267 | {
268 | "datasource": {
269 | "type": "yesoreyeram-infinity-datasource",
270 | "uid": "infinity"
271 | },
272 | "fieldConfig": {
273 | "defaults": {
274 | "color": {
275 | "mode": "palette-classic"
276 | },
277 | "custom": {
278 | "axisBorderShow": false,
279 | "axisCenteredZero": false,
280 | "axisColorMode": "text",
281 | "axisLabel": "",
282 | "axisPlacement": "auto",
283 | "barAlignment": 0,
284 | "drawStyle": "line",
285 | "fillOpacity": 0,
286 | "gradientMode": "none",
287 | "hideFrom": {
288 | "legend": false,
289 | "tooltip": false,
290 | "viz": false
291 | },
292 | "insertNulls": false,
293 | "lineInterpolation": "linear",
294 | "lineWidth": 1,
295 | "pointSize": 5,
296 | "scaleDistribution": {
297 | "type": "linear"
298 | },
299 | "showPoints": "auto",
300 | "spanNulls": false,
301 | "stacking": {
302 | "group": "A",
303 | "mode": "none"
304 | },
305 | "thresholdsStyle": {
306 | "mode": "off"
307 | }
308 | },
309 | "mappings": [],
310 | "thresholds": {
311 | "mode": "absolute",
312 | "steps": [
313 | {
314 | "color": "green",
315 | "value": null
316 | },
317 | {
318 | "color": "red",
319 | "value": 80
320 | }
321 | ]
322 | }
323 | },
324 | "overrides": []
325 | },
326 | "gridPos": {
327 | "h": 8,
328 | "w": 8,
329 | "x": 8,
330 | "y": 1
331 | },
332 | "id": 2,
333 | "options": {
334 | "legend": {
335 | "calcs": [],
336 | "displayMode": "list",
337 | "placement": "bottom",
338 | "showLegend": true
339 | },
340 | "tooltip": {
341 | "mode": "single",
342 | "sort": "none"
343 | }
344 | },
345 | "targets": [
346 | {
347 | "columns": [],
348 | "datasource": {
349 | "type": "yesoreyeram-infinity-datasource",
350 | "uid": "infinity"
351 | },
352 | "filters": [],
353 | "format": "as-is",
354 | "global_query_id": "",
355 | "refId": "A",
356 | "root_selector": "",
357 | "source": "url",
358 | "type": "json",
359 | "url": "/query",
360 | "url_options": {
361 | "body_content_type": "application/json",
362 | "body_type": "raw",
363 | "data": "{ \n \"targets\": [{ \"target\":\"upper_25\" }]\n}",
364 | "method": "POST"
365 | }
366 | }
367 | ],
368 | "title": "Single time series - ( Infinity plugin - frontend parser )",
369 | "type": "timeseries"
370 | },
371 | {
372 | "datasource": {
373 | "type": "yesoreyeram-infinity-datasource",
374 | "uid": "infinity"
375 | },
376 | "fieldConfig": {
377 | "defaults": {
378 | "color": {
379 | "mode": "palette-classic"
380 | },
381 | "custom": {
382 | "axisBorderShow": false,
383 | "axisCenteredZero": false,
384 | "axisColorMode": "text",
385 | "axisLabel": "",
386 | "axisPlacement": "auto",
387 | "barAlignment": 0,
388 | "drawStyle": "line",
389 | "fillOpacity": 0,
390 | "gradientMode": "none",
391 | "hideFrom": {
392 | "legend": false,
393 | "tooltip": false,
394 | "viz": false
395 | },
396 | "insertNulls": false,
397 | "lineInterpolation": "linear",
398 | "lineWidth": 1,
399 | "pointSize": 5,
400 | "scaleDistribution": {
401 | "type": "linear"
402 | },
403 | "showPoints": "auto",
404 | "spanNulls": false,
405 | "stacking": {
406 | "group": "A",
407 | "mode": "none"
408 | },
409 | "thresholdsStyle": {
410 | "mode": "off"
411 | }
412 | },
413 | "mappings": [],
414 | "thresholds": {
415 | "mode": "absolute",
416 | "steps": [
417 | {
418 | "color": "green",
419 | "value": null
420 | },
421 | {
422 | "color": "red",
423 | "value": 80
424 | }
425 | ]
426 | }
427 | },
428 | "overrides": []
429 | },
430 | "gridPos": {
431 | "h": 8,
432 | "w": 8,
433 | "x": 16,
434 | "y": 1
435 | },
436 | "id": 7,
437 | "options": {
438 | "legend": {
439 | "calcs": [],
440 | "displayMode": "list",
441 | "placement": "bottom",
442 | "showLegend": true
443 | },
444 | "tooltip": {
445 | "mode": "single",
446 | "sort": "none"
447 | }
448 | },
449 | "targets": [
450 | {
451 | "columns": [
452 | {
453 | "selector": "0",
454 | "text": "upper_25",
455 | "type": "number"
456 | },
457 | {
458 | "selector": "1",
459 | "text": "Time",
460 | "type": "timestamp_epoch"
461 | }
462 | ],
463 | "datasource": {
464 | "type": "yesoreyeram-infinity-datasource",
465 | "uid": "infinity"
466 | },
467 | "filters": [],
468 | "format": "timeseries",
469 | "global_query_id": "",
470 | "parser": "backend",
471 | "refId": "A",
472 | "root_selector": "datapoints",
473 | "source": "url",
474 | "type": "json",
475 | "url": "/query",
476 | "url_options": {
477 | "body_content_type": "application/json",
478 | "body_type": "raw",
479 | "data": "{ \n \"targets\": [{ \"target\":\"upper_25\" }]\n}",
480 | "method": "POST"
481 | }
482 | }
483 | ],
484 | "title": "Single time series - ( Infinity plugin - backend parser )",
485 | "type": "timeseries"
486 | },
487 | {
488 | "collapsed": false,
489 | "gridPos": {
490 | "h": 1,
491 | "w": 24,
492 | "x": 0,
493 | "y": 9
494 | },
495 | "id": 5,
496 | "panels": [],
497 | "title": "Multiple time series",
498 | "type": "row"
499 | },
500 | {
501 | "datasource": {
502 | "type": "grafana-simple-json-datasource",
503 | "uid": "simple-json"
504 | },
505 | "fieldConfig": {
506 | "defaults": {
507 | "color": {
508 | "mode": "palette-classic"
509 | },
510 | "custom": {
511 | "axisBorderShow": false,
512 | "axisCenteredZero": false,
513 | "axisColorMode": "text",
514 | "axisLabel": "",
515 | "axisPlacement": "auto",
516 | "barAlignment": 0,
517 | "drawStyle": "line",
518 | "fillOpacity": 0,
519 | "gradientMode": "none",
520 | "hideFrom": {
521 | "legend": false,
522 | "tooltip": false,
523 | "viz": false
524 | },
525 | "insertNulls": false,
526 | "lineInterpolation": "linear",
527 | "lineWidth": 1,
528 | "pointSize": 5,
529 | "scaleDistribution": {
530 | "type": "linear"
531 | },
532 | "showPoints": "auto",
533 | "spanNulls": false,
534 | "stacking": {
535 | "group": "A",
536 | "mode": "none"
537 | },
538 | "thresholdsStyle": {
539 | "mode": "off"
540 | }
541 | },
542 | "mappings": [],
543 | "thresholds": {
544 | "mode": "absolute",
545 | "steps": [
546 | {
547 | "color": "green",
548 | "value": null
549 | },
550 | {
551 | "color": "red",
552 | "value": 80
553 | }
554 | ]
555 | }
556 | },
557 | "overrides": []
558 | },
559 | "gridPos": {
560 | "h": 8,
561 | "w": 8,
562 | "x": 0,
563 | "y": 10
564 | },
565 | "id": 6,
566 | "options": {
567 | "legend": {
568 | "calcs": [],
569 | "displayMode": "list",
570 | "placement": "bottom",
571 | "showLegend": true
572 | },
573 | "tooltip": {
574 | "mode": "single",
575 | "sort": "none"
576 | }
577 | },
578 | "targets": [
579 | {
580 | "datasource": {
581 | "type": "grafana-simple-json-datasource",
582 | "uid": "simple-json"
583 | },
584 | "refId": "A",
585 | "target": "upper_25",
586 | "type": "timeserie"
587 | },
588 | {
589 | "datasource": {
590 | "type": "grafana-simple-json-datasource",
591 | "uid": "simple-json"
592 | },
593 | "hide": false,
594 | "refId": "B",
595 | "target": "upper_50",
596 | "type": "timeserie"
597 | }
598 | ],
599 | "title": "Multiple time series - ( Simple JSON datasource plugin )",
600 | "type": "timeseries"
601 | },
602 | {
603 | "datasource": {
604 | "type": "yesoreyeram-infinity-datasource",
605 | "uid": "infinity"
606 | },
607 | "fieldConfig": {
608 | "defaults": {
609 | "color": {
610 | "mode": "palette-classic"
611 | },
612 | "custom": {
613 | "axisBorderShow": false,
614 | "axisCenteredZero": false,
615 | "axisColorMode": "text",
616 | "axisLabel": "",
617 | "axisPlacement": "auto",
618 | "barAlignment": 0,
619 | "drawStyle": "line",
620 | "fillOpacity": 0,
621 | "gradientMode": "none",
622 | "hideFrom": {
623 | "legend": false,
624 | "tooltip": false,
625 | "viz": false
626 | },
627 | "insertNulls": false,
628 | "lineInterpolation": "linear",
629 | "lineWidth": 1,
630 | "pointSize": 5,
631 | "scaleDistribution": {
632 | "type": "linear"
633 | },
634 | "showPoints": "auto",
635 | "spanNulls": false,
636 | "stacking": {
637 | "group": "A",
638 | "mode": "none"
639 | },
640 | "thresholdsStyle": {
641 | "mode": "off"
642 | }
643 | },
644 | "mappings": [],
645 | "thresholds": {
646 | "mode": "absolute",
647 | "steps": [
648 | {
649 | "color": "green",
650 | "value": null
651 | },
652 | {
653 | "color": "red",
654 | "value": 80
655 | }
656 | ]
657 | }
658 | },
659 | "overrides": []
660 | },
661 | "gridPos": {
662 | "h": 8,
663 | "w": 8,
664 | "x": 8,
665 | "y": 10
666 | },
667 | "id": 3,
668 | "options": {
669 | "legend": {
670 | "calcs": [],
671 | "displayMode": "list",
672 | "placement": "bottom",
673 | "showLegend": true
674 | },
675 | "tooltip": {
676 | "mode": "single",
677 | "sort": "none"
678 | }
679 | },
680 | "targets": [
681 | {
682 | "columns": [],
683 | "datasource": {
684 | "type": "yesoreyeram-infinity-datasource",
685 | "uid": "infinity"
686 | },
687 | "filters": [],
688 | "format": "as-is",
689 | "global_query_id": "",
690 | "parser": "simple",
691 | "refId": "A",
692 | "root_selector": "",
693 | "source": "url",
694 | "type": "json",
695 | "url": "/query",
696 | "url_options": {
697 | "body_content_type": "application/json",
698 | "body_type": "raw",
699 | "data": "{ \n \"targets\": [{ \"target\":\"upper_25\" }]\n}",
700 | "method": "POST"
701 | }
702 | },
703 | {
704 | "columns": [],
705 | "datasource": {
706 | "type": "yesoreyeram-infinity-datasource",
707 | "uid": "infinity"
708 | },
709 | "filters": [],
710 | "format": "as-is",
711 | "global_query_id": "",
712 | "hide": false,
713 | "parser": "simple",
714 | "refId": "B",
715 | "root_selector": "",
716 | "source": "url",
717 | "type": "json",
718 | "url": "/query",
719 | "url_options": {
720 | "body_content_type": "application/json",
721 | "body_type": "raw",
722 | "data": "{ \n \"targets\": [{ \"target\":\"upper_50\" }]\n}",
723 | "method": "POST"
724 | }
725 | }
726 | ],
727 | "title": "Multiple time series - ( Infinity plugin - frontend parser )",
728 | "type": "timeseries"
729 | },
730 | {
731 | "datasource": {
732 | "type": "yesoreyeram-infinity-datasource",
733 | "uid": "infinity"
734 | },
735 | "fieldConfig": {
736 | "defaults": {
737 | "color": {
738 | "mode": "palette-classic"
739 | },
740 | "custom": {
741 | "axisBorderShow": false,
742 | "axisCenteredZero": false,
743 | "axisColorMode": "text",
744 | "axisLabel": "",
745 | "axisPlacement": "auto",
746 | "barAlignment": 0,
747 | "drawStyle": "line",
748 | "fillOpacity": 0,
749 | "gradientMode": "none",
750 | "hideFrom": {
751 | "legend": false,
752 | "tooltip": false,
753 | "viz": false
754 | },
755 | "insertNulls": false,
756 | "lineInterpolation": "linear",
757 | "lineWidth": 1,
758 | "pointSize": 5,
759 | "scaleDistribution": {
760 | "type": "linear"
761 | },
762 | "showPoints": "auto",
763 | "spanNulls": false,
764 | "stacking": {
765 | "group": "A",
766 | "mode": "none"
767 | },
768 | "thresholdsStyle": {
769 | "mode": "off"
770 | }
771 | },
772 | "mappings": [],
773 | "thresholds": {
774 | "mode": "absolute",
775 | "steps": [
776 | {
777 | "color": "green",
778 | "value": null
779 | },
780 | {
781 | "color": "red",
782 | "value": 80
783 | }
784 | ]
785 | }
786 | },
787 | "overrides": []
788 | },
789 | "gridPos": {
790 | "h": 8,
791 | "w": 8,
792 | "x": 16,
793 | "y": 10
794 | },
795 | "id": 8,
796 | "options": {
797 | "legend": {
798 | "calcs": [],
799 | "displayMode": "list",
800 | "placement": "bottom",
801 | "showLegend": true
802 | },
803 | "tooltip": {
804 | "mode": "single",
805 | "sort": "none"
806 | }
807 | },
808 | "targets": [
809 | {
810 | "columns": [
811 | {
812 | "selector": "0",
813 | "text": "upper_25",
814 | "type": "number"
815 | },
816 | {
817 | "selector": "1",
818 | "text": "Time",
819 | "type": "timestamp_epoch"
820 | }
821 | ],
822 | "datasource": {
823 | "type": "yesoreyeram-infinity-datasource",
824 | "uid": "infinity"
825 | },
826 | "filters": [],
827 | "format": "timeseries",
828 | "global_query_id": "",
829 | "parser": "backend",
830 | "refId": "A",
831 | "root_selector": "datapoints",
832 | "source": "url",
833 | "type": "json",
834 | "url": "/query",
835 | "url_options": {
836 | "body_content_type": "application/json",
837 | "body_type": "raw",
838 | "data": "{ \n \"targets\": [{ \"target\":\"upper_25\" }]\n}",
839 | "method": "POST"
840 | }
841 | },
842 | {
843 | "columns": [
844 | {
845 | "selector": "0",
846 | "text": "upper_50",
847 | "type": "number"
848 | },
849 | {
850 | "selector": "1",
851 | "text": "Time",
852 | "type": "timestamp_epoch"
853 | }
854 | ],
855 | "datasource": {
856 | "type": "yesoreyeram-infinity-datasource",
857 | "uid": "infinity"
858 | },
859 | "filters": [],
860 | "format": "timeseries",
861 | "global_query_id": "",
862 | "hide": false,
863 | "parser": "backend",
864 | "refId": "B",
865 | "root_selector": "datapoints",
866 | "source": "url",
867 | "type": "json",
868 | "url": "/query",
869 | "url_options": {
870 | "body_content_type": "application/json",
871 | "body_type": "raw",
872 | "data": "{ \n \"targets\": [{ \"target\":\"upper_50\" }]\n}",
873 | "method": "POST"
874 | }
875 | }
876 | ],
877 | "title": "Single time series - ( Infinity plugin - backend parser )",
878 | "type": "timeseries"
879 | },
880 | {
881 | "collapsed": false,
882 | "gridPos": {
883 | "h": 1,
884 | "w": 24,
885 | "x": 0,
886 | "y": 18
887 | },
888 | "id": 9,
889 | "panels": [],
890 | "title": "Table",
891 | "type": "row"
892 | },
893 | {
894 | "datasource": {
895 | "type": "grafana-simple-json-datasource",
896 | "uid": "simple-json"
897 | },
898 | "fieldConfig": {
899 | "defaults": {
900 | "color": {
901 | "mode": "thresholds"
902 | },
903 | "custom": {
904 | "align": "auto",
905 | "cellOptions": {
906 | "type": "auto"
907 | },
908 | "inspect": false
909 | },
910 | "mappings": [],
911 | "thresholds": {
912 | "mode": "absolute",
913 | "steps": [
914 | {
915 | "color": "green",
916 | "value": null
917 | },
918 | {
919 | "color": "red",
920 | "value": 80
921 | }
922 | ]
923 | }
924 | },
925 | "overrides": []
926 | },
927 | "gridPos": {
928 | "h": 6,
929 | "w": 8,
930 | "x": 0,
931 | "y": 19
932 | },
933 | "id": 10,
934 | "options": {
935 | "cellHeight": "sm",
936 | "footer": {
937 | "countRows": false,
938 | "fields": "",
939 | "reducer": ["sum"],
940 | "show": false
941 | },
942 | "showHeader": true
943 | },
944 | "pluginVersion": "10.3.0-64589",
945 | "targets": [
946 | {
947 | "datasource": {
948 | "type": "grafana-simple-json-datasource",
949 | "uid": "simple-json"
950 | },
951 | "refId": "A",
952 | "target": "upper_25",
953 | "type": "table"
954 | }
955 | ],
956 | "title": "Table query - ( Simple JSON datasource plugin )",
957 | "type": "table"
958 | },
959 | {
960 | "datasource": {
961 | "type": "yesoreyeram-infinity-datasource",
962 | "uid": "infinity"
963 | },
964 | "fieldConfig": {
965 | "defaults": {
966 | "color": {
967 | "mode": "thresholds"
968 | },
969 | "custom": {
970 | "align": "auto",
971 | "cellOptions": {
972 | "type": "auto"
973 | },
974 | "inspect": false
975 | },
976 | "mappings": [],
977 | "thresholds": {
978 | "mode": "absolute",
979 | "steps": [
980 | {
981 | "color": "green",
982 | "value": null
983 | },
984 | {
985 | "color": "red",
986 | "value": 80
987 | }
988 | ]
989 | }
990 | },
991 | "overrides": []
992 | },
993 | "gridPos": {
994 | "h": 6,
995 | "w": 8,
996 | "x": 8,
997 | "y": 19
998 | },
999 | "id": 11,
1000 | "options": {
1001 | "cellHeight": "sm",
1002 | "footer": {
1003 | "countRows": false,
1004 | "fields": "",
1005 | "reducer": ["sum"],
1006 | "show": false
1007 | },
1008 | "showHeader": true
1009 | },
1010 | "pluginVersion": "10.3.0-64589",
1011 | "targets": [
1012 | {
1013 | "columns": [],
1014 | "datasource": {
1015 | "type": "yesoreyeram-infinity-datasource",
1016 | "uid": "infinity"
1017 | },
1018 | "filters": [],
1019 | "format": "as-is",
1020 | "global_query_id": "",
1021 | "refId": "A",
1022 | "root_selector": "",
1023 | "source": "url",
1024 | "type": "json",
1025 | "url": "/query",
1026 | "url_options": {
1027 | "body_content_type": "application/json",
1028 | "body_type": "raw",
1029 | "data": "{ \n \"targets\": [{ \"type\":\"table\" }]\n}",
1030 | "method": "POST"
1031 | }
1032 | }
1033 | ],
1034 | "title": "Table query - ( Infinity plugin - frontend parser )",
1035 | "type": "table"
1036 | },
1037 | {
1038 | "datasource": {
1039 | "type": "yesoreyeram-infinity-datasource",
1040 | "uid": "infinity"
1041 | },
1042 | "fieldConfig": {
1043 | "defaults": {
1044 | "color": {
1045 | "mode": "thresholds"
1046 | },
1047 | "custom": {
1048 | "align": "auto",
1049 | "cellOptions": {
1050 | "type": "auto"
1051 | },
1052 | "inspect": false
1053 | },
1054 | "mappings": [],
1055 | "thresholds": {
1056 | "mode": "absolute",
1057 | "steps": [
1058 | {
1059 | "color": "green",
1060 | "value": null
1061 | },
1062 | {
1063 | "color": "red",
1064 | "value": 80
1065 | }
1066 | ]
1067 | }
1068 | },
1069 | "overrides": []
1070 | },
1071 | "gridPos": {
1072 | "h": 6,
1073 | "w": 8,
1074 | "x": 16,
1075 | "y": 19
1076 | },
1077 | "id": 12,
1078 | "options": {
1079 | "cellHeight": "sm",
1080 | "footer": {
1081 | "countRows": false,
1082 | "fields": "",
1083 | "reducer": ["sum"],
1084 | "show": false
1085 | },
1086 | "showHeader": true
1087 | },
1088 | "pluginVersion": "10.3.0-64589",
1089 | "targets": [
1090 | {
1091 | "columns": [
1092 | {
1093 | "selector": "0",
1094 | "text": "Time",
1095 | "type": "timestamp_epoch"
1096 | },
1097 | {
1098 | "selector": "1",
1099 | "text": "Country",
1100 | "type": "string"
1101 | },
1102 | {
1103 | "selector": "2",
1104 | "text": "Number",
1105 | "type": "number"
1106 | }
1107 | ],
1108 | "datasource": {
1109 | "type": "yesoreyeram-infinity-datasource",
1110 | "uid": "infinity"
1111 | },
1112 | "filters": [],
1113 | "format": "as-is",
1114 | "global_query_id": "",
1115 | "parser": "backend",
1116 | "refId": "A",
1117 | "root_selector": "rows",
1118 | "source": "url",
1119 | "type": "json",
1120 | "url": "/query",
1121 | "url_options": {
1122 | "body_content_type": "application/json",
1123 | "body_type": "raw",
1124 | "data": "{ \n \"targets\": [{ \"type\":\"table\" }]\n}",
1125 | "method": "POST"
1126 | }
1127 | }
1128 | ],
1129 | "title": "Table query - ( Infinity plugin - backend parser )",
1130 | "type": "table"
1131 | }
1132 | ],
1133 | "refresh": "",
1134 | "schemaVersion": 39,
1135 | "tags": [],
1136 | "templating": {
1137 | "list": []
1138 | },
1139 | "time": {
1140 | "from": "now-6h",
1141 | "to": "now"
1142 | },
1143 | "timepicker": {},
1144 | "timezone": "",
1145 | "title": "Simple JSON plugin to Infinity plugin migration",
1146 | "uid": "migration",
1147 | "version": 2,
1148 | "weekStart": ""
1149 | }
1150 |
--------------------------------------------------------------------------------
/provisioning/dashboards/default.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: 1
2 |
3 | providers:
4 | - name: Default Dashboards
5 | type: file
6 | options:
7 | path: /dashboards
8 |
--------------------------------------------------------------------------------
/provisioning/datasources/default.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: 1
2 | datasources:
3 | - name: Simple JSON - Fake service
4 | type: grafana-simple-json-datasource
5 | uid: simple-json
6 | url: http://server:3333
7 | access: proxy
8 | isDefault: true
9 | - name: Infinity - Fake service
10 | type: yesoreyeram-infinity-datasource
11 | uid: infinity
12 | url: http://server:3333
13 |
--------------------------------------------------------------------------------
/server/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18
2 |
3 | WORKDIR /app
4 |
5 | RUN git clone https://github.com/bergquist/fake-simple-json-datasource && cd fake-simple-json-datasource && yarn install
6 |
7 | WORKDIR /app/fake-simple-json-datasource
8 |
9 | CMD ["node","./index.js"]
--------------------------------------------------------------------------------
/spec/datasource_spec.js:
--------------------------------------------------------------------------------
1 | import {Datasource} from "../module";
2 | import Q from "q";
3 |
4 | describe('GenericDatasource', function() {
5 | var ctx = {};
6 |
7 | beforeEach(function() {
8 | ctx.$q = Q;
9 | ctx.backendSrv = {};
10 | ctx.templateSrv = {};
11 | ctx.ds = new Datasource({}, ctx.$q, ctx.backendSrv, ctx.templateSrv);
12 | });
13 |
14 | it('should return an empty array when no targets are set', function(done) {
15 | ctx.ds.query({targets: []}).then(function(result) {
16 | expect(result.data).to.have.length(0);
17 | done();
18 | });
19 | });
20 |
21 | it('should return the server results when a target is set', function(done) {
22 | ctx.backendSrv.datasourceRequest = function(request) {
23 | return ctx.$q.when({
24 | _request: request,
25 | data: [
26 | {
27 | target: 'X',
28 | datapoints: [1, 2, 3]
29 | }
30 | ]
31 | });
32 | };
33 |
34 | ctx.templateSrv.replace = function(data) {
35 | return data;
36 | }
37 |
38 | ctx.ds.query({targets: ['hits']}).then(function(result) {
39 | expect(result._request.data.targets).to.have.length(1);
40 |
41 | var series = result.data[0];
42 | expect(series.target).to.equal('X');
43 | expect(series.datapoints).to.have.length(3);
44 | done();
45 | });
46 | });
47 |
48 | it ('should return the metric results when a target is null', function(done) {
49 | ctx.backendSrv.datasourceRequest = function(request) {
50 | return ctx.$q.when({
51 | _request: request,
52 | data: [
53 | "metric_0",
54 | "metric_1",
55 | "metric_2",
56 | ]
57 | });
58 | };
59 |
60 | ctx.templateSrv.replace = function(data) {
61 | return data;
62 | }
63 |
64 | ctx.ds.metricFindQuery({target: null}).then(function(result) {
65 | expect(result).to.have.length(3);
66 | expect(result[0].text).to.equal('metric_0');
67 | expect(result[0].value).to.equal('metric_0');
68 | expect(result[1].text).to.equal('metric_1');
69 | expect(result[1].value).to.equal('metric_1');
70 | expect(result[2].text).to.equal('metric_2');
71 | expect(result[2].value).to.equal('metric_2');
72 | done();
73 | });
74 | });
75 |
76 | it ('should return the metric target results when a target is set', function(done) {
77 | ctx.backendSrv.datasourceRequest = function(request) {
78 | var target = request.data.target;
79 | var result = [target + "_0", target + "_1", target + "_2"];
80 |
81 | return ctx.$q.when({
82 | _request: request,
83 | data: result
84 | });
85 | };
86 |
87 | ctx.templateSrv.replace = function(data) {
88 | return data;
89 | }
90 |
91 | ctx.ds.metricFindQuery('search').then(function(result) {
92 | expect(result).to.have.length(3);
93 | expect(result[0].text).to.equal('search_0');
94 | expect(result[0].value).to.equal('search_0');
95 | expect(result[1].text).to.equal('search_1');
96 | expect(result[1].value).to.equal('search_1');
97 | expect(result[2].text).to.equal('search_2');
98 | expect(result[2].value).to.equal('search_2');
99 | done();
100 | });
101 | });
102 |
103 | it ('should return the metric results when the target is an empty string', function(done) {
104 | ctx.backendSrv.datasourceRequest = function(request) {
105 | return ctx.$q.when({
106 | _request: request,
107 | data: [
108 | "metric_0",
109 | "metric_1",
110 | "metric_2",
111 | ]
112 | });
113 | };
114 |
115 | ctx.templateSrv.replace = function(data) {
116 | return data;
117 | }
118 |
119 | ctx.ds.metricFindQuery('').then(function(result) {
120 | expect(result).to.have.length(3);
121 | expect(result[0].text).to.equal('metric_0');
122 | expect(result[0].value).to.equal('metric_0');
123 | expect(result[1].text).to.equal('metric_1');
124 | expect(result[1].value).to.equal('metric_1');
125 | expect(result[2].text).to.equal('metric_2');
126 | expect(result[2].value).to.equal('metric_2');
127 | done();
128 | });
129 | });
130 |
131 | it ('should return the metric results when the args are an empty object', function(done) {
132 | ctx.backendSrv.datasourceRequest = function(request) {
133 | return ctx.$q.when({
134 | _request: request,
135 | data: [
136 | "metric_0",
137 | "metric_1",
138 | "metric_2",
139 | ]
140 | });
141 | };
142 |
143 | ctx.templateSrv.replace = function(data) {
144 | return data;
145 | }
146 |
147 | ctx.ds.metricFindQuery().then(function(result) {
148 | expect(result).to.have.length(3);
149 | expect(result[0].text).to.equal('metric_0');
150 | expect(result[0].value).to.equal('metric_0');
151 | expect(result[1].text).to.equal('metric_1');
152 | expect(result[1].value).to.equal('metric_1');
153 | expect(result[2].text).to.equal('metric_2');
154 | expect(result[2].value).to.equal('metric_2');
155 | done();
156 | });
157 | });
158 |
159 | it ('should return the metric target results when the args are a string', function(done) {
160 | ctx.backendSrv.datasourceRequest = function(request) {
161 | var target = request.data.target;
162 | var result = [target + "_0", target + "_1", target + "_2"];
163 |
164 | return ctx.$q.when({
165 | _request: request,
166 | data: result
167 | });
168 | };
169 |
170 | ctx.templateSrv.replace = function(data) {
171 | return data;
172 | }
173 |
174 | ctx.ds.metricFindQuery('search').then(function(result) {
175 | expect(result).to.have.length(3);
176 | expect(result[0].text).to.equal('search_0');
177 | expect(result[0].value).to.equal('search_0');
178 | expect(result[1].text).to.equal('search_1');
179 | expect(result[1].value).to.equal('search_1');
180 | expect(result[2].text).to.equal('search_2');
181 | expect(result[2].value).to.equal('search_2');
182 | done();
183 | });
184 | });
185 |
186 | it ('should return data as text and as value', function(done) {
187 | var result = ctx.ds.mapToTextValue({data: ["zero", "one", "two"]});
188 |
189 | expect(result).to.have.length(3);
190 | expect(result[0].text).to.equal('zero');
191 | expect(result[0].value).to.equal('zero');
192 | expect(result[1].text).to.equal('one');
193 | expect(result[1].value).to.equal('one');
194 | expect(result[2].text).to.equal('two');
195 | expect(result[2].value).to.equal('two');
196 | done();
197 | });
198 |
199 | it ('should return text as text and value as value', function(done) {
200 | var data = [
201 | {text: "zero", value: "value_0"},
202 | {text: "one", value: "value_1"},
203 | {text: "two", value: "value_2"},
204 | ];
205 |
206 | var result = ctx.ds.mapToTextValue({data: data});
207 |
208 | expect(result).to.have.length(3);
209 | expect(result[0].text).to.equal('zero');
210 | expect(result[0].value).to.equal('value_0');
211 | expect(result[1].text).to.equal('one');
212 | expect(result[1].value).to.equal('value_1');
213 | expect(result[2].text).to.equal('two');
214 | expect(result[2].value).to.equal('value_2');
215 | done();
216 | });
217 |
218 | it ('should return data as text and index as value', function(done) {
219 | var data = [
220 | {a: "zero", b: "value_0"},
221 | {a: "one", b: "value_1"},
222 | {a: "two", b: "value_2"},
223 | ];
224 |
225 | var result = ctx.ds.mapToTextValue({data: data});
226 |
227 | expect(result).to.have.length(3);
228 | expect(result[0].text).to.equal(data[0]);
229 | expect(result[0].value).to.equal(0);
230 | expect(result[1].text).to.equal(data[1]);
231 | expect(result[1].value).to.equal(1);
232 | expect(result[2].text).to.equal(data[2]);
233 | expect(result[2].value).to.equal(2);
234 | done();
235 | });
236 |
237 | it('should support tag keys', function(done) {
238 | var data = [{'type': 'string', 'text': 'One', 'key': 'one'}, {'type': 'string', 'text': 'two', 'key': 'Two'}];
239 |
240 | ctx.backendSrv.datasourceRequest = function(request) {
241 | return ctx.$q.when({
242 | _request: request,
243 | data: data
244 | });
245 | };
246 |
247 | ctx.ds.getTagKeys().then(function(result) {
248 | expect(result).to.have.length(2);
249 | expect(result[0].type).to.equal(data[0].type);
250 | expect(result[0].text).to.equal(data[0].text);
251 | expect(result[0].key).to.equal(data[0].key);
252 | expect(result[1].type).to.equal(data[1].type);
253 | expect(result[1].text).to.equal(data[1].text);
254 | expect(result[1].key).to.equal(data[1].key);
255 | done();
256 | });
257 | });
258 |
259 | it('should support tag values', function(done) {
260 | var data = [{'key': 'eins', 'text': 'Eins!'}, {'key': 'zwei', 'text': 'Zwei'}, {'key': 'drei', 'text': 'Drei!'}];
261 |
262 | ctx.backendSrv.datasourceRequest = function(request) {
263 | return ctx.$q.when({
264 | _request: request,
265 | data: data
266 | });
267 | };
268 |
269 | ctx.ds.getTagValues().then(function(result) {
270 | expect(result).to.have.length(3);
271 | expect(result[0].text).to.equal(data[0].text);
272 | expect(result[0].key).to.equal(data[0].key);
273 | expect(result[1].text).to.equal(data[1].text);
274 | expect(result[1].key).to.equal(data[1].key);
275 | expect(result[2].text).to.equal(data[2].text);
276 | expect(result[2].key).to.equal(data[2].key);
277 | done();
278 | });
279 | });
280 |
281 | });
282 |
--------------------------------------------------------------------------------
/spec/test-main.js:
--------------------------------------------------------------------------------
1 | import prunk from 'prunk';
2 | import {jsdom} from 'jsdom';
3 | import chai from 'chai';
4 |
5 | // Mock Grafana modules that are not available outside of the core project
6 | // Required for loading module.js
7 | prunk.mock('./css/query-editor.css!', 'no css, dude.');
8 | prunk.mock('app/plugins/sdk', {
9 | QueryCtrl: null
10 | });
11 |
12 | // Setup jsdom
13 | // Required for loading angularjs
14 | global.document = jsdom('');
15 | global.window = global.document.parentWindow;
16 |
17 | // Setup Chai
18 | chai.should();
19 | global.assert = chai.assert;
20 | global.expect = chai.expect;
21 |
--------------------------------------------------------------------------------
/src/css/query-editor.css:
--------------------------------------------------------------------------------
1 | .generic-datasource-query-row .query-keyword {
2 | width: 75px;
3 | }
--------------------------------------------------------------------------------
/src/datasource.js:
--------------------------------------------------------------------------------
1 | import _ from "lodash";
2 |
3 | export class GenericDatasource {
4 |
5 | constructor(instanceSettings, $q, backendSrv, templateSrv) {
6 | this.type = instanceSettings.type;
7 | this.url = instanceSettings.url;
8 | this.name = instanceSettings.name;
9 | this.q = $q;
10 | this.backendSrv = backendSrv;
11 | this.templateSrv = templateSrv;
12 | this.withCredentials = instanceSettings.withCredentials;
13 | this.headers = {'Content-Type': 'application/json'};
14 | if (typeof instanceSettings.basicAuth === 'string' && instanceSettings.basicAuth.length > 0) {
15 | this.headers['Authorization'] = instanceSettings.basicAuth;
16 | }
17 | }
18 |
19 | query(options) {
20 | var query = this.buildQueryParameters(options);
21 | query.targets = query.targets.filter(t => !t.hide);
22 |
23 | if (query.targets.length <= 0) {
24 | return this.q.when({data: []});
25 | }
26 |
27 | if (this.templateSrv.getAdhocFilters) {
28 | query.adhocFilters = this.templateSrv.getAdhocFilters(this.name);
29 | } else {
30 | query.adhocFilters = [];
31 | }
32 |
33 | return this.doRequest({
34 | url: this.url + '/query',
35 | data: query,
36 | method: 'POST'
37 | });
38 | }
39 |
40 | testDatasource() {
41 | return this.doRequest({
42 | url: this.url + '/',
43 | method: 'GET',
44 | }).then(response => {
45 | if (response.status === 200) {
46 | return { status: "success", message: "Data source is working", title: "Success" };
47 | }
48 | });
49 | }
50 |
51 | annotationQuery(options) {
52 | var query = this.templateSrv.replace(options.annotation.query, {}, 'glob');
53 | var annotationQuery = {
54 | range: options.range,
55 | annotation: {
56 | name: options.annotation.name,
57 | datasource: options.annotation.datasource,
58 | enable: options.annotation.enable,
59 | iconColor: options.annotation.iconColor,
60 | query: query
61 | },
62 | rangeRaw: options.rangeRaw
63 | };
64 |
65 | return this.doRequest({
66 | url: this.url + '/annotations',
67 | method: 'POST',
68 | data: annotationQuery
69 | }).then(result => {
70 | return result.data;
71 | });
72 | }
73 |
74 | metricFindQuery(query) {
75 | var interpolated = {
76 | target: this.templateSrv.replace(query, null, 'regex')
77 | };
78 |
79 | return this.doRequest({
80 | url: this.url + '/search',
81 | data: interpolated,
82 | method: 'POST',
83 | }).then(this.mapToTextValue);
84 | }
85 |
86 | mapToTextValue(result) {
87 | return _.map(result.data, (d, i) => {
88 | if (d && d.text && d.value) {
89 | return { text: d.text, value: d.value };
90 | } else if (_.isObject(d)) {
91 | return { text: d, value: i};
92 | }
93 | return { text: d, value: d };
94 | });
95 | }
96 |
97 | doRequest(options) {
98 | options.withCredentials = this.withCredentials;
99 | options.headers = this.headers;
100 |
101 | return this.backendSrv.datasourceRequest(options);
102 | }
103 |
104 | buildQueryParameters(options) {
105 | //remove placeholder targets
106 | options.targets = _.filter(options.targets, target => {
107 | return target.target !== 'select metric';
108 | });
109 |
110 | var targets = _.map(options.targets, target => {
111 | return {
112 | target: this.templateSrv.replace(target.target, options.scopedVars, 'regex'),
113 | refId: target.refId,
114 | hide: target.hide,
115 | type: target.type || 'timeserie'
116 | };
117 | });
118 |
119 | options.targets = targets;
120 |
121 | return options;
122 | }
123 |
124 | getTagKeys(options) {
125 | return new Promise((resolve, reject) => {
126 | this.doRequest({
127 | url: this.url + '/tag-keys',
128 | method: 'POST',
129 | data: options
130 | }).then(result => {
131 | return resolve(result.data);
132 | });
133 | });
134 | }
135 |
136 | getTagValues(options) {
137 | return new Promise((resolve, reject) => {
138 | this.doRequest({
139 | url: this.url + '/tag-values',
140 | method: 'POST',
141 | data: options
142 | }).then(result => {
143 | return resolve(result.data);
144 | });
145 | });
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/src/img/simpleJson_logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
636 |
--------------------------------------------------------------------------------
/src/module.js:
--------------------------------------------------------------------------------
1 | import {GenericDatasource} from './datasource';
2 | import {GenericDatasourceQueryCtrl} from './query_ctrl';
3 |
4 | class GenericConfigCtrl {}
5 | GenericConfigCtrl.templateUrl = 'partials/config.html';
6 |
7 | class GenericQueryOptionsCtrl {}
8 | GenericQueryOptionsCtrl.templateUrl = 'partials/query.options.html';
9 |
10 | class GenericAnnotationsQueryCtrl {}
11 | GenericAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html'
12 |
13 | export {
14 | GenericDatasource as Datasource,
15 | GenericDatasourceQueryCtrl as QueryCtrl,
16 | GenericConfigCtrl as ConfigCtrl,
17 | GenericQueryOptionsCtrl as QueryOptionsCtrl,
18 | GenericAnnotationsQueryCtrl as AnnotationsQueryCtrl
19 | };
20 |
--------------------------------------------------------------------------------
/src/partials/annotations.editor.html:
--------------------------------------------------------------------------------
1 |
2 | Query
3 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/partials/config.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/partials/query.editor.html:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
--------------------------------------------------------------------------------
/src/partials/query.options.html:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/plugin.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SimpleJson",
3 | "id": "grafana-simple-json-datasource",
4 | "type": "datasource",
5 |
6 | "partials": {
7 | "config": "public/app/plugins/datasource/simplejson/partials/config.html"
8 | },
9 |
10 | "metrics": true,
11 | "annotations": true,
12 |
13 | "info": {
14 | "description": "simple json datasource",
15 | "author": {
16 | "name": "Grafana Labs",
17 | "url": "https://grafana.com"
18 | },
19 | "logos": {
20 | "small": "img/simpleJson_logo.svg",
21 | "large": "img/simpleJson_logo.svg"
22 | },
23 | "links": [
24 | {"name": "GitHub", "url": "https://github.com/grafana/simple-json-datasource"},
25 | {"name": "MIT License", "url": "https://github.com/grafana/simple-json-datasource/blob/master/LICENSE"}
26 | ],
27 | "version": "1.4.3",
28 | "updated": "2024-01-15"
29 | },
30 |
31 | "dependencies": {
32 | "grafanaVersion": "3.x.x",
33 | "plugins": [ ]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/query_ctrl.js:
--------------------------------------------------------------------------------
1 | import {QueryCtrl} from 'app/plugins/sdk';
2 | import './css/query-editor.css!'
3 |
4 | export class GenericDatasourceQueryCtrl extends QueryCtrl {
5 |
6 | constructor($scope, $injector) {
7 | super($scope, $injector);
8 |
9 | this.scope = $scope;
10 | this.target.target = this.target.target || 'select metric';
11 | this.target.type = this.target.type || 'timeserie';
12 | }
13 |
14 | getOptions(query) {
15 | return this.datasource.metricFindQuery(query || '');
16 | }
17 |
18 | toggleEditorMode() {
19 | this.target.rawQuery = !this.target.rawQuery;
20 | }
21 |
22 | onChangeInternal() {
23 | this.panelCtrl.refresh(); // Asks the panel to refresh data.
24 | }
25 | }
26 |
27 | GenericDatasourceQueryCtrl.templateUrl = 'partials/query.editor.html';
28 |
29 |
--------------------------------------------------------------------------------