feat: initial commit

This commit is contained in:
Thom Lamb
2026-06-23 11:18:54 -05:00
parent fc3ce9a074
commit 1bb980f070
790 changed files with 149255 additions and 218 deletions
+10
View File
@@ -0,0 +1,10 @@
FROM golang:1-alpine as builder
WORKDIR $GOPATH/src/github.com/loadimpact/k6
ADD . .
RUN apk --no-cache add --virtual .build-deps git make build-base && \
go get . && CGO_ENABLED=0 go install -a -ldflags '-s -w'
FROM alpine:3.7
RUN apk add --no-cache ca-certificates
COPY --from=builder /go/bin/k6 /usr/bin/k6
ENTRYPOINT ["k6"]
+19
View File
@@ -0,0 +1,19 @@
FROM grafana/grafana:5.2.4
USER root
RUN apt-get update && apt-get -y install curl
# Change the default data directory (otherwise grafana.db won't persist)
RUN mkdir /var/lib/grafanadb
ENV GF_PATHS_DATA /var/lib/grafanadb
# Init Grafana sqlite db and preconfigure our data source to be our influxdb k6 db
RUN bash -c '/run.sh & sleep 15 && curl -s -H "Content-Type: application/json" -X POST \
--data '"'"'{"name": "myinfluxdb", "type": "influxdb", "access": "proxy", "url": "http://influxdb:8086", "database": "k6", "isDefault": true}'"'"' \
http://admin:admin@localhost:3000/api/datasources \
&& kill -SIGINT %%'
CMD ["/run.sh"]
+6
View File
@@ -0,0 +1,6 @@
FROM influxdb:1.8
# Create a k6 db
RUN /bin/bash -c "influxd run & sleep 5 && influx -execute 'CREATE DATABASE k6' && kill %1 && sleep 5"
CMD ["influxd"]
@@ -0,0 +1,8 @@
apiVersion: 1
providers:
- name: 'default'
org_id: 1
folder: ''
type: 'file'
options:
path: /var/lib/grafana/dashboards
@@ -0,0 +1,9 @@
apiVersion: 1
datasources:
- name: k6influxdb
type: influxdb
access: proxy
database: k6
url: http://influxdb:8086
isDefault: true
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
version: "3.4"
networks:
k6:
grafana:
influxdb:
services:
influxdb:
container_name: influxdb
image: influxdb:1.8
networks:
- k6
- grafana
ports:
- "8086:8086"
environment:
- INFLUXDB_DB=k6
grafana:
container_name: grafana
image: grafana/grafana:7.5.5
networks:
- grafana
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_BASIC_ENABLED=false
- GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=grafana-datadog-datasource
- GF_INSTALL_PLUGINS=https://github.com/sirkjohannsen/grafana-datasource-datadog/archive/refs/heads/master.zip;datadog
volumes:
- ./grafana:/var/lib/grafana
k6:
container_name: k6
networks:
- k6
image: grafana/k6:latest
environment:
- K6_OUT=influxdb=http://influxdb:8086/k6
ports:
- "6565:6565"
volumes:
- ./testRunner:/testRunner
@@ -0,0 +1,53 @@
{{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ if gt (.Alerts.Resolved | len) 0 }}, RESOLVED:{{ .Alerts.Resolved | len }}{{ end }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }}
{{ define "__text_values_list" }}{{ $len := len .Values }}{{ if $len }}{{ $first := gt $len 1 }}{{ range $refID, $value := .Values -}}
{{ $refID }}={{ $value }}{{ if $first }}, {{ end }}{{ $first = false }}{{ end -}}
{{ else }}[no value]{{ end }}{{ end }}
{{ define "__text_alert_list" }}{{ range . }}
Value: {{ template "__text_values_list" . }}
Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}{{ if gt (len .GeneratorURL) 0 }}Source: {{ .GeneratorURL }}
{{ end }}{{ if gt (len .SilenceURL) 0 }}Silence: {{ .SilenceURL }}
{{ end }}{{ if gt (len .DashboardURL) 0 }}Dashboard: {{ .DashboardURL }}
{{ end }}{{ if gt (len .PanelURL) 0 }}Panel: {{ .PanelURL }}
{{ end }}{{ end }}{{ end }}
{{ define "default.title" }}{{ template "__subject" . }}{{ end }}
{{ define "default.message" }}{{ if gt (len .Alerts.Firing) 0 }}**Firing**
{{ template "__text_alert_list" .Alerts.Firing }}{{ if gt (len .Alerts.Resolved) 0 }}
{{ end }}{{ end }}{{ if gt (len .Alerts.Resolved) 0 }}**Resolved**
{{ template "__text_alert_list" .Alerts.Resolved }}{{ end }}{{ end }}
{{ define "__teams_text_alert_list" }}{{ range . }}
Value: {{ template "__text_values_list" . }}
Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}
Annotations:
{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}
{{ if gt (len .GeneratorURL) 0 }}Source: [{{ .GeneratorURL }}]({{ .GeneratorURL }})
{{ end }}{{ if gt (len .SilenceURL) 0 }}Silence: [{{ .SilenceURL }}]({{ .SilenceURL }})
{{ end }}{{ if gt (len .DashboardURL) 0 }}Dashboard: [{{ .DashboardURL }}]({{ .DashboardURL }})
{{ end }}{{ if gt (len .PanelURL) 0 }}Panel: [{{ .PanelURL }}]({{ .PanelURL }})
{{ end }}
{{ end }}{{ end }}
{{ define "teams.default.message" }}{{ if gt (len .Alerts.Firing) 0 }}**Firing**
{{ template "__teams_text_alert_list" .Alerts.Firing }}{{ if gt (len .Alerts.Resolved) 0 }}
{{ end }}{{ end }}{{ if gt (len .Alerts.Resolved) 0 }}**Resolved**
{{ template "__teams_text_alert_list" .Alerts.Resolved }}{{ end }}{{ end }}
Binary file not shown.
@@ -0,0 +1,3 @@
# Don't diff files in dist/
*.map binary
dist/** binary
@@ -0,0 +1 @@
node_modules
@@ -0,0 +1,14 @@
{
"esnext": true,
"disallowImplicitTypeConversion": ["string"],
"disallowKeywords": ["with"],
"disallowMultipleLineBreaks": true,
"disallowMixedSpacesAndTabs": true,
"disallowTrailingWhitespace": true,
"requireSpacesInFunctionExpression": {
"beforeOpeningCurlyBrace": true
},
"disallowSpacesInsideArrayBrackets": true,
"disallowSpacesInsideParentheses": true,
"validateIndentation": 2
}
@@ -0,0 +1,42 @@
{
"browser": true,
"esnext": true,
"bitwise":false,
"curly": true,
"eqnull": true,
"strict": false,
"devel": true,
"eqeqeq": true,
"forin": false,
"immed": true,
"supernew": true,
"expr": true,
"indent": 2,
"latedef": false,
"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,
"Promise": true,
"define": true,
"require": true,
"Chromath": false,
"setImmediate": true,
"describe": true,
"beforeEach": true,
"it": true,
"expect": true,
"EventSource": true
}
}
@@ -0,0 +1,4 @@
language: node_js
before_script:
- npm install -g grunt
script: grunt
@@ -0,0 +1,144 @@
module.exports = function(grunt) {
require('load-grunt-tasks')(grunt);
grunt.loadNpmTasks('grunt-execute');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.initConfig({
clean: ["dist"],
copy: {
src_to_dist: {
cwd: 'src',
expand: true,
src: ['**/*', '!**/*.js', '!**/*.scss'],
dest: 'dist'
},
vendor_to_dist: {
cwd: 'vendor',
expand: true,
src: ['**/*'],
dest: 'dist'
},
vendor_to_test: {
cwd: 'vendor',
expand: true,
src: ['**/*'],
dest: 'dist/test'
},
pluginDef: {
expand: true,
src: ['README.md'],
dest: 'dist'
}
},
watch: {
rebuild_all: {
files: ['src/**/*'],
tasks: ['watchTask'],
options: {spawn: false}
}
},
babel: {
options: {
sourceMap: true,
presets: ['es2015']
},
dist: {
options: {
plugins: ['transform-es2015-modules-systemjs', 'transform-es2015-for-of']
},
files: [{
cwd: 'src',
expand: true,
src: ['**/*.js'],
dest: 'dist',
ext:'.js'
}]
},
distTestNoSystemJs: {
files: [{
cwd: 'src',
expand: true,
src: ['**/*.js'],
dest: 'dist/test',
ext:'.js'
}]
},
distTestsSpecsNoSystemJs: {
files: [{
expand: true,
cwd: 'spec',
src: ['**/*.js'],
dest: 'dist/test/spec',
ext:'.js'
}]
}
},
jshint: {
source: {
files: {
src: ['src/**/*.js'],
}
},
options: {
jshintrc: true,
reporter: require('jshint-stylish'),
ignores: [
'node_modules/*',
'dist/*',
]
}
},
jscs: {
src: ['src/**/*.js'],
options: {
config: ".jscs.json",
},
},
mochaTest: {
test: {
options: {
reporter: 'spec'
},
src: [
'dist/test/spec/test-main.js',
'dist/test/spec/*_spec.js'
]
}
}
});
grunt.registerTask('default', [
'clean',
'copy:src_to_dist',
'copy:pluginDef',
'jshint',
'jscs',
'babel',
'copy:vendor_to_dist',
'copy:vendor_to_test',
'mochaTest'
]);
grunt.registerTask('dev', ['default', 'watch']);
grunt.registerTask('watchTask', [
// 'clean',
'copy:src_to_dist',
'copy:pluginDef',
'babel',
'copy:vendor_to_dist',
'jshint',
'jscs'
]);
};
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Grafana
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,30 @@
# Datadog Grafana Datasource Plugin
![Build Passing](https://travis-ci.org/voidfiles/grafana-datasource-datadog.svg?branch=master)
Datadog has a nice dashboard system, but I was having trouble creating a dashboard that combined multiple datasources and grafana was the easiest way to bring it all together.
## To Contribute
Fork, make your change, and submit a Pull Request. Make sure you add tests where approprate, and don't break exisisting tests.
## Usage
### Query Editor
![query editor](https://cloud.githubusercontent.com/assets/4932851/22584216/337c26aa-ea02-11e6-84a8-20207bedd15b.png)
It's easy - select aggregation and metric. If you want to filter result, select one or more tags.
DataDog datasource supports advanced functions. Select it from dropdown and arrange by clicking function name.
### Annotations
![annotations editor](https://cloud.githubusercontent.com/assets/4932851/22584362/1705598c-ea03-11e6-94a0-b51435d74420.png)
Annotations allows to get events from DataDog and display it on graph. You can filter events by sources, tags and priority.
### Templating
There are two options for getting values of tempate variable - metrics and tags. For fetching list of available metrics leave _Query_ field blank or specify `*`. For tags type `tag`. Filter results by using _Regex_ field. Multi-value variables are supported for using in tags (selected values will be converted into comma separated list of tags).
#### Ad-hoc filters
There's new special type of template variable in Grafana called _Ad-hoc filters_. This variable will apply to _all_ DataDog queries on dashboard (all with specified datasource). This alollows to use it like a quick filter. Ad-hoc variable for DataDog fetches all key-value pairs from tags, for example, `region:east, region:west`, and use it as queries tags. To create this variable, select _Ad-hoc filters_ type and choose your DataDog datasource. You can set any name to this variable.
+30
View File
@@ -0,0 +1,30 @@
# Datadog Grafana Datasource Plugin
![Build Passing](https://travis-ci.org/voidfiles/grafana-datasource-datadog.svg?branch=master)
Datadog has a nice dashboard system, but I was having trouble creating a dashboard that combined multiple datasources and grafana was the easiest way to bring it all together.
## To Contribute
Fork, make your change, and submit a Pull Request. Make sure you add tests where approprate, and don't break exisisting tests.
## Usage
### Query Editor
![query editor](https://cloud.githubusercontent.com/assets/4932851/22584216/337c26aa-ea02-11e6-84a8-20207bedd15b.png)
It's easy - select aggregation and metric. If you want to filter result, select one or more tags.
DataDog datasource supports advanced functions. Select it from dropdown and arrange by clicking function name.
### Annotations
![annotations editor](https://cloud.githubusercontent.com/assets/4932851/22584362/1705598c-ea03-11e6-94a0-b51435d74420.png)
Annotations allows to get events from DataDog and display it on graph. You can filter events by sources, tags and priority.
### Templating
There are two options for getting values of tempate variable - metrics and tags. For fetching list of available metrics leave _Query_ field blank or specify `*`. For tags type `tag`. Filter results by using _Regex_ field. Multi-value variables are supported for using in tags (selected values will be converted into comma separated list of tags).
#### Ad-hoc filters
There's new special type of template variable in Grafana called _Ad-hoc filters_. This variable will apply to _all_ DataDog queries on dashboard (all with specified datasource). This alollows to use it like a quick filter. Ad-hoc variable for DataDog fetches all key-value pairs from tags, for example, `region:east, region:west`, and use it as queries tags. To create this variable, select _Ad-hoc filters_ type and choose your DataDog datasource. You can set any name to this variable.
@@ -0,0 +1,116 @@
'use strict';
System.register(['angular', 'lodash', 'jquery', './dfunc'], function (_export, _context) {
"use strict";
var angular, _, $, dfunc;
function getAllFunctionNames(categories) {
return _.reduce(categories, function (list, category) {
_.each(category, function (func) {
list.push(func.name);
});
return list;
}, []);
}
function createFunctionDropDownMenu(categories) {
return _.map(categories, function (list, category) {
return {
text: category,
submenu: _.map(list, function (value) {
return {
text: value.name,
click: "ctrl.addFunction('" + value.name + "')"
};
})
};
});
}
return {
setters: [function (_angular) {
angular = _angular.default;
}, function (_lodash) {
_ = _lodash.default;
}, function (_jquery) {
$ = _jquery.default;
}, function (_dfunc) {
dfunc = _dfunc.default;
}],
execute: function () {
angular.module('grafana.directives').directive('datadogAddFunc', function ($compile) {
var inputTemplate = '<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>';
var buttonTemplate = '<a class="gf-form-label query-part dropdown-toggle"' + ' tabindex="1" gf-dropdown="functionMenu" data-toggle="dropdown">' + '<i class="fa fa-plus"></i></a>';
return {
link: function link($scope, elem) {
var categories = dfunc.getCategories();
var allFunctions = getAllFunctionNames(categories);
var ctrl = $scope.ctrl;
$scope.functionMenu = createFunctionDropDownMenu(categories);
var $input = $(inputTemplate);
var $button = $(buttonTemplate);
$input.appendTo(elem);
$button.appendTo(elem);
$input.attr('data-provide', 'typeahead');
$input.typeahead({
source: allFunctions,
minLength: 1,
items: 10,
updater: function updater(value) {
var funcDef = dfunc.getFuncDef(value);
if (!funcDef) {
// try find close match
value = value.toLowerCase();
funcDef = _.find(allFunctions, function (funcName) {
return funcName.toLowerCase().indexOf(value) === 0;
});
if (!funcDef) {
return;
}
}
$scope.$apply(function () {
ctrl.addFunction(funcDef);
});
$input.trigger('blur');
return '';
}
});
$button.click(function () {
$button.hide();
$input.show();
$input.focus();
});
$input.keyup(function () {
elem.toggleClass('open', $input.val() === '');
});
$input.blur(function () {
// clicking the function dropdown menu wont
// work if you remove class at once
setTimeout(function () {
$input.val('');
$input.hide();
$button.show();
elem.removeClass('open');
}, 200);
});
$compile(elem.contents())($scope);
}
};
});
}
};
});
//# sourceMappingURL=add_datadog_func.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,433 @@
'use strict';
System.register(['lodash', './showdown.min.js', './query_builder'], function (_export, _context) {
"use strict";
var _, showdown, queryBuilder, _createClass, DataDogDatasource;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
/*
* Convert tags to key-value pairs
* [region:east, region:nw] => {region: [east, nw]}
*/
function mapTagsToKVPairs(tags) {
var kv_tags = _.filter(tags, function (tag) {
return tag.indexOf(':') !== -1;
});
var kv_pairs = kv_tags.map(function (tag) {
return tag.split(':', 2); // Limit to 2
});
var kv_object = {};
kv_pairs.forEach(function (pair) {
var key = pair[0];
var val = pair[1];
if (kv_object[key]) {
kv_object[key].push(val);
} else {
kv_object[key] = [val];
}
});
return kv_object;
}
/*
* Convert DataDog event text from markdown to pure HTML
* http://docs.datadoghq.com/guides/markdown/
*/
function convertDataDogMdToHtml(str) {
var MD_START = '%%%\n';
var MD_END = '\n%%%';
var md_start_index = str.indexOf(MD_START) + MD_START.length;
var md_end_index = str.indexOf(MD_END);
var md = str.substring(md_start_index, md_end_index);
md = removeImagesFromDataDogMarkdown(md);
var converter = new showdown.Converter();
return converter.makeHtml(md);
}
function isDataDogMarkdown(str) {
var MD_START = '%%%\n';
return str.indexOf(MD_START) >= 0;
}
function removeImagesFromDataDogMarkdown(str) {
var dataDogImagePattern = /\[{0,1}\!\[.*\]\(https{0,1}\:\/\/p\.datadoghq\.com\/snapshot.+\)/g;
return str.replace(dataDogImagePattern, '');
}
function dataDogTagFormat(value) {
if (_.isArray(value)) {
return value.join(',');
}
return value;
}
return {
setters: [function (_lodash) {
_ = _lodash.default;
}, function (_showdownMinJs) {
showdown = _showdownMinJs.default;
}, function (_query_builder) {
queryBuilder = _query_builder;
}],
execute: function () {
_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;
};
}();
_export('DataDogDatasource', DataDogDatasource = function () {
function DataDogDatasource(instanceSettings, backendSrv, templateSrv) {
_classCallCheck(this, DataDogDatasource);
this.type = instanceSettings.type;
this.url = instanceSettings.url;
this.name = instanceSettings.name;
this.api_key = instanceSettings.jsonData.api_key;
this.application_key = instanceSettings.jsonData.app_key;
this.supportMetrics = true;
this.backendSrv = backendSrv;
this.templateSrv = templateSrv;
this._cached_metrics = false;
}
// Function to check Datasource health
_createClass(DataDogDatasource, [{
key: 'testDatasource',
value: function testDatasource() {
return this.invokeDataDogApiRequest('/downtime').then(function () {
return {
status: "success",
title: "Success",
message: "Data source is working"
};
}).catch(function (error) {
var message = "Connection error";
if (error && error.message) {
message = error.message;
}
return {
status: "error",
title: "Error",
message: message
};
});
}
}, {
key: 'tagFindQuery',
value: function tagFindQuery() {
return this.getTagsFromCache().then(function (tags) {
return _.map(tags, function (hosts, tag) {
return {
text: tag,
value: tag
};
});
});
}
}, {
key: 'metricFindQuery',
value: function metricFindQuery(query) {
var _this = this;
if (query === 'tag') {
return this.tagFindQuery();
}
if (this._cached_metrics) {
return Promise.resolve(this._cached_metrics);
}
if (this.fetching) {
return this.fetching;
}
var d = new Date();
d.setDate(d.getDate() - 1);
var from = Math.floor(d.getTime() / 1000);
this.fetching = this.getMetrics(from).then(function (metrics) {
_this._cached_metrics = _.map(metrics, function (metric) {
return {
text: metric,
value: metric
};
});
return _this._cached_metrics;
});
return this.fetching;
}
}, {
key: 'getTagKeys',
value: function getTagKeys() {
return this.getTagsFromCache().then(function (tagsHosts) {
var tags = Object.keys(tagsHosts);
var kv = mapTagsToKVPairs(tags);
var grafanaTags = Object.keys(kv);
return grafanaTags.map(function (tag) {
return {
text: tag,
value: tag
};
});
});
}
}, {
key: 'getTagValues',
value: function getTagValues(options) {
return this.getTagsFromCache().then(function (tagsHosts) {
var tags = Object.keys(tagsHosts);
var kv = mapTagsToKVPairs(tags);
var grafanaValues = kv[options.key];
return grafanaValues.map(function (val) {
return {
text: val,
value: val
};
});
});
}
}, {
key: 'query',
value: function query(options) {
var from = Math.floor(options.range.from.valueOf() / 1000);
var to = Math.floor(options.range.to.valueOf() / 1000);
var targets = options.targets.filter(function (t) {
return !t.hide;
});
if (targets.length <= 0) {
return Promise.resolve({ data: [] });
}
// add global adhoc filters
var adhocFilters = this.templateSrv.getAdhocFilters(this.name);
var queries = _.map(targets, function (target) {
var query = void 0;
if (target.rawQuery) {
query = target.query;
} else {
query = queryBuilder.buildQuery(target, adhocFilters);
}
return query;
});
var queryString = queries.join(',');
queryString = this.templateSrv.replace(queryString, options.scopedVars, dataDogTagFormat);
var params = {
from: from,
to: to,
query: queryString
};
return this.invokeDataDogApiRequest('/query', params).then(function (result) {
var dataResponse = _.map(result.series, function (series, i) {
var target = targets[i];
return {
'target': target.alias || series.expression,
'datapoints': _.map(series.pointlist, function (point) {
return [point[1], point[0]];
})
};
});
return { data: dataResponse };
});
}
}, {
key: 'annotationQuery',
value: function annotationQuery(options) {
var timeFrom = Math.floor(options.range.from.valueOf() / 1000);
var timeTo = Math.floor(options.range.to.valueOf() / 1000);
var _options$annotation = options.annotation,
priority = _options$annotation.priority,
sources = _options$annotation.sources,
tags = _options$annotation.tags;
return this.getEventStream(timeFrom, timeTo, priority, sources, tags).then(function (eventStreams) {
var eventAnnotations = eventStreams.map(function (eventStream) {
var allEvents = eventStream.children;
var filteredEvents = _.filter(allEvents, function (event) {
return event.alert_type !== 'success';
});
return _.map(filteredEvents, function (event) {
var renderedText = eventStream.text;
if (isDataDogMarkdown(eventStream.text)) {
renderedText = convertDataDogMdToHtml(eventStream.text);
}
return {
annotation: options.annotation,
time: event.date_happened * 1000,
title: eventStream.title,
text: renderedText,
tags: eventStream.tags
};
});
});
return _.flatten(eventAnnotations);
});
}
}, {
key: 'getHosts',
value: function getHosts() {
return this.searchEntities('hosts');
}
}, {
key: 'searchEntities',
value: function searchEntities(entity) {
var params = { q: '' };
if (entity) {
params.q = entity + ':';
}
return this.invokeDataDogApiRequest('/search', params).then(function (result) {
if (result && result.results) {
return result.results[entity];
}
});
}
}, {
key: 'getMetrics',
value: function getMetrics(timeFrom) {
var params = {};
if (timeFrom) {
params.from = timeFrom;
}
return this.invokeDataDogApiRequest('/metrics', params).then(function (result) {
if (result.metrics) {
return result.metrics;
} else {
return [];
}
});
}
}, {
key: 'getTagsHosts',
value: function getTagsHosts() {
return this.invokeDataDogApiRequest('/tags/hosts').then(function (result) {
if (result && result.tags) {
return result.tags;
}
});
}
}, {
key: 'getTagsFromCache',
value: function getTagsFromCache() {
var _this2 = this;
var getTags = void 0;
if (this._cached_tags && this._cached_tags.length) {
getTags = Promise.resolve(this._cached_tags);
} else {
getTags = this.getTagsHosts().then(function (tags) {
_this2._cached_tags = tags;
return tags;
});
}
return getTags;
}
}, {
key: 'getEventStream',
value: function getEventStream(timeFrom, timeTo, priority, sources, tags) {
var params = {
start: timeFrom,
end: timeTo
};
if (priority) {
params.priority = priority;
}
if (sources) {
params.sources = sources;
}
if (tags) {
params.tags = tags;
}
return this.invokeDataDogApiRequest('/events', params).then(function (result) {
if (result.events) {
return result.events;
} else {
return [];
}
});
}
}, {
key: 'invokeDataDogApiRequest',
value: function invokeDataDogApiRequest(url) {
var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// Set auth params
params.api_key = this.api_key;
params.application_key = this.application_key;
return this.backendSrv.datasourceRequest({
method: 'GET',
url: this.url + url,
params: params
}).then(function (response) {
if (response.data) {
return response.data;
} else {
throw { message: 'DataDog API request error' };
}
}).catch(function (error) {
var message = 'DataDog API request error';
if (error.statusText) {
message = error.status + ' ' + error.statusText;
throw { message: message };
} else if (error.err.statusText) {
throw { message: error.err.statusText };
} else {
throw { message: message };
}
});
}
}]);
return DataDogDatasource;
}());
_export('DataDogDatasource', DataDogDatasource);
}
};
});
//# sourceMappingURL=datasource.js.map
File diff suppressed because one or more lines are too long
+293
View File
@@ -0,0 +1,293 @@
'use strict';
System.register(['lodash'], function (_export, _context) {
"use strict";
var _, index, categories, createFuncInstance, getFuncDef, getCategories, foo;
function addFuncDef(funcDef) {
funcDef.params = funcDef.params || [];
funcDef.defaultParams = funcDef.defaultParams || [];
funcDef.append = funcDef.append || false;
if (funcDef.category) {
funcDef.category.push(funcDef);
}
index[funcDef.name] = funcDef;
index[funcDef.name] = funcDef;
}
function FuncInstance(funcDef, options) {
this.def = funcDef;
this.params = [];
if (options && options.withDefaultParams) {
this.params = funcDef.defaultParams.slice(0);
}
this.updateText();
}
return {
setters: [function (_lodash) {
_ = _lodash.default;
}],
execute: function () {
index = [];
categories = {
Arithmatic: [],
Interpolation: [],
Timeshift: [],
Rate: [],
Smoothing: [],
Rollup: [],
Rank: [],
Count: [],
Regression: [],
Algorithms: []
};
addFuncDef({
name: 'abs',
category: categories.Arithmatic
});
addFuncDef({
name: 'log2',
category: categories.Arithmatic
});
addFuncDef({
name: 'cumsum',
category: categories.Arithmatic
});
addFuncDef({
name: 'integral',
category: categories.Arithmatic
});
addFuncDef({
name: 'fill',
category: categories.Interpolation,
params: [{ name: "fill", type: 'string', options: ['null', 'zero', 'linear', 'last'] }, { name: "limit", type: 'int', optional: true }],
defaultParams: ['zero'],
append: true
});
addFuncDef({
name: 'hour_before',
category: categories.Timeshift
});
addFuncDef({
name: 'day_before',
category: categories.Timeshift
});
addFuncDef({
name: 'week_before',
category: categories.Timeshift
});
addFuncDef({
name: 'month_before',
category: categories.Timeshift
});
addFuncDef({
name: 'per_second',
category: categories.Rate
});
addFuncDef({
name: 'per_minute',
category: categories.Rate
});
addFuncDef({
name: 'per_hour',
category: categories.Rate
});
addFuncDef({
name: 'dt',
category: categories.Rate
});
addFuncDef({
name: 'diff',
category: categories.Rate
});
addFuncDef({
name: 'derivative',
category: categories.Rate
});
addFuncDef({
name: 'ewma_3',
category: categories.Smoothing
});
addFuncDef({
name: 'ewma_5',
category: categories.Smoothing
});
addFuncDef({
name: 'ewma_10',
category: categories.Smoothing
});
addFuncDef({
name: 'ewma_20',
category: categories.Smoothing
});
addFuncDef({
name: 'median_3',
category: categories.Smoothing
});
addFuncDef({
name: 'median_5',
category: categories.Smoothing
});
addFuncDef({
name: 'median_7',
category: categories.Smoothing
});
addFuncDef({
name: 'median_9',
category: categories.Smoothing
});
addFuncDef({
name: 'rollup',
category: categories.Rollup,
params: [{ name: "rollup", type: 'string', options: ['sum', 'avg', 'min', 'max'] }, { name: "period", type: 'int' }],
defaultParams: ['sum', 600],
append: true
});
addFuncDef({
name: 'count_nonzero',
category: categories.Count
});
addFuncDef({
name: 'count_not_null',
category: categories.Count
});
addFuncDef({
name: 'top',
category: categories.Rank,
params: [{ name: "top", type: 'int', options: [5, 10, 15] }, { name: "aggr", type: 'string', options: ['mean', 'min', 'max', 'area', 'l2norm', 'last'] }, { name: "dir", type: 'string', options: ['asc', 'desc'] }],
defaultParams: [5, 'mean', 'dir']
});
addFuncDef({
name: 'robust_trend',
category: categories.Regression
});
addFuncDef({
name: 'piecewise_constant',
category: categories.Regression
});
addFuncDef({
name: 'anomalies',
category: categories.Algorithms,
params: [{ name: "anomalies", type: 'string', options: ['basic', 'agile', 'robust', 'adaptive'] }, { name: "bonds", type: 'int', options: [1, 2, 3, 4, 5, 6] }],
defaultParams: ['basic', 2]
});
addFuncDef({
name: 'anomalies',
category: categories.Algorithms,
params: [{ name: "anomalies", type: 'string', options: ['basic', 'agile', 'robust', 'adaptive'] }, { name: "bonds", type: 'int', options: [1, 2, 3, 4, 5, 6] }],
defaultParams: ['basic', 2]
});
addFuncDef({
name: 'outliers',
category: categories.Algorithms,
params: [{ name: "outliers", type: 'string', options: ['DBSCAN', 'MAD', 'scaledDBSCAN', 'scaledMAD'] }, { name: "tolerance", type: 'int', options: [0.33, 0.5, 1, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0] }],
defaultParams: ['basic', 2]
});
_.each(categories, function (funcList, catName) {
categories[catName] = _.sortBy(funcList, 'name');
});FuncInstance.prototype.render = function (metricExp) {
var str = this.def.name + '(';
var parameters = _.map(this.params, function (value) {
return value;
});
if (metricExp && !this.def.append) {
parameters.unshift(metricExp);
}
var funcRender = str + parameters.join(', ') + ')';
console.log(funcRender, this.def.append);
if (this.def.append) {
return metricExp + '.' + funcRender;
} else {
return funcRender;
}
};
FuncInstance.prototype.updateParam = function (strValue, index) {
// handle optional parameters
// if string contains ',' and next param is optional, split and update both
if (strValue === '' && this.def.params[index].optional) {
this.params.splice(index, 1);
} else {
this.params[index] = strValue;
}
this.updateText();
};
FuncInstance.prototype.updateText = function () {
if (this.params.length === 0) {
this.text = this.def.name + '()';
return;
}
var text = this.def.name + '(';
text += this.params.join(', ');
text += ')';
this.text = text;
};
createFuncInstance = function createFuncInstance(funcDef, options) {
if (_.isString(funcDef)) {
if (!index[funcDef]) {
throw { message: 'Method not found ' + name };
}
funcDef = index[funcDef];
}
return new FuncInstance(funcDef, options);
};
getFuncDef = function getFuncDef(name) {
return index[name];
};
getCategories = function getCategories() {
return categories;
};
_export('default', foo = {
createFuncInstance: createFuncInstance,
getFuncDef: getFuncDef,
getCategories: getCategories
});
}
};
});
//# sourceMappingURL=dfunc.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,251 @@
'use strict';
System.register(['angular', 'lodash', 'jquery'], function (_export, _context) {
"use strict";
var angular, _, $;
return {
setters: [function (_angular) {
angular = _angular.default;
}, function (_lodash) {
_ = _lodash.default;
}, function (_jquery) {
$ = _jquery.default;
}],
execute: function () {
'use strict';
angular.module('grafana.directives').directive('datadogFuncEditor', function ($compile, templateSrv) {
var funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
var paramTemplate = '<input type="text" style="display:none"' + ' class="input-mini tight-form-func-param"></input>';
var funcControlsTemplate = '<div class="tight-form-func-controls">' + '<span class="pointer fa fa-arrow-left"></span>' + '<span class="pointer fa fa-question-circle"></span>' + '<span class="pointer fa fa-remove" ></span>' + '<span class="pointer fa fa-arrow-right"></span>' + '</div>';
return {
restrict: 'A',
link: function postLink($scope, elem) {
var $funcLink = $(funcSpanTemplate);
var $funcControls = $(funcControlsTemplate);
var ctrl = $scope.ctrl;
var func = $scope.func;
var funcDef = func.def;
var scheduledRelink = false;
var paramCountAtLink = 0;
function clickFuncParam(paramIndex) {
/*jshint validthis:true */
var $link = $(this);
var $input = $link.next();
$input.val(func.params[paramIndex]);
$input.css('width', $link.width() + 16 + 'px');
$link.hide();
$input.show();
$input.focus();
$input.select();
var typeahead = $input.data('typeahead');
if (typeahead) {
$input.val('');
typeahead.lookup();
}
}
function scheduledRelinkIfNeeded() {
if (paramCountAtLink === func.params.length) {
return;
}
if (!scheduledRelink) {
scheduledRelink = true;
setTimeout(function () {
relink();
scheduledRelink = false;
}, 200);
}
}
function inputBlur(paramIndex) {
/*jshint validthis:true */
var $input = $(this);
var $link = $input.prev();
var newValue = $input.val();
if (newValue !== '' || func.def.params[paramIndex].optional) {
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
func.updateParam($input.val(), paramIndex);
scheduledRelinkIfNeeded();
$scope.$apply(function () {
ctrl.targetChanged();
});
$input.hide();
$link.show();
}
}
function inputKeyPress(paramIndex, e) {
/*jshint validthis:true */
if (e.which === 13) {
inputBlur.call(this, paramIndex);
}
}
function inputKeyDown() {
/*jshint validthis:true */
this.style.width = (3 + this.value.length) * 8 + 'px';
}
function addTypeahead($input, paramIndex) {
$input.attr('data-provide', 'typeahead');
var options = funcDef.params[paramIndex].options;
if (funcDef.params[paramIndex].type === 'int') {
options = _.map(options, function (val) {
return val.toString();
});
}
$input.typeahead({
source: options,
minLength: 0,
items: 20,
updater: function updater(value) {
setTimeout(function () {
inputBlur.call($input[0], paramIndex);
}, 0);
return value;
}
});
var typeahead = $input.data('typeahead');
typeahead.lookup = function () {
this.query = this.$element.val() || '';
return this.process(this.source);
};
}
function toggleFuncControls() {
var targetDiv = elem.closest('.tight-form');
if (elem.hasClass('show-function-controls')) {
elem.removeClass('show-function-controls');
targetDiv.removeClass('has-open-function');
$funcControls.hide();
return;
}
elem.addClass('show-function-controls');
targetDiv.addClass('has-open-function');
$funcControls.show();
}
function addElementsAndCompile() {
$funcControls.appendTo(elem);
$funcLink.appendTo(elem);
_.each(funcDef.params, function (param, index) {
if (param.optional && func.params.length <= index) {
return;
}
if (index > 0) {
$('<span>, </span>').appendTo(elem);
}
var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
var $paramLink = $('<a ng-click="" class="datadog-func-param-link">' + paramValue + '</a>');
var $input = $(paramTemplate);
paramCountAtLink++;
$paramLink.appendTo(elem);
$input.appendTo(elem);
$input.blur(_.partial(inputBlur, index));
$input.keyup(inputKeyDown);
$input.keypress(_.partial(inputKeyPress, index));
$paramLink.click(_.partial(clickFuncParam, index));
if (funcDef.params[index].options) {
addTypeahead($input, index);
}
});
$('<span>)</span>').appendTo(elem);
$compile(elem.contents())($scope);
}
function ifJustAddedFocusFistParam() {
if ($scope.func.added) {
$scope.func.added = false;
setTimeout(function () {
elem.find('.datadog-func-param-link').first().click();
}, 10);
}
}
function registerFuncControlsToggle() {
$funcLink.click(toggleFuncControls);
}
function registerFuncControlsActions() {
$funcControls.click(function (e) {
var $target = $(e.target);
if ($target.hasClass('fa-remove')) {
toggleFuncControls();
$scope.$apply(function () {
ctrl.removeFunction($scope.func);
});
return;
}
if ($target.hasClass('fa-arrow-left')) {
$scope.$apply(function () {
_.move(ctrl.functions, $scope.$index, $scope.$index - 1);
ctrl.targetChanged();
});
return;
}
if ($target.hasClass('fa-arrow-right')) {
$scope.$apply(function () {
_.move(ctrl.functions, $scope.$index, $scope.$index + 1);
ctrl.targetChanged();
});
return;
}
if ($target.hasClass('fa-question-circle')) {
window.open("http://docs.datadoghq.com/graphing/#apply-more-advanced-functions", '_blank');
return;
}
});
}
function relink() {
elem.children().remove();
addElementsAndCompile();
ifJustAddedFocusFistParam();
registerFuncControlsToggle();
registerFuncControlsActions();
}
relink();
}
};
});
}
};
});
//# sourceMappingURL=func_editor.js.map
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

+51
View File
@@ -0,0 +1,51 @@
'use strict';
System.register(['./datasource', './query_ctrl'], function (_export, _context) {
"use strict";
var DataDogDatasource, DataDogQueryCtrl, DataDogConfigCtrl, DataDogQueryOptionsCtrl, DataDogAnnotationsQueryCtrl;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
return {
setters: [function (_datasource) {
DataDogDatasource = _datasource.DataDogDatasource;
}, function (_query_ctrl) {
DataDogQueryCtrl = _query_ctrl.DataDogQueryCtrl;
}],
execute: function () {
_export('ConfigCtrl', DataDogConfigCtrl = function DataDogConfigCtrl() {
_classCallCheck(this, DataDogConfigCtrl);
});
DataDogConfigCtrl.templateUrl = "partials/config.html";
_export('QueryOptionsCtrl', DataDogQueryOptionsCtrl = function DataDogQueryOptionsCtrl() {
_classCallCheck(this, DataDogQueryOptionsCtrl);
});
DataDogQueryOptionsCtrl.templateUrl = "partials/query.options.html";
_export('AnnotationsQueryCtrl', DataDogAnnotationsQueryCtrl = function DataDogAnnotationsQueryCtrl() {
_classCallCheck(this, DataDogAnnotationsQueryCtrl);
});
DataDogAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';
_export('Datasource', DataDogDatasource);
_export('QueryCtrl', DataDogQueryCtrl);
_export('ConfigCtrl', DataDogConfigCtrl);
_export('QueryOptionsCtrl', DataDogQueryOptionsCtrl);
_export('AnnotationsQueryCtrl', DataDogAnnotationsQueryCtrl);
}
};
});
//# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../src/module.js"],"names":["DataDogDatasource","DataDogQueryCtrl","DataDogConfigCtrl","templateUrl","DataDogQueryOptionsCtrl","DataDogAnnotationsQueryCtrl"],"mappings":";;;;;;;;;;;;;;;AAAQA,uB,eAAAA,iB;;AACAC,sB,eAAAA,gB;;;4BAEFC,iB;;;;AACNA,wBAAkBC,WAAlB,GAAgC,sBAAhC;;kCAEMC,uB;;;;AACNA,8BAAwBD,WAAxB,GAAsC,6BAAtC;;sCAEME,2B;;;;AACNA,kCAA4BF,WAA5B,GAA0C,kCAA1C;;4BAGEH,iB;;2BACAC,gB;;4BACAC,iB;;kCACAE,uB;;sCACAC,2B","file":"module.js","sourcesContent":["import {DataDogDatasource} from './datasource';\nimport {DataDogQueryCtrl} from './query_ctrl';\n\nclass DataDogConfigCtrl {}\nDataDogConfigCtrl.templateUrl = \"partials/config.html\";\n\nclass DataDogQueryOptionsCtrl {}\nDataDogQueryOptionsCtrl.templateUrl = \"partials/query.options.html\";\n\nclass DataDogAnnotationsQueryCtrl {}\nDataDogAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';\n\nexport {\n DataDogDatasource as Datasource,\n DataDogQueryCtrl as QueryCtrl,\n DataDogConfigCtrl as ConfigCtrl,\n DataDogQueryOptionsCtrl as QueryOptionsCtrl,\n DataDogAnnotationsQueryCtrl as AnnotationsQueryCtrl\n};\n"]}
@@ -0,0 +1,29 @@
<div class="gf-form-group">
<div class="gf-form max-width-30">
<label class="gf-form-label width-7">Sources</label>
<input type="text" class="gf-form-input"
ng-model='ctrl.annotation.sources'
placeholder="">
</input>
<info-popover mode="right-absolute">
A comma separated string of sources
</info-popover>
</div>
<div class="gf-form max-width-30">
<label class="gf-form-label width-7">Tags</label>
<input type="text" class="gf-form-input"
ng-model='ctrl.annotation.tags'
placeholder="">
</input>
<info-popover mode="right-absolute">
A comma separated string of tags
</info-popover>
</div>
<div class="gf-form">
<label class="gf-form-label width-7">Priority</label>
<select class="gf-form-input width-7"
ng-model="ctrl.annotation.priority"
ng-options="p.v as p.n for p in [{n: 'None', v: undefined}, {n: 'Low', v: 'low'}, {n: 'Normal', v: 'normal'}]">
</select>
</div>
</div>
@@ -0,0 +1,35 @@
<datasource-http-settings
current="ctrl.current"
suggest-url="https://app.datadoghq.com/api/v1">
</datasource-http-settings>
<div class="gf-form-group">
<h3 class="page-heading">DataDog settings</h3>
<p>You can manage your account's API and application keys <a href="https://app.datadoghq.com/account/settings#api">here</a>.</p>
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-30">
<span class="gf-form-label width-7">API Key</span>
<input class="gf-form-input" type="text"
ng-model='ctrl.current.jsonData.api_key' min-length="0"
required></input>
<info-popover mode="right-absolute">
<p>Specify your datadog API Key</p>
</info-popover>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form max-width-30">
<span class="gf-form-label width-7">APP Key</span>
<input class="gf-form-input" type="text"
ng-model='ctrl.current.jsonData.app_key' min-length="0"
required></input>
<info-popover mode="right-absolute">
<p>Specify your datadog APP Key</p>
</info-popover>
</div>
</div>
</div>
</div>
@@ -0,0 +1,72 @@
<query-editor-row query-ctrl="ctrl" has-text-edit-mode="true" can-collapse="true">
<div class="gf-form" ng-if="ctrl.target.rawQuery">
<input type="text" class="gf-form-input" ng-model="ctrl.target.query" spellcheck="false" ng-blur="ctrl.refresh()"></input>
</div>
<div ng-if="!ctrl.target.rawQuery">
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">AGG</label>
<metric-segment segment="ctrl.aggregationSegment" get-options="ctrl.getAggregations()" on-change="ctrl.aggregationChanged()"></metric-segment>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">METRIC</label>
<metric-segment segment="ctrl.metricSegment" get-options="ctrl.getMetrics()" on-change="ctrl.metricChanged()"></metric-segment>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">POST</label>
<metric-segment segment="ctrl.asSegment" get-options="ctrl.getAs()" on-change="ctrl.asChanged()"></metric-segment>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">SCOPE</label>
</div>
<div class="gf-form" ng-repeat="segment in ctrl.tagSegments">
<metric-segment segment="segment" get-options="ctrl.getTags(segment, $index)" on-change="ctrl.tagSegmentUpdated(segment, $index)"></metric-segment>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">FUNCS</label>
</div>
<div ng-repeat="func in ctrl.functions" class="gf-form">
<span datadog-func-editor class="gf-form-label query-part"></span>
</div>
<div class="gf-form dropdown">
<span datadog-add-func></span>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form max-width-30">
<label class="gf-form-label query-keyword width-7">ALIAS BY</label>
<input type="text" class="gf-form-input" ng-model="ctrl.target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="ctrl.refresh()">
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
</div>
</query-editor-row>
@@ -0,0 +1,2 @@
<section class="grafana-metric-options">
</section>
@@ -0,0 +1,34 @@
{
"id": "grafana-datadog-datasource",
"name": "DataDog",
"type": "datasource",
"metrics": true,
"annotations": true,
"staticRoot": ".",
"info": {
"description": "datasource plugin for DataDog",
"author": {
"name": "Alex Kessinger",
"url": "http://rumproarious.com"
},
"keywords": ["datadog"],
"logos": {
"small": "img/datadog.png",
"large": "img/datadog.png"
},
"links": [
{"name": "GitHub", "url": "https://github.com/voidfiles/grafana-datasource-datagog"},
{"name": "License", "url": "https://github.com/voidfiles/grafana-datasource-datagog/blob/master/LICENSE"}
],
"version": "0.0.1",
"updated": "2016-12-18"
},
"dependencies": {
"grafanaVersion": "3.x.x",
"plugins": [ ]
}
}
@@ -0,0 +1,85 @@
'use strict';
System.register(['lodash', './dfunc'], function (_export, _context) {
"use strict";
var _, dfunc;
function buildQuery(target, adhocFilters) {
var aggregation = target.aggregation,
metric = target.metric,
tags = target.tags,
functions = target.functions;
var query = aggregation + ':' + metric;
var functionInstances = getFunctionInstances(functions);
if (tags && tags.length || adhocFilters && adhocFilters.length) {
query += '{';
if (tags && tags.length) {
query += tags.join(',');
}
if (adhocFilters && adhocFilters.length) {
var adhocTags = buildAdHocFilterString(adhocFilters);
if (tags && tags.length) {
query += ',';
}
query += adhocTags;
}
query += '}';
} else {
query += '{*}';
}
if (target.as) {
query += '.' + target.as + '()';
}
var groupedFuncs = _.groupBy(functionInstances, function (func) {
if (func.def.append) {
return 'appends';
} else {
return 'wraps';
}
});
_.each(groupedFuncs.appends, function (func) {
query += '.' + func.render();
});
_.each(groupedFuncs.wraps, function (func) {
query = func.render(query);
});
return query;
}
_export('buildQuery', buildQuery);
function getFunctionInstances(functions) {
return _.map(functions, function (func) {
var f = dfunc.createFuncInstance(func.funcDef, { withDefaultParams: false });
f.params = func.params.slice();
return f;
});
}
function buildAdHocFilterString(adhocFilters) {
return adhocFilters.map(function (filter) {
return filter.key + ':' + filter.value;
}).join(',');
}
return {
setters: [function (_lodash) {
_ = _lodash.default;
}, function (_dfunc) {
dfunc = _dfunc.default;
}],
execute: function () {}
};
});
//# sourceMappingURL=query_builder.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../src/query_builder.js"],"names":["buildQuery","target","adhocFilters","aggregation","metric","tags","functions","query","functionInstances","getFunctionInstances","length","join","adhocTags","buildAdHocFilterString","as","groupedFuncs","_","groupBy","func","def","append","each","appends","render","wraps","map","f","dfunc","createFuncInstance","funcDef","withDefaultParams","params","slice","filter","key","value"],"mappings":";;;;;;;AAGO,WAASA,UAAT,CAAoBC,MAApB,EAA4BC,YAA5B,EAA0C;AAAA,QAC1CC,WAD0C,GACFF,MADE,CAC1CE,WAD0C;AAAA,QAC7BC,MAD6B,GACFH,MADE,CAC7BG,MAD6B;AAAA,QACrBC,IADqB,GACFJ,MADE,CACrBI,IADqB;AAAA,QACfC,SADe,GACFL,MADE,CACfK,SADe;;AAE/C,QAAIC,QAAWJ,WAAX,SAA0BC,MAA9B;;AAEA,QAAII,oBAAqBC,qBAAqBH,SAArB,CAAzB;;AAEA,QAAKD,QAAQA,KAAKK,MAAd,IAA0BR,gBAAgBA,aAAaQ,MAA3D,EAAoE;AAClEH,eAAS,GAAT;;AAEA,UAAIF,QAAQA,KAAKK,MAAjB,EAAyB;AACvBH,iBAASF,KAAKM,IAAL,CAAU,GAAV,CAAT;AACD;;AAED,UAAIT,gBAAgBA,aAAaQ,MAAjC,EAAyC;AACvC,YAAIE,YAAYC,uBAAuBX,YAAvB,CAAhB;AACA,YAAIG,QAAQA,KAAKK,MAAjB,EAAyB;AACvBH,mBAAS,GAAT;AACD;AACDA,iBAASK,SAAT;AACD;;AAEDL,eAAS,GAAT;AACD,KAhBD,MAgBO;AACLA,eAAS,KAAT;AACD;;AAED,QAAIN,OAAOa,EAAX,EAAe;AACbP,eAAS,MAAMN,OAAOa,EAAb,GAAkB,IAA3B;AACD;;AAED,QAAIC,eAAeC,EAAEC,OAAF,CAAUT,iBAAV,EAA6B,gBAAQ;AACtD,UAAIU,KAAKC,GAAL,CAASC,MAAb,EAAqB;AACnB,eAAO,SAAP;AACD,OAFD,MAEO;AACL,eAAO,OAAP;AACD;AACF,KANkB,CAAnB;;AAQAJ,MAAEK,IAAF,CAAON,aAAaO,OAApB,EAA6B,gBAAQ;AACnCf,eAAS,MAAMW,KAAKK,MAAL,EAAf;AACD,KAFD;;AAIAP,MAAEK,IAAF,CAAON,aAAaS,KAApB,EAA2B,gBAAQ;AACjCjB,cAAQW,KAAKK,MAAL,CAAYhB,KAAZ,CAAR;AACD,KAFD;;AAIA,WAAOA,KAAP;AACD;;wBA/CeP,U;;AAiDhB,WAASS,oBAAT,CAA8BH,SAA9B,EAAyC;AACvC,WAAOU,EAAES,GAAF,CAAMnB,SAAN,EAAiB,gBAAQ;AAC9B,UAAIoB,IAAIC,MAAMC,kBAAN,CAAyBV,KAAKW,OAA9B,EAAuC,EAACC,mBAAmB,KAApB,EAAvC,CAAR;AACAJ,QAAEK,MAAF,GAAWb,KAAKa,MAAL,CAAYC,KAAZ,EAAX;AACA,aAAON,CAAP;AACD,KAJM,CAAP;AAKD;;AAED,WAASb,sBAAT,CAAgCX,YAAhC,EAA8C;AAC5C,WAAOA,aAAauB,GAAb,CAAiB,kBAAU;AAChC,aAAOQ,OAAOC,GAAP,GAAa,GAAb,GAAmBD,OAAOE,KAAjC;AACD,KAFM,EAEJxB,IAFI,CAEC,GAFD,CAAP;AAGD;;;AAhEMK,O;;AACAW,W","file":"query_builder.js","sourcesContent":["import _ from 'lodash';\nimport dfunc from './dfunc';\n\nexport function buildQuery(target, adhocFilters) {\n let {aggregation, metric, tags, functions} = target;\n let query = `${aggregation}:${metric}`;\n\n let functionInstances = getFunctionInstances(functions);\n\n if ((tags && tags.length) || (adhocFilters && adhocFilters.length)) {\n query += '{';\n\n if (tags && tags.length) {\n query += tags.join(',');\n }\n\n if (adhocFilters && adhocFilters.length) {\n let adhocTags = buildAdHocFilterString(adhocFilters);\n if (tags && tags.length) {\n query += ',';\n }\n query += adhocTags;\n }\n\n query += '}';\n } else {\n query += '{*}';\n }\n\n if (target.as) {\n query += '.' + target.as + '()';\n }\n\n var groupedFuncs = _.groupBy(functionInstances, func => {\n if (func.def.append) {\n return 'appends';\n } else {\n return 'wraps';\n }\n });\n\n _.each(groupedFuncs.appends, func => {\n query += '.' + func.render();\n });\n\n _.each(groupedFuncs.wraps, func => {\n query = func.render(query);\n });\n\n return query;\n}\n\nfunction getFunctionInstances(functions) {\n return _.map(functions, func => {\n var f = dfunc.createFuncInstance(func.funcDef, {withDefaultParams: false});\n f.params = func.params.slice();\n return f;\n });\n}\n\nfunction buildAdHocFilterString(adhocFilters) {\n return adhocFilters.map(filter => {\n return filter.key + ':' + filter.value;\n }).join(',');\n}\n"]}
@@ -0,0 +1,264 @@
'use strict';
System.register(['lodash', './dfunc', 'app/plugins/sdk', './func_editor', './add_datadog_func', './query_builder'], function (_export, _context) {
"use strict";
var _, dfunc, QueryCtrl, queryBuilder, _createClass, DataDogQueryCtrl;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
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;
}
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;
}
return {
setters: [function (_lodash) {
_ = _lodash.default;
}, function (_dfunc) {
dfunc = _dfunc.default;
}, function (_appPluginsSdk) {
QueryCtrl = _appPluginsSdk.QueryCtrl;
}, function (_func_editor) {}, function (_add_datadog_func) {}, function (_query_builder) {
queryBuilder = _query_builder;
}],
execute: function () {
_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;
};
}();
_export('DataDogQueryCtrl', DataDogQueryCtrl = function (_QueryCtrl) {
_inherits(DataDogQueryCtrl, _QueryCtrl);
function DataDogQueryCtrl($scope, $injector, uiSegmentSrv, templateSrv) {
_classCallCheck(this, DataDogQueryCtrl);
var _this = _possibleConstructorReturn(this, (DataDogQueryCtrl.__proto__ || Object.getPrototypeOf(DataDogQueryCtrl)).call(this, $scope, $injector));
_this.removeText = '-- remove tag --';
_this.uiSegmentSrv = uiSegmentSrv;
_this.templateSrv = templateSrv;
if (_this.target.aggregation) {
_this.aggregationSegment = new uiSegmentSrv.newSegment(_this.target.aggregation);
} else {
_this.aggregationSegment = new uiSegmentSrv.newSegment({
value: 'Select Aggregation',
fake: true,
custom: false
});
}
if (_this.target.metric) {
_this.metricSegment = new uiSegmentSrv.newSegment(_this.target.metric);
} else {
_this.metricSegment = new uiSegmentSrv.newSegment({
value: 'Select Metric',
fake: true,
custom: false
});
}
_this.target.tags = _this.target.tags || [];
_this.tagSegments = _this.target.tags.map(uiSegmentSrv.newSegment);
_this.fixTagSegments();
_this.functions = [];
_this.target.functions = _this.target.functions || [];
_this.functions = _.map(_this.target.functions, function (func) {
var f = dfunc.createFuncInstance(func.funcDef, { withDefaultParams: false });
f.params = func.params.slice();
return f;
});
if (_this.target.as) {
_this.asSegment = uiSegmentSrv.newSegment(_this.target.as);
} else {
_this.asSegment = uiSegmentSrv.newSegment({
value: 'Select As',
fake: true,
custom: false
});
}
return _this;
}
_createClass(DataDogQueryCtrl, [{
key: 'toggleEditorMode',
value: function toggleEditorMode() {
this.target.rawQuery = !this.target.rawQuery;
}
}, {
key: 'getMetrics',
value: function getMetrics() {
return this.datasource.metricFindQuery().then(this.uiSegmentSrv.transformToSegments(true));
}
}, {
key: 'getAggregations',
value: function getAggregations() {
return Promise.resolve([{ text: 'avg by', value: 'avg' }, { text: 'max by', value: 'max' }, { text: 'min by', value: 'min' }, { text: 'sub by', value: 'sum' }]);
}
}, {
key: 'getAs',
value: function getAs() {
return Promise.resolve([{ text: 'None', value: 'None' }, { text: 'as_count', value: 'as_count' }, { text: 'as_rate', value: 'as_rate' }]);
}
}, {
key: 'getTags',
value: function getTags(segment) {
var _this2 = this;
return this.datasource.tagFindQuery().then(this.uiSegmentSrv.transformToSegments(true)).then(function (results) {
if (segment.type !== 'plus-button') {
var removeSegment = _this2.uiSegmentSrv.newFake(_this2.removeText);
results.unshift(removeSegment);
}
return results;
});
}
}, {
key: 'aggregationChanged',
value: function aggregationChanged() {
this.target.aggregation = this.aggregationSegment.value;
this.panelCtrl.refresh();
}
}, {
key: 'metricChanged',
value: function metricChanged() {
this.target.metric = this.metricSegment.value;
this.panelCtrl.refresh();
}
}, {
key: 'asChanged',
value: function asChanged() {
if (this.asSegment.value === 'None') {
this.target.as = null;
} else {
this.target.as = this.asSegment.value;
}
this.panelCtrl.refresh();
}
}, {
key: 'fixTagSegments',
value: function fixTagSegments() {
var count = this.tagSegments.length;
var lastSegment = this.tagSegments[Math.max(count - 1, 0)];
if (!lastSegment || lastSegment.type !== 'plus-button') {
this.tagSegments.push(this.uiSegmentSrv.newPlusButton());
}
}
}, {
key: 'targetChanged',
value: function targetChanged() {
if (this.error) {
return;
}
this.panelCtrl.refresh();
}
}, {
key: 'persistFunctions',
value: function persistFunctions() {
this.target.functions = _.map(this.functions, function (func) {
return {
funcDef: func.def.name,
params: func.params.slice()
};
});
}
}, {
key: 'removeFunction',
value: function removeFunction(func) {
this.functions = _.without(this.functions, func);
this.persistFunctions();
this.targetChanged();
}
}, {
key: 'addFunction',
value: function addFunction(funcDef) {
var func = dfunc.createFuncInstance(funcDef, { withDefaultParams: true });
func.added = true;
this.functions.push(func);
this.persistFunctions();
this.targetChanged();
}
}, {
key: 'tagSegmentUpdated',
value: function tagSegmentUpdated(segment, index) {
if (segment.value === this.removeText) {
this.tagSegments.splice(index, 1);
}
var realSegments = _.filter(this.tagSegments, function (segment) {
return segment.value;
});
this.target.tags = realSegments.map(function (segment) {
return segment.value;
});
this.tagSegments = _.map(this.target.tags, this.uiSegmentSrv.newSegment);
this.fixTagSegments();
this.panelCtrl.refresh();
}
}, {
key: 'getCollapsedText',
value: function getCollapsedText() {
if (this.target.rawQuery) {
return this.target.query;
} else {
return queryBuilder.buildQuery(this.target);
}
}
}]);
return DataDogQueryCtrl;
}(QueryCtrl));
_export('DataDogQueryCtrl', DataDogQueryCtrl);
DataDogQueryCtrl.templateUrl = 'partials/query.editor.html';
}
};
});
//# sourceMappingURL=query_ctrl.js.map
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,115 @@
'use strict';
var _angular = require('angular');
var _angular2 = _interopRequireDefault(_angular);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _jquery = require('jquery');
var _jquery2 = _interopRequireDefault(_jquery);
var _dfunc = require('./dfunc');
var _dfunc2 = _interopRequireDefault(_dfunc);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
_angular2.default.module('grafana.directives').directive('datadogAddFunc', function ($compile) {
var inputTemplate = '<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>';
var buttonTemplate = '<a class="gf-form-label query-part dropdown-toggle"' + ' tabindex="1" gf-dropdown="functionMenu" data-toggle="dropdown">' + '<i class="fa fa-plus"></i></a>';
return {
link: function link($scope, elem) {
var categories = _dfunc2.default.getCategories();
var allFunctions = getAllFunctionNames(categories);
var ctrl = $scope.ctrl;
$scope.functionMenu = createFunctionDropDownMenu(categories);
var $input = (0, _jquery2.default)(inputTemplate);
var $button = (0, _jquery2.default)(buttonTemplate);
$input.appendTo(elem);
$button.appendTo(elem);
$input.attr('data-provide', 'typeahead');
$input.typeahead({
source: allFunctions,
minLength: 1,
items: 10,
updater: function updater(value) {
var funcDef = _dfunc2.default.getFuncDef(value);
if (!funcDef) {
// try find close match
value = value.toLowerCase();
funcDef = _lodash2.default.find(allFunctions, function (funcName) {
return funcName.toLowerCase().indexOf(value) === 0;
});
if (!funcDef) {
return;
}
}
$scope.$apply(function () {
ctrl.addFunction(funcDef);
});
$input.trigger('blur');
return '';
}
});
$button.click(function () {
$button.hide();
$input.show();
$input.focus();
});
$input.keyup(function () {
elem.toggleClass('open', $input.val() === '');
});
$input.blur(function () {
// clicking the function dropdown menu wont
// work if you remove class at once
setTimeout(function () {
$input.val('');
$input.hide();
$button.show();
elem.removeClass('open');
}, 200);
});
$compile(elem.contents())($scope);
}
};
});
function getAllFunctionNames(categories) {
return _lodash2.default.reduce(categories, function (list, category) {
_lodash2.default.each(category, function (func) {
list.push(func.name);
});
return list;
}, []);
}
function createFunctionDropDownMenu(categories) {
return _lodash2.default.map(categories, function (list, category) {
return {
text: category,
submenu: _lodash2.default.map(list, function (value) {
return {
text: value.name,
click: "ctrl.addFunction('" + value.name + "')"
};
})
};
});
}
//# sourceMappingURL=add_datadog_func.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,422 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DataDogDatasource = undefined;
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; }; }();
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _showdownMin = require('./showdown.min.js');
var _showdownMin2 = _interopRequireDefault(_showdownMin);
var _query_builder = require('./query_builder');
var queryBuilder = _interopRequireWildcard(_query_builder);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DataDogDatasource = exports.DataDogDatasource = function () {
function DataDogDatasource(instanceSettings, backendSrv, templateSrv) {
_classCallCheck(this, DataDogDatasource);
this.type = instanceSettings.type;
this.url = instanceSettings.url;
this.name = instanceSettings.name;
this.api_key = instanceSettings.jsonData.api_key;
this.application_key = instanceSettings.jsonData.app_key;
this.supportMetrics = true;
this.backendSrv = backendSrv;
this.templateSrv = templateSrv;
this._cached_metrics = false;
}
// Function to check Datasource health
_createClass(DataDogDatasource, [{
key: 'testDatasource',
value: function testDatasource() {
return this.invokeDataDogApiRequest('/downtime').then(function () {
return {
status: "success",
title: "Success",
message: "Data source is working"
};
}).catch(function (error) {
var message = "Connection error";
if (error && error.message) {
message = error.message;
}
return {
status: "error",
title: "Error",
message: message
};
});
}
}, {
key: 'tagFindQuery',
value: function tagFindQuery() {
return this.getTagsFromCache().then(function (tags) {
return _lodash2.default.map(tags, function (hosts, tag) {
return {
text: tag,
value: tag
};
});
});
}
}, {
key: 'metricFindQuery',
value: function metricFindQuery(query) {
var _this = this;
if (query === 'tag') {
return this.tagFindQuery();
}
if (this._cached_metrics) {
return Promise.resolve(this._cached_metrics);
}
if (this.fetching) {
return this.fetching;
}
var d = new Date();
d.setDate(d.getDate() - 1);
var from = Math.floor(d.getTime() / 1000);
this.fetching = this.getMetrics(from).then(function (metrics) {
_this._cached_metrics = _lodash2.default.map(metrics, function (metric) {
return {
text: metric,
value: metric
};
});
return _this._cached_metrics;
});
return this.fetching;
}
}, {
key: 'getTagKeys',
value: function getTagKeys() {
return this.getTagsFromCache().then(function (tagsHosts) {
var tags = Object.keys(tagsHosts);
var kv = mapTagsToKVPairs(tags);
var grafanaTags = Object.keys(kv);
return grafanaTags.map(function (tag) {
return {
text: tag,
value: tag
};
});
});
}
}, {
key: 'getTagValues',
value: function getTagValues(options) {
return this.getTagsFromCache().then(function (tagsHosts) {
var tags = Object.keys(tagsHosts);
var kv = mapTagsToKVPairs(tags);
var grafanaValues = kv[options.key];
return grafanaValues.map(function (val) {
return {
text: val,
value: val
};
});
});
}
}, {
key: 'query',
value: function query(options) {
var from = Math.floor(options.range.from.valueOf() / 1000);
var to = Math.floor(options.range.to.valueOf() / 1000);
var targets = options.targets.filter(function (t) {
return !t.hide;
});
if (targets.length <= 0) {
return Promise.resolve({ data: [] });
}
// add global adhoc filters
var adhocFilters = this.templateSrv.getAdhocFilters(this.name);
var queries = _lodash2.default.map(targets, function (target) {
var query = void 0;
if (target.rawQuery) {
query = target.query;
} else {
query = queryBuilder.buildQuery(target, adhocFilters);
}
return query;
});
var queryString = queries.join(',');
queryString = this.templateSrv.replace(queryString, options.scopedVars, dataDogTagFormat);
var params = {
from: from,
to: to,
query: queryString
};
return this.invokeDataDogApiRequest('/query', params).then(function (result) {
var dataResponse = _lodash2.default.map(result.series, function (series, i) {
var target = targets[i];
return {
'target': target.alias || series.expression,
'datapoints': _lodash2.default.map(series.pointlist, function (point) {
return [point[1], point[0]];
})
};
});
return { data: dataResponse };
});
}
}, {
key: 'annotationQuery',
value: function annotationQuery(options) {
var timeFrom = Math.floor(options.range.from.valueOf() / 1000);
var timeTo = Math.floor(options.range.to.valueOf() / 1000);
var _options$annotation = options.annotation,
priority = _options$annotation.priority,
sources = _options$annotation.sources,
tags = _options$annotation.tags;
return this.getEventStream(timeFrom, timeTo, priority, sources, tags).then(function (eventStreams) {
var eventAnnotations = eventStreams.map(function (eventStream) {
var allEvents = eventStream.children;
var filteredEvents = _lodash2.default.filter(allEvents, function (event) {
return event.alert_type !== 'success';
});
return _lodash2.default.map(filteredEvents, function (event) {
var renderedText = eventStream.text;
if (isDataDogMarkdown(eventStream.text)) {
renderedText = convertDataDogMdToHtml(eventStream.text);
}
return {
annotation: options.annotation,
time: event.date_happened * 1000,
title: eventStream.title,
text: renderedText,
tags: eventStream.tags
};
});
});
return _lodash2.default.flatten(eventAnnotations);
});
}
}, {
key: 'getHosts',
value: function getHosts() {
return this.searchEntities('hosts');
}
// entity should be 'hosts' or 'metrics'
// http://docs.datadoghq.com/api/?lang=console#search
}, {
key: 'searchEntities',
value: function searchEntities(entity) {
var params = { q: '' };
if (entity) {
params.q = entity + ':';
}
return this.invokeDataDogApiRequest('/search', params).then(function (result) {
if (result && result.results) {
return result.results[entity];
}
});
}
}, {
key: 'getMetrics',
value: function getMetrics(timeFrom) {
var params = {};
if (timeFrom) {
params.from = timeFrom;
}
return this.invokeDataDogApiRequest('/metrics', params).then(function (result) {
if (result.metrics) {
return result.metrics;
} else {
return [];
}
});
}
}, {
key: 'getTagsHosts',
value: function getTagsHosts() {
return this.invokeDataDogApiRequest('/tags/hosts').then(function (result) {
if (result && result.tags) {
return result.tags;
}
});
}
}, {
key: 'getTagsFromCache',
value: function getTagsFromCache() {
var _this2 = this;
var getTags = void 0;
if (this._cached_tags && this._cached_tags.length) {
getTags = Promise.resolve(this._cached_tags);
} else {
getTags = this.getTagsHosts().then(function (tags) {
_this2._cached_tags = tags;
return tags;
});
}
return getTags;
}
}, {
key: 'getEventStream',
value: function getEventStream(timeFrom, timeTo, priority, sources, tags) {
var params = {
start: timeFrom,
end: timeTo
};
if (priority) {
params.priority = priority;
}
if (sources) {
params.sources = sources;
}
if (tags) {
params.tags = tags;
}
return this.invokeDataDogApiRequest('/events', params).then(function (result) {
if (result.events) {
return result.events;
} else {
return [];
}
});
}
}, {
key: 'invokeDataDogApiRequest',
value: function invokeDataDogApiRequest(url) {
var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// Set auth params
params.api_key = this.api_key;
params.application_key = this.application_key;
return this.backendSrv.datasourceRequest({
method: 'GET',
url: this.url + url,
params: params
}).then(function (response) {
if (response.data) {
return response.data;
} else {
throw { message: 'DataDog API request error' };
}
}).catch(function (error) {
var message = 'DataDog API request error';
if (error.statusText) {
message = error.status + ' ' + error.statusText;
throw { message: message };
} else if (error.err.statusText) {
throw { message: error.err.statusText };
} else {
throw { message: message };
}
});
}
}]);
return DataDogDatasource;
}();
/*
* Convert tags to key-value pairs
* [region:east, region:nw] => {region: [east, nw]}
*/
function mapTagsToKVPairs(tags) {
var kv_tags = _lodash2.default.filter(tags, function (tag) {
return tag.indexOf(':') !== -1;
});
var kv_pairs = kv_tags.map(function (tag) {
return tag.split(':', 2); // Limit to 2
});
var kv_object = {};
kv_pairs.forEach(function (pair) {
var key = pair[0];
var val = pair[1];
if (kv_object[key]) {
kv_object[key].push(val);
} else {
kv_object[key] = [val];
}
});
return kv_object;
}
/*
* Convert DataDog event text from markdown to pure HTML
* http://docs.datadoghq.com/guides/markdown/
*/
function convertDataDogMdToHtml(str) {
var MD_START = '%%%\n';
var MD_END = '\n%%%';
var md_start_index = str.indexOf(MD_START) + MD_START.length;
var md_end_index = str.indexOf(MD_END);
var md = str.substring(md_start_index, md_end_index);
md = removeImagesFromDataDogMarkdown(md);
var converter = new _showdownMin2.default.Converter();
return converter.makeHtml(md);
}
function isDataDogMarkdown(str) {
var MD_START = '%%%\n';
return str.indexOf(MD_START) >= 0;
}
function removeImagesFromDataDogMarkdown(str) {
var dataDogImagePattern = /\[{0,1}\!\[.*\]\(https{0,1}\:\/\/p\.datadoghq\.com\/snapshot.+\)/g;
return str.replace(dataDogImagePattern, '');
}
function dataDogTagFormat(value) {
if (_lodash2.default.isArray(value)) {
return value.join(',');
}
return value;
}
//# sourceMappingURL=datasource.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,295 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var index = [];
var categories = {
Arithmatic: [],
Interpolation: [],
Timeshift: [],
Rate: [],
Smoothing: [],
Rollup: [],
Rank: [],
Count: [],
Regression: [],
Algorithms: []
};
function addFuncDef(funcDef) {
funcDef.params = funcDef.params || [];
funcDef.defaultParams = funcDef.defaultParams || [];
funcDef.append = funcDef.append || false;
if (funcDef.category) {
funcDef.category.push(funcDef);
}
index[funcDef.name] = funcDef;
index[funcDef.name] = funcDef;
}
addFuncDef({
name: 'abs',
category: categories.Arithmatic
});
addFuncDef({
name: 'log2',
category: categories.Arithmatic
});
addFuncDef({
name: 'cumsum',
category: categories.Arithmatic
});
addFuncDef({
name: 'integral',
category: categories.Arithmatic
});
addFuncDef({
name: 'fill',
category: categories.Interpolation,
params: [{ name: "fill", type: 'string', options: ['null', 'zero', 'linear', 'last'] }, { name: "limit", type: 'int', optional: true }],
defaultParams: ['zero'],
append: true
});
addFuncDef({
name: 'hour_before',
category: categories.Timeshift
});
addFuncDef({
name: 'day_before',
category: categories.Timeshift
});
addFuncDef({
name: 'week_before',
category: categories.Timeshift
});
addFuncDef({
name: 'month_before',
category: categories.Timeshift
});
addFuncDef({
name: 'per_second',
category: categories.Rate
});
addFuncDef({
name: 'per_minute',
category: categories.Rate
});
addFuncDef({
name: 'per_hour',
category: categories.Rate
});
addFuncDef({
name: 'dt',
category: categories.Rate
});
addFuncDef({
name: 'diff',
category: categories.Rate
});
addFuncDef({
name: 'derivative',
category: categories.Rate
});
addFuncDef({
name: 'ewma_3',
category: categories.Smoothing
});
addFuncDef({
name: 'ewma_5',
category: categories.Smoothing
});
addFuncDef({
name: 'ewma_10',
category: categories.Smoothing
});
addFuncDef({
name: 'ewma_20',
category: categories.Smoothing
});
addFuncDef({
name: 'median_3',
category: categories.Smoothing
});
addFuncDef({
name: 'median_5',
category: categories.Smoothing
});
addFuncDef({
name: 'median_7',
category: categories.Smoothing
});
addFuncDef({
name: 'median_9',
category: categories.Smoothing
});
addFuncDef({
name: 'rollup',
category: categories.Rollup,
params: [{ name: "rollup", type: 'string', options: ['sum', 'avg', 'min', 'max'] }, { name: "period", type: 'int' }],
defaultParams: ['sum', 600],
append: true
});
addFuncDef({
name: 'count_nonzero',
category: categories.Count
});
addFuncDef({
name: 'count_not_null',
category: categories.Count
});
addFuncDef({
name: 'top',
category: categories.Rank,
params: [{ name: "top", type: 'int', options: [5, 10, 15] }, { name: "aggr", type: 'string', options: ['mean', 'min', 'max', 'area', 'l2norm', 'last'] }, { name: "dir", type: 'string', options: ['asc', 'desc'] }],
defaultParams: [5, 'mean', 'dir']
});
addFuncDef({
name: 'robust_trend',
category: categories.Regression
});
addFuncDef({
name: 'piecewise_constant',
category: categories.Regression
});
addFuncDef({
name: 'anomalies',
category: categories.Algorithms,
params: [{ name: "anomalies", type: 'string', options: ['basic', 'agile', 'robust', 'adaptive'] }, { name: "bonds", type: 'int', options: [1, 2, 3, 4, 5, 6] }],
defaultParams: ['basic', 2]
});
addFuncDef({
name: 'anomalies',
category: categories.Algorithms,
params: [{ name: "anomalies", type: 'string', options: ['basic', 'agile', 'robust', 'adaptive'] }, { name: "bonds", type: 'int', options: [1, 2, 3, 4, 5, 6] }],
defaultParams: ['basic', 2]
});
addFuncDef({
name: 'outliers',
category: categories.Algorithms,
params: [{ name: "outliers", type: 'string', options: ['DBSCAN', 'MAD', 'scaledDBSCAN', 'scaledMAD'] }, { name: "tolerance", type: 'int', options: [0.33, 0.5, 1, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0] }],
defaultParams: ['basic', 2]
});
_lodash2.default.each(categories, function (funcList, catName) {
categories[catName] = _lodash2.default.sortBy(funcList, 'name');
});
function FuncInstance(funcDef, options) {
this.def = funcDef;
this.params = [];
if (options && options.withDefaultParams) {
this.params = funcDef.defaultParams.slice(0);
}
this.updateText();
}
FuncInstance.prototype.render = function (metricExp) {
var str = this.def.name + '(';
var parameters = _lodash2.default.map(this.params, function (value) {
return value;
});
if (metricExp && !this.def.append) {
parameters.unshift(metricExp);
}
var funcRender = str + parameters.join(', ') + ')';
console.log(funcRender, this.def.append);
if (this.def.append) {
return metricExp + '.' + funcRender;
} else {
return funcRender;
}
};
FuncInstance.prototype.updateParam = function (strValue, index) {
// handle optional parameters
// if string contains ',' and next param is optional, split and update both
if (strValue === '' && this.def.params[index].optional) {
this.params.splice(index, 1);
} else {
this.params[index] = strValue;
}
this.updateText();
};
FuncInstance.prototype.updateText = function () {
if (this.params.length === 0) {
this.text = this.def.name + '()';
return;
}
var text = this.def.name + '(';
text += this.params.join(', ');
text += ')';
this.text = text;
};
var createFuncInstance = function createFuncInstance(funcDef, options) {
if (_lodash2.default.isString(funcDef)) {
if (!index[funcDef]) {
throw { message: 'Method not found ' + name };
}
funcDef = index[funcDef];
}
return new FuncInstance(funcDef, options);
};
var getFuncDef = function getFuncDef(name) {
return index[name];
};
var getCategories = function getCategories() {
return categories;
};
var foo;
exports.default = foo = {
createFuncInstance: createFuncInstance,
getFuncDef: getFuncDef,
getCategories: getCategories
};
//# sourceMappingURL=dfunc.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,247 @@
'use strict';
var _angular = require('angular');
var _angular2 = _interopRequireDefault(_angular);
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _jquery = require('jquery');
var _jquery2 = _interopRequireDefault(_jquery);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
'use strict';
_angular2.default.module('grafana.directives').directive('datadogFuncEditor', function ($compile, templateSrv) {
var funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
var paramTemplate = '<input type="text" style="display:none"' + ' class="input-mini tight-form-func-param"></input>';
var funcControlsTemplate = '<div class="tight-form-func-controls">' + '<span class="pointer fa fa-arrow-left"></span>' + '<span class="pointer fa fa-question-circle"></span>' + '<span class="pointer fa fa-remove" ></span>' + '<span class="pointer fa fa-arrow-right"></span>' + '</div>';
return {
restrict: 'A',
link: function postLink($scope, elem) {
var $funcLink = (0, _jquery2.default)(funcSpanTemplate);
var $funcControls = (0, _jquery2.default)(funcControlsTemplate);
var ctrl = $scope.ctrl;
var func = $scope.func;
var funcDef = func.def;
var scheduledRelink = false;
var paramCountAtLink = 0;
function clickFuncParam(paramIndex) {
/*jshint validthis:true */
var $link = (0, _jquery2.default)(this);
var $input = $link.next();
$input.val(func.params[paramIndex]);
$input.css('width', $link.width() + 16 + 'px');
$link.hide();
$input.show();
$input.focus();
$input.select();
var typeahead = $input.data('typeahead');
if (typeahead) {
$input.val('');
typeahead.lookup();
}
}
function scheduledRelinkIfNeeded() {
if (paramCountAtLink === func.params.length) {
return;
}
if (!scheduledRelink) {
scheduledRelink = true;
setTimeout(function () {
relink();
scheduledRelink = false;
}, 200);
}
}
function inputBlur(paramIndex) {
/*jshint validthis:true */
var $input = (0, _jquery2.default)(this);
var $link = $input.prev();
var newValue = $input.val();
if (newValue !== '' || func.def.params[paramIndex].optional) {
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
func.updateParam($input.val(), paramIndex);
scheduledRelinkIfNeeded();
$scope.$apply(function () {
ctrl.targetChanged();
});
$input.hide();
$link.show();
}
}
function inputKeyPress(paramIndex, e) {
/*jshint validthis:true */
if (e.which === 13) {
inputBlur.call(this, paramIndex);
}
}
function inputKeyDown() {
/*jshint validthis:true */
this.style.width = (3 + this.value.length) * 8 + 'px';
}
function addTypeahead($input, paramIndex) {
$input.attr('data-provide', 'typeahead');
var options = funcDef.params[paramIndex].options;
if (funcDef.params[paramIndex].type === 'int') {
options = _lodash2.default.map(options, function (val) {
return val.toString();
});
}
$input.typeahead({
source: options,
minLength: 0,
items: 20,
updater: function updater(value) {
setTimeout(function () {
inputBlur.call($input[0], paramIndex);
}, 0);
return value;
}
});
var typeahead = $input.data('typeahead');
typeahead.lookup = function () {
this.query = this.$element.val() || '';
return this.process(this.source);
};
}
function toggleFuncControls() {
var targetDiv = elem.closest('.tight-form');
if (elem.hasClass('show-function-controls')) {
elem.removeClass('show-function-controls');
targetDiv.removeClass('has-open-function');
$funcControls.hide();
return;
}
elem.addClass('show-function-controls');
targetDiv.addClass('has-open-function');
$funcControls.show();
}
function addElementsAndCompile() {
$funcControls.appendTo(elem);
$funcLink.appendTo(elem);
_lodash2.default.each(funcDef.params, function (param, index) {
if (param.optional && func.params.length <= index) {
return;
}
if (index > 0) {
(0, _jquery2.default)('<span>, </span>').appendTo(elem);
}
var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
var $paramLink = (0, _jquery2.default)('<a ng-click="" class="datadog-func-param-link">' + paramValue + '</a>');
var $input = (0, _jquery2.default)(paramTemplate);
paramCountAtLink++;
$paramLink.appendTo(elem);
$input.appendTo(elem);
$input.blur(_lodash2.default.partial(inputBlur, index));
$input.keyup(inputKeyDown);
$input.keypress(_lodash2.default.partial(inputKeyPress, index));
$paramLink.click(_lodash2.default.partial(clickFuncParam, index));
if (funcDef.params[index].options) {
addTypeahead($input, index);
}
});
(0, _jquery2.default)('<span>)</span>').appendTo(elem);
$compile(elem.contents())($scope);
}
function ifJustAddedFocusFistParam() {
if ($scope.func.added) {
$scope.func.added = false;
setTimeout(function () {
elem.find('.datadog-func-param-link').first().click();
}, 10);
}
}
function registerFuncControlsToggle() {
$funcLink.click(toggleFuncControls);
}
function registerFuncControlsActions() {
$funcControls.click(function (e) {
var $target = (0, _jquery2.default)(e.target);
if ($target.hasClass('fa-remove')) {
toggleFuncControls();
$scope.$apply(function () {
ctrl.removeFunction($scope.func);
});
return;
}
if ($target.hasClass('fa-arrow-left')) {
$scope.$apply(function () {
_lodash2.default.move(ctrl.functions, $scope.$index, $scope.$index - 1);
ctrl.targetChanged();
});
return;
}
if ($target.hasClass('fa-arrow-right')) {
$scope.$apply(function () {
_lodash2.default.move(ctrl.functions, $scope.$index, $scope.$index + 1);
ctrl.targetChanged();
});
return;
}
if ($target.hasClass('fa-question-circle')) {
window.open("http://docs.datadoghq.com/graphing/#apply-more-advanced-functions", '_blank');
return;
}
});
}
function relink() {
elem.children().remove();
addElementsAndCompile();
ifJustAddedFocusFistParam();
registerFuncControlsToggle();
registerFuncControlsActions();
}
relink();
}
};
});
//# sourceMappingURL=func_editor.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,37 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.AnnotationsQueryCtrl = exports.QueryOptionsCtrl = exports.ConfigCtrl = exports.QueryCtrl = exports.Datasource = undefined;
var _datasource = require('./datasource');
var _query_ctrl = require('./query_ctrl');
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DataDogConfigCtrl = function DataDogConfigCtrl() {
_classCallCheck(this, DataDogConfigCtrl);
};
DataDogConfigCtrl.templateUrl = "partials/config.html";
var DataDogQueryOptionsCtrl = function DataDogQueryOptionsCtrl() {
_classCallCheck(this, DataDogQueryOptionsCtrl);
};
DataDogQueryOptionsCtrl.templateUrl = "partials/query.options.html";
var DataDogAnnotationsQueryCtrl = function DataDogAnnotationsQueryCtrl() {
_classCallCheck(this, DataDogAnnotationsQueryCtrl);
};
DataDogAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';
exports.Datasource = _datasource.DataDogDatasource;
exports.QueryCtrl = _query_ctrl.DataDogQueryCtrl;
exports.ConfigCtrl = DataDogConfigCtrl;
exports.QueryOptionsCtrl = DataDogQueryOptionsCtrl;
exports.AnnotationsQueryCtrl = DataDogAnnotationsQueryCtrl;
//# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../../src/module.js"],"names":["DataDogConfigCtrl","templateUrl","DataDogQueryOptionsCtrl","DataDogAnnotationsQueryCtrl","Datasource","QueryCtrl","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;QACDC,S;QACCC,U,GAArBN,iB;QAC2BO,gB,GAA3BL,uB;QAC+BM,oB,GAA/BL,2B","file":"module.js","sourcesContent":["import {DataDogDatasource} from './datasource';\nimport {DataDogQueryCtrl} from './query_ctrl';\n\nclass DataDogConfigCtrl {}\nDataDogConfigCtrl.templateUrl = \"partials/config.html\";\n\nclass DataDogQueryOptionsCtrl {}\nDataDogQueryOptionsCtrl.templateUrl = \"partials/query.options.html\";\n\nclass DataDogAnnotationsQueryCtrl {}\nDataDogAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';\n\nexport {\n DataDogDatasource as Datasource,\n DataDogQueryCtrl as QueryCtrl,\n DataDogConfigCtrl as ConfigCtrl,\n DataDogQueryOptionsCtrl as QueryOptionsCtrl,\n DataDogAnnotationsQueryCtrl as AnnotationsQueryCtrl\n};\n"]}
@@ -0,0 +1,84 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.buildQuery = buildQuery;
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _dfunc = require('./dfunc');
var _dfunc2 = _interopRequireDefault(_dfunc);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function buildQuery(target, adhocFilters) {
var aggregation = target.aggregation,
metric = target.metric,
tags = target.tags,
functions = target.functions;
var query = aggregation + ':' + metric;
var functionInstances = getFunctionInstances(functions);
if (tags && tags.length || adhocFilters && adhocFilters.length) {
query += '{';
if (tags && tags.length) {
query += tags.join(',');
}
if (adhocFilters && adhocFilters.length) {
var adhocTags = buildAdHocFilterString(adhocFilters);
if (tags && tags.length) {
query += ',';
}
query += adhocTags;
}
query += '}';
} else {
query += '{*}';
}
if (target.as) {
query += '.' + target.as + '()';
}
var groupedFuncs = _lodash2.default.groupBy(functionInstances, function (func) {
if (func.def.append) {
return 'appends';
} else {
return 'wraps';
}
});
_lodash2.default.each(groupedFuncs.appends, function (func) {
query += '.' + func.render();
});
_lodash2.default.each(groupedFuncs.wraps, function (func) {
query = func.render(query);
});
return query;
}
function getFunctionInstances(functions) {
return _lodash2.default.map(functions, function (func) {
var f = _dfunc2.default.createFuncInstance(func.funcDef, { withDefaultParams: false });
f.params = func.params.slice();
return f;
});
}
function buildAdHocFilterString(adhocFilters) {
return adhocFilters.map(function (filter) {
return filter.key + ':' + filter.value;
}).join(',');
}
//# sourceMappingURL=query_builder.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../../src/query_builder.js"],"names":["buildQuery","target","adhocFilters","aggregation","metric","tags","functions","query","functionInstances","getFunctionInstances","length","join","adhocTags","buildAdHocFilterString","as","groupedFuncs","groupBy","func","def","append","each","appends","render","wraps","map","f","createFuncInstance","funcDef","withDefaultParams","params","slice","filter","key","value"],"mappings":";;;;;QAGgBA,U,GAAAA,U;;AAHhB;;;;AACA;;;;;;AAEO,SAASA,UAAT,CAAoBC,MAApB,EAA4BC,YAA5B,EAA0C;AAAA,MAC1CC,WAD0C,GACFF,MADE,CAC1CE,WAD0C;AAAA,MAC7BC,MAD6B,GACFH,MADE,CAC7BG,MAD6B;AAAA,MACrBC,IADqB,GACFJ,MADE,CACrBI,IADqB;AAAA,MACfC,SADe,GACFL,MADE,CACfK,SADe;;AAE/C,MAAIC,QAAWJ,WAAX,SAA0BC,MAA9B;;AAEA,MAAII,oBAAqBC,qBAAqBH,SAArB,CAAzB;;AAEA,MAAKD,QAAQA,KAAKK,MAAd,IAA0BR,gBAAgBA,aAAaQ,MAA3D,EAAoE;AAClEH,aAAS,GAAT;;AAEA,QAAIF,QAAQA,KAAKK,MAAjB,EAAyB;AACvBH,eAASF,KAAKM,IAAL,CAAU,GAAV,CAAT;AACD;;AAED,QAAIT,gBAAgBA,aAAaQ,MAAjC,EAAyC;AACvC,UAAIE,YAAYC,uBAAuBX,YAAvB,CAAhB;AACA,UAAIG,QAAQA,KAAKK,MAAjB,EAAyB;AACvBH,iBAAS,GAAT;AACD;AACDA,eAASK,SAAT;AACD;;AAEDL,aAAS,GAAT;AACD,GAhBD,MAgBO;AACLA,aAAS,KAAT;AACD;;AAED,MAAIN,OAAOa,EAAX,EAAe;AACbP,aAAS,MAAMN,OAAOa,EAAb,GAAkB,IAA3B;AACD;;AAED,MAAIC,eAAe,iBAAEC,OAAF,CAAUR,iBAAV,EAA6B,gBAAQ;AACtD,QAAIS,KAAKC,GAAL,CAASC,MAAb,EAAqB;AACnB,aAAO,SAAP;AACD,KAFD,MAEO;AACL,aAAO,OAAP;AACD;AACF,GANkB,CAAnB;;AAQA,mBAAEC,IAAF,CAAOL,aAAaM,OAApB,EAA6B,gBAAQ;AACnCd,aAAS,MAAMU,KAAKK,MAAL,EAAf;AACD,GAFD;;AAIA,mBAAEF,IAAF,CAAOL,aAAaQ,KAApB,EAA2B,gBAAQ;AACjChB,YAAQU,KAAKK,MAAL,CAAYf,KAAZ,CAAR;AACD,GAFD;;AAIA,SAAOA,KAAP;AACD;;AAED,SAASE,oBAAT,CAA8BH,SAA9B,EAAyC;AACvC,SAAO,iBAAEkB,GAAF,CAAMlB,SAAN,EAAiB,gBAAQ;AAC9B,QAAImB,IAAI,gBAAMC,kBAAN,CAAyBT,KAAKU,OAA9B,EAAuC,EAACC,mBAAmB,KAApB,EAAvC,CAAR;AACAH,MAAEI,MAAF,GAAWZ,KAAKY,MAAL,CAAYC,KAAZ,EAAX;AACA,WAAOL,CAAP;AACD,GAJM,CAAP;AAKD;;AAED,SAASZ,sBAAT,CAAgCX,YAAhC,EAA8C;AAC5C,SAAOA,aAAasB,GAAb,CAAiB,kBAAU;AAChC,WAAOO,OAAOC,GAAP,GAAa,GAAb,GAAmBD,OAAOE,KAAjC;AACD,GAFM,EAEJtB,IAFI,CAEC,GAFD,CAAP;AAGD","file":"query_builder.js","sourcesContent":["import _ from 'lodash';\nimport dfunc from './dfunc';\n\nexport function buildQuery(target, adhocFilters) {\n let {aggregation, metric, tags, functions} = target;\n let query = `${aggregation}:${metric}`;\n\n let functionInstances = getFunctionInstances(functions);\n\n if ((tags && tags.length) || (adhocFilters && adhocFilters.length)) {\n query += '{';\n\n if (tags && tags.length) {\n query += tags.join(',');\n }\n\n if (adhocFilters && adhocFilters.length) {\n let adhocTags = buildAdHocFilterString(adhocFilters);\n if (tags && tags.length) {\n query += ',';\n }\n query += adhocTags;\n }\n\n query += '}';\n } else {\n query += '{*}';\n }\n\n if (target.as) {\n query += '.' + target.as + '()';\n }\n\n var groupedFuncs = _.groupBy(functionInstances, func => {\n if (func.def.append) {\n return 'appends';\n } else {\n return 'wraps';\n }\n });\n\n _.each(groupedFuncs.appends, func => {\n query += '.' + func.render();\n });\n\n _.each(groupedFuncs.wraps, func => {\n query = func.render(query);\n });\n\n return query;\n}\n\nfunction getFunctionInstances(functions) {\n return _.map(functions, func => {\n var f = dfunc.createFuncInstance(func.funcDef, {withDefaultParams: false});\n f.params = func.params.slice();\n return f;\n });\n}\n\nfunction buildAdHocFilterString(adhocFilters) {\n return adhocFilters.map(filter => {\n return filter.key + ':' + filter.value;\n }).join(',');\n}\n"]}
@@ -0,0 +1,230 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DataDogQueryCtrl = undefined;
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; }; }();
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _dfunc = require('./dfunc');
var _dfunc2 = _interopRequireDefault(_dfunc);
var _sdk = require('app/plugins/sdk');
require('./func_editor');
require('./add_datadog_func');
var _query_builder = require('./query_builder');
var queryBuilder = _interopRequireWildcard(_query_builder);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
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; }
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; }
var DataDogQueryCtrl = exports.DataDogQueryCtrl = function (_QueryCtrl) {
_inherits(DataDogQueryCtrl, _QueryCtrl);
function DataDogQueryCtrl($scope, $injector, uiSegmentSrv, templateSrv) {
_classCallCheck(this, DataDogQueryCtrl);
var _this = _possibleConstructorReturn(this, (DataDogQueryCtrl.__proto__ || Object.getPrototypeOf(DataDogQueryCtrl)).call(this, $scope, $injector));
_this.removeText = '-- remove tag --';
_this.uiSegmentSrv = uiSegmentSrv;
_this.templateSrv = templateSrv;
if (_this.target.aggregation) {
_this.aggregationSegment = new uiSegmentSrv.newSegment(_this.target.aggregation);
} else {
_this.aggregationSegment = new uiSegmentSrv.newSegment({
value: 'Select Aggregation',
fake: true,
custom: false
});
}
if (_this.target.metric) {
_this.metricSegment = new uiSegmentSrv.newSegment(_this.target.metric);
} else {
_this.metricSegment = new uiSegmentSrv.newSegment({
value: 'Select Metric',
fake: true,
custom: false
});
}
_this.target.tags = _this.target.tags || [];
_this.tagSegments = _this.target.tags.map(uiSegmentSrv.newSegment);
_this.fixTagSegments();
_this.functions = [];
_this.target.functions = _this.target.functions || [];
_this.functions = _lodash2.default.map(_this.target.functions, function (func) {
var f = _dfunc2.default.createFuncInstance(func.funcDef, { withDefaultParams: false });
f.params = func.params.slice();
return f;
});
if (_this.target.as) {
_this.asSegment = uiSegmentSrv.newSegment(_this.target.as);
} else {
_this.asSegment = uiSegmentSrv.newSegment({
value: 'Select As',
fake: true,
custom: false
});
}
return _this;
}
_createClass(DataDogQueryCtrl, [{
key: 'toggleEditorMode',
value: function toggleEditorMode() {
this.target.rawQuery = !this.target.rawQuery;
}
}, {
key: 'getMetrics',
value: function getMetrics() {
return this.datasource.metricFindQuery().then(this.uiSegmentSrv.transformToSegments(true));
}
}, {
key: 'getAggregations',
value: function getAggregations() {
return Promise.resolve([{ text: 'avg by', value: 'avg' }, { text: 'max by', value: 'max' }, { text: 'min by', value: 'min' }, { text: 'sub by', value: 'sum' }]);
}
}, {
key: 'getAs',
value: function getAs() {
return Promise.resolve([{ text: 'None', value: 'None' }, { text: 'as_count', value: 'as_count' }, { text: 'as_rate', value: 'as_rate' }]);
}
}, {
key: 'getTags',
value: function getTags(segment) {
var _this2 = this;
return this.datasource.tagFindQuery().then(this.uiSegmentSrv.transformToSegments(true)).then(function (results) {
if (segment.type !== 'plus-button') {
var removeSegment = _this2.uiSegmentSrv.newFake(_this2.removeText);
results.unshift(removeSegment);
}
return results;
});
}
}, {
key: 'aggregationChanged',
value: function aggregationChanged() {
this.target.aggregation = this.aggregationSegment.value;
this.panelCtrl.refresh();
}
}, {
key: 'metricChanged',
value: function metricChanged() {
this.target.metric = this.metricSegment.value;
this.panelCtrl.refresh();
}
}, {
key: 'asChanged',
value: function asChanged() {
if (this.asSegment.value === 'None') {
this.target.as = null;
} else {
this.target.as = this.asSegment.value;
}
this.panelCtrl.refresh();
}
}, {
key: 'fixTagSegments',
value: function fixTagSegments() {
var count = this.tagSegments.length;
var lastSegment = this.tagSegments[Math.max(count - 1, 0)];
if (!lastSegment || lastSegment.type !== 'plus-button') {
this.tagSegments.push(this.uiSegmentSrv.newPlusButton());
}
}
}, {
key: 'targetChanged',
value: function targetChanged() {
if (this.error) {
return;
}
this.panelCtrl.refresh();
}
}, {
key: 'persistFunctions',
value: function persistFunctions() {
this.target.functions = _lodash2.default.map(this.functions, function (func) {
return {
funcDef: func.def.name,
params: func.params.slice()
};
});
}
}, {
key: 'removeFunction',
value: function removeFunction(func) {
this.functions = _lodash2.default.without(this.functions, func);
this.persistFunctions();
this.targetChanged();
}
}, {
key: 'addFunction',
value: function addFunction(funcDef) {
var func = _dfunc2.default.createFuncInstance(funcDef, { withDefaultParams: true });
func.added = true;
this.functions.push(func);
this.persistFunctions();
this.targetChanged();
}
}, {
key: 'tagSegmentUpdated',
value: function tagSegmentUpdated(segment, index) {
if (segment.value === this.removeText) {
this.tagSegments.splice(index, 1);
}
var realSegments = _lodash2.default.filter(this.tagSegments, function (segment) {
return segment.value;
});
this.target.tags = realSegments.map(function (segment) {
return segment.value;
});
this.tagSegments = _lodash2.default.map(this.target.tags, this.uiSegmentSrv.newSegment);
this.fixTagSegments();
this.panelCtrl.refresh();
}
}, {
key: 'getCollapsedText',
value: function getCollapsedText() {
if (this.target.rawQuery) {
return this.target.query;
} else {
return queryBuilder.buildQuery(this.target);
}
}
}]);
return DataDogQueryCtrl;
}(_sdk.QueryCtrl);
DataDogQueryCtrl.templateUrl = 'partials/query.editor.html';
//# sourceMappingURL=query_ctrl.js.map
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,79 @@
"use strict";
var _module = require("../module");
var _q = require("q");
var _q2 = _interopRequireDefault(_q);
var _sinon = require("sinon");
var _sinon2 = _interopRequireDefault(_sinon);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
describe('DataDogDatasource', function () {
var ctx = {};
beforeEach(function () {
ctx.$q = _q2.default;
ctx.backendSrv = {
datasourceRequest: function datasourceRequest() {
return ctx.$q.when({
status: 200
});
}
};
ctx.templateSrv = {
replace: function replace(str) {
return str;
},
getAdhocFilters: function getAdhocFilters() {
return [];
}
};
var instanceSettings = {
url: 'https://app.datadoghq.com/api/v1',
jsonData: {
api_key: '0000deadbeaf0000',
app_key: '0000abcd0000abcd'
}
};
ctx.ds = new _module.Datasource(instanceSettings, ctx.backendSrv, ctx.templateSrv);
});
describe('When doing DataDog API request', function () {
beforeEach(function () {
var targets = [{ query: 'avg:system.load.5{*}', rawQuery: true }];
ctx.options = {
range: {
from: 12340000,
to: 12340000
},
targets: targets
};
});
it('should send request with proper params', function (done) {
var expected_params = {
method: 'GET',
url: 'https://app.datadoghq.com/api/v1/query',
params: {
api_key: '0000deadbeaf0000',
application_key: '0000abcd0000abcd',
from: 12340,
to: 12340,
query: 'avg:system.load.5{*}'
}
};
var datasourceRequest = _sinon2.default.spy(ctx.ds.backendSrv, 'datasourceRequest');
ctx.ds.query(ctx.options);
expect(datasourceRequest).to.have.been.calledWith(expected_params);
done();
});
});
});
//# sourceMappingURL=datasource_spec.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../../../spec/datasource_spec.js"],"names":["describe","ctx","beforeEach","$q","backendSrv","datasourceRequest","when","status","templateSrv","replace","str","getAdhocFilters","instanceSettings","url","jsonData","api_key","app_key","ds","targets","query","rawQuery","options","range","from","to","it","done","expected_params","method","params","application_key","spy","expect","have","been","calledWith"],"mappings":";;AAAA;;AACA;;;;AACA;;;;;;AAEAA,SAAS,mBAAT,EAA8B,YAAM;AAClC,MAAIC,MAAM,EAAV;;AAEAC,aAAW,YAAW;AACpBD,QAAIE,EAAJ;AACAF,QAAIG,UAAJ,GAAiB;AACfC,yBAAmB,6BAAM;AACvB,eAAOJ,IAAIE,EAAJ,CAAOG,IAAP,CAAY;AACjBC,kBAAQ;AADS,SAAZ,CAAP;AAGD;AALc,KAAjB;AAOAN,QAAIO,WAAJ,GAAkB;AAChBC,eAAS,iBAACC,GAAD;AAAA,eAASA,GAAT;AAAA,OADO;AAEhBC,uBAAiB;AAAA,eAAM,EAAN;AAAA;AAFD,KAAlB;;AAKA,QAAIC,mBAAmB;AACrBC,WAAK,kCADgB;AAErBC,gBAAU;AACRC,iBAAS,kBADD;AAERC,iBAAS;AAFD;AAFW,KAAvB;AAOAf,QAAIgB,EAAJ,GAAS,uBAAeL,gBAAf,EAAiCX,IAAIG,UAArC,EAAiDH,IAAIO,WAArD,CAAT;AACD,GAtBD;;AAwBAR,WAAS,gCAAT,EAA2C,YAAM;AAC/CE,eAAW,YAAW;AACpB,UAAIgB,UAAU,CACZ,EAAEC,OAAO,sBAAT,EAAiCC,UAAU,IAA3C,EADY,CAAd;;AAIAnB,UAAIoB,OAAJ,GAAc;AACZC,eAAO;AACLC,gBAAM,QADD;AAELC,cAAI;AAFC,SADK;AAKZN,iBAASA;AALG,OAAd;AAOD,KAZD;;AAcAO,OAAG,wCAAH,EAA6C,UAACC,IAAD,EAAU;AACrD,UAAIC,kBAAkB;AACpBC,gBAAQ,KADY;AAEpBf,aAAK,wCAFe;AAGpBgB,gBAAQ;AACNd,mBAAS,kBADH;AAENe,2BAAiB,kBAFX;AAGNP,gBAAM,KAHA;AAINC,cAAI,KAJE;AAKNL,iBAAO;AALD;AAHY,OAAtB;AAWA,UAAId,oBAAoB,gBAAM0B,GAAN,CAAU9B,IAAIgB,EAAJ,CAAOb,UAAjB,EAA6B,mBAA7B,CAAxB;;AAEAH,UAAIgB,EAAJ,CAAOE,KAAP,CAAalB,IAAIoB,OAAjB;AACAW,aAAO3B,iBAAP,EAA0BmB,EAA1B,CAA6BS,IAA7B,CAAkCC,IAAlC,CAAuCC,UAAvC,CAAkDR,eAAlD;AACAD;AACD,KAjBD;AAkBD,GAjCD;AAkCD,CA7DD","file":"datasource_spec.js","sourcesContent":["import {Datasource} from \"../module\";\nimport Q from \"q\";\nimport sinon from 'sinon';\n\ndescribe('DataDogDatasource', () => {\n let ctx = {};\n\n beforeEach(function() {\n ctx.$q = Q;\n ctx.backendSrv = {\n datasourceRequest: () => {\n return ctx.$q.when({\n status: 200\n });\n }\n };\n ctx.templateSrv = {\n replace: (str) => str,\n getAdhocFilters: () => []\n };\n\n let instanceSettings = {\n url: 'https://app.datadoghq.com/api/v1',\n jsonData: {\n api_key: '0000deadbeaf0000',\n app_key: '0000abcd0000abcd'\n }\n };\n ctx.ds = new Datasource(instanceSettings, ctx.backendSrv, ctx.templateSrv);\n });\n\n describe('When doing DataDog API request', () => {\n beforeEach(function() {\n let targets = [\n { query: 'avg:system.load.5{*}', rawQuery: true }\n ];\n\n ctx.options = {\n range: {\n from: 12340000,\n to: 12340000\n },\n targets: targets\n };\n });\n\n it('should send request with proper params', (done) => {\n let expected_params = {\n method: 'GET',\n url: 'https://app.datadoghq.com/api/v1/query',\n params: {\n api_key: '0000deadbeaf0000',\n application_key: '0000abcd0000abcd',\n from: 12340,\n to: 12340,\n query: 'avg:system.load.5{*}'\n }\n };\n let datasourceRequest = sinon.spy(ctx.ds.backendSrv, 'datasourceRequest');\n\n ctx.ds.query(ctx.options);\n expect(datasourceRequest).to.have.been.calledWith(expected_params);\n done();\n });\n });\n});\n"]}
@@ -0,0 +1,84 @@
'use strict';
var _dfunc = require('../dfunc');
var _dfunc2 = _interopRequireDefault(_dfunc);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
describe('when creating func instance from func names', function () {
it('should return func instance', function () {
var func = _dfunc2.default.createFuncInstance('top');
expect(func).to.be.ok;
expect(func.def.name).to.equal('top');
expect(func.def.params.length).to.equal(3);
expect(func.def.defaultParams.length).to.equal(3);
});
it('should return func instance from funcDef', function () {
var func = _dfunc2.default.createFuncInstance('top');
var func2 = _dfunc2.default.createFuncInstance(func.def);
expect(func2).to.be.ok;
});
it('func instance should have text representation', function () {
var func = _dfunc2.default.createFuncInstance('top');
func.params[0] = 5;
func.params[1] = 'mean';
func.params[2] = 'dir';
func.updateText();
expect(func.text).to.equal("top(5, mean, dir)");
});
});
describe('when rendering func instance', function () {
it('should handle single metric param', function () {
var func = _dfunc2.default.createFuncInstance('abs');
expect(func.render('a')).to.equal("abs(a)");
});
it('should include default params if options enable it', function () {
var func = _dfunc2.default.createFuncInstance('top', { withDefaultParams: true });
expect(func.render('a')).to.equal("top(a, 5, mean, dir)");
});
it('should handle int or interval params with number', function () {
var func = _dfunc2.default.createFuncInstance('anomalies');
func.params[0] = 'basic';
func.params[1] = '5';
expect(func.render('hello')).to.equal("anomalies(hello, basic, 5)");
});
});
describe('when requesting function categories', function () {
it('should return function categories', function () {
var catIndex = _dfunc2.default.getCategories();
expect(catIndex.Arithmatic.length).to.be.greaterThan(3);
});
});
describe('when updating func param', function () {
it('should update param value and update text representation', function () {
var func = _dfunc2.default.createFuncInstance('top', { withDefaultParams: true });
func.updateParam('10', 0);
expect(func.params[0]).to.be.equal('10');
func.updateText();
expect(func.text).to.be.equal('top(10, mean, dir)');
});
it('should parse numbers as float', function () {
var func = _dfunc2.default.createFuncInstance('outliers');
func.updateParam('0.5', 1);
expect(func.params[1]).to.be.equal('0.5');
});
});
describe('when updating func param with optional second parameter', function () {
it('should update value and text', function () {
var func = _dfunc2.default.createFuncInstance('fill');
func.updateParam('null', 0);
expect(func.params[0]).to.be.equal('null');
});
});
//# sourceMappingURL=dfunc_spec.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,194 @@
'use strict';
var _module = require('../module');
require('app/core/services/segment_srv');
var _sinon = require('sinon');
var _sinon2 = _interopRequireDefault(_sinon);
var _dfunc = require('../dfunc');
var _dfunc2 = _interopRequireDefault(_dfunc);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
describe('DataDogQueryCtrl', function () {
var ctx = {};
beforeEach(angularMocks.module('grafana.core'));
beforeEach(angularMocks.module('grafana.controllers'));
beforeEach(angularMocks.module('grafana.services'));
beforeEach(ctx.providePhase());
beforeEach(angularMocks.inject(function ($rootScope, $controller, $q) {
ctx.$q = $q;
ctx.scope = $rootScope.$new();
ctx.target = {};
ctx.datasource.metricFindQuery = _sinon2.default.stub().returns(ctx.$q.when([]));
ctx.panelCtrl = { panel: {} };
ctx.panelCtrl.refresh = _sinon2.default.spy();
ctx.ctrl = $controller(_module.QueryCtrl, { $scope: ctx.scope }, {
panelCtrl: ctx.panelCtrl,
datasource: ctx.datasource,
target: ctx.target
});
ctx.scope.$digest();
}));
describe('init', function () {
it('should validate metric key exists', function () {
expect(ctx.datasource.metricFindQuery.getCall(0).args[0]).to.be('test.prod.*');
});
it('should delete last segment if no metrics are found', function () {
expect(ctx.ctrl.segments[2].value).to.be('select metric');
});
it('should parse expression and build function model', function () {
expect(ctx.ctrl.functions.length).to.be(2);
});
});
describe('when adding function', function () {
beforeEach(function () {
ctx.ctrl.target.target = 'test.prod.*.count';
ctx.ctrl.datasource.metricFindQuery = _sinon2.default.stub().returns(ctx.$q.when([{ expandable: false }]));
ctx.ctrl.parseTarget();
ctx.ctrl.addFunction(gfunc.getFuncDef('aliasByNode'));
});
it('should add function with correct node number', function () {
expect(ctx.ctrl.functions[0].params[0]).to.be(2);
});
it('should update target', function () {
expect(ctx.ctrl.target.target).to.be('aliasByNode(test.prod.*.count, 2)');
});
it('should call refresh', function () {
expect(ctx.panelCtrl.refresh.called).to.be(true);
});
});
describe('when adding function before any metric segment', function () {
beforeEach(function () {
ctx.ctrl.target.target = '';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([{ expandable: true }]));
ctx.ctrl.parseTarget();
ctx.ctrl.addFunction(gfunc.getFuncDef('asPercent'));
});
it('should add function and remove select metric link', function () {
expect(ctx.ctrl.segments.length).to.be(0);
});
});
describe('when initalizing target without metric expression and only function', function () {
beforeEach(function () {
ctx.ctrl.target.target = 'asPercent(#A, #B)';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.ctrl.parseTarget();
ctx.scope.$digest();
});
it('should not add select metric segment', function () {
expect(ctx.ctrl.segments.length).to.be(0);
});
it('should add both series refs as params', function () {
expect(ctx.ctrl.functions[0].params.length).to.be(2);
});
});
describe('when initializing a target with single param func using variable', function () {
beforeEach(function () {
ctx.ctrl.target.target = 'movingAverage(prod.count, $var)';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.ctrl.parseTarget();
});
it('should add 2 segments', function () {
expect(ctx.ctrl.segments.length).to.be(2);
});
it('should add function param', function () {
expect(ctx.ctrl.functions[0].params.length).to.be(1);
});
});
describe('when initalizing target without metric expression and function with series-ref', function () {
beforeEach(function () {
ctx.ctrl.target.target = 'asPercent(metric.node.count, #A)';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.ctrl.parseTarget();
});
it('should add segments', function () {
expect(ctx.ctrl.segments.length).to.be(3);
});
it('should have correct func params', function () {
expect(ctx.ctrl.functions[0].params.length).to.be(1);
});
});
describe('when getting altSegments and metricFindQuery retuns empty array', function () {
beforeEach(function () {
ctx.ctrl.target.target = 'test.count';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.ctrl.parseTarget();
ctx.ctrl.getAltSegments(1).then(function (results) {
ctx.altSegments = results;
});
ctx.scope.$digest();
});
it('should have no segments', function () {
expect(ctx.altSegments.length).to.be(0);
});
});
describe('targetChanged', function () {
beforeEach(function () {
ctx.ctrl.datasource.metricFindQuery = _sinon2.default.stub().returns(ctx.$q.when([{ expandable: false }]));
ctx.ctrl.parseTarget();
ctx.ctrl.target.target = '';
ctx.ctrl.targetChanged();
});
it('should rebuld target after expression model', function () {
expect(ctx.ctrl.target.target).to.be('aliasByNode(scaleToSeconds(test.prod.*, 1), 2)');
});
it('should call panelCtrl.refresh', function () {
expect(ctx.panelCtrl.refresh.called).to.be(true);
});
});
describe('when updating targets with nested query', function () {
beforeEach(function () {
ctx.ctrl.target.target = 'scaleToSeconds(#A)';
ctx.ctrl.datasource.metricFindQuery = _sinon2.default.stub().returns(ctx.$q.when([{ expandable: false }]));
ctx.ctrl.parseTarget();
ctx.ctrl.panelCtrl.panel.targets = [{
target: 'nested.query.count',
refId: 'A'
}];
ctx.ctrl.updateModelTarget();
});
it('target should remain the same', function () {
expect(ctx.ctrl.target.target).to.be('scaleToSeconds(#A)');
});
it('targetFull should include nexted queries', function () {
expect(ctx.ctrl.target.targetFull).to.be('scaleToSeconds(nested.query.count)');
});
});
});
//# sourceMappingURL=query_ctrl_soon.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,64 @@
'use strict';
var _prunk = require('prunk');
var _prunk2 = _interopRequireDefault(_prunk);
var _jsdom = require('jsdom');
var _chai = require('chai');
var _chai2 = _interopRequireDefault(_chai);
var _sinonChai = require('sinon-chai');
var _sinonChai2 = _interopRequireDefault(_sinonChai);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// Mock angular module
// JSHint options
/* globals global: false */
var angularMocks = {
module: function module() {
return {
directive: function directive() {}
};
}
};
var datemathMock = {
parse: function parse() {}
};
var momentMock = {
duration: function duration(num, str) {
return 60;
}
};
// Mock Grafana modules that are not available outside of the core project
// Required for loading module.js
_prunk2.default.mock('./css/query-editor.css!', 'no css, dude.');
_prunk2.default.mock('app/plugins/sdk', {
QueryCtrl: null
});
_prunk2.default.mock('app/core/utils/datemath', datemathMock);
_prunk2.default.mock('moment', momentMock);
_prunk2.default.mock('angular', angularMocks);
_prunk2.default.mock('jquery', 'module not found');
// Setup jsdom
// Required for loading angularjs
global.document = (0, _jsdom.jsdom)('<html><head><script></script></head><body></body></html>');
global.window = global.document.parentWindow;
global.navigator = window.navigator = {};
global.Node = window.Node;
// Setup Chai
_chai2.default.should();
_chai2.default.use(_sinonChai2.default);
global.assert = _chai2.default.assert;
global.expect = _chai2.default.expect;
//# sourceMappingURL=test-main.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../../../spec/test-main.js"],"names":["angularMocks","module","directive","datemathMock","parse","momentMock","duration","num","str","mock","QueryCtrl","global","document","window","parentWindow","navigator","Node","should","use","assert","expect"],"mappings":";;AAGA;;;;AACA;;AACA;;;;AACA;;;;;;AAEA;AARA;AACA;;AAQA,IAAIA,eAAe;AACjBC,UAAQ,kBAAW;AACjB,WAAO;AACLC,iBAAW,qBAAW,CAAE;AADnB,KAAP;AAGD;AALgB,CAAnB;;AAQA,IAAIC,eAAe;AACjBC,SAAO,iBAAW,CAAE;AADH,CAAnB;;AAIA,IAAIC,aAAa;AACfC,YAAU,kBAASC,GAAT,EAAcC,GAAd,EAAmB;AAC3B,WAAO,EAAP;AACD;AAHc,CAAjB;;AAMA;AACA;AACA,gBAAMC,IAAN,CAAW,yBAAX,EAAsC,eAAtC;AACA,gBAAMA,IAAN,CAAW,iBAAX,EAA8B;AAC5BC,aAAW;AADiB,CAA9B;AAGA,gBAAMD,IAAN,CAAW,yBAAX,EAAsCN,YAAtC;AACA,gBAAMM,IAAN,CAAW,QAAX,EAAqBJ,UAArB;AACA,gBAAMI,IAAN,CAAW,SAAX,EAAsBT,YAAtB;AACA,gBAAMS,IAAN,CAAW,QAAX,EAAqB,kBAArB;;AAEA;AACA;AACAE,OAAOC,QAAP,GAAkB,kBAAM,0DAAN,CAAlB;AACAD,OAAOE,MAAP,GAAgBF,OAAOC,QAAP,CAAgBE,YAAhC;AACAH,OAAOI,SAAP,GAAmBF,OAAOE,SAAP,GAAmB,EAAtC;AACAJ,OAAOK,IAAP,GAAcH,OAAOG,IAArB;;AAEA;AACA,eAAKC,MAAL;AACA,eAAKC,GAAL;AACAP,OAAOQ,MAAP,GAAgB,eAAKA,MAArB;AACAR,OAAOS,MAAP,GAAgB,eAAKA,MAArB","file":"test-main.js","sourcesContent":["// JSHint options\n/* globals global: false */\n\nimport prunk from 'prunk';\nimport {jsdom} from 'jsdom';\nimport chai from 'chai';\nimport sinonChai from 'sinon-chai';\n\n// Mock angular module\nvar angularMocks = {\n module: function() {\n return {\n directive: function() {}\n };\n }\n};\n\nvar datemathMock = {\n parse: function() {}\n};\n\nvar momentMock = {\n duration: function(num, str) {\n return 60;\n }\n};\n\n// Mock Grafana modules that are not available outside of the core project\n// Required for loading module.js\nprunk.mock('./css/query-editor.css!', 'no css, dude.');\nprunk.mock('app/plugins/sdk', {\n QueryCtrl: null\n});\nprunk.mock('app/core/utils/datemath', datemathMock);\nprunk.mock('moment', momentMock);\nprunk.mock('angular', angularMocks);\nprunk.mock('jquery', 'module not found');\n\n// Setup jsdom\n// Required for loading angularjs\nglobal.document = jsdom('<html><head><script></script></head><body></body></html>');\nglobal.window = global.document.parentWindow;\nglobal.navigator = window.navigator = {};\nglobal.Node = window.Node;\n\n// Setup Chai\nchai.should();\nchai.use(sinonChai);\nglobal.assert = chai.assert;\nglobal.expect = chai.expect;\n"]}
@@ -0,0 +1,7 @@
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- ./dist:/var/lib/grafana/plugins/datadog-datasource
@@ -0,0 +1,49 @@
{
"name": "grafana-datadog-datasource",
"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/voidfiles/datadog-datasource.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/voidfiles/grafana-datasource-datagog/issues"
},
"devDependencies": {
"babel": "~6.5.1",
"babel-plugin-transform-es2015-for-of": "^6.6.0",
"babel-plugin-transform-es2015-modules-systemjs": "^6.5.0",
"babel-preset-es2015": "^6.5.0",
"chai": "~3.5.0",
"grunt": "~0.4.5",
"grunt-babel": "~6.0.0",
"grunt-contrib-clean": "~0.6.0",
"grunt-contrib-copy": "~0.8.2",
"grunt-contrib-jshint": "^1.0.0",
"grunt-contrib-uglify": "~0.11.0",
"grunt-contrib-watch": "^0.6.1",
"grunt-execute": "~0.2.2",
"grunt-jscs": "^2.8.0",
"grunt-mocha-test": "~0.12.7",
"grunt-systemjs-builder": "^0.2.5",
"jsdom": "~3.1.2",
"jshint-stylish": "^2.1.0",
"load-grunt-tasks": "~3.2.0",
"mocha": "^2.4.5",
"prunk": "~1.2.1",
"q": "~1.4.1",
"sinon": "~1.16.1",
"sinon-chai": "~2.8.0"
},
"dependencies": {
"lodash": "~4.0.0"
},
"homepage": "https://github.com/voidfiles/grafana-datasource-datagog/issues"
}
@@ -0,0 +1,66 @@
import {Datasource} from "../module";
import Q from "q";
import sinon from 'sinon';
describe('DataDogDatasource', () => {
let ctx = {};
beforeEach(function() {
ctx.$q = Q;
ctx.backendSrv = {
datasourceRequest: () => {
return ctx.$q.when({
status: 200
});
}
};
ctx.templateSrv = {
replace: (str) => str,
getAdhocFilters: () => []
};
let instanceSettings = {
url: 'https://app.datadoghq.com/api/v1',
jsonData: {
api_key: '0000deadbeaf0000',
app_key: '0000abcd0000abcd'
}
};
ctx.ds = new Datasource(instanceSettings, ctx.backendSrv, ctx.templateSrv);
});
describe('When doing DataDog API request', () => {
beforeEach(function() {
let targets = [
{ query: 'avg:system.load.5{*}', rawQuery: true }
];
ctx.options = {
range: {
from: 12340000,
to: 12340000
},
targets: targets
};
});
it('should send request with proper params', (done) => {
let expected_params = {
method: 'GET',
url: 'https://app.datadoghq.com/api/v1/query',
params: {
api_key: '0000deadbeaf0000',
application_key: '0000abcd0000abcd',
from: 12340,
to: 12340,
query: 'avg:system.load.5{*}'
}
};
let datasourceRequest = sinon.spy(ctx.ds.backendSrv, 'datasourceRequest');
ctx.ds.query(ctx.options);
expect(datasourceRequest).to.have.been.calledWith(expected_params);
done();
});
});
});
@@ -0,0 +1,79 @@
import dfunc from '../dfunc';
describe('when creating func instance from func names', function() {
it('should return func instance', function() {
var func = dfunc.createFuncInstance('top');
expect(func).to.be.ok;
expect(func.def.name).to.equal('top');
expect(func.def.params.length).to.equal(3);
expect(func.def.defaultParams.length).to.equal(3);
});
it('should return func instance from funcDef', function() {
var func = dfunc.createFuncInstance('top');
var func2 = dfunc.createFuncInstance(func.def);
expect(func2).to.be.ok;
});
it('func instance should have text representation', function() {
var func = dfunc.createFuncInstance('top');
func.params[0] = 5;
func.params[1] = 'mean';
func.params[2] = 'dir';
func.updateText();
expect(func.text).to.equal("top(5, mean, dir)");
});
});
describe('when rendering func instance', function() {
it('should handle single metric param', function() {
var func = dfunc.createFuncInstance('abs');
expect(func.render('a')).to.equal("abs(a)");
});
it('should include default params if options enable it', function() {
var func = dfunc.createFuncInstance('top', { withDefaultParams: true });
expect(func.render('a')).to.equal("top(a, 5, mean, dir)");
});
it('should handle int or interval params with number', function() {
var func = dfunc.createFuncInstance('anomalies');
func.params[0] = 'basic';
func.params[1] = '5';
expect(func.render('hello')).to.equal("anomalies(hello, basic, 5)");
});
});
describe('when requesting function categories', function() {
it('should return function categories', function() {
var catIndex = dfunc.getCategories();
expect(catIndex.Arithmatic.length).to.be.greaterThan(3);
});
});
describe('when updating func param', function() {
it('should update param value and update text representation', function() {
var func = dfunc.createFuncInstance('top', { withDefaultParams: true });
func.updateParam('10', 0);
expect(func.params[0]).to.be.equal('10');
func.updateText();
expect(func.text).to.be.equal('top(10, mean, dir)');
});
it('should parse numbers as float', function() {
var func = dfunc.createFuncInstance('outliers');
func.updateParam('0.5', 1);
expect(func.params[1]).to.be.equal('0.5');
});
});
describe('when updating func param with optional second parameter', function() {
it('should update value and text', function() {
var func = dfunc.createFuncInstance('fill');
func.updateParam('null', 0);
expect(func.params[0]).to.be.equal('null');
});
});
@@ -0,0 +1,184 @@
import {QueryCtrl} from "../module";
import 'app/core/services/segment_srv';
import sinon from 'sinon';
import dfunc from '../dfunc';
describe('DataDogQueryCtrl', function() {
let ctx = {};
beforeEach(angularMocks.module('grafana.core'));
beforeEach(angularMocks.module('grafana.controllers'));
beforeEach(angularMocks.module('grafana.services'));
beforeEach(ctx.providePhase());
beforeEach(angularMocks.inject(($rootScope, $controller, $q) => {
ctx.$q = $q;
ctx.scope = $rootScope.$new();
ctx.target = {};
ctx.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
ctx.panelCtrl = {panel: {}};
ctx.panelCtrl.refresh = sinon.spy();
ctx.ctrl = $controller(QueryCtrl, {$scope: ctx.scope}, {
panelCtrl: ctx.panelCtrl,
datasource: ctx.datasource,
target: ctx.target
});
ctx.scope.$digest();
}));
describe('init', function() {
it('should validate metric key exists', function() {
expect(ctx.datasource.metricFindQuery.getCall(0).args[0]).to.be('test.prod.*');
});
it('should delete last segment if no metrics are found', function() {
expect(ctx.ctrl.segments[2].value).to.be('select metric');
});
it('should parse expression and build function model', function() {
expect(ctx.ctrl.functions.length).to.be(2);
});
});
describe('when adding function', function() {
beforeEach(function() {
ctx.ctrl.target.target = 'test.prod.*.count';
ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{expandable: false}]));
ctx.ctrl.parseTarget();
ctx.ctrl.addFunction(gfunc.getFuncDef('aliasByNode'));
});
it('should add function with correct node number', function() {
expect(ctx.ctrl.functions[0].params[0]).to.be(2);
});
it('should update target', function() {
expect(ctx.ctrl.target.target).to.be('aliasByNode(test.prod.*.count, 2)');
});
it('should call refresh', function() {
expect(ctx.panelCtrl.refresh.called).to.be(true);
});
});
describe('when adding function before any metric segment', function() {
beforeEach(function() {
ctx.ctrl.target.target = '';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([{expandable: true}]));
ctx.ctrl.parseTarget();
ctx.ctrl.addFunction(gfunc.getFuncDef('asPercent'));
});
it('should add function and remove select metric link', function() {
expect(ctx.ctrl.segments.length).to.be(0);
});
});
describe('when initalizing target without metric expression and only function', function() {
beforeEach(function() {
ctx.ctrl.target.target = 'asPercent(#A, #B)';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.ctrl.parseTarget();
ctx.scope.$digest();
});
it('should not add select metric segment', function() {
expect(ctx.ctrl.segments.length).to.be(0);
});
it('should add both series refs as params', function() {
expect(ctx.ctrl.functions[0].params.length).to.be(2);
});
});
describe('when initializing a target with single param func using variable', function() {
beforeEach(function() {
ctx.ctrl.target.target = 'movingAverage(prod.count, $var)';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.ctrl.parseTarget();
});
it('should add 2 segments', function() {
expect(ctx.ctrl.segments.length).to.be(2);
});
it('should add function param', function() {
expect(ctx.ctrl.functions[0].params.length).to.be(1);
});
});
describe('when initalizing target without metric expression and function with series-ref', function() {
beforeEach(function() {
ctx.ctrl.target.target = 'asPercent(metric.node.count, #A)';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.ctrl.parseTarget();
});
it('should add segments', function() {
expect(ctx.ctrl.segments.length).to.be(3);
});
it('should have correct func params', function() {
expect(ctx.ctrl.functions[0].params.length).to.be(1);
});
});
describe('when getting altSegments and metricFindQuery retuns empty array', function() {
beforeEach(function() {
ctx.ctrl.target.target = 'test.count';
ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([]));
ctx.ctrl.parseTarget();
ctx.ctrl.getAltSegments(1).then(function(results) {
ctx.altSegments = results;
});
ctx.scope.$digest();
});
it('should have no segments', function() {
expect(ctx.altSegments.length).to.be(0);
});
});
describe('targetChanged', function() {
beforeEach(function() {
ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{expandable: false}]));
ctx.ctrl.parseTarget();
ctx.ctrl.target.target = '';
ctx.ctrl.targetChanged();
});
it('should rebuld target after expression model', function() {
expect(ctx.ctrl.target.target).to.be('aliasByNode(scaleToSeconds(test.prod.*, 1), 2)');
});
it('should call panelCtrl.refresh', function() {
expect(ctx.panelCtrl.refresh.called).to.be(true);
});
});
describe('when updating targets with nested query', function() {
beforeEach(function() {
ctx.ctrl.target.target = 'scaleToSeconds(#A)';
ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{expandable: false}]));
ctx.ctrl.parseTarget();
ctx.ctrl.panelCtrl.panel.targets = [ {
target: 'nested.query.count',
refId: 'A'
}];
ctx.ctrl.updateModelTarget();
});
it('target should remain the same', function() {
expect(ctx.ctrl.target.target).to.be('scaleToSeconds(#A)');
});
it('targetFull should include nexted queries', function() {
expect(ctx.ctrl.target.targetFull).to.be('scaleToSeconds(nested.query.count)');
});
});
});
@@ -0,0 +1,50 @@
// JSHint options
/* globals global: false */
import prunk from 'prunk';
import {jsdom} from 'jsdom';
import chai from 'chai';
import sinonChai from 'sinon-chai';
// Mock angular module
var angularMocks = {
module: function() {
return {
directive: function() {}
};
}
};
var datemathMock = {
parse: function() {}
};
var momentMock = {
duration: function(num, str) {
return 60;
}
};
// Mock Grafana modules that are not available outside of the core project
// Required for loading module.js
prunk.mock('./css/query-editor.css!', 'no css, dude.');
prunk.mock('app/plugins/sdk', {
QueryCtrl: null
});
prunk.mock('app/core/utils/datemath', datemathMock);
prunk.mock('moment', momentMock);
prunk.mock('angular', angularMocks);
prunk.mock('jquery', 'module not found');
// Setup jsdom
// Required for loading angularjs
global.document = jsdom('<html><head><script></script></head><body></body></html>');
global.window = global.document.parentWindow;
global.navigator = window.navigator = {};
global.Node = window.Node;
// Setup Chai
chai.should();
chai.use(sinonChai);
global.assert = chai.assert;
global.expect = chai.expect;
@@ -0,0 +1,103 @@
import angular from 'angular';
import _ from 'lodash';
import $ from 'jquery';
import dfunc from './dfunc';
angular
.module('grafana.directives')
.directive('datadogAddFunc', function($compile) {
var inputTemplate = '<input type="text"'+
' class="gf-form-input"' +
' spellcheck="false" style="display:none"></input>';
var buttonTemplate = '<a class="gf-form-label query-part dropdown-toggle"' +
' tabindex="1" gf-dropdown="functionMenu" data-toggle="dropdown">' +
'<i class="fa fa-plus"></i></a>';
return {
link: function($scope, elem) {
var categories = dfunc.getCategories();
var allFunctions = getAllFunctionNames(categories);
var ctrl = $scope.ctrl;
$scope.functionMenu = createFunctionDropDownMenu(categories);
var $input = $(inputTemplate);
var $button = $(buttonTemplate);
$input.appendTo(elem);
$button.appendTo(elem);
$input.attr('data-provide', 'typeahead');
$input.typeahead({
source: allFunctions,
minLength: 1,
items: 10,
updater: function (value) {
var funcDef = dfunc.getFuncDef(value);
if (!funcDef) {
// try find close match
value = value.toLowerCase();
funcDef = _.find(allFunctions, function(funcName) {
return funcName.toLowerCase().indexOf(value) === 0;
});
if (!funcDef) { return; }
}
$scope.$apply(function() {
ctrl.addFunction(funcDef);
});
$input.trigger('blur');
return '';
}
});
$button.click(function() {
$button.hide();
$input.show();
$input.focus();
});
$input.keyup(function() {
elem.toggleClass('open', $input.val() === '');
});
$input.blur(function() {
// clicking the function dropdown menu wont
// work if you remove class at once
setTimeout(function() {
$input.val('');
$input.hide();
$button.show();
elem.removeClass('open');
}, 200);
});
$compile(elem.contents())($scope);
}
};
});
function getAllFunctionNames(categories) {
return _.reduce(categories, function(list, category) {
_.each(category, function(func) {
list.push(func.name);
});
return list;
}, []);
}
function createFunctionDropDownMenu(categories) {
return _.map(categories, function(list, category) {
return {
text: category,
submenu: _.map(list, function(value) {
return {
text: value.name,
click: "ctrl.addFunction('" + value.name + "')",
};
})
};
});
}
@@ -0,0 +1,374 @@
import _ from 'lodash';
import showdown from './showdown.min.js';
import * as queryBuilder from './query_builder';
export class DataDogDatasource {
constructor (instanceSettings, backendSrv, templateSrv) {
this.type = instanceSettings.type;
this.url = instanceSettings.url;
this.name = instanceSettings.name;
this.api_key = instanceSettings.jsonData.api_key;
this.application_key = instanceSettings.jsonData.app_key;
this.supportMetrics = true;
this.backendSrv = backendSrv;
this.templateSrv = templateSrv;
this._cached_metrics = false;
}
// Function to check Datasource health
testDatasource() {
return this.invokeDataDogApiRequest('/downtime')
.then(() => {
return {
status: "success",
title: "Success",
message: "Data source is working"
};
})
.catch(error => {
var message = "Connection error";
if (error && error.message) {
message = error.message;
}
return {
status: "error",
title: "Error",
message: message
};
});
}
tagFindQuery() {
return this.getTagsFromCache()
.then(tags => {
return _.map(tags, (hosts, tag) => {
return {
text: tag,
value: tag,
};
});
});
}
metricFindQuery(query) {
if (query === 'tag') {
return this.tagFindQuery();
}
if (this._cached_metrics) {
return Promise.resolve(this._cached_metrics);
}
if (this.fetching) {
return this.fetching;
}
var d = new Date();
d.setDate(d.getDate() - 1);
var from = Math.floor(d.getTime() / 1000);
this.fetching = this.getMetrics(from).then(metrics => {
this._cached_metrics = _.map(metrics, metric => {
return {
text: metric,
value: metric,
};
});
return this._cached_metrics;
});
return this.fetching;
}
getTagKeys() {
return this.getTagsFromCache()
.then(tagsHosts => {
let tags = Object.keys(tagsHosts);
let kv = mapTagsToKVPairs(tags);
let grafanaTags = Object.keys(kv);
return grafanaTags.map(tag => {
return {
text: tag,
value: tag,
};
});
});
}
getTagValues(options) {
return this.getTagsFromCache()
.then(tagsHosts => {
let tags = Object.keys(tagsHosts);
let kv = mapTagsToKVPairs(tags);
let grafanaValues = kv[options.key];
return grafanaValues.map(val => {
return {
text: val,
value: val,
};
});
});
}
query(options) {
var from = Math.floor(options.range.from.valueOf() / 1000);
var to = Math.floor(options.range.to.valueOf() / 1000);
var targets = options.targets.filter(function (t) { return !t.hide; });
if (targets.length <= 0) {
return Promise.resolve({data: []});
}
// add global adhoc filters
let adhocFilters = this.templateSrv.getAdhocFilters(this.name);
var queries = _.map(targets, target => {
let query;
if (target.rawQuery) {
query = target.query;
} else {
query = queryBuilder.buildQuery(target, adhocFilters);
}
return query;
});
var queryString = queries.join(',');
queryString = this.templateSrv.replace(queryString, options.scopedVars, dataDogTagFormat);
var params = {
from: from,
to: to,
query: queryString,
};
return this.invokeDataDogApiRequest('/query', params)
.then(result => {
var dataResponse = _.map(result.series, (series, i) => {
var target = targets[i];
return {
'target': target.alias || series.expression,
'datapoints': _.map(series.pointlist, point => {
return [point[1], point[0]];
})
};
});
return {data: dataResponse};
});
}
annotationQuery(options) {
let timeFrom = Math.floor(options.range.from.valueOf() / 1000);
let timeTo = Math.floor(options.range.to.valueOf() / 1000);
let {priority, sources, tags} = options.annotation;
return this.getEventStream(timeFrom, timeTo, priority, sources, tags)
.then(eventStreams => {
let eventAnnotations = eventStreams.map(eventStream => {
let allEvents = eventStream.children;
let filteredEvents = _.filter(allEvents, event => {
return event.alert_type !== 'success';
});
return _.map(filteredEvents, event => {
let renderedText = eventStream.text;
if (isDataDogMarkdown(eventStream.text)) {
renderedText = convertDataDogMdToHtml(eventStream.text);
}
return {
annotation: options.annotation,
time: event.date_happened * 1000,
title: eventStream.title,
text: renderedText,
tags: eventStream.tags
};
});
});
return _.flatten(eventAnnotations);
});
}
getHosts() {
return this.searchEntities('hosts');
}
// entity should be 'hosts' or 'metrics'
// http://docs.datadoghq.com/api/?lang=console#search
searchEntities(entity) {
let params = {q: ''};
if (entity) {
params.q = `${entity}:`;
}
return this.invokeDataDogApiRequest('/search', params)
.then(result => {
if (result && result.results) {
return result.results[entity];
}
});
}
getMetrics(timeFrom) {
let params = {};
if (timeFrom) {
params.from = timeFrom;
}
return this.invokeDataDogApiRequest('/metrics', params)
.then(result => {
if (result.metrics) {
return result.metrics;
} else {
return [];
}
});
}
getTagsHosts() {
return this.invokeDataDogApiRequest('/tags/hosts')
.then(result => {
if (result && result.tags) {
return result.tags;
}
});
}
getTagsFromCache() {
let getTags;
if (this._cached_tags && this._cached_tags.length) {
getTags = Promise.resolve(this._cached_tags);
} else {
getTags = this.getTagsHosts().then(tags => {
this._cached_tags = tags;
return tags;
});
}
return getTags;
}
getEventStream(timeFrom, timeTo, priority, sources, tags) {
let params = {
start: timeFrom,
end: timeTo
};
if (priority) {
params.priority = priority;
}
if (sources) {
params.sources = sources;
}
if (tags) {
params.tags = tags;
}
return this.invokeDataDogApiRequest('/events', params)
.then(result => {
if (result.events) {
return result.events;
} else {
return [];
}
});
}
invokeDataDogApiRequest(url, params = {}) {
// Set auth params
params.api_key = this.api_key;
params.application_key = this.application_key;
return this.backendSrv.datasourceRequest({
method: 'GET',
url: this.url + url,
params: params
})
.then(response => {
if (response.data) {
return response.data;
} else {
throw {message: 'DataDog API request error'};
}
})
.catch(error => {
var message = 'DataDog API request error';
if (error.statusText) {
message = error.status + ' ' + error.statusText;
throw {message: message};
} else if (error.err.statusText) {
throw {message: error.err.statusText};
} else {
throw {message: message};
}
});
}
}
/*
* Convert tags to key-value pairs
* [region:east, region:nw] => {region: [east, nw]}
*/
function mapTagsToKVPairs(tags) {
let kv_tags = _.filter(tags, tag => {
return (tag.indexOf(':') !== -1);
});
let kv_pairs = kv_tags.map(tag => {
return tag.split(':', 2); // Limit to 2
});
let kv_object = {};
kv_pairs.forEach(pair => {
let key = pair[0];
let val = pair[1];
if (kv_object[key]) {
kv_object[key].push(val);
} else {
kv_object[key] = [val];
}
});
return kv_object;
}
/*
* Convert DataDog event text from markdown to pure HTML
* http://docs.datadoghq.com/guides/markdown/
*/
function convertDataDogMdToHtml(str) {
const MD_START = '%%%\n';
const MD_END = '\n%%%';
let md_start_index = str.indexOf(MD_START) + MD_START.length;
let md_end_index = str.indexOf(MD_END);
let md = str.substring(md_start_index, md_end_index);
md = removeImagesFromDataDogMarkdown(md);
let converter = new showdown.Converter();
return converter.makeHtml(md);
}
function isDataDogMarkdown(str) {
const MD_START = '%%%\n';
return str.indexOf(MD_START) >= 0;
}
function removeImagesFromDataDogMarkdown(str) {
let dataDogImagePattern = /\[{0,1}\!\[.*\]\(https{0,1}\:\/\/p\.datadoghq\.com\/snapshot.+\)/g;
return str.replace(dataDogImagePattern, '');
}
function dataDogTagFormat(value) {
if (_.isArray(value)) {
return value.join(',');
}
return value;
}
@@ -0,0 +1,316 @@
import _ from 'lodash';
var index = [];
var categories = {
Arithmatic: [],
Interpolation: [],
Timeshift: [],
Rate: [],
Smoothing: [],
Rollup: [],
Rank: [],
Count: [],
Regression: [],
Algorithms: []
};
function addFuncDef(funcDef) {
funcDef.params = funcDef.params || [];
funcDef.defaultParams = funcDef.defaultParams || [];
funcDef.append = funcDef.append || false;
if (funcDef.category) {
funcDef.category.push(funcDef);
}
index[funcDef.name] = funcDef;
index[funcDef.name] = funcDef;
}
addFuncDef({
name: 'abs',
category: categories.Arithmatic,
});
addFuncDef({
name: 'log2',
category: categories.Arithmatic,
});
addFuncDef({
name: 'cumsum',
category: categories.Arithmatic,
});
addFuncDef({
name: 'integral',
category: categories.Arithmatic,
});
addFuncDef({
name: 'fill',
category: categories.Interpolation,
params: [
{ name: "fill", type: 'string', options: ['null', 'zero', 'linear', 'last'] },
{ name: "limit", type: 'int', optional: true},
],
defaultParams: ['zero'],
append: true,
});
addFuncDef({
name: 'hour_before',
category: categories.Timeshift,
});
addFuncDef({
name: 'day_before',
category: categories.Timeshift,
});
addFuncDef({
name: 'week_before',
category: categories.Timeshift,
});
addFuncDef({
name: 'month_before',
category: categories.Timeshift,
});
addFuncDef({
name: 'per_second',
category: categories.Rate,
});
addFuncDef({
name: 'per_minute',
category: categories.Rate,
});
addFuncDef({
name: 'per_hour',
category: categories.Rate,
});
addFuncDef({
name: 'dt',
category: categories.Rate,
});
addFuncDef({
name: 'diff',
category: categories.Rate,
});
addFuncDef({
name: 'derivative',
category: categories.Rate,
});
addFuncDef({
name: 'ewma_3',
category: categories.Smoothing,
});
addFuncDef({
name: 'ewma_5',
category: categories.Smoothing,
});
addFuncDef({
name: 'ewma_10',
category: categories.Smoothing,
});
addFuncDef({
name: 'ewma_20',
category: categories.Smoothing,
});
addFuncDef({
name: 'median_3',
category: categories.Smoothing,
});
addFuncDef({
name: 'median_5',
category: categories.Smoothing,
});
addFuncDef({
name: 'median_7',
category: categories.Smoothing,
});
addFuncDef({
name: 'median_9',
category: categories.Smoothing,
});
addFuncDef({
name: 'rollup',
category: categories.Rollup,
params: [
{ name: "rollup", type: 'string', options: ['sum', 'avg', 'min', 'max'] },
{ name: "period", type: 'int'},
],
defaultParams: ['sum', 600],
append: true
});
addFuncDef({
name: 'count_nonzero',
category: categories.Count,
});
addFuncDef({
name: 'count_not_null',
category: categories.Count,
});
addFuncDef({
name: 'top',
category: categories.Rank,
params: [
{ name: "top", type: 'int', options: [5, 10, 15]},
{ name: "aggr", type: 'string', options: ['mean', 'min', 'max', 'area', 'l2norm', 'last']},
{ name: "dir", type: 'string', options: ['asc', 'desc']},
],
defaultParams: [5, 'mean', 'dir'],
});
addFuncDef({
name: 'robust_trend',
category: categories.Regression,
});
addFuncDef({
name: 'piecewise_constant',
category: categories.Regression,
});
addFuncDef({
name: 'anomalies',
category: categories.Algorithms,
params: [
{ name: "anomalies", type: 'string', options: ['basic', 'agile', 'robust', 'adaptive']},
{ name: "bonds", type: 'int', options: [1, 2, 3, 4, 5, 6]},
],
defaultParams: ['basic', 2],
});
addFuncDef({
name: 'anomalies',
category: categories.Algorithms,
params: [
{ name: "anomalies", type: 'string', options: ['basic', 'agile', 'robust', 'adaptive']},
{ name: "bonds", type: 'int', options: [1, 2, 3, 4, 5, 6]},
],
defaultParams: ['basic', 2],
});
addFuncDef({
name: 'outliers',
category: categories.Algorithms,
params: [
{ name: "outliers", type: 'string', options: ['DBSCAN', 'MAD', 'scaledDBSCAN', 'scaledMAD']},
{ name: "tolerance", type: 'int', options: [
0.33,
0.5,
1,
1.5,
2.0,
2.5,
3.0,
3.5,
4.0,
4.5,
5.0]},
],
defaultParams: ['basic', 2],
});
_.each(categories, function(funcList, catName) {
categories[catName] = _.sortBy(funcList, 'name');
});
function FuncInstance(funcDef, options) {
this.def = funcDef;
this.params = [];
if (options && options.withDefaultParams) {
this.params = funcDef.defaultParams.slice(0);
}
this.updateText();
}
FuncInstance.prototype.render = function(metricExp) {
var str = this.def.name + '(';
var parameters = _.map(this.params, function(value) {
return value;
});
if (metricExp && !this.def.append) {
parameters.unshift(metricExp);
}
var funcRender = str + parameters.join(', ') + ')';
console.log(funcRender, this.def.append);
if (this.def.append) {
return metricExp + '.' + funcRender;
} else {
return funcRender;
}
};
FuncInstance.prototype.updateParam = function(strValue, index) {
// handle optional parameters
// if string contains ',' and next param is optional, split and update both
if (strValue === '' && this.def.params[index].optional) {
this.params.splice(index, 1);
}
else {
this.params[index] = strValue;
}
this.updateText();
};
FuncInstance.prototype.updateText = function () {
if (this.params.length === 0) {
this.text = this.def.name + '()';
return;
}
var text = this.def.name + '(';
text += this.params.join(', ');
text += ')';
this.text = text;
};
var createFuncInstance = function(funcDef, options) {
if (_.isString(funcDef)) {
if (!index[funcDef]) {
throw { message: 'Method not found ' + name };
}
funcDef = index[funcDef];
}
return new FuncInstance(funcDef, options);
};
var getFuncDef = function(name) {
return index[name];
};
var getCategories = function() {
return categories;
};
var foo;
export default foo = {
createFuncInstance: createFuncInstance,
getFuncDef: getFuncDef,
getCategories: getCategories,
};
@@ -0,0 +1,243 @@
import angular from 'angular';
import _ from 'lodash';
import $ from 'jquery';
'use strict';
angular
.module('grafana.directives')
.directive('datadogFuncEditor', function($compile, templateSrv) {
var funcSpanTemplate = '<a ng-click="">{{func.def.name}}</a><span>(</span>';
var paramTemplate = '<input type="text" style="display:none"' +
' class="input-mini tight-form-func-param"></input>';
var funcControlsTemplate =
'<div class="tight-form-func-controls">' +
'<span class="pointer fa fa-arrow-left"></span>' +
'<span class="pointer fa fa-question-circle"></span>' +
'<span class="pointer fa fa-remove" ></span>' +
'<span class="pointer fa fa-arrow-right"></span>' +
'</div>';
return {
restrict: 'A',
link: function postLink($scope, elem) {
var $funcLink = $(funcSpanTemplate);
var $funcControls = $(funcControlsTemplate);
var ctrl = $scope.ctrl;
var func = $scope.func;
var funcDef = func.def;
var scheduledRelink = false;
var paramCountAtLink = 0;
function clickFuncParam(paramIndex) {
/*jshint validthis:true */
var $link = $(this);
var $input = $link.next();
$input.val(func.params[paramIndex]);
$input.css('width', ($link.width() + 16) + 'px');
$link.hide();
$input.show();
$input.focus();
$input.select();
var typeahead = $input.data('typeahead');
if (typeahead) {
$input.val('');
typeahead.lookup();
}
}
function scheduledRelinkIfNeeded() {
if (paramCountAtLink === func.params.length) {
return;
}
if (!scheduledRelink) {
scheduledRelink = true;
setTimeout(function() {
relink();
scheduledRelink = false;
}, 200);
}
}
function inputBlur(paramIndex) {
/*jshint validthis:true */
var $input = $(this);
var $link = $input.prev();
var newValue = $input.val();
if (newValue !== '' || func.def.params[paramIndex].optional) {
$link.html(templateSrv.highlightVariablesAsHtml(newValue));
func.updateParam($input.val(), paramIndex);
scheduledRelinkIfNeeded();
$scope.$apply(function() {
ctrl.targetChanged();
});
$input.hide();
$link.show();
}
}
function inputKeyPress(paramIndex, e) {
/*jshint validthis:true */
if(e.which === 13) {
inputBlur.call(this, paramIndex);
}
}
function inputKeyDown() {
/*jshint validthis:true */
this.style.width = (3 + this.value.length) * 8 + 'px';
}
function addTypeahead($input, paramIndex) {
$input.attr('data-provide', 'typeahead');
var options = funcDef.params[paramIndex].options;
if (funcDef.params[paramIndex].type === 'int') {
options = _.map(options, function(val) { return val.toString(); });
}
$input.typeahead({
source: options,
minLength: 0,
items: 20,
updater: function (value) {
setTimeout(function() {
inputBlur.call($input[0], paramIndex);
}, 0);
return value;
}
});
var typeahead = $input.data('typeahead');
typeahead.lookup = function () {
this.query = this.$element.val() || '';
return this.process(this.source);
};
}
function toggleFuncControls() {
var targetDiv = elem.closest('.tight-form');
if (elem.hasClass('show-function-controls')) {
elem.removeClass('show-function-controls');
targetDiv.removeClass('has-open-function');
$funcControls.hide();
return;
}
elem.addClass('show-function-controls');
targetDiv.addClass('has-open-function');
$funcControls.show();
}
function addElementsAndCompile() {
$funcControls.appendTo(elem);
$funcLink.appendTo(elem);
_.each(funcDef.params, function(param, index) {
if (param.optional && func.params.length <= index) {
return;
}
if (index > 0) {
$('<span>, </span>').appendTo(elem);
}
var paramValue = templateSrv.highlightVariablesAsHtml(func.params[index]);
var $paramLink = $('<a ng-click="" class="datadog-func-param-link">' + paramValue + '</a>');
var $input = $(paramTemplate);
paramCountAtLink++;
$paramLink.appendTo(elem);
$input.appendTo(elem);
$input.blur(_.partial(inputBlur, index));
$input.keyup(inputKeyDown);
$input.keypress(_.partial(inputKeyPress, index));
$paramLink.click(_.partial(clickFuncParam, index));
if (funcDef.params[index].options) {
addTypeahead($input, index);
}
});
$('<span>)</span>').appendTo(elem);
$compile(elem.contents())($scope);
}
function ifJustAddedFocusFistParam() {
if ($scope.func.added) {
$scope.func.added = false;
setTimeout(function() {
elem.find('.datadog-func-param-link').first().click();
}, 10);
}
}
function registerFuncControlsToggle() {
$funcLink.click(toggleFuncControls);
}
function registerFuncControlsActions() {
$funcControls.click(function(e) {
var $target = $(e.target);
if ($target.hasClass('fa-remove')) {
toggleFuncControls();
$scope.$apply(function() {
ctrl.removeFunction($scope.func);
});
return;
}
if ($target.hasClass('fa-arrow-left')) {
$scope.$apply(function() {
_.move(ctrl.functions, $scope.$index, $scope.$index - 1);
ctrl.targetChanged();
});
return;
}
if ($target.hasClass('fa-arrow-right')) {
$scope.$apply(function() {
_.move(ctrl.functions, $scope.$index, $scope.$index + 1);
ctrl.targetChanged();
});
return;
}
if ($target.hasClass('fa-question-circle')) {
window.open("http://docs.datadoghq.com/graphing/#apply-more-advanced-functions", '_blank');
return;
}
});
}
function relink() {
elem.children().remove();
addElementsAndCompile();
ifJustAddedFocusFistParam();
registerFuncControlsToggle();
registerFuncControlsActions();
}
relink();
}
};
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,19 @@
import {DataDogDatasource} from './datasource';
import {DataDogQueryCtrl} from './query_ctrl';
class DataDogConfigCtrl {}
DataDogConfigCtrl.templateUrl = "partials/config.html";
class DataDogQueryOptionsCtrl {}
DataDogQueryOptionsCtrl.templateUrl = "partials/query.options.html";
class DataDogAnnotationsQueryCtrl {}
DataDogAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html';
export {
DataDogDatasource as Datasource,
DataDogQueryCtrl as QueryCtrl,
DataDogConfigCtrl as ConfigCtrl,
DataDogQueryOptionsCtrl as QueryOptionsCtrl,
DataDogAnnotationsQueryCtrl as AnnotationsQueryCtrl
};
@@ -0,0 +1,29 @@
<div class="gf-form-group">
<div class="gf-form max-width-30">
<label class="gf-form-label width-7">Sources</label>
<input type="text" class="gf-form-input"
ng-model='ctrl.annotation.sources'
placeholder="">
</input>
<info-popover mode="right-absolute">
A comma separated string of sources
</info-popover>
</div>
<div class="gf-form max-width-30">
<label class="gf-form-label width-7">Tags</label>
<input type="text" class="gf-form-input"
ng-model='ctrl.annotation.tags'
placeholder="">
</input>
<info-popover mode="right-absolute">
A comma separated string of tags
</info-popover>
</div>
<div class="gf-form">
<label class="gf-form-label width-7">Priority</label>
<select class="gf-form-input width-7"
ng-model="ctrl.annotation.priority"
ng-options="p.v as p.n for p in [{n: 'None', v: undefined}, {n: 'Low', v: 'low'}, {n: 'Normal', v: 'normal'}]">
</select>
</div>
</div>
@@ -0,0 +1,35 @@
<datasource-http-settings
current="ctrl.current"
suggest-url="https://app.datadoghq.com/api/v1">
</datasource-http-settings>
<div class="gf-form-group">
<h3 class="page-heading">DataDog settings</h3>
<p>You can manage your account's API and application keys <a href="https://app.datadoghq.com/account/settings#api">here</a>.</p>
<div class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-30">
<span class="gf-form-label width-7">API Key</span>
<input class="gf-form-input" type="text"
ng-model='ctrl.current.jsonData.api_key' min-length="0"
required></input>
<info-popover mode="right-absolute">
<p>Specify your datadog API Key</p>
</info-popover>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form max-width-30">
<span class="gf-form-label width-7">APP Key</span>
<input class="gf-form-input" type="text"
ng-model='ctrl.current.jsonData.app_key' min-length="0"
required></input>
<info-popover mode="right-absolute">
<p>Specify your datadog APP Key</p>
</info-popover>
</div>
</div>
</div>
</div>
@@ -0,0 +1,72 @@
<query-editor-row query-ctrl="ctrl" has-text-edit-mode="true" can-collapse="true">
<div class="gf-form" ng-if="ctrl.target.rawQuery">
<input type="text" class="gf-form-input" ng-model="ctrl.target.query" spellcheck="false" ng-blur="ctrl.refresh()"></input>
</div>
<div ng-if="!ctrl.target.rawQuery">
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">AGG</label>
<metric-segment segment="ctrl.aggregationSegment" get-options="ctrl.getAggregations()" on-change="ctrl.aggregationChanged()"></metric-segment>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">METRIC</label>
<metric-segment segment="ctrl.metricSegment" get-options="ctrl.getMetrics()" on-change="ctrl.metricChanged()"></metric-segment>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">POST</label>
<metric-segment segment="ctrl.asSegment" get-options="ctrl.getAs()" on-change="ctrl.asChanged()"></metric-segment>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">SCOPE</label>
</div>
<div class="gf-form" ng-repeat="segment in ctrl.tagSegments">
<metric-segment segment="segment" get-options="ctrl.getTags(segment, $index)" on-change="ctrl.tagSegmentUpdated(segment, $index)"></metric-segment>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">FUNCS</label>
</div>
<div ng-repeat="func in ctrl.functions" class="gf-form">
<span datadog-func-editor class="gf-form-label query-part"></span>
</div>
<div class="gf-form dropdown">
<span datadog-add-func></span>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form max-width-30">
<label class="gf-form-label query-keyword width-7">ALIAS BY</label>
<input type="text" class="gf-form-input" ng-model="ctrl.target.alias" spellcheck='false' placeholder="Naming pattern" ng-blur="ctrl.refresh()">
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
</div>
</query-editor-row>
@@ -0,0 +1,2 @@
<section class="grafana-metric-options">
</section>
@@ -0,0 +1,34 @@
{
"id": "grafana-datadog-datasource",
"name": "DataDog",
"type": "datasource",
"metrics": true,
"annotations": true,
"staticRoot": ".",
"info": {
"description": "datasource plugin for DataDog",
"author": {
"name": "Alex Kessinger",
"url": "http://rumproarious.com"
},
"keywords": ["datadog"],
"logos": {
"small": "img/datadog.png",
"large": "img/datadog.png"
},
"links": [
{"name": "GitHub", "url": "https://github.com/voidfiles/grafana-datasource-datagog"},
{"name": "License", "url": "https://github.com/voidfiles/grafana-datasource-datagog/blob/master/LICENSE"}
],
"version": "0.0.1",
"updated": "2016-12-18"
},
"dependencies": {
"grafanaVersion": "3.x.x",
"plugins": [ ]
}
}
@@ -0,0 +1,65 @@
import _ from 'lodash';
import dfunc from './dfunc';
export function buildQuery(target, adhocFilters) {
let {aggregation, metric, tags, functions} = target;
let query = `${aggregation}:${metric}`;
let functionInstances = getFunctionInstances(functions);
if ((tags && tags.length) || (adhocFilters && adhocFilters.length)) {
query += '{';
if (tags && tags.length) {
query += tags.join(',');
}
if (adhocFilters && adhocFilters.length) {
let adhocTags = buildAdHocFilterString(adhocFilters);
if (tags && tags.length) {
query += ',';
}
query += adhocTags;
}
query += '}';
} else {
query += '{*}';
}
if (target.as) {
query += '.' + target.as + '()';
}
var groupedFuncs = _.groupBy(functionInstances, func => {
if (func.def.append) {
return 'appends';
} else {
return 'wraps';
}
});
_.each(groupedFuncs.appends, func => {
query += '.' + func.render();
});
_.each(groupedFuncs.wraps, func => {
query = func.render(query);
});
return query;
}
function getFunctionInstances(functions) {
return _.map(functions, func => {
var f = dfunc.createFuncInstance(func.funcDef, {withDefaultParams: false});
f.params = func.params.slice();
return f;
});
}
function buildAdHocFilterString(adhocFilters) {
return adhocFilters.map(filter => {
return filter.key + ':' + filter.value;
}).join(',');
}
@@ -0,0 +1,185 @@
import _ from 'lodash';
import dfunc from './dfunc';
import {QueryCtrl} from 'app/plugins/sdk';
import './func_editor';
import './add_datadog_func';
import * as queryBuilder from './query_builder';
export class DataDogQueryCtrl extends QueryCtrl {
constructor($scope, $injector, uiSegmentSrv, templateSrv) {
super($scope, $injector);
this.removeText = '-- remove tag --';
this.uiSegmentSrv = uiSegmentSrv;
this.templateSrv = templateSrv;
if (this.target.aggregation) {
this.aggregationSegment = new uiSegmentSrv.newSegment(
this.target.aggregation
);
} else {
this.aggregationSegment = new uiSegmentSrv.newSegment({
value: 'Select Aggregation',
fake: true,
custom: false,
});
}
if (this.target.metric) {
this.metricSegment = new uiSegmentSrv.newSegment(
this.target.metric
);
} else {
this.metricSegment = new uiSegmentSrv.newSegment({
value: 'Select Metric',
fake: true,
custom: false,
});
}
this.target.tags = this.target.tags || [];
this.tagSegments = this.target.tags.map(uiSegmentSrv.newSegment);
this.fixTagSegments();
this.functions = [];
this.target.functions = this.target.functions || [];
this.functions = _.map(this.target.functions, func => {
var f = dfunc.createFuncInstance(func.funcDef, {withDefaultParams: false});
f.params = func.params.slice();
return f;
});
if (this.target.as) {
this.asSegment = uiSegmentSrv.newSegment(this.target.as);
} else {
this.asSegment = uiSegmentSrv.newSegment({
value: 'Select As',
fake: true,
custom: false,
});
}
}
toggleEditorMode() {
this.target.rawQuery = !this.target.rawQuery;
}
getMetrics() {
return this.datasource.metricFindQuery()
.then(this.uiSegmentSrv.transformToSegments(true));
}
getAggregations() {
return Promise.resolve([
{text: 'avg by', value: 'avg'},
{text: 'max by', value: 'max'},
{text: 'min by', value: 'min'},
{text: 'sub by', value: 'sum'},
]);
}
getAs() {
return Promise.resolve([
{text: 'None', value: 'None'},
{text: 'as_count', value: 'as_count'},
{text: 'as_rate', value: 'as_rate'},
]);
}
getTags(segment) {
return this.datasource.tagFindQuery()
.then(this.uiSegmentSrv.transformToSegments(true))
.then(results => {
if (segment.type !== 'plus-button') {
let removeSegment = this.uiSegmentSrv.newFake(this.removeText);
results.unshift(removeSegment);
}
return results;
});
}
aggregationChanged() {
this.target.aggregation = this.aggregationSegment.value;
this.panelCtrl.refresh();
}
metricChanged() {
this.target.metric = this.metricSegment.value;
this.panelCtrl.refresh();
}
asChanged() {
if (this.asSegment.value === 'None') {
this.target.as = null;
} else {
this.target.as = this.asSegment.value;
}
this.panelCtrl.refresh();
}
fixTagSegments() {
var count = this.tagSegments.length;
var lastSegment = this.tagSegments[Math.max(count-1, 0)];
if (!lastSegment || lastSegment.type !== 'plus-button') {
this.tagSegments.push(this.uiSegmentSrv.newPlusButton());
}
}
targetChanged() {
if (this.error) {
return;
}
this.panelCtrl.refresh();
}
persistFunctions () {
this.target.functions = _.map(this.functions, func => {
return {
funcDef: func.def.name,
params: func.params.slice(),
};
});
}
removeFunction(func) {
this.functions = _.without(this.functions, func);
this.persistFunctions();
this.targetChanged();
}
addFunction(funcDef) {
var func = dfunc.createFuncInstance(funcDef, {withDefaultParams: true});
func.added = true;
this.functions.push(func);
this.persistFunctions();
this.targetChanged();
}
tagSegmentUpdated(segment, index) {
if (segment.value === this.removeText) {
this.tagSegments.splice(index, 1);
}
let realSegments = _.filter(this.tagSegments, segment => segment.value);
this.target.tags = realSegments.map(segment => segment.value);
this.tagSegments = _.map(this.target.tags, this.uiSegmentSrv.newSegment);
this.fixTagSegments();
this.panelCtrl.refresh();
}
getCollapsedText() {
if (this.target.rawQuery) {
return this.target.query;
} else {
return queryBuilder.buildQuery(this.target);
}
}
}
DataDogQueryCtrl.templateUrl = 'partials/query.editor.html';
File diff suppressed because one or more lines are too long
View File
@@ -0,0 +1 @@
docker-compose -f ../docker-compose.yml run --rm k6 run /testRunner/scripts/run.js -e PIVOT_FILE=BestPracticeWorkbook/PatientTypeValidation.json*BestPracticeWorkbook/YOYPatientType.json*BestPracticeWorkbook/OverviewDashboard/CasesByServiceLine.json*BestPracticeWorkbook/OverviewDashboard/FixedDirectCostByYear.json*BestPracticeWorkbook/OverviewDashboard/ServiceLineActualVsExpected.json*BestPracticeWorkbook/OverviewDashboard/YOYCM.json*BestPracticeWorkbook/OverviewDashboard/YOYNetRevenue.json*BestPracticeWorkbook/OverviewDashboard/YOYTotalCosts.json --out=influxdb=http://influxdb:8086/k6
@@ -0,0 +1 @@
docker-compose -f ../docker-compose.yml run --rm k6 run /testRunner/scripts/dummyTest.mjs
+1
View File
@@ -0,0 +1 @@
docker-compose -f ../docker-compose.yml run --rm k6 run /testRunner/scripts/runTests.js -e PIVOT_FILE=OrthopedicCosts.json*PatientTypeValidation.json*ServiceLineYOY.json*YOYPatientType.json*CasesByServiceLineAndCM.json*GrossRevenueByServiceLine.json --out=influxdb=http://influxdb:8086/k6
+1
View File
@@ -0,0 +1 @@
sudo docker-compose -f ../docker/docker-compose.yml run --rm k6 run /testRunner/scripts/run.js -e PIVOT_FILE=StagingIncomeStatement1.json*StagingIncomeStatement2.json*StagingIncomeStatement3.json*StagingExpandablePivot1.json --out=influxdb=http://influxdb:8086/k6
@@ -0,0 +1,7 @@
import http from 'k6/http';
import { sleep } from 'k6';
export default function () {
http.get('https://test.k6.io');
sleep(1);
}
@@ -0,0 +1,92 @@
let k6 = require("k6");
let utils = require("./utils.js");
let modules = [];
let datas = [];
let splitFiles = __ENV.PIVOT_FILE.split("*");
for (let i = 0; i < splitFiles.length; i++) {
let file = "/testRunner/tests/BestPracticeWorkbook/" + splitFiles[i];
let fileText = "";
if (!String(file).endsWith(".json")) {
modules[file] = require(file);
} else {
fileText = open(file).replace(/(\r\n|\n|\r)/gm, "");
}
datas.push({ fileName: file, text: fileText });
}
module.exports.options = {
teardownTimeout: "2m0s",
stages: [
// { duration: "0m0s", target: 1 },
// { duration: "0m10s", target: 1 },
// { duration: "0m0s", target: 5 },
// { duration: "3m0s", target: 5 },
// { duration: "0m0s", target: 10 },
// { duration: "3m0s", target: 10 },
// { duration: "0m0s", target: 20 },
// { duration: "3m0s", target: 20 },
// { duration: "0m0s", target: 30 },
// { duration: "3m0s", target: 30},
// { duration: "0m0s", target: 40 },
// { duration: "3m0s", target: 40 },
// { duration: "0m0s", target: 50 },
// { duration: "3m0s", target: 50 },
// { duration: "0m0s", target: 75 },
// { duration: "5m0s", target: 75 },
// { duration: "0m0s", target: 100 },
// { duration: "5m0s", target: 100 },
// { duration: "0m0s", target: 150 },
// { duration: "5m0s", target: 150 },
// { duration: "0m0s", target: 200 },
// { duration: "5m0s", target: 200 },
// { duration: "0m0s", target: 250 },
// { duration: "5m0s", target: 250 },
// { duration: "0m0s", target: 300 },
// { duration: "5m0s", target: 300 },
// { duration: "0m0s", target: 350 },
// { duration: "5m0s", target: 350 },
{ duration: "0m0s", target: 450 },
{ duration: "5m0s", target: 450 },
{ duration: "0m0s", target: 550 },
{ duration: "5m0s", target: 550 },
{ duration: "0m0s", target: 650 },
{ duration: "5m0s", target: 650 },
{ duration: "0m0s", target: 750 },
{ duration: "5m0s", target: 750 },
{ duration: "0m0s", target: 850 },
{ duration: "5m0s", target: 850 },
{ duration: "0m0s", target: 1000 },
{ duration: "5m0s", target: 1000 },
{ duration: "0m0s", target: 1500 },
{ duration: "5m0s", target: 1500 },
{ duration: "0m0s", target: 2000 },
{ duration: "5m0s", target: 2000 },
],
};
export function setup() {
console.log("setting up token");
return utils.generateToken();
}
module.exports.default = function run(token) {
let clone = [];
Object.assign(clone, datas);
while (clone.length > 0) {
let ran = Math.floor(Math.random() * clone.length);
let currentItems = clone.splice(ran, Math.floor(Math.random() * 3 + 1));
k6.sleep(Math.round(Math.random() * 20 + 1));
for (let i = 0; i < currentItems.length; i++) {
k6.group(currentItems[i].fileName.substring(7), function () {
utils.runHttp(currentItems[i], token);
});
}
}
}
@@ -0,0 +1,67 @@
const http = require("k6/http");
const isLogging = false;
export function runHttp(jsonBlob, token) {
let response;
if (isLogging) console.log("running file: ", jsonBlob.fileName)
let json = JSON.stringify(jsonBlob.text);
const startDay = Math.floor(Math.random() * 30) + 1;
const endDay = Math.floor(Math.random() * 30) + 1;
json = json.replace("{startDay}", startDay);
json = json.replace("{endDay}", endDay);
json = JSON.parse(json);
response = http.post(
'https://analytics-api-perf.dev.stratanetwork.net/api/v1/data-sources/1307/query',
json,
{
headers: {
authorization: `Bearer ${token}`,
Accept: '*/*',
"Accept-Encoding": "gzip, deflate, br",
"Clientappname": 'strata-analytics',
'content-type': 'application/json',
'sec-ch-ua': '"Google Chrome";v="113", "Chromium";v="113", "Not-A.Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
},
}
)
if(response.status !== 200) console.log("endpoint failed", jsonBlob.fileName);
else if (isLogging) console.log("success: ", jsonBlob.fileName);
}
export function generateToken() {
const url = "https://identity.dev.stratanetwork.net";
const username = `perf1`;
const password = "Password1";
const dbguid = 'b8018250-af34-403e-bdcc-1770714042b2';
// token
const tokenUrl = url + "/connect/token";
const body = {
database_guid: dbguid,
grant_type: 'password',
username: username,
password: password,
scope: 'strata.api.external offline_access',
client_id: 'IntegrationTest',
client_secret: 'UhAEQsGByTCrwPdHPLmaet_DmLEfcJVbZqGBxArdeRup_TPLrYKNpUNRHejjdnhd',
};
const params = {
headers: {
'content-type': 'application/x-www-form-urlencoded',
'sec-ch-ua': '"Google Chrome";v="113", "Chromium";v="113", "Not-A.Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
}
};
const response = http.post(tokenUrl, body, params);
if (response.status !== 200) {
if (isLogging) console.log("error getting token: ", response);
return false
};
const token = JSON.parse(response.body).access_token;
if (isLogging) console.log("success getting token! access token: ", token);
return token;
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,188 @@
{
"rows": [
{
"name": "Rollup",
"dataColumnId": 575222,
"columnAlias": "ROLLUP_B16G7",
"sortDirection": "asc"
}
],
"values": [
{
"name": "0-17_Encounter Record Number",
"dataColumnId": 585958,
"calculationDataColumnIds": [
585958
],
"aliasedIds": [
"585958"
],
"columnAlias": "PIVOT_ENCOUNTERRECORDNUMBER_MFUF8_0",
"groupValueIndex": 0,
"groupId": "NQ6DZ",
"valueId": "U8KU2",
"calculation": "count(distinct [585958])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "JE94H",
"dataMartId": 1307,
"name": "Rollup",
"dataColumnName": "Rollup",
"dataColumnId": 527444,
"sqlColumnName": "ROLLUP_HXIUV",
"columnType": "text",
"values": [
"0-17"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [],
"aliasedDataColumnIds": [
"585958"
]
}
]
}
]
},
{
"name": "18-64_Encounter Record Number",
"dataColumnId": 585958,
"calculationDataColumnIds": [
585958
],
"aliasedIds": [
"585958"
],
"columnAlias": "PIVOT_ENCOUNTERRECORDNUMBER_MFUF8_1",
"groupValueIndex": 1,
"groupId": "NQ6DZ",
"valueId": "U8KU2",
"calculation": "count(distinct [585958])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "JE94H",
"dataMartId": 1307,
"name": "Rollup",
"dataColumnName": "Rollup",
"dataColumnId": 527444,
"sqlColumnName": "ROLLUP_HXIUV",
"columnType": "text",
"values": [
"18-64"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [],
"aliasedDataColumnIds": [
"585958"
]
}
]
}
]
},
{
"name": "65 and Over_Encounter Record Number",
"dataColumnId": 585958,
"calculationDataColumnIds": [
585958
],
"aliasedIds": [
"585958"
],
"columnAlias": "PIVOT_ENCOUNTERRECORDNUMBER_MFUF8_2",
"groupValueIndex": 2,
"groupId": "NQ6DZ",
"valueId": "U8KU2",
"calculation": "count(distinct [585958])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "JE94H",
"dataMartId": 1307,
"name": "Rollup",
"dataColumnName": "Rollup",
"dataColumnId": 527444,
"sqlColumnName": "ROLLUP_HXIUV",
"columnType": "text",
"values": [
"65 and Over"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [],
"aliasedDataColumnIds": [
"585958"
]
}
]
}
]
}
],
"warnings": [],
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "DCCKX",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 539377,
"sqlColumnName": "CALENDARDATETIME_3Z5DS",
"columnType": "date",
"values": [
"01/{startDay}/2018",
"01/{endDay}/2020"
],
"conditions": [],
"filterType": "calendar",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [
539377
]
}
]
}
],
"withTotals": true,
"rowLimit": 10000
}
@@ -0,0 +1,303 @@
{
"rows": [
{
"name": "Rollup",
"dataColumnId": 575222,
"columnAlias": "ROLLUP_85QAA",
"sortDirection": "asc"
}
],
"values": [
{
"name": "01-2019_Net Revenue",
"dataColumnId": 586760,
"calculationDataColumnIds": [
586760
],
"aliasedIds": [
"586760"
],
"columnAlias": "PIVOT_NETREVENUE_CIY57_0",
"groupValueIndex": 0,
"groupId": "TMOS9",
"valueId": "WJ6BD",
"calculation": "sum([586760])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "NE3DE",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 539377,
"sqlColumnName": "CALENDARDATETIME_OL9IX",
"columnType": "date",
"values": [
"01-2019"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "month",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [],
"aliasedDataColumnIds": [
"586760"
]
}
]
}
]
},
{
"name": "02-2019_Net Revenue",
"dataColumnId": 586760,
"calculationDataColumnIds": [
586760
],
"aliasedIds": [
"586760"
],
"columnAlias": "PIVOT_NETREVENUE_CIY57_1",
"groupValueIndex": 1,
"groupId": "TMOS9",
"valueId": "WJ6BD",
"calculation": "sum([586760])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "NE3DE",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 539377,
"sqlColumnName": "CALENDARDATETIME_OL9IX",
"columnType": "date",
"values": [
"02-2019"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "month",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [],
"aliasedDataColumnIds": [
"586760"
]
}
]
}
]
},
{
"name": "03-2019_Net Revenue",
"dataColumnId": 586760,
"calculationDataColumnIds": [
586760
],
"aliasedIds": [
"586760"
],
"columnAlias": "PIVOT_NETREVENUE_CIY57_2",
"groupValueIndex": 2,
"groupId": "TMOS9",
"valueId": "WJ6BD",
"calculation": "sum([586760])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "NE3DE",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 539377,
"sqlColumnName": "CALENDARDATETIME_OL9IX",
"columnType": "date",
"values": [
"03-2019"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "month",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [],
"aliasedDataColumnIds": [
"586760"
]
}
]
}
]
},
{
"name": "04-2019_Net Revenue",
"dataColumnId": 586760,
"calculationDataColumnIds": [
586760
],
"aliasedIds": [
"586760"
],
"columnAlias": "PIVOT_NETREVENUE_CIY57_3",
"groupValueIndex": 3,
"groupId": "TMOS9",
"valueId": "WJ6BD",
"calculation": "sum([586760])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "NE3DE",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 539377,
"sqlColumnName": "CALENDARDATETIME_OL9IX",
"columnType": "date",
"values": [
"04-2019"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "month",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [],
"aliasedDataColumnIds": [
"586760"
]
}
]
}
]
},
{
"name": "05-2019_Net Revenue",
"dataColumnId": 586760,
"calculationDataColumnIds": [
586760
],
"aliasedIds": [
"586760"
],
"columnAlias": "PIVOT_NETREVENUE_CIY57_4",
"groupValueIndex": 4,
"groupId": "TMOS9",
"valueId": "WJ6BD",
"calculation": "sum([586760])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "NE3DE",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 539377,
"sqlColumnName": "CALENDARDATETIME_OL9IX",
"columnType": "date",
"values": [
"05-2019"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "month",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [],
"aliasedDataColumnIds": [
"586760"
]
}
]
}
]
},
{
"name": "06-2019_Net Revenue",
"dataColumnId": 586760,
"calculationDataColumnIds": [
586760
],
"aliasedIds": [
"586760"
],
"columnAlias": "PIVOT_NETREVENUE_CIY57_5",
"groupValueIndex": 5,
"groupId": "TMOS9",
"valueId": "WJ6BD",
"calculation": "sum([586760])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "NE3DE",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 539377,
"sqlColumnName": "CALENDARDATETIME_OL9IX",
"columnType": "date",
"values": [
"06-2019"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "month",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [],
"aliasedDataColumnIds": [
"586760"
]
}
]
}
]
}
],
"warnings": [],
"filterGroups": [
{
"logicalOperator": "and",
"filters": []
}
],
"withTotals": true,
"rowLimit": 10000
}
@@ -0,0 +1,141 @@
{
"rows": [
{
"name": "Rollup",
"dataColumnId": 575222,
"columnAlias": "ROLLUP_VX6NF",
"sortDirection": "asc"
},
{
"name": "Case Mix Group",
"dataColumnId": 530020,
"columnAlias": "NAME_D6Y37",
"sortDirection": "asc"
},
{
"name": "Encounter Record Number",
"dataColumnId": 562870,
"columnAlias": "ENCOUNTERRECORDNUMBER_8TTDW",
"sortDirection": "asc"
}
],
"values": [
{
"name": "Total Charges",
"dataColumnId": 586855,
"calculationDataColumnIds": [
586855
],
"aliasedIds": [
"586855"
],
"columnAlias": "VALUE_TOTALCHARGES_VAB3W_0",
"groupId": "FHCM9",
"valueId": "7HYFA",
"calculation": "sum([586855])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": []
}
]
},
{
"name": "Total Costs",
"dataColumnId": 586856,
"calculationDataColumnIds": [
586856
],
"aliasedIds": [
"586856"
],
"columnAlias": "VALUE_TOTALCOSTS_64PFM_1",
"groupId": "FHCM9",
"valueId": "UV541",
"calculation": "sum([586856])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": []
}
]
},
{
"name": "Net Revenue",
"dataColumnId": 586760,
"calculationDataColumnIds": [
586760
],
"aliasedIds": [
"586760"
],
"columnAlias": "VALUE_NETREVENUE_LTUVC_2",
"groupId": "FHCM9",
"valueId": "1QKYV",
"calculation": "sum([586760])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": []
}
]
}
],
"warnings": [],
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "T47AV",
"dataMartId": 1307,
"name": "Rollup",
"dataColumnName": "Rollup",
"dataColumnId": 575222,
"sqlColumnName": "ROLLUP_ET1NC",
"columnType": "text",
"values": [
"Orthopedics"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": []
},
{
"id": "KPL54",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 539377,
"sqlColumnName": "CALENDARDATETIME_Q3UU2",
"columnType": "date",
"values": [
"01/{startDay}/2019",
"01/{endDay}/2023"
],
"conditions": [],
"filterType": "calendar",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [
539377
]
}
]
}
],
"withTotals": true,
"rowLimit": 10000
}
@@ -0,0 +1,62 @@
{
"rows": [
{
"dataColumnId": 575222,
"columnAlias": "ROLLUP_OYJKZ",
"sortDirection": "asc"
}
],
"values": [
{
"dataColumnId": 585958,
"calculationDataColumnIds": [
585958
],
"aliasedIds": [
"585958"
],
"columnAlias": "ENCOUNTERRECORDNUMBER_HYOWJ",
"calculation": "count(distinct [585958])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": []
}
]
}
],
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "8W28A",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 527101,
"sqlColumnName": "CALENDARDATETIME_QBAR8",
"columnType": "date",
"values": [
"01/{startDay}/2020",
"01/{endDay}/2021"
],
"conditions": [],
"filterType": "calendar",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [
527101
]
}
]
}
],
"withTotals": false,
"rowLimit": 5000
}
@@ -0,0 +1,114 @@
{
"rows": [
{
"dataColumnId": 539392,
"columnAlias": "FISCALQUARTER_4Z4AO",
"sortDirection": "asc"
},
{
"dataColumnId": 575222,
"columnAlias": "ROLLUP_QO8WQ",
"sortDirection": "asc"
}
],
"values": [
{
"dataColumnId": 586855,
"calculationDataColumnIds": [
586855
],
"aliasedIds": [
"586855"
],
"columnAlias": "TOTALCHARGES_EBYJD",
"calculation": "sum([586855])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": []
}
]
}
],
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "5G66H",
"dataMartId": 1307,
"name": "Fiscal Year",
"dataColumnName": "Fiscal Year",
"dataColumnId": 539396,
"sqlColumnName": "FISCALYEAR_3U8QR",
"columnType": "numeric",
"values": [
2019,
2020
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [
539396
]
},
{
"id": "N6UI1",
"dataMartId": 1307,
"name": "Rollup",
"dataColumnName": "Rollup",
"dataColumnId": 575222,
"sqlColumnName": "ROLLUP_WC7SC",
"columnType": "text",
"values": [
"Orthopedics"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": []
},
{
"id": "8W28A",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 527101,
"sqlColumnName": "CALENDARDATETIME_QBAR8",
"columnType": "date",
"values": [
"01/{startDay}/2020",
"01/{endDay}/2021"
],
"conditions": [],
"filterType": "calendar",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [
527101
]
}
]
}
],
"withTotals": false,
"rowLimit": 5000
}
@@ -0,0 +1,88 @@
{
"rows": [
{
"dataColumnId": 539377,
"columnAlias": "CALENDARDATETIME_4L6JT",
"datePart": "year",
"sortDirection": "asc"
}
],
"values": [
{
"dataColumnId": 586664,
"calculationDataColumnIds": [
586664
],
"aliasedIds": [
"586664"
],
"columnAlias": "FIXEDDIRECTCOST_FU1CS",
"calculation": "sum([586664])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": []
}
]
}
],
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "M8X9O",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 539377,
"sqlColumnName": "CALENDARDATETIME_FGBJE",
"columnType": "date",
"values": [
"2019",
"2020"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [
539377
]
},
{
"id": "8W28A",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 527101,
"sqlColumnName": "CALENDARDATETIME_QBAR8",
"columnType": "date",
"values": [
"01/{startDay}/2020",
"01/{endDay}/2021"
],
"conditions": [],
"filterType": "calendar",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [
527101
]
}
]
}
],
"withTotals": false,
"rowLimit": 5000
}
@@ -0,0 +1,79 @@
{
"rows": [
{
"dataColumnId": 575222,
"columnAlias": "ROLLUP_2UUGY",
"sortDirection": "asc"
}
],
"values": [
{
"dataColumnId": 586854,
"calculationDataColumnIds": [
586854
],
"aliasedIds": [
"586854"
],
"columnAlias": "TOTALACTUALPAYMENTS_GGKTJ",
"calculation": "sum([586854])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": []
}
]
},
{
"dataColumnId": 586652,
"calculationDataColumnIds": [
586652
],
"aliasedIds": [
"586652"
],
"columnAlias": "EXPECTEDPAYMENT_1L8Q4",
"calculation": "sum([586652])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": []
}
]
}
],
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "8W28A",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 527101,
"sqlColumnName": "CALENDARDATETIME_QBAR8",
"columnType": "date",
"values": [
"01/{startDay}/2020",
"01/{endDay}/2021"
],
"conditions": [],
"filterType": "calendar",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [
527101
]
}
]
}
],
"withTotals": false,
"rowLimit": 5000
}
@@ -0,0 +1,131 @@
{
"rows": [],
"values": [
{
"dataColumnId": 586760,
"calculationDataColumnIds": [
586760,
586883
],
"aliasedIds": [
"586760",
"586883"
],
"columnAlias": "_H9VJX",
"calculation": "sum([586760]) - sum([586883])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "9WGYT",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 539377,
"sqlColumnName": "CALENDARDATETIME_NYCXU",
"columnType": "date",
"values": [
"2019"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [],
"aliasedDataColumnIds": [
"586760",
"586883"
]
}
]
}
]
},
{
"dataColumnId": 586760,
"calculationDataColumnIds": [
586760,
586883
],
"aliasedIds": [
"586760",
"586883"
],
"columnAlias": "_WB167",
"calculation": "sum([586760]) - sum([586883])",
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "9WGYT",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 539377,
"sqlColumnName": "CALENDARDATETIME_NYCXU",
"columnType": "date",
"values": [
"2020"
],
"conditions": [],
"filterType": "list",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [],
"aliasedDataColumnIds": [
"586760",
"586883"
]
}
]
}
]
}
],
"filterGroups": [
{
"logicalOperator": "and",
"filters": [
{
"id": "8W28A",
"dataMartId": 1307,
"name": "Calendar Date Time",
"dataColumnName": "Calendar Date Time",
"dataColumnId": 527101,
"sqlColumnName": "CALENDARDATETIME_QBAR8",
"columnType": "date",
"values": [
"01/{startDay}/2020",
"01/{endDay}/2021"
],
"conditions": [],
"filterType": "calendar",
"listUseNotIn": false,
"datePart": "year",
"dateTimeFrameOffset": 0,
"dateTimeFrameOffsetDisplay": 0,
"dateTimeFrameCount": 0,
"dateTimeFrameCountDisplay": 0,
"isCustom": false,
"relatedDataColumnIds": [
527101
]
}
]
}
],
"withTotals": false,
"rowLimit": 5000
}

Some files were not shown because too many files have changed in this diff Show More