├── overview.png ├── query_editor.png ├── dist ├── partials │ ├── config.html │ ├── query.options.html │ ├── annotations.editor.html │ └── query.editor.html ├── img │ ├── logo_large.png │ └── logo_small.png ├── css │ └── query-editor.css ├── plugin.json ├── module.js.map ├── serverside │ ├── mssqldriver.js │ ├── drilldriver.js │ └── sqlproxyserver.js ├── module.js ├── query_ctrl.js.map ├── README.md ├── query_ctrl.js ├── datasource.js └── datasource.js.map ├── src ├── partials │ ├── config.html │ ├── query.options.html │ ├── annotations.editor.html │ └── query.editor.html ├── img │ ├── logo_large.png │ └── logo_small.png ├── css │ └── query-editor.css ├── serverside │ ├── sqlproxy.service │ ├── mssqldriver.js │ ├── drilldriver.js │ └── sqlproxyserver.js ├── module.js ├── plugin.json ├── query_ctrl.js └── datasource.js ├── .jscs.json ├── .gitignore ├── spec ├── test-main.js ├── sqlproxyserver_spec.js └── datasource_spec.js ├── LICENSE ├── package.json ├── Gruntfile.js └── README.md /overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbrian/grafana-simple-sql-datasource/HEAD/overview.png -------------------------------------------------------------------------------- /query_editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbrian/grafana-simple-sql-datasource/HEAD/query_editor.png -------------------------------------------------------------------------------- /dist/partials/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/partials/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /dist/img/logo_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbrian/grafana-simple-sql-datasource/HEAD/dist/img/logo_large.png -------------------------------------------------------------------------------- /dist/img/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbrian/grafana-simple-sql-datasource/HEAD/dist/img/logo_small.png -------------------------------------------------------------------------------- /src/img/logo_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbrian/grafana-simple-sql-datasource/HEAD/src/img/logo_large.png -------------------------------------------------------------------------------- /src/img/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gbrian/grafana-simple-sql-datasource/HEAD/src/img/logo_small.png -------------------------------------------------------------------------------- /src/partials/query.options.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | -------------------------------------------------------------------------------- /dist/partials/query.options.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | -------------------------------------------------------------------------------- /dist/css/query-editor.css: -------------------------------------------------------------------------------- 1 | .grafana-simple-sql-datasource .hidden { 2 | display: none; 3 | } 4 | 5 | .ace-sql-editor{ 6 | position: absolute; 7 | top: 0; 8 | right: 0; 9 | bottom: 0; 10 | left: 0; 11 | } -------------------------------------------------------------------------------- /src/css/query-editor.css: -------------------------------------------------------------------------------- 1 | .grafana-simple-sql-datasource .hidden { 2 | display: none; 3 | } 4 | 5 | .ace-sql-editor{ 6 | position: absolute; 7 | top: 0; 8 | right: 0; 9 | bottom: 0; 10 | left: 0; 11 | } -------------------------------------------------------------------------------- /src/serverside/sqlproxy.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=grafana-simple-sql-datasource 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/usr/bin/node /var/lib/grafana/plugins/grafana-simple-sql-datasource-master/dist/serverside/sqlproxyserver.js 7 | Restart=on-failure 8 | 9 | [Install] 10 | WantedBy=multi-user.target 11 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /dist/partials/annotations.editor.html: -------------------------------------------------------------------------------- 1 | 2 |
Query
3 |
4 |
5 |
6 | 7 |
8 | 10 | 11 |
12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /src/partials/annotations.editor.html: -------------------------------------------------------------------------------- 1 | 2 |
Query
3 |
4 |
5 |
6 | 7 |
8 | 10 | 11 |
12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /.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 | deploy.bat 39 | .vscode/ 40 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | global.navigator = window.navigator = {}; 17 | global.Node = window.Node; 18 | 19 | // Setup Chai 20 | chai.should(); 21 | global.assert = chai.assert; 22 | global.expect = chai.expect; 23 | -------------------------------------------------------------------------------- /src/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SimpleSql", 3 | "id": "grafana-simple-sql-datasource", 4 | "type": "datasource", 5 | 6 | "partials": { 7 | "config": "public/app/plugins/datasource/simplesql/partials/config.html" 8 | }, 9 | 10 | "metrics": true, 11 | "annotations": true, 12 | 13 | "info": { 14 | "description": "simple sql datasource", 15 | "author": { 16 | "name": "GBrian.", 17 | "url": "" 18 | }, 19 | "logos": { 20 | "small": "img/logo_small.png", 21 | "large": "img/logo_large.png" 22 | }, 23 | "links": [ 24 | {"name": "GitHub", "url": "https://github.com/gbrian/grafana-simple-sql-datasource"}, 25 | {"name": "MIT License", "url": "https://github.com/gbrian/grafana-simple-sql-datasource/blob/master/LICENSE"} 26 | ], 27 | "version": "0.0.2", 28 | "updated": "2017-02-14" 29 | }, 30 | 31 | "dependencies": { 32 | "grafanaVersion": "3.x.x", 33 | "plugins": [ ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /dist/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SimpleSql", 3 | "id": "grafana-simple-sql-datasource", 4 | "type": "datasource", 5 | 6 | "partials": { 7 | "config": "public/app/plugins/datasource/simplesql/partials/config.html" 8 | }, 9 | 10 | "metrics": true, 11 | "annotations": true, 12 | 13 | "info": { 14 | "description": "simple sql datasource", 15 | "author": { 16 | "name": "GBrian.", 17 | "url": "" 18 | }, 19 | "logos": { 20 | "small": "img/logo_small.png", 21 | "large": "img/logo_large.png" 22 | }, 23 | "links": [ 24 | {"name": "GitHub", "url": "https://github.com/gbrian/grafana-simple-sql-datasource"}, 25 | {"name": "MIT License", "url": "https://github.com/gbrian/grafana-simple-sql-datasource/blob/master/LICENSE"} 26 | ], 27 | "version": "0.0.2", 28 | "updated": "2017-02-14" 29 | }, 30 | 31 | "dependencies": { 32 | "grafanaVersion": "3.x.x", 33 | "plugins": [ ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Gustavo Brian 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 | -------------------------------------------------------------------------------- /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, uiSegmentSrv) { 7 | super($scope, $injector); 8 | 9 | this.scope = $scope; 10 | this.uiSegmentSrv = uiSegmentSrv; 11 | this.target.target = this.target.target || 'select metric'; 12 | this.target.type = this.target.type || 'timeserie'; 13 | this.target.cmdtype = this.target.cmdtype || 'sql'; 14 | this.target.utc = this.target.utc || 'localtime'; 15 | } 16 | 17 | getOptions() { 18 | return this.datasource.metricFindQuery(this.target) 19 | .then(this.uiSegmentSrv.transformToSegments(false)); 20 | // Options have to be transformed by uiSegmentSrv to be usable by metric-segment-model directive 21 | } 22 | 23 | toggleEditorMode() { 24 | this.target.rawQuery = !this.target.rawQuery; 25 | } 26 | 27 | onChangeInternal() { 28 | this.panelCtrl.refresh(); // Asks the panel to refresh data. 29 | } 30 | } 31 | 32 | GenericDatasourceQueryCtrl.templateUrl = 'partials/query.editor.html'; 33 | 34 | -------------------------------------------------------------------------------- /dist/module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/module.js"],"names":["GenericDatasource","GenericDatasourceQueryCtrl","GenericConfigCtrl","templateUrl","GenericQueryOptionsCtrl","GenericAnnotationsQueryCtrl"],"mappings":";;;;;;;;;;;;;;;AAAQA,uB,eAAAA,iB;;AACAC,gC,eAAAA,0B;;;4BAEFC,iB;;;;AACNA,wBAAkBC,WAAlB,GAAgC,sBAAhC;;kCAEMC,uB;;;;AACNA,8BAAwBD,WAAxB,GAAsC,6BAAtC;;sCAEME,2B;;;;AACNA,kCAA4BF,WAA5B,GAA0C,kCAA1C;;4BAGEH,iB;;2BACAC,0B;;4BACAC,iB;;kCACAE,uB;;sCACAC,2B","file":"module.js","sourcesContent":["import {GenericDatasource} from './datasource';\r\nimport {GenericDatasourceQueryCtrl} from './query_ctrl';\r\n\r\nclass GenericConfigCtrl {}\r\nGenericConfigCtrl.templateUrl = 'partials/config.html';\r\n\r\nclass GenericQueryOptionsCtrl {}\r\nGenericQueryOptionsCtrl.templateUrl = 'partials/query.options.html';\r\n\r\nclass GenericAnnotationsQueryCtrl {}\r\nGenericAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html'\r\n\r\nexport {\r\n GenericDatasource as Datasource,\r\n GenericDatasourceQueryCtrl as QueryCtrl,\r\n GenericConfigCtrl as ConfigCtrl,\r\n GenericQueryOptionsCtrl as QueryOptionsCtrl,\r\n GenericAnnotationsQueryCtrl as AnnotationsQueryCtrl\r\n};\r\n"]} -------------------------------------------------------------------------------- /dist/serverside/mssqldriver.js: -------------------------------------------------------------------------------- 1 | var mssql = require("mssql"); 2 | var q = require("q"); 3 | 4 | function mssqldriver(options){ 5 | this.options = options; 6 | } 7 | 8 | mssqldriver.prototype.buildQuery = function(cmd, parameters){ 9 | if(parameters){ 10 | var re = /[@$]([a-z0-9A-Z]*)/g; 11 | var m = null; 12 | while((m = re.exec(cmd))) 13 | if(parameters.hasOwnProperty(m[1])) 14 | cmd = cmd.replace(m[0], parameters[m[1]]); 15 | } 16 | return q.resolve(cmd); 17 | } 18 | 19 | mssqldriver.prototype.connect = function(url){ 20 | return mssql.connect(url || this.options.url); 21 | } 22 | 23 | mssqldriver.prototype.query = function(command, parameters){ 24 | var defer = q.defer(); 25 | var driver = this; 26 | this.buildQuery(command, parameters) 27 | .then(sql => this.connect() 28 | .then(conn => new mssql.Request(conn).query(sql, (err, results) => 29 | defer[err ? "reject" : "resolve"](err || driver.parseResults(results)) 30 | )) 31 | ) 32 | .catch(defer.reject); 33 | return defer.promise; 34 | } 35 | 36 | mssqldriver.prototype.parseResults = function(results){ 37 | return results ? { 38 | columns: results.columns, 39 | rows: results.map(r => r) 40 | }:{}; 41 | } 42 | 43 | module.exports = mssqldriver; -------------------------------------------------------------------------------- /src/serverside/mssqldriver.js: -------------------------------------------------------------------------------- 1 | var mssql = require("mssql"); 2 | var q = require("q"); 3 | 4 | function mssqldriver(options){ 5 | this.options = options; 6 | } 7 | 8 | mssqldriver.prototype.buildQuery = function(cmd, parameters){ 9 | if(parameters){ 10 | var re = /[@$]([a-z0-9A-Z]*)/g; 11 | var m = null; 12 | while((m = re.exec(cmd))) 13 | if(parameters.hasOwnProperty(m[1])) 14 | cmd = cmd.replace(m[0], parameters[m[1]]); 15 | } 16 | return q.resolve(cmd); 17 | } 18 | 19 | mssqldriver.prototype.connect = function(url){ 20 | return mssql.connect(url || this.options.url); 21 | } 22 | 23 | mssqldriver.prototype.query = function(command, parameters){ 24 | var defer = q.defer(); 25 | var driver = this; 26 | this.buildQuery(command, parameters) 27 | .then(sql => this.connect() 28 | .then(conn => new mssql.Request(conn).query(sql, (err, results) => 29 | defer[err ? "reject" : "resolve"](err || driver.parseResults(results)) 30 | )) 31 | ) 32 | .catch(defer.reject); 33 | return defer.promise; 34 | } 35 | 36 | mssqldriver.prototype.parseResults = function(results){ 37 | return results ? { 38 | columns: results.columns, 39 | rows: results.map(r => r) 40 | }:{}; 41 | } 42 | 43 | module.exports = mssqldriver; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grafana-simple-sql-datasource", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/gbrian/grafana-simple-sql-datasource.git" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/gbrian/grafana-simple-sql-datasource/issues" 18 | }, 19 | "devDependencies": { 20 | "grunt": "~0.4.5", 21 | "babel": "~6.5.1", 22 | "grunt-babel": "~6.0.0", 23 | "grunt-contrib-copy": "~0.8.2", 24 | "grunt-contrib-watch": "^0.6.1", 25 | "grunt-contrib-uglify": "~0.11.0", 26 | "grunt-mocha-test": "~0.12.7", 27 | "grunt-systemjs-builder": "^0.2.5", 28 | "load-grunt-tasks": "~3.2.0", 29 | "grunt-execute": "~0.2.2", 30 | "grunt-contrib-clean": "~0.6.0", 31 | "prunk": "~1.2.1", 32 | "q": "^1.4.1", 33 | "chai": "~3.5.0" 34 | }, 35 | "dependencies": { 36 | "babel-plugin-transform-es2015-for-of": "^6.6.0", 37 | "babel-plugin-transform-es2015-modules-systemjs": "^6.5.0", 38 | "babel-preset-es2015": "^6.5.0", 39 | "body-parser": "^1.16.0", 40 | "express": "^4.14.1", 41 | "express-cors": "0.0.3", 42 | "lodash": "^4.0.1", 43 | "mocha": "^2.4.5", 44 | "moment": "^2.17.1", 45 | "mssql": "^3.3.0", 46 | "node-rest-client": "^2.5.0", 47 | "q": "^1.4.1" 48 | }, 49 | "homepage": "https://github.com/gbrian/grafana-simple-sql-datasource#readme" 50 | } 51 | -------------------------------------------------------------------------------- /dist/module.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | System.register(['./datasource', './query_ctrl'], function (_export, _context) { 4 | "use strict"; 5 | 6 | var GenericDatasource, GenericDatasourceQueryCtrl, GenericConfigCtrl, GenericQueryOptionsCtrl, GenericAnnotationsQueryCtrl; 7 | 8 | function _classCallCheck(instance, Constructor) { 9 | if (!(instance instanceof Constructor)) { 10 | throw new TypeError("Cannot call a class as a function"); 11 | } 12 | } 13 | 14 | return { 15 | setters: [function (_datasource) { 16 | GenericDatasource = _datasource.GenericDatasource; 17 | }, function (_query_ctrl) { 18 | GenericDatasourceQueryCtrl = _query_ctrl.GenericDatasourceQueryCtrl; 19 | }], 20 | execute: function () { 21 | _export('ConfigCtrl', GenericConfigCtrl = function GenericConfigCtrl() { 22 | _classCallCheck(this, GenericConfigCtrl); 23 | }); 24 | 25 | GenericConfigCtrl.templateUrl = 'partials/config.html'; 26 | 27 | _export('QueryOptionsCtrl', GenericQueryOptionsCtrl = function GenericQueryOptionsCtrl() { 28 | _classCallCheck(this, GenericQueryOptionsCtrl); 29 | }); 30 | 31 | GenericQueryOptionsCtrl.templateUrl = 'partials/query.options.html'; 32 | 33 | _export('AnnotationsQueryCtrl', GenericAnnotationsQueryCtrl = function GenericAnnotationsQueryCtrl() { 34 | _classCallCheck(this, GenericAnnotationsQueryCtrl); 35 | }); 36 | 37 | GenericAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html'; 38 | 39 | _export('Datasource', GenericDatasource); 40 | 41 | _export('QueryCtrl', GenericDatasourceQueryCtrl); 42 | 43 | _export('ConfigCtrl', GenericConfigCtrl); 44 | 45 | _export('QueryOptionsCtrl', GenericQueryOptionsCtrl); 46 | 47 | _export('AnnotationsQueryCtrl', GenericAnnotationsQueryCtrl); 48 | } 49 | }; 50 | }); 51 | //# sourceMappingURL=module.js.map 52 | -------------------------------------------------------------------------------- /dist/serverside/drilldriver.js: -------------------------------------------------------------------------------- 1 | // https://drill.apache.org/docs/rest-api/ 2 | 3 | var q = require("q"); 4 | var Client = require('node-rest-client').Client; 5 | 6 | var client = new Client(); 7 | 8 | function drilldriver(options){ 9 | this.options = options; 10 | } 11 | 12 | drilldriver.prototype.buildQuery = function(cmd, parameters){ 13 | if(parameters){ 14 | var re = /[@$]([a-z0-9A-Z]*)/g; 15 | var m = null; 16 | while((m = re.exec(cmd))) 17 | if(parameters.hasOwnProperty(m[1])) 18 | cmd = cmd.replace(m[0], parameters[m[1]]); 19 | } 20 | return q.resolve(cmd); 21 | } 22 | 23 | drilldriver.prototype.get = function(url){ 24 | var defer = q.defer(); 25 | var req = client.get(this.options.url + url, defer.resolve); 26 | req.on('error', defer.reject); 27 | return defer.promise; 28 | } 29 | 30 | drilldriver.prototype.post = function(url, data){ 31 | var defer = q.defer(); 32 | var req = client.post(this.options.url + url, data, defer.resolve); 33 | req.on('error', defer.reject); 34 | return defer.promise; 35 | } 36 | 37 | drilldriver.prototype.connect = function(url){ 38 | return this.get("/options.json"); 39 | } 40 | 41 | drilldriver.prototype.query = function(command, parameters){ 42 | var driver = this; 43 | return this.buildQuery(command, parameters) 44 | .then(sql => driver.post("/query.json", {"queryType" : "SQL", "query" : sql})); 45 | } 46 | 47 | drilldriver.prototype.parseResults = function(results){ 48 | if(!results || !results.rows.length) 49 | return {}; 50 | // Try figure out types 51 | /*const typeCheck { 52 | number: function(n){ 53 | 54 | } 55 | };*/ 56 | var frow = results.rows[0]; 57 | var types = Object.keys(frow).map(key => { 58 | frow[key] 59 | return "string"; 60 | }); 61 | // results.columns = results.columns.map(c => { reutrn {}}) 62 | return results; 63 | } 64 | 65 | module.exports = drilldriver; -------------------------------------------------------------------------------- /src/serverside/drilldriver.js: -------------------------------------------------------------------------------- 1 | // https://drill.apache.org/docs/rest-api/ 2 | 3 | var q = require("q"); 4 | var Client = require('node-rest-client').Client; 5 | 6 | var client = new Client(); 7 | 8 | function drilldriver(options){ 9 | this.options = options; 10 | } 11 | 12 | drilldriver.prototype.buildQuery = function(cmd, parameters){ 13 | if(parameters){ 14 | var re = /[@$]([a-z0-9A-Z]*)/g; 15 | var m = null; 16 | while((m = re.exec(cmd))) 17 | if(parameters.hasOwnProperty(m[1])) 18 | cmd = cmd.replace(m[0], parameters[m[1]]); 19 | } 20 | return q.resolve(cmd); 21 | } 22 | 23 | drilldriver.prototype.get = function(url){ 24 | var defer = q.defer(); 25 | var req = client.get(this.options.url + url, defer.resolve); 26 | req.on('error', defer.reject); 27 | return defer.promise; 28 | } 29 | 30 | drilldriver.prototype.post = function(url, data){ 31 | var defer = q.defer(); 32 | var req = client.post(this.options.url + url, data, defer.resolve); 33 | req.on('error', defer.reject); 34 | return defer.promise; 35 | } 36 | 37 | drilldriver.prototype.connect = function(url){ 38 | return this.get("/options.json"); 39 | } 40 | 41 | drilldriver.prototype.query = function(command, parameters){ 42 | var driver = this; 43 | return this.buildQuery(command, parameters) 44 | .then(sql => driver.post("/query.json", {"queryType" : "SQL", "query" : sql})); 45 | } 46 | 47 | drilldriver.prototype.parseResults = function(results){ 48 | if(!results || !results.rows.length) 49 | return {}; 50 | // Try figure out types 51 | /*const typeCheck { 52 | number: function(n){ 53 | 54 | } 55 | };*/ 56 | var frow = results.rows[0]; 57 | var types = Object.keys(frow).map(key => { 58 | frow[key] 59 | return "string"; 60 | }); 61 | // results.columns = results.columns.map(c => { reutrn {}}) 62 | return results; 63 | } 64 | 65 | module.exports = drilldriver; -------------------------------------------------------------------------------- /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','./bin/*','./serverside/*'], 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: ['es2015'] 38 | }, 39 | dist: { 40 | options: { 41 | plugins: ['transform-es2015-modules-systemjs', 'transform-es2015-for-of'] 42 | }, 43 | files: [{ 44 | cwd: 'src', 45 | expand: true, 46 | src: ['**/*.js','!**/server.js','!serverside/*'], 47 | dest: 'dist', 48 | ext:'.js' 49 | }] 50 | }, 51 | distTestNoSystemJs: { 52 | files: [{ 53 | cwd: 'src', 54 | expand: true, 55 | src: ['**/*.js'], 56 | dest: 'dist/test', 57 | ext:'.js' 58 | }] 59 | }, 60 | distTestsSpecsNoSystemJs: { 61 | files: [{ 62 | expand: true, 63 | cwd: 'spec', 64 | src: ['**/*.js'], 65 | dest: 'dist/test/spec', 66 | ext:'.js' 67 | }] 68 | } 69 | }, 70 | 71 | mochaTest: { 72 | test: { 73 | options: { 74 | reporter: 'spec' 75 | }, 76 | src: ['dist/test/spec/test-main.js', 'dist/test/spec/*_spec.js'] 77 | } 78 | } 79 | }); 80 | 81 | grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel', 'mochaTest']); 82 | }; -------------------------------------------------------------------------------- /dist/query_ctrl.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/query_ctrl.js"],"names":["QueryCtrl","GenericDatasourceQueryCtrl","$scope","$injector","uiSegmentSrv","scope","target","type","cmdtype","utc","datasource","metricFindQuery","then","transformToSegments","rawQuery","panelCtrl","refresh","templateUrl"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAQA,e,kBAAAA,S;;;;;;;;;;;;;;;;;;;;;4CAGKC,0B;;;AAEX,4CAAYC,MAAZ,EAAoBC,SAApB,EAA+BC,YAA/B,EAA8C;AAAA;;AAAA,8JACtCF,MADsC,EAC9BC,SAD8B;;AAG5C,gBAAKE,KAAL,GAAaH,MAAb;AACA,gBAAKE,YAAL,GAAoBA,YAApB;AACA,gBAAKE,MAAL,CAAYA,MAAZ,GAAqB,MAAKA,MAAL,CAAYA,MAAZ,IAAsB,eAA3C;AACA,gBAAKA,MAAL,CAAYC,IAAZ,GAAmB,MAAKD,MAAL,CAAYC,IAAZ,IAAoB,WAAvC;AACA,gBAAKD,MAAL,CAAYE,OAAZ,GAAsB,MAAKF,MAAL,CAAYE,OAAZ,IAAuB,KAA7C;AACA,gBAAKF,MAAL,CAAYG,GAAZ,GAAkB,MAAKH,MAAL,CAAYG,GAAZ,IAAmB,WAArC;AAR4C;AAS7C;;;;uCAEY;AACX,mBAAO,KAAKC,UAAL,CAAgBC,eAAhB,CAAgC,KAAKL,MAArC,EACJM,IADI,CACC,KAAKR,YAAL,CAAkBS,mBAAlB,CAAsC,KAAtC,CADD,CAAP;AAEE;AACH;;;6CAEkB;AACjB,iBAAKP,MAAL,CAAYQ,QAAZ,GAAuB,CAAC,KAAKR,MAAL,CAAYQ,QAApC;AACD;;;6CAEkB;AACjB,iBAAKC,SAAL,CAAeC,OAAf,GADiB,CACS;AAC3B;;;;QAzB6ChB,S;;;;AA4BhDC,iCAA2BgB,WAA3B,GAAyC,4BAAzC","file":"query_ctrl.js","sourcesContent":["import {QueryCtrl} from 'app/plugins/sdk';\r\nimport './css/query-editor.css!'\r\n\r\nexport class GenericDatasourceQueryCtrl extends QueryCtrl {\r\n\r\n constructor($scope, $injector, uiSegmentSrv) {\r\n super($scope, $injector);\r\n\r\n this.scope = $scope;\r\n this.uiSegmentSrv = uiSegmentSrv;\r\n this.target.target = this.target.target || 'select metric';\r\n this.target.type = this.target.type || 'timeserie';\r\n this.target.cmdtype = this.target.cmdtype || 'sql';\r\n this.target.utc = this.target.utc || 'localtime';\r\n }\r\n\r\n getOptions() {\r\n return this.datasource.metricFindQuery(this.target)\r\n .then(this.uiSegmentSrv.transformToSegments(false));\r\n // Options have to be transformed by uiSegmentSrv to be usable by metric-segment-model directive\r\n }\r\n\r\n toggleEditorMode() {\r\n this.target.rawQuery = !this.target.rawQuery;\r\n }\r\n\r\n onChangeInternal() {\r\n this.panelCtrl.refresh(); // Asks the panel to refresh data.\r\n }\r\n}\r\n\r\nGenericDatasourceQueryCtrl.templateUrl = 'partials/query.editor.html';\r\n\r\n"]} -------------------------------------------------------------------------------- /dist/README.md: -------------------------------------------------------------------------------- 1 | # grafana-simple-sql-datasource 2 | 3 | Allows querying SQL based datasources like SQL Server. 4 | 5 | ![SQL Plugi](https://raw.githubusercontent.com/gbrian/grafana-simple-sql-datasource/master/overview.png "Query editor") 6 | 7 | 8 | ## Usage 9 | Currently the plugin requires a proxy server running to communicate with the database. 10 | 11 | **Install sqlproxyserver** 12 | 13 | * Run `npm install` at the `dist/serverside` folder to install all dependencies 14 | * Run npm install on the plugin directory 15 | * Run server side code `dist/serverside/sqlproxyserver.js` 16 | * Test on your browser `http://myserver:port/con=mssql://user:name@server/database` you must get a `{"status":"sucess"}` response 17 | 18 | **Add new datasource** 19 | Add a new datasource to Grafana and set the url to: 20 | 21 | ```` 22 | http://myserver:port/con=mssql://user:name@server/database 23 | ```` 24 | 25 | Where: 26 | 27 | * **myserver:port** : Is the server where `sqlproxyserver` is running 28 | * **con**: Specifies the sql connection string 29 | 30 | ## SQL Databases 31 | Currently supported SQL databases 32 | 33 | ### SQL Server 34 | SQL Server connection is managed by the mssqp package https://www.npmjs.com/package/mssql 35 | 36 | ## Features 37 | Following features has been implemented 38 | 39 | ![Query editor](https://raw.githubusercontent.com/gbrian/grafana-simple-sql-datasource/master/query_editor.png "Query editor") 40 | 41 | ### Metrics 42 | It is possible to define two different types: `timeseries` and `table` 43 | 44 | ### Annotation 45 | Annotation querires must return the following fields: 46 | 47 | * **title**: Annotation header 48 | * **text**: Annotation description 49 | * **tags**: Annotation tags 50 | * **time**: Annotation time 51 | 52 | ## Notes 53 | ### Time 54 | UTC and Localtime. Currently you must specify if time returned by the query is UTC or local. 55 | The plugin will convert localtime to UTC in order to be correctly renderer. 56 | ### Template 57 | You can use `$from` and `$to` to refer to selected time period in your queries like: 58 | 59 | ```` 60 | SELECT field FROM table WHERE datestart >= '$from' AND dateStart <= '$to' 61 | ```` 62 | 63 | ## Thanks to 64 | Grafana team and [@bergquist](https://github.com/bergquist) 65 | 66 | -------------------------------------------------------------------------------- /dist/partials/query.editor.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 |
6 | 9 |
10 | 11 |
12 |