mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'example_datasource'
This commit is contained in:
commit
19f0f7535f
1
.gitignore
vendored
1
.gitignore
vendored
@ -37,3 +37,4 @@ profile.cov
|
||||
.notouch
|
||||
/pkg/cmd/grafana-cli/grafana-cli
|
||||
/pkg/cmd/grafana-server/grafana-server
|
||||
/examples/*/dist
|
13
examples/datasource-plugin-genericdatasource/.jscs.json
Normal file
13
examples/datasource-plugin-genericdatasource/.jscs.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"disallowImplicitTypeConversion": ["string"],
|
||||
"disallowKeywords": ["with"],
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
"requireSpacesInFunctionExpression": {
|
||||
"beforeOpeningCurlyBrace": true
|
||||
},
|
||||
"disallowSpacesInsideArrayBrackets": true,
|
||||
"disallowSpacesInsideParentheses": true,
|
||||
"validateIndentation": 2
|
||||
}
|
36
examples/datasource-plugin-genericdatasource/.jshintrc
Normal file
36
examples/datasource-plugin-genericdatasource/.jshintrc
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
54
examples/datasource-plugin-genericdatasource/Gruntfile.js
Normal file
54
examples/datasource-plugin-genericdatasource/Gruntfile.js
Normal file
@ -0,0 +1,54 @@
|
||||
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']);
|
||||
};
|
75
examples/datasource-plugin-genericdatasource/README.md
Normal file
75
examples/datasource-plugin-genericdatasource/README.md
Normal file
@ -0,0 +1,75 @@
|
||||
#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
|
37
examples/datasource-plugin-genericdatasource/package.json
Normal file
37
examples/datasource-plugin-genericdatasource/package.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"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"
|
||||
}
|
27
examples/datasource-plugin-genericdatasource/plugin.json
Normal file
27
examples/datasource-plugin-genericdatasource/plugin.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"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": [ ]
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
.generic-datasource-query-row .query-keyword {
|
||||
width: 75px;
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
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;
|
||||
}
|
||||
}
|
15
examples/datasource-plugin-genericdatasource/src/module.js
Normal file
15
examples/datasource-plugin-genericdatasource/src/module.js
Normal file
@ -0,0 +1,15 @@
|
||||
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
|
||||
};
|
@ -0,0 +1,2 @@
|
||||
<datasource-http-settings current="ctrl.current">
|
||||
</datasource-http-settings>
|
@ -0,0 +1,8 @@
|
||||
<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>
|
@ -0,0 +1,4 @@
|
||||
<section class="grafana-metric-options" >
|
||||
<div class="gf-form">
|
||||
</div>
|
||||
</section>
|
@ -0,0 +1,26 @@
|
||||
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';
|
||||
|
Loading…
Reference in New Issue
Block a user