Commit 3435df8b by bergquist

feat(examples): add datasource plugin example

parent 5bf2b6c3
{
"disallowImplicitTypeConversion": ["string"],
"disallowKeywords": ["with"],
"disallowMultipleLineBreaks": true,
"disallowMixedSpacesAndTabs": true,
"disallowTrailingWhitespace": true,
"requireSpacesInFunctionExpression": {
"beforeOpeningCurlyBrace": true
},
"disallowSpacesInsideArrayBrackets": true,
"disallowSpacesInsideParentheses": true,
"validateIndentation": 2
}
\ No newline at end of file
{
"browser": true,
"esnext": true,
"bitwise":false,
"curly": true,
"eqnull": true,
"devel": true,
"eqeqeq": true,
"forin": false,
"immed": true,
"supernew": true,
"expr": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"noempty": true,
"undef": true,
"boss": true,
"trailing": true,
"laxbreak": true,
"laxcomma": true,
"sub": true,
"unused": true,
"maxdepth": 6,
"maxlen": 140,
"globals": {
"System": true,
"define": true,
"require": true,
"Chromath": false,
"setImmediate": true
}
}
module.exports = function(grunt) {
require('load-grunt-tasks')(grunt);
grunt.loadNpmTasks('grunt-execute');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.initConfig({
clean: ["dist"],
copy: {
src_to_dist: {
cwd: 'src',
expand: true,
src: ['**/*', '!**/*.js', '!**/*.scss'],
dest: 'dist'
},
pluginDef: {
expand: true,
src: 'plugin.json',
dest: 'dist',
}
},
watch: {
rebuild_all: {
files: ['src/**/*', 'plugin.json'],
tasks: ['default'],
options: {spawn: false}
},
},
babel: {
options: {
sourceMap: true,
presets: ["es2015"],
plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"],
},
dist: {
files: [{
cwd: 'src',
expand: true,
src: ['**/*.js'],
dest: 'dist',
ext:'.js'
}]
},
},
});
grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel']);
};
#Generic backend datasource#
This is a very minimalistic datasource that forwards http requests in a defined format. The idea is that anybody should be able to build an api and retrieve data from any datasource without built-in support in grafana.
Its also serves as an living example implementation of a datasource.
A guide for installing plugins can be found at [placeholder for links].
Your backend need implement 3 urls
* "/" Should return 200 ok. Used for "Test connection" on the datasource config page.
* "/search" Used by the find metric options on the query tab in panels
* "/query" Should return metrics based on input
## Metric discovery ##
### Request ###
```
{ refId: 'F', target: 'select metric' }
```
### Expected Response ###
An array of options based on the target input
####Example####
```
["upper_25","upper_50","upper_75","upper_90","upper_95"]
```
## Metric query ##
### Request ###
```
{
range: { from: '2015-12-22T03:06:13.851Z',to: '2015-12-22T06:48:24.137Z' },
interval: '5s',
targets:
[ { refId: 'B', target: 'upper_75' },
{ refId: 'A', target: 'upper_90' } ],
format: 'json',
maxDataPoints: 2495 //decided by the panel
}
```
### Expected response ###
An array of
```
{
"target":"target_name",
"datapoints":[
[intvalue, timestamp in epoch],
[intvalue, timestamp in epoch]
]
}
```
###Example###
```
[
{
"target":"upper_75",
"datapoints":[
[622,1450754160000],
[365,1450754220000]
]
},
{
"target":"upper_90",
"datapoints":[
[861,1450754160000],
[767,1450754220000]
]
}
]
```
## Example backend implementation ##
https://gist.github.com/bergquist/bc4aa5baface3cffa109
\ No newline at end of file
{
"name": "kentik-app",
"private": true,
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/raintank/kentik-app-poc.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/raintank/kentik-app-poc/issues"
},
"devDependencies": {
"grunt": "~0.4.5",
"babel": "~6.5.1",
"grunt-babel": "~6.0.0",
"grunt-contrib-copy": "~0.8.2",
"grunt-contrib-watch": "^0.6.1",
"grunt-contrib-uglify": "~0.11.0",
"grunt-systemjs-builder": "^0.2.5",
"load-grunt-tasks": "~3.2.0",
"grunt-execute": "~0.2.2",
"grunt-contrib-clean": "~0.6.0"
},
"dependencies": {
"babel-plugin-transform-es2015-modules-systemjs": "^6.5.0",
"babel-preset-es2015": "^6.5.0",
"lodash": "~4.0.0"
},
"homepage": "https://github.com/raintank/kentik-app-poc#readme"
}
{
"name": "GenericDatasource",
"id": "datasource-plugin-genericdatasource",
"type": "datasource",
"module": "plugins/genericdatasource/datasource",
"staticRoot": ".",
"metrics": true,
"annotations": false,
"info": {
"description": "generic datsource plugin",
"author": {
"name": "Raintank Inc.",
"url": "http://raintank.io"
},
"version": "0.9.0",
"updated": "2016-02-10"
},
"dependencies": {
"grafanaVersion": "2.6.x",
"plugins": [ ]
}
}
.generic-datasource-query-row .query-keyword {
width: 75px;
}
\ No newline at end of file
export class GenericDatasource {
constructor(instanceSettings, $q, backendSrv) {
this.type = instanceSettings.type;
this.url = instanceSettings.url;
this.name = instanceSettings.name;
this.q = $q;
this.backendSrv = backendSrv;
}
// Called once per panel (graph)
query(options) {
var query = this.buildQueryParameters(options);
if (query.targets.length <= 0) {
return this.q.when([]);
}
return this.backendSrv.datasourceRequest({
url: this.url + '/query',
data: query,
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
}
// Required
// Used for testing datasource in datasource configuration pange
testDatasource() {
return this.backendSrv.datasourceRequest({
url: this.url + '/',
method: 'GET'
}).then(response => {
if (response.status === 200) {
return { status: "success", message: "Data source is working", title: "Success" };
}
});
}
// Optional
// Required for templating
metricFindQuery(options) {
return this.backendSrv.datasourceRequest({
url: this.url + '/search',
data: options,
method: 'POST',
headers: { 'Content-Type': 'application/json' }
}).then(this.mapToTextValue);
}
mapToTextValue(result) {
return _.map(result.data, (d, i) => {
return { text: d, value: i};
});
}
buildQueryParameters(options) {
//remove placeholder targets
options.targets = _.filter(options.targets, target => {
return target.target !== 'select metric';
});
return options;
}
}
import {GenericDatasource} from './datasource';
import {GenericDatasourceQueryCtrl} from './query_ctrl';
class GenericConfigCtrl {}
GenericConfigCtrl.templateUrl = 'partials/config.html';
class GenericQueryOptionsCtrl {}
GenericQueryOptionsCtrl.templateUrl = 'partials/query.options.html';
export {
GenericDatasource as Datasource,
GenericDatasourceQueryCtrl as QueryCtrl,
GenericConfigCtrl as ConfigCtrl,
GenericQueryOptionsCtrl as QueryOptionsCtrl
};
<datasource-http-settings current="ctrl.current">
</datasource-http-settings>
<query-editor-row ctrl="ctrl" class="generic-datasource-query-row">
<ul class="tight-form-list">
<li class="tight-form-item query-keyword">Query</li>
<li>
<metric-segment-model property="ctrl.target.target" get-options="ctrl.getOptions()" on-change="ctrl.onChangeInternal()" css-class="tight-form-item-xxlarge"></metric-segment-model>
</li>
</ul>
</query-editor-row>
<section class="grafana-metric-options" >
<div class="gf-form">
</div>
</section>
import {QueryCtrl} from 'app/plugins/sdk';
import './css/query-editor.css!'
export class GenericDatasourceQueryCtrl extends QueryCtrl {
constructor($scope, $injector, uiSegmentSrv) {
super($scope, $injector);
this.scope = $scope;
this.uiSegmentSrv = uiSegmentSrv;
this.target.target = this.target.target || 'select metric';
}
getOptions() {
return this.datasource.metricFindQuery(this.target)
.then(this.uiSegmentSrv.transformToSegments(false));
// Options have to be transformed by uiSegmentSrv to be usable by metric-segment-model directive
}
onChangeInternal() {
this.panelCtrl.refresh(); // Asks the panel to refresh data.
}
}
GenericDatasourceQueryCtrl.templateUrl = 'partials/query.editor.html';
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment