feat: initial commit
This commit is contained in:
@@ -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"]
|
||||
@@ -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"]
|
||||
@@ -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
@@ -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
|
||||
|
||||

|
||||
|
||||
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
|
||||

|
||||
|
||||
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 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,30 @@
|
||||
# Datadog Grafana Datasource Plugin
|
||||
|
||||

|
||||
|
||||
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
|
||||

|
||||
|
||||
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 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
@@ -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 |
@@ -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"]}
|
||||
+29
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
+62
@@ -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
|
||||
}
|
||||
+114
@@ -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
|
||||
}
|
||||
+88
@@ -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
|
||||
}
|
||||
+79
@@ -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
Reference in New Issue
Block a user