feat: initial commit

This commit is contained in:
Thom Lamb
2026-06-23 11:18:54 -05:00
parent fc3ce9a074
commit 1bb980f070
790 changed files with 149255 additions and 218 deletions
@@ -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"]}