├── .gitignore
├── .prettierrc.js
├── CHANGELOG.md
├── LICENSE
├── README.md
├── README_zh.md
├── dist
    ├── CHANGELOG.md
    ├── LICENSE
    ├── README.md
    ├── img
    │   └── logo.png
    ├── index.html
    ├── module.js
    ├── module.js.map
    ├── partials
    │   ├── config.html
    │   └── query.editor.html
    └── plugin.json
├── img
    ├── step-1.png
    └── step-2.png
├── jest.config.js
├── package.json
├── src
    ├── config_ctrl.ts
    ├── datasource.ts
    ├── img
    │   └── logo.png
    ├── module.ts
    ├── partials
    │   ├── config.html
    │   └── query.editor.html
    ├── plugin.json
    └── query_ctrl.ts
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
 1 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
 2 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
 3 | 
 4 | # User-specific stuff:
 5 | .idea/**/workspace.xml
 6 | .idea/**/tasks.xml
 7 | .idea/dictionaries
 8 | 
 9 | # Sensitive or high-churn files:
10 | .idea/**/dataSources/
11 | .idea/**/dataSources.ids
12 | .idea/**/dataSources.xml
13 | .idea/**/dataSources.local.xml
14 | .idea/**/sqlDataSources.xml
15 | .idea/**/dynamic.xml
16 | .idea/**/uiDesigner.xml
17 | 
18 | # Gradle:
19 | .idea/**/gradle.xml
20 | .idea/**/libraries
21 | 
22 | # CMake
23 | cmake-build-debug/
24 | 
25 | # Mongo Explorer plugin:
26 | .idea/**/mongoSettings.xml
27 | 
28 | ## File-based project format:
29 | *.iws
30 | 
31 | ## Plugin-specific files:
32 | 
33 | # IntelliJ
34 | out/
35 | 
36 | # mpeltonen/sbt-idea plugin
37 | .idea_modules/
38 | 
39 | # JIRA plugin
40 | atlassian-ide-plugin.xml
41 | 
42 | # Cursive Clojure plugin
43 | .idea/replstate.xml
44 | 
45 | # Crashlytics plugin (for Android Studio and IntelliJ)
46 | com_crashlytics_export_strings.xml
47 | crashlytics.properties
48 | crashlytics-build.properties
49 | fabric.properties
50 | 
51 | #Grunt Related -- https://github.com/gruntjs/grunt/blob/master/.gitignore
52 | node_modules
53 | .npm-debug.log
54 | tmp
55 | 
56 | # Directory for instrumented libs generated by jscoverage/JSCover
57 | lib-cov
58 | 
59 | # Coverage directory used by tools like istanbul
60 | coverage
61 | 
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |   ...require('@grafana/toolkit/src/config/prettier.plugin.config.json'),
3 | };
4 | 
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | 
3 | ## 0.0.2
4 | 
5 | - Converts from Grunt to Grafana toolkit and converts the remaining JavaScript files to TypeScript. Using Yarn for npm packages.
6 | - Fixes dataframe timeshift processing.
7 | 
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2020 Autohome Inc.
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | # Grafana Compare Query Datasource
 2 | 
 3 | ---
 4 | 
 5 | [中文文档](https://github.com/AutohomeCorp/autohome-compareQueries-datasource/blob/master/README_zh.md)
 6 | 
 7 | Webpack copy of [grafana-meta-queries](https://github.com/GoshPosh/grafana-meta-queries) implementing https://github.com/grafana/grafana/issues/2093
 8 | 
 9 | This plugin is built as a datasource plugin that combines and contrasts different time shifts data.
10 | 
11 | Time shift supports the following units: s (seconds), m (minutes), h (hours), d (days), w (weeks), M (months), y (years)
12 | 
13 | ## Screenshots
14 | 
15 | 
16 | 
17 | 
18 | 
19 | ## Installation
20 | 
21 | Clone this project into the grafana plugins directory (if you install grafana with the package, the default is /var / lib / grafana / plugins). Restart grafana and the datasource plugin should be detected and automatically used.
22 | 
23 | ## Usage
24 | 
25 | - Create a datasource and select the Compare Query Datasource plugin.
26 | - Create a basic query
27 | - Create a query use compare query datasource and set base query as compare query
28 | - Add time shift on the compare query
29 | 
30 | ## Build
31 | 
32 | Important work on a machine that has Grafana installed.
33 | ```
34 | $ sudo apt-get install npm
35 | $ sudo apt-get remove cmdtest
36 | $ sudo apt-get remove yarn
37 | $ sudo npm install -g yarn
38 | $ cd /var/lib/grafana/plugins
39 | $ sudo git clone https://github.com/alimli/autohome-compareQueries-datasource.git
40 | $ sudo chown -R ubuntu:ubuntu autohome-compareQueries-datasource
41 | $ cd autohome-compareQueries-datasource/
42 | $ yarn install
43 | $ yarn build
44 | ```
45 | 
46 | ## Credits
47 | 
48 | Based on
49 | 
50 | - [grafana](https://github.com/grafana/grafana)
51 | - [simple-json-datasource](https://github.com/grafana/simple-json-datasource)
52 | - [grafana-meta-queries](https://github.com/GoshPosh/grafana-meta-queries)
53 | 
54 | Made by [AutoHome Team](https://github.com/AutohomeCorp)
55 | 
--------------------------------------------------------------------------------
/README_zh.md:
--------------------------------------------------------------------------------
 1 | # Grafana Compare Query Datasource
 2 | 
 3 | ---
 4 | 
 5 | [Engilsh](https://github.com/AutohomeCorp/autohome-compareQueries-datasource/blob/master/README.md)
 6 | 
 7 | 基于Grafana Datasource实现,主要解决问题: https://github.com/grafana/grafana/issues/2093
 8 | 
 9 | 这是一个数据源插件,可以在同一个面板上进行同环比曲线展示。
10 | 
11 | Time shift 支持以下单位:s(秒),m(分钟),h(小时),d(天),w(周),M(月),y(年)
12 | 
13 | # 截图
14 | 
15 | 
16 | 
17 | 
18 | 
19 | # 安装
20 | 
21 | 将此项目克隆到 grafana plugins 目录中(如果使用软件包安装 grafana,则默认为/ var / lib / grafana / plugins)。 重新启动 grafana。
22 | 
23 | # 用法
24 | 
25 | - 创建 grafana-compare-queries 类型的数据源。
26 | - 创建一个基本查询
27 | - 创建一个对比查询已基础查询为基础。
28 | - 在对比查询中增加对比查询的时间。
29 | 
30 | # 致谢
31 | 
32 | - [grafana](https://github.com/grafana/grafana)
33 | - [simple-json-datasource](https://github.com/grafana/simple-json-datasource)
34 | - [grafana-meta-queries](https://github.com/GoshPosh/grafana-meta-queries)
35 | 
36 | Made by [AutoHome Team](https://github.com/AutohomeCorp)
37 | 
--------------------------------------------------------------------------------
/dist/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | 
3 | ## 0.0.2
4 | 
5 | - Converts from Grunt to Grafana toolkit and converts the remaining JavaScript files to TypeScript. Using Yarn for npm packages.
6 | - Fixes dataframe timeshift processing.
7 | 
--------------------------------------------------------------------------------
/dist/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2020 Autohome Inc.
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 
--------------------------------------------------------------------------------
/dist/README.md:
--------------------------------------------------------------------------------
 1 | # Grafana Compare Query Datasource
 2 | 
 3 | ---
 4 | 
 5 | [中文文档](https://github.com/AutohomeCorp/autohome-compareQueries-datasource/blob/master/README_zh.md)
 6 | 
 7 | Webpack copy of [grafana-meta-queries](https://github.com/GoshPosh/grafana-meta-queries) implementing https://github.com/grafana/grafana/issues/2093
 8 | 
 9 | This plugin is built as a datasource plugin that combines and contrasts different time shifts data.
10 | 
11 | Time shift supports the following units: s (seconds), m (minutes), h (hours), d (days), w (weeks), M (months), y (years)
12 | 
13 | ## Screenshots
14 | 
15 | 
16 | 
17 | 
18 | 
19 | ## Installation
20 | 
21 | Clone this project into the grafana plugins directory (if you install grafana with the package, the default is /var / lib / grafana / plugins). Restart grafana and the datasource plugin should be detected and automatically used.
22 | 
23 | ## Usage
24 | 
25 | - Create a datasource and select the Compare Query Datasource plugin.
26 | - Create a basic query
27 | - Create a query use compare query datasource and set base query as compare query
28 | - Add time shift on the compare query
29 | 
30 | ## Build
31 | 
32 | Important work on a machine that has Grafana installed.
33 | ```
34 | $ sudo apt-get install npm
35 | $ sudo apt-get remove cmdtest
36 | $ sudo apt-get remove yarn
37 | $ sudo npm install -g yarn
38 | $ cd /var/lib/grafana/plugins
39 | $ sudo git clone https://github.com/alimli/autohome-compareQueries-datasource.git
40 | $ sudo chown -R ubuntu:ubuntu autohome-compareQueries-datasource
41 | $ cd autohome-compareQueries-datasource/
42 | $ yarn install
43 | $ yarn build
44 | ```
45 | 
46 | ## Credits
47 | 
48 | Based on
49 | 
50 | - [grafana](https://github.com/grafana/grafana)
51 | - [simple-json-datasource](https://github.com/grafana/simple-json-datasource)
52 | - [grafana-meta-queries](https://github.com/GoshPosh/grafana-meta-queries)
53 | 
54 | Made by [AutoHome Team](https://github.com/AutohomeCorp)
55 | 
--------------------------------------------------------------------------------
/dist/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AutohomeCorp/autohome-compareQueries-datasource/6cc213a7c26d6119e12fc989e554c0f86c8cac7c/dist/img/logo.png
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |   
4 |     
5 |     Webpack App
6 |   
7 |   
8 |   
9 | 
--------------------------------------------------------------------------------
/dist/module.js:
--------------------------------------------------------------------------------
1 | define(["lodash","moment","@grafana/data","app/plugins/sdk"],(function(t,e,r,n){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var i=e[n]={i:n,l:!1,exports:{}};return t[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)r.d(n,i,function(e){return t[e]}.bind(null,i));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="/",r(r.s=4)}([function(e,r){e.exports=t},function(t,r){t.exports=e},function(t,e){t.exports=r},function(t,e){t.exports=n},function(t,e,r){"use strict";r.r(e);var n=r(0),i=r.n(n),a=r(1),o=r.n(a),u=r(2),f=function(){function t(t,e,r){this.units=["y","M","w","d","h","m","s"],this.datasourceSrv=e,this.$q=t,this.templateSrv=r}return t.$inject=["$q","datasourceSrv","templateSrv"],t.prototype.testDatasource=function(){return new Promise((function(t,e){t({status:"success",message:"Compare Query Source is working correctly",title:"Success"})}))},t.prototype.query=function(t){var e=this,r=i.a.groupBy(t.targets,(function(t){return void 0===t.datasource.uid?t.datasource:t.datasource.uid})),n=i.a.groupBy(t.targets,"refId"),a=[];return i.a.forEach(r,(function(r,o){var u=i.a.cloneDeep(t),f=e.datasourceSrv.get(o).then((function(i){if(i.meta.id===e.meta.id)return e._compareQuery(t,r,n,e);u.targets=r;var a=i.query(u);return"function"==typeof a.toPromise?a.toPromise():a}));a.push(f)})),this.$q.all(a).then((function(t){return{data:i.a.flatten(i.a.filter(i.a.map(t,(function(t){var e=t.data;return e&&(e=i.a.filter(t.data,(function(t){return!0!==t.hide}))),e})),(function(t){return null!=t})))}}))},t.prototype._compareQuery=function(t,e,r,n){var a=[];return i.a.forEach(e,(function(e){var o=e.query;if(null!==o&&""!==o&&null!==r[o]){var f=i.a.cloneDeep(r[o][0]);if(f.hide=!1,f){var s=f.datasource;e.timeShifts&&e.timeShifts.length>0&&i.a.forEach(e.timeShifts,(function(r){var o,c,l=r.aliasType||"suffix",p=r.delimiter||"_",d=n.datasourceSrv.get(s).then((function(e){if(e.meta.id===n.meta.id)return{data:[]};if(o=n.templateSrv.replace(r.value,t.scopedVars),c=n.templateSrv.replace(r.alias,t.scopedVars)||o,null===o||""===o||void 0===o)return{data:[]};var a=i.a.cloneDeep(t);a.range.from=n.addTimeShift(a.range.from,o),a.range.to=n.addTimeShift(a.range.to,o),a.range.raw={from:a.range.from,to:a.range.to},a.rangeRaw=a.range.raw,f.refId=f.refId+"_"+o,a.targets=[f],a.requestId=a.requestId+"_"+o;var u=e.query(a);return"function"==typeof u.toPromise?u.toPromise():u})).then((function(t){var r=t.data;return r.forEach((function(t){if(t.target)t.target=n.generalAlias(t.target,c,l,p),void 0!==t.title&&null!==t.title&&(t.title=n.generalAlias(t.title,c,l,p));else if(t.fields)t.fields.forEach((function(t){t.name&&(t.name=n.generalAlias(t.name,c,l,p)),t.config&&t.config.displayName&&(t.config.displayName=n.generalAlias(t.config.displayName,c,l,p)),t.config&&t.config.displayNameFromDS&&(t.config.displayNameFromDS=n.generalAlias(t.config.displayNameFromDS,c,l,p))}));else if(t.columns)for(var r=1;r0){var f=t.fields.find((function(t){return"time"===t.type}));if(f){var s={name:f.name,type:f.type,config:f.config||{},labels:f.labels,values:new u.ArrayVector};for(r=0;r10)return;return{num:parseInt(e.substring(0,n),10),unit:e.charAt(n)}},t.prototype.addTimeShift=function(t,e){var r=this.parseTimeShift(e);if(r){var n=0-r.num,a=r.unit;return i.a.includes(this.units,a)?t.clone().add(n,a):void 0}},t}(),s=function(t,e){return(s=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)};var c=function(t){function e(e,r){var n=t.call(this,e,r)||this;return n.aliasTypes=["suffix","prefix","absolute"],n.target.timeShifts||(n.target.timeShifts=[]),0===n.target.timeShifts.length&&n.addTimeShifts(),void 0===n.target.process&&(n.target.process=!0),n}return e.$inject=["$scope","$injector"],function(t,e){function r(){this.constructor=t}s(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}(e,t),e.prototype.targetBlur=function(){this.refresh()},e.prototype.onChangeInternal=function(){this.refresh()},e.prototype.addTimeShifts=function(){var t=this.getTimeShiftId();this.target.timeShifts.push({id:t})},e.prototype.removeTimeShift=function(t){if(!(this.target.timeShifts&&this.target.timeShifts.length<=1)){var e=i.a.indexOf(this.target.timeShifts,t);this.target.timeShifts.splice(e,1),this.refreshTimeShifts()}},e.prototype.refreshTimeShifts=function(){this.refresh()},e.prototype.onAliasAsChange=function(t){console.error("timeShift.aliasAs="+this.target.aliasAs),console.error("aliasAs="+t)},e.prototype.getTimeShiftId=function(){for(var t=0;;){if(i.a.every(this.target.timeShifts,(function(e){return e.id!==t})))return t;t++}},e.templateUrl="partials/query.editor.html",e}(r(3).QueryCtrl),l=function(){function t(){}return t.templateUrl="partials/config.html",t}();r.d(e,"Datasource",(function(){return f})),r.d(e,"QueryCtrl",(function(){return c})),r.d(e,"ConfigCtrl",(function(){return l}))}])}));
2 | //# sourceMappingURL=module.js.map
--------------------------------------------------------------------------------
/dist/module.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///external \"lodash\"","webpack:///external \"moment\"","webpack:///external \"@grafana/data\"","webpack:///external \"app/plugins/sdk\"","webpack:///./datasource.ts","webpack:///../node_modules/tslib/tslib.es6.js","webpack:///./query_ctrl.ts","webpack:///./config_ctrl.ts","webpack:///./module.ts"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","__WEBPACK_EXTERNAL_MODULE__0__","__WEBPACK_EXTERNAL_MODULE__1__","__WEBPACK_EXTERNAL_MODULE__2__","__WEBPACK_EXTERNAL_MODULE__3__","$q","datasourceSrv","templateSrv","units","this","testDatasource","Promise","resolve","reject","status","message","title","query","options","_this","sets","groupBy","targets","ds","undefined","datasource","uid","querys","promises","forEach","dsName","opt","cloneDeep","promise","then","meta","id","_compareQuery","toPromise","push","all","results","data","flatten","filter","map","result","datum","hide","comparePromises","target","queryObj","compareDsName","timeShifts","length","timeShift","timeShiftValue","timeShiftAlias","aliasType","delimiter","comparePromise","compareDs","replace","scopedVars","alias","compareOptions","range","from","addTimeShift","to","raw","rangeRaw","refId","requestId","compareResult","line","generalAlias","fields","field","config","displayName","displayNameFromDS","columns","column","text","process","parseShiftToMs","type","rows","row","datapoints","datapoint","unshiftedTimeField","find","timeField","labels","values","set","original","timeShiftObj","parseTimeShift","num","unit","includes","curTime","shiftTime","clone","add","valueOf","dateTime","len","isNaN","charAt","parseInt","substring","time","extendStatics","b","setPrototypeOf","__proto__","Array","$scope","$injector","aliasTypes","addTimeShifts","__","constructor","targetBlur","refresh","onChangeInternal","getTimeShiftId","removeTimeShift","index","indexOf","splice","refreshTimeShifts","onAliasAsChange","aliasAs","console","error","every","templateUrl"],"mappings":"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","file":"module.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 4);\n","module.exports = __WEBPACK_EXTERNAL_MODULE__0__;","module.exports = __WEBPACK_EXTERNAL_MODULE__1__;","module.exports = __WEBPACK_EXTERNAL_MODULE__2__;","module.exports = __WEBPACK_EXTERNAL_MODULE__3__;","import _ from 'lodash';\r\nimport moment from 'moment'; // eslint-disable-line no-restricted-imports\r\n\r\nimport { ArrayVector, MutableField } from '@grafana/data';\r\n\r\nexport class CompareQueriesDatasource {\r\n  datasourceSrv: any;\r\n  $q: any;\r\n  templateSrv: any;\r\n  meta: any;\r\n  units = ['y', 'M', 'w', 'd', 'h', 'm', 's'];\r\n\r\n  /** @ngInject */\r\n  constructor($q, datasourceSrv, templateSrv) {\r\n    this.datasourceSrv = datasourceSrv;\r\n    this.$q = $q;\r\n    this.templateSrv = templateSrv;\r\n  }\r\n\r\n  testDatasource() {\r\n    return new Promise(function(resolve, reject) {\r\n      resolve({\r\n        status: 'success',\r\n        message: 'Compare Query Source is working correctly',\r\n        title: 'Success',\r\n      });\r\n    });\r\n  }\r\n\r\n  // Called once per panel (graph)\r\n  query(options) {\r\n    var _this = this;\r\n    var sets = _.groupBy(options.targets, function(ds) {\r\n      // Trying to maintain compatibility with grafana lower then 8.3.x\r\n      if (ds.datasource.uid === undefined) {\r\n        return ds.datasource;\r\n      }\r\n      return ds.datasource.uid;\r\n    });\r\n    var querys = _.groupBy(options.targets, 'refId');\r\n    var promises: any[] = [];\r\n    _.forEach(sets, function(targets, dsName) {\r\n      var opt = _.cloneDeep(options);\r\n\r\n      var promise = _this.datasourceSrv.get(dsName).then(function(ds) {\r\n        if (ds.meta.id === _this.meta.id) {\r\n          return _this._compareQuery(options, targets, querys, _this);\r\n        } else {\r\n          opt.targets = targets;\r\n          let result = ds.query(opt);\r\n          return typeof result.toPromise === 'function' ? result.toPromise() : result;\r\n        }\r\n      });\r\n      promises.push(promise);\r\n    });\r\n    let result = this.$q.all(promises).then(function(results) {\r\n      return {\r\n        data: _.flatten(\r\n          _.filter(\r\n            _.map(results, function(result) {\r\n              var data = result.data;\r\n              if (data) {\r\n                data = _.filter(result.data, function(datum) {\r\n                  return datum.hide !== true;\r\n                });\r\n              }\r\n              return data;\r\n            }),\r\n            function(result) {\r\n              return result !== undefined && result !== null;\r\n            }\r\n          )\r\n        ),\r\n      };\r\n    });\r\n    return result;\r\n  }\r\n\r\n  _compareQuery(options, targets, querys, _this) {\r\n    var comparePromises: any[] = [];\r\n    //console.log('_compareQuery targets', targets)\r\n    _.forEach(targets, function(target) {\r\n      var query = target.query;\r\n      if (query === null || query === '' || querys[query] === null) {\r\n        return;\r\n      }\r\n      var queryObj = _.cloneDeep(querys[query][0]);\r\n      queryObj.hide = false;\r\n      if (queryObj) {\r\n        var compareDsName = queryObj.datasource;\r\n        if (target.timeShifts && target.timeShifts.length > 0) {\r\n          _.forEach(target.timeShifts, function(timeShift) {\r\n            var timeShiftValue;\r\n            var timeShiftAlias;\r\n            var aliasType = timeShift.aliasType || 'suffix';\r\n            var delimiter = timeShift.delimiter || '_';\r\n\r\n            var comparePromise = _this.datasourceSrv\r\n              .get(compareDsName)\r\n              .then(function(compareDs) {\r\n                if (compareDs.meta.id === _this.meta.id) {\r\n                  return { data: [] };\r\n                }\r\n                timeShiftValue = _this.templateSrv.replace(timeShift.value, options.scopedVars);\r\n                timeShiftAlias = _this.templateSrv.replace(timeShift.alias, options.scopedVars) || timeShiftValue;\r\n\r\n                if (timeShiftValue === null || timeShiftValue === '' || typeof timeShiftValue === 'undefined') {\r\n                  return { data: [] };\r\n                }\r\n                let compareOptions = _.cloneDeep(options);\r\n                compareOptions.range.from = _this.addTimeShift(compareOptions.range.from, timeShiftValue);\r\n                compareOptions.range.to = _this.addTimeShift(compareOptions.range.to, timeShiftValue);\r\n                compareOptions.range.raw = {\r\n                  from: compareOptions.range.from,\r\n                  to: compareOptions.range.to,\r\n                };\r\n                compareOptions.rangeRaw = compareOptions.range.raw;\r\n\r\n                queryObj.refId = queryObj.refId + '_' + timeShiftValue;\r\n                compareOptions.targets = [queryObj];\r\n                compareOptions.requestId = compareOptions.requestId + '_' + timeShiftValue;\r\n\r\n                var compareResult = compareDs.query(compareOptions);\r\n                return typeof compareResult.toPromise === 'function' ? compareResult.toPromise() : compareResult;\r\n              })\r\n              .then(function(compareResult) {\r\n                var data = compareResult.data;\r\n                data.forEach(function(line) {\r\n                  if (line.target) {\r\n                    // if old time series format\r\n                    line.target = _this.generalAlias(line.target, timeShiftAlias, aliasType, delimiter);\r\n                    typeof line.title !== 'undefined' &&\r\n                      line.title !== null &&\r\n                      (line.title = _this.generalAlias(line.title, timeShiftAlias, aliasType, delimiter));\r\n                  } else if (line.fields) {\r\n                    //else if new data frames format with multiple series\r\n                    line.fields.forEach(function(field) {\r\n                      if (field.name) {\r\n                        field.name = _this.generalAlias(field.name, timeShiftAlias, aliasType, delimiter);\r\n                      }\r\n\r\n                      if (field.config && field.config.displayName) {\r\n                        field.config.displayName = _this.generalAlias(\r\n                          field.config.displayName,\r\n                          timeShiftAlias,\r\n                          aliasType,\r\n                          delimiter\r\n                        );\r\n                      }\r\n\r\n                      if (field.config && field.config.displayNameFromDS) {\r\n                        field.config.displayNameFromDS = _this.generalAlias(\r\n                          field.config.displayNameFromDS,\r\n                          timeShiftAlias,\r\n                          aliasType,\r\n                          delimiter\r\n                        );\r\n                      }\r\n                    });\r\n                  } else if (line.columns) {\r\n                    // else if table. always skip first column for joins\r\n                    for (let i = 1; i < line.columns.length; i++) {\r\n                      let column = line.columns[i];\r\n                      if (column.text) {\r\n                        column.text = _this.generalAlias(column.text, timeShiftAlias, aliasType, delimiter);\r\n                      }\r\n                    }\r\n                  }\r\n\r\n                  if (target.process) {\r\n                    let timeShift_ms = _this.parseShiftToMs(timeShiftValue);\r\n\r\n                    if (line.type === 'table') {\r\n                      if (line.rows) {\r\n                        line.rows.forEach(function(row) {\r\n                          row[0] = row[0] + timeShift_ms;\r\n                        });\r\n                      }\r\n                    } else {\r\n                      if (line.datapoints) {\r\n                        // if old time series format\r\n                        line.datapoints.forEach(function(datapoint) {\r\n                          datapoint[1] = datapoint[1] + timeShift_ms;\r\n                        });\r\n                      } else if (line.fields && line.fields.length > 0) {\r\n                        //else if new data frames format\r\n                        const unshiftedTimeField = line.fields.find(field => field.type === 'time');\r\n\r\n                        if (unshiftedTimeField) {\r\n                          const timeField: MutableField = {\r\n                            name: unshiftedTimeField.name,\r\n                            type: unshiftedTimeField.type,\r\n                            config: unshiftedTimeField.config || {},\r\n                            labels: unshiftedTimeField.labels,\r\n                            values: new ArrayVector(),\r\n                          };\r\n\r\n                          for (let i = 0; i < line.length; i++) {\r\n                            timeField.values.set(i, unshiftedTimeField.values.get(i) + timeShift_ms);\r\n                          }\r\n                          line.fields[0] = timeField;\r\n                        }\r\n                      }\r\n                    }\r\n                  }\r\n\r\n                  line.hide = target.hide;\r\n                });\r\n                return {\r\n                  data: data,\r\n                };\r\n              });\r\n\r\n            comparePromises.push(comparePromise);\r\n          });\r\n        }\r\n      }\r\n    });\r\n\r\n    return this.$q.all(comparePromises).then(function(results) {\r\n      return {\r\n        data: _.flatten(\r\n          _.filter(\r\n            _.map(results, function(result) {\r\n              var data = result.data;\r\n              if (data) {\r\n                data = _.filter(result.data, function(datum) {\r\n                  return datum.hide !== true;\r\n                });\r\n              }\r\n              return data;\r\n            }),\r\n            function(result) {\r\n              return result !== undefined && result !== null;\r\n            }\r\n          )\r\n        ),\r\n      };\r\n    });\r\n  }\r\n\r\n  generalAlias(original, alias, aliasType, delimiter) {\r\n    switch (aliasType) {\r\n      case 'prefix':\r\n        return alias + delimiter + original;\r\n      case 'absolute':\r\n        return alias;\r\n      case 'suffix':\r\n      default:\r\n        return original + delimiter + alias;\r\n    }\r\n  }\r\n\r\n  parseShiftToMs(timeShift) {\r\n    let timeShiftObj = this.parseTimeShift(timeShift);\r\n    if (!timeShiftObj) {\r\n      return;\r\n    }\r\n    var num = 0 - timeShiftObj.num;\r\n    var unit = timeShiftObj.unit;\r\n    if (!_.includes(this.units, unit)) {\r\n      return undefined;\r\n    } else {\r\n      let curTime = moment();\r\n      let shiftTime = curTime.clone().add(num, unit);\r\n      return curTime.valueOf() - shiftTime.valueOf();\r\n    }\r\n  }\r\n  parseTimeShift(timeShift) {\r\n    var dateTime = timeShift;\r\n    var len = timeShift.length;\r\n    var i = 0;\r\n\r\n    while (i < len && !isNaN(dateTime.charAt(i))) {\r\n      i++;\r\n      if (i > 10) {\r\n        return undefined;\r\n      }\r\n    }\r\n    var num = parseInt(dateTime.substring(0, i), 10);\r\n    var unit = dateTime.charAt(i);\r\n    return {\r\n      num: num,\r\n      unit: unit,\r\n    };\r\n  }\r\n\r\n  addTimeShift(time, timeShift) {\r\n    let timeShiftObj = this.parseTimeShift(timeShift);\r\n    if (!timeShiftObj) {\r\n      return;\r\n    }\r\n    var num = 0 - timeShiftObj.num;\r\n    var unit = timeShiftObj.unit;\r\n\r\n    if (!_.includes(this.units, unit)) {\r\n      return undefined;\r\n    } else {\r\n      let curTime = time;\r\n      let shiftTime = curTime.clone().add(num, unit);\r\n      return shiftTime;\r\n    }\r\n  }\r\n}\r\n","/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n    extendStatics = Object.setPrototypeOf ||\r\n        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };\r\n    return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n    extendStatics(d, b);\r\n    function __() { this.constructor = d; }\r\n    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n    __assign = Object.assign || function __assign(t) {\r\n        for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n            s = arguments[i];\r\n            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n        }\r\n        return t;\r\n    }\r\n    return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n    var t = {};\r\n    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n        t[p] = s[p];\r\n    if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n                t[p[i]] = s[p[i]];\r\n        }\r\n    return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n    if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n    return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n    return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n    if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n    return new (P || (P = Promise))(function (resolve, reject) {\r\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n    });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n    return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n    function verb(n) { return function (v) { return step([n, v]); }; }\r\n    function step(op) {\r\n        if (f) throw new TypeError(\"Generator is already executing.\");\r\n        while (_) try {\r\n            if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n            if (y = 0, t) op = [op[0] & 2, t.value];\r\n            switch (op[0]) {\r\n                case 0: case 1: t = op; break;\r\n                case 4: _.label++; return { value: op[1], done: false };\r\n                case 5: _.label++; y = op[1]; op = [0]; continue;\r\n                case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n                default:\r\n                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n                    if (t[2]) _.ops.pop();\r\n                    _.trys.pop(); continue;\r\n            }\r\n            op = body.call(thisArg, _);\r\n        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n    }\r\n}\r\n\r\nexport function __createBinding(o, m, k, k2) {\r\n    if (k2 === undefined) k2 = k;\r\n    o[k2] = m[k];\r\n}\r\n\r\nexport function __exportStar(m, exports) {\r\n    for (var p in m) if (p !== \"default\" && !exports.hasOwnProperty(p)) exports[p] = m[p];\r\n}\r\n\r\nexport function __values(o) {\r\n    var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n    if (m) return m.call(o);\r\n    if (o && typeof o.length === \"number\") return {\r\n        next: function () {\r\n            if (o && i >= o.length) o = void 0;\r\n            return { value: o && o[i++], done: !o };\r\n        }\r\n    };\r\n    throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n    var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n    if (!m) return o;\r\n    var i = m.call(o), r, ar = [], e;\r\n    try {\r\n        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n    }\r\n    catch (error) { e = { error: error }; }\r\n    finally {\r\n        try {\r\n            if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n        }\r\n        finally { if (e) throw e.error; }\r\n    }\r\n    return ar;\r\n}\r\n\r\nexport function __spread() {\r\n    for (var ar = [], i = 0; i < arguments.length; i++)\r\n        ar = ar.concat(__read(arguments[i]));\r\n    return ar;\r\n}\r\n\r\nexport function __spreadArrays() {\r\n    for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n    for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n        for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n            r[k] = a[j];\r\n    return r;\r\n};\r\n\r\nexport function __await(v) {\r\n    return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n    if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n    var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n    return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n    function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n    function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n    function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n    function fulfill(value) { resume(\"next\", value); }\r\n    function reject(value) { resume(\"throw\", value); }\r\n    function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n    var i, p;\r\n    return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n    function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n    if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n    var m = o[Symbol.asyncIterator], i;\r\n    return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n    function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n    function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n    if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n    return cooked;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n    if (mod && mod.__esModule) return mod;\r\n    var result = {};\r\n    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];\r\n    result.default = mod;\r\n    return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n    return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, privateMap) {\r\n    if (!privateMap.has(receiver)) {\r\n        throw new TypeError(\"attempted to get private field on non-instance\");\r\n    }\r\n    return privateMap.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, privateMap, value) {\r\n    if (!privateMap.has(receiver)) {\r\n        throw new TypeError(\"attempted to set private field on non-instance\");\r\n    }\r\n    privateMap.set(receiver, value);\r\n    return value;\r\n}\r\n","import _ from 'lodash';\r\n// import kbn from 'app/core/utils/kbn';\r\nimport { QueryCtrl } from 'grafana/app/plugins/sdk';\r\n\r\nexport class CompareQueriesQueryCtrl extends QueryCtrl {\r\n  static templateUrl = 'partials/query.editor.html';\r\n  errors: any;\r\n  query: any;\r\n  target: any;\r\n  aliasTypes = ['suffix', 'prefix', 'absolute'];\r\n  /** @ngInject **/\r\n  constructor($scope, $injector) {\r\n    super($scope, $injector);\r\n    if (!this.target.timeShifts) {\r\n      this.target.timeShifts = [];\r\n    }\r\n    if (this.target.timeShifts.length === 0) {\r\n      this.addTimeShifts();\r\n    }\r\n    if (typeof this.target.process === 'undefined') {\r\n      this.target.process = true;\r\n    }\r\n  }\r\n\r\n  targetBlur() {\r\n    this.refresh();\r\n  }\r\n  onChangeInternal() {\r\n    this.refresh(); // Asks the panel to refresh data.\r\n  }\r\n  addTimeShifts() {\r\n    let id = this.getTimeShiftId();\r\n    this.target.timeShifts.push({ id: id });\r\n  }\r\n  removeTimeShift(timeShift) {\r\n    if (this.target.timeShifts && this.target.timeShifts.length <= 1) {\r\n      return;\r\n    }\r\n    var index = _.indexOf(this.target.timeShifts, timeShift);\r\n    this.target.timeShifts.splice(index, 1);\r\n    this.refreshTimeShifts();\r\n  }\r\n  refreshTimeShifts() {\r\n    this.refresh();\r\n  }\r\n  onAliasAsChange(aliasAs) {\r\n    console.error('timeShift.aliasAs=' + this.target.aliasAs);\r\n    console.error('aliasAs=' + aliasAs);\r\n  }\r\n  getTimeShiftId() {\r\n    let id = 0;\r\n    while (true) {\r\n      let notExits = _.every(this.target.timeShifts, function(timeShift) {\r\n        return timeShift.id !== id;\r\n      });\r\n      if (notExits) {\r\n        return id;\r\n      } else {\r\n        id++;\r\n      }\r\n    }\r\n  }\r\n}\r\n","import _ from 'lodash';\r\n\r\nexport class CompareQueriesConfigCtrl {\r\n  static templateUrl = 'partials/config.html';\r\n  current: any;\r\n}\r\n","import { CompareQueriesDatasource } from './datasource';\r\nimport { CompareQueriesQueryCtrl } from './query_ctrl';\r\nimport { CompareQueriesConfigCtrl } from './config_ctrl';\r\nexport {\r\n  CompareQueriesDatasource as Datasource,\r\n  CompareQueriesQueryCtrl as QueryCtrl,\r\n  CompareQueriesConfigCtrl as ConfigCtrl,\r\n};\r\n"],"sourceRoot":""}
--------------------------------------------------------------------------------
/dist/partials/config.html:
--------------------------------------------------------------------------------
1 | 
2 | 
--------------------------------------------------------------------------------
/dist/partials/query.editor.html:
--------------------------------------------------------------------------------
 1 | 
 2 |   
25 | 
26 |   
91 | 
92 | 
--------------------------------------------------------------------------------
/dist/plugin.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "CompareQueries",
 3 |   "id": "autohome-comparequeries-datasource",
 4 |   "type": "datasource",
 5 | 
 6 |   "metrics": true,
 7 |   "mixed": true,
 8 |   "hiddenQueries": true,
 9 |   "annotations": false,
10 | 
11 |   "queryOptions": {
12 |     "minInterval": true
13 |   },
14 | 
15 |   "info": {
16 |     "description": "This plugin is built as a datasource plugin that combines and contrasts different time shifts data.",
17 |     "version": "1.0.1",
18 |     "updated": "2021-12-10",
19 |     "keywords": [
20 |       "datasource",
21 |       "compare",
22 |       "time shift"
23 |     ],
24 |     "author": {
25 |       "name": "AutohomeCorp",
26 |       "url": "https://github.com/AutohomeCorp/autohome-compareQueries-datasource"
27 |     },
28 |     "links": [
29 |       {
30 |         "name": "GitHub",
31 |         "url": "https://github.com/AutohomeCorp/autohome-compareQueries-datasource#readme"
32 |       }
33 |     ],
34 |     "logos": {
35 |       "small": "img/logo.png",
36 |       "large": "img/logo.png"
37 |     }
38 |   },
39 |   "dependencies": {
40 |     "grafanaDependency": ">=6.3.0",
41 |     "plugins": []
42 |   }
43 | }
44 | 
--------------------------------------------------------------------------------
/img/step-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AutohomeCorp/autohome-compareQueries-datasource/6cc213a7c26d6119e12fc989e554c0f86c8cac7c/img/step-1.png
--------------------------------------------------------------------------------
/img/step-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AutohomeCorp/autohome-compareQueries-datasource/6cc213a7c26d6119e12fc989e554c0f86c8cac7c/img/step-2.png
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | // This file is needed because it is used by vscode and other tools that
2 | // call `jest` directly.  However, unless you are doing anything special
3 | // do not edit this file
4 | 
5 | const standard = require('@grafana/toolkit/src/config/jest.plugin.config');
6 | 
7 | // This process will use the same config that `yarn test` is using
8 | module.exports = standard.jestConfig();
9 | 
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "autohome-comparequeries-datasource",
 3 |   "version": "0.0.2",
 4 |   "description": "Grafana plugin for Compare Queries",
 5 |   "main": "index.js",
 6 |   "scripts": {
 7 |     "build": "grafana-toolkit plugin:build",
 8 |     "test": "grafana-toolkit plugin:test",
 9 |     "dev": "grafana-toolkit plugin:dev",
10 |     "watch": "grafana-toolkit plugin:dev --watch"
11 |   },
12 |   "repository": {
13 |     "type": "git",
14 |     "url": "https://github.com/AutohomeCorp/autohome-comparequeries-datasource"
15 |   },
16 |   "keywords": [
17 |     "Compare Queries"
18 |   ],
19 |   "author": "nfw",
20 |   "license": "MIT",
21 |   "bugs": {
22 |     "url": "https://github.com/AutohomeCorp/grafana-compare-queries/issues"
23 |   },
24 |   "devDependencies": {
25 |     "@grafana/data": "next",
26 |     "@grafana/ui": "next",
27 |     "@grafana/toolkit": "next",
28 |     "@types/grafana": "github:CorpGlory/types-grafana.git",
29 |     "lodash": ">=4.17.19"
30 |   },
31 |   "homepage": ""
32 | }
33 | 
--------------------------------------------------------------------------------
/src/config_ctrl.ts:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | 
3 | export class CompareQueriesConfigCtrl {
4 |   static templateUrl = 'partials/config.html';
5 |   current: any;
6 | }
7 | 
--------------------------------------------------------------------------------
/src/datasource.ts:
--------------------------------------------------------------------------------
  1 | import _ from 'lodash';
  2 | import moment from 'moment'; // eslint-disable-line no-restricted-imports
  3 | 
  4 | import { ArrayVector, MutableField } from '@grafana/data';
  5 | 
  6 | export class CompareQueriesDatasource {
  7 |   datasourceSrv: any;
  8 |   $q: any;
  9 |   templateSrv: any;
 10 |   meta: any;
 11 |   units = ['y', 'M', 'w', 'd', 'h', 'm', 's'];
 12 | 
 13 |   /** @ngInject */
 14 |   constructor($q, datasourceSrv, templateSrv) {
 15 |     this.datasourceSrv = datasourceSrv;
 16 |     this.$q = $q;
 17 |     this.templateSrv = templateSrv;
 18 |   }
 19 | 
 20 |   testDatasource() {
 21 |     return new Promise(function(resolve, reject) {
 22 |       resolve({
 23 |         status: 'success',
 24 |         message: 'Compare Query Source is working correctly',
 25 |         title: 'Success',
 26 |       });
 27 |     });
 28 |   }
 29 | 
 30 |   // Called once per panel (graph)
 31 |   query(options) {
 32 |     var _this = this;
 33 |     var sets = _.groupBy(options.targets, function(ds) {
 34 |       // Trying to maintain compatibility with grafana lower then 8.3.x
 35 |       if (ds.datasource.uid === undefined) {
 36 |         return ds.datasource;
 37 |       }
 38 |       return ds.datasource.uid;
 39 |     });
 40 |     var querys = _.groupBy(options.targets, 'refId');
 41 |     var promises: any[] = [];
 42 |     _.forEach(sets, function(targets, dsName) {
 43 |       var opt = _.cloneDeep(options);
 44 | 
 45 |       var promise = _this.datasourceSrv.get(dsName).then(function(ds) {
 46 |         if (ds.meta.id === _this.meta.id) {
 47 |           return _this._compareQuery(options, targets, querys, _this);
 48 |         } else {
 49 |           opt.targets = targets;
 50 |           let result = ds.query(opt);
 51 |           return typeof result.toPromise === 'function' ? result.toPromise() : result;
 52 |         }
 53 |       });
 54 |       promises.push(promise);
 55 |     });
 56 |     let result = this.$q.all(promises).then(function(results) {
 57 |       return {
 58 |         data: _.flatten(
 59 |           _.filter(
 60 |             _.map(results, function(result) {
 61 |               var data = result.data;
 62 |               if (data) {
 63 |                 data = _.filter(result.data, function(datum) {
 64 |                   return datum.hide !== true;
 65 |                 });
 66 |               }
 67 |               return data;
 68 |             }),
 69 |             function(result) {
 70 |               return result !== undefined && result !== null;
 71 |             }
 72 |           )
 73 |         ),
 74 |       };
 75 |     });
 76 |     return result;
 77 |   }
 78 | 
 79 |   _compareQuery(options, targets, querys, _this) {
 80 |     var comparePromises: any[] = [];
 81 |     //console.log('_compareQuery targets', targets)
 82 |     _.forEach(targets, function(target) {
 83 |       var query = target.query;
 84 |       if (query === null || query === '' || querys[query] === null) {
 85 |         return;
 86 |       }
 87 |       var queryObj = _.cloneDeep(querys[query][0]);
 88 |       queryObj.hide = false;
 89 |       if (queryObj) {
 90 |         var compareDsName = queryObj.datasource;
 91 |         if (target.timeShifts && target.timeShifts.length > 0) {
 92 |           _.forEach(target.timeShifts, function(timeShift) {
 93 |             var timeShiftValue;
 94 |             var timeShiftAlias;
 95 |             var aliasType = timeShift.aliasType || 'suffix';
 96 |             var delimiter = timeShift.delimiter || '_';
 97 | 
 98 |             var comparePromise = _this.datasourceSrv
 99 |               .get(compareDsName)
100 |               .then(function(compareDs) {
101 |                 if (compareDs.meta.id === _this.meta.id) {
102 |                   return { data: [] };
103 |                 }
104 |                 timeShiftValue = _this.templateSrv.replace(timeShift.value, options.scopedVars);
105 |                 timeShiftAlias = _this.templateSrv.replace(timeShift.alias, options.scopedVars) || timeShiftValue;
106 | 
107 |                 if (timeShiftValue === null || timeShiftValue === '' || typeof timeShiftValue === 'undefined') {
108 |                   return { data: [] };
109 |                 }
110 |                 let compareOptions = _.cloneDeep(options);
111 |                 compareOptions.range.from = _this.addTimeShift(compareOptions.range.from, timeShiftValue);
112 |                 compareOptions.range.to = _this.addTimeShift(compareOptions.range.to, timeShiftValue);
113 |                 compareOptions.range.raw = {
114 |                   from: compareOptions.range.from,
115 |                   to: compareOptions.range.to,
116 |                 };
117 |                 compareOptions.rangeRaw = compareOptions.range.raw;
118 | 
119 |                 queryObj.refId = queryObj.refId + '_' + timeShiftValue;
120 |                 compareOptions.targets = [queryObj];
121 |                 compareOptions.requestId = compareOptions.requestId + '_' + timeShiftValue;
122 | 
123 |                 var compareResult = compareDs.query(compareOptions);
124 |                 return typeof compareResult.toPromise === 'function' ? compareResult.toPromise() : compareResult;
125 |               })
126 |               .then(function(compareResult) {
127 |                 var data = compareResult.data;
128 |                 data.forEach(function(line) {
129 |                   if (line.target) {
130 |                     // if old time series format
131 |                     line.target = _this.generalAlias(line.target, timeShiftAlias, aliasType, delimiter);
132 |                     typeof line.title !== 'undefined' &&
133 |                       line.title !== null &&
134 |                       (line.title = _this.generalAlias(line.title, timeShiftAlias, aliasType, delimiter));
135 |                   } else if (line.fields) {
136 |                     //else if new data frames format with multiple series
137 |                     line.fields.forEach(function(field) {
138 |                       if (field.name) {
139 |                         field.name = _this.generalAlias(field.name, timeShiftAlias, aliasType, delimiter);
140 |                       }
141 | 
142 |                       if (field.config && field.config.displayName) {
143 |                         field.config.displayName = _this.generalAlias(
144 |                           field.config.displayName,
145 |                           timeShiftAlias,
146 |                           aliasType,
147 |                           delimiter
148 |                         );
149 |                       }
150 | 
151 |                       if (field.config && field.config.displayNameFromDS) {
152 |                         field.config.displayNameFromDS = _this.generalAlias(
153 |                           field.config.displayNameFromDS,
154 |                           timeShiftAlias,
155 |                           aliasType,
156 |                           delimiter
157 |                         );
158 |                       }
159 |                     });
160 |                   } else if (line.columns) {
161 |                     // else if table. always skip first column for joins
162 |                     for (let i = 1; i < line.columns.length; i++) {
163 |                       let column = line.columns[i];
164 |                       if (column.text) {
165 |                         column.text = _this.generalAlias(column.text, timeShiftAlias, aliasType, delimiter);
166 |                       }
167 |                     }
168 |                   }
169 | 
170 |                   if (target.process) {
171 |                     let timeShift_ms = _this.parseShiftToMs(timeShiftValue);
172 | 
173 |                     if (line.type === 'table') {
174 |                       if (line.rows) {
175 |                         line.rows.forEach(function(row) {
176 |                           row[0] = row[0] + timeShift_ms;
177 |                         });
178 |                       }
179 |                     } else {
180 |                       if (line.datapoints) {
181 |                         // if old time series format
182 |                         line.datapoints.forEach(function(datapoint) {
183 |                           datapoint[1] = datapoint[1] + timeShift_ms;
184 |                         });
185 |                       } else if (line.fields && line.fields.length > 0) {
186 |                         //else if new data frames format
187 |                         const unshiftedTimeField = line.fields.find(field => field.type === 'time');
188 | 
189 |                         if (unshiftedTimeField) {
190 |                           const timeField: MutableField = {
191 |                             name: unshiftedTimeField.name,
192 |                             type: unshiftedTimeField.type,
193 |                             config: unshiftedTimeField.config || {},
194 |                             labels: unshiftedTimeField.labels,
195 |                             values: new ArrayVector(),
196 |                           };
197 | 
198 |                           for (let i = 0; i < line.length; i++) {
199 |                             timeField.values.set(i, unshiftedTimeField.values.get(i) + timeShift_ms);
200 |                           }
201 |                           line.fields[0] = timeField;
202 |                         }
203 |                       }
204 |                     }
205 |                   }
206 | 
207 |                   line.hide = target.hide;
208 |                 });
209 |                 return {
210 |                   data: data,
211 |                 };
212 |               });
213 | 
214 |             comparePromises.push(comparePromise);
215 |           });
216 |         }
217 |       }
218 |     });
219 | 
220 |     return this.$q.all(comparePromises).then(function(results) {
221 |       return {
222 |         data: _.flatten(
223 |           _.filter(
224 |             _.map(results, function(result) {
225 |               var data = result.data;
226 |               if (data) {
227 |                 data = _.filter(result.data, function(datum) {
228 |                   return datum.hide !== true;
229 |                 });
230 |               }
231 |               return data;
232 |             }),
233 |             function(result) {
234 |               return result !== undefined && result !== null;
235 |             }
236 |           )
237 |         ),
238 |       };
239 |     });
240 |   }
241 | 
242 |   generalAlias(original, alias, aliasType, delimiter) {
243 |     switch (aliasType) {
244 |       case 'prefix':
245 |         return alias + delimiter + original;
246 |       case 'absolute':
247 |         return alias;
248 |       case 'suffix':
249 |       default:
250 |         return original + delimiter + alias;
251 |     }
252 |   }
253 | 
254 |   parseShiftToMs(timeShift) {
255 |     let timeShiftObj = this.parseTimeShift(timeShift);
256 |     if (!timeShiftObj) {
257 |       return;
258 |     }
259 |     var num = 0 - timeShiftObj.num;
260 |     var unit = timeShiftObj.unit;
261 |     if (!_.includes(this.units, unit)) {
262 |       return undefined;
263 |     } else {
264 |       let curTime = moment();
265 |       let shiftTime = curTime.clone().add(num, unit);
266 |       return curTime.valueOf() - shiftTime.valueOf();
267 |     }
268 |   }
269 |   parseTimeShift(timeShift) {
270 |     var dateTime = timeShift;
271 |     var len = timeShift.length;
272 |     var i = 0;
273 | 
274 |     while (i < len && !isNaN(dateTime.charAt(i))) {
275 |       i++;
276 |       if (i > 10) {
277 |         return undefined;
278 |       }
279 |     }
280 |     var num = parseInt(dateTime.substring(0, i), 10);
281 |     var unit = dateTime.charAt(i);
282 |     return {
283 |       num: num,
284 |       unit: unit,
285 |     };
286 |   }
287 | 
288 |   addTimeShift(time, timeShift) {
289 |     let timeShiftObj = this.parseTimeShift(timeShift);
290 |     if (!timeShiftObj) {
291 |       return;
292 |     }
293 |     var num = 0 - timeShiftObj.num;
294 |     var unit = timeShiftObj.unit;
295 | 
296 |     if (!_.includes(this.units, unit)) {
297 |       return undefined;
298 |     } else {
299 |       let curTime = time;
300 |       let shiftTime = curTime.clone().add(num, unit);
301 |       return shiftTime;
302 |     }
303 |   }
304 | }
305 | 
--------------------------------------------------------------------------------
/src/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AutohomeCorp/autohome-compareQueries-datasource/6cc213a7c26d6119e12fc989e554c0f86c8cac7c/src/img/logo.png
--------------------------------------------------------------------------------
/src/module.ts:
--------------------------------------------------------------------------------
1 | import { CompareQueriesDatasource } from './datasource';
2 | import { CompareQueriesQueryCtrl } from './query_ctrl';
3 | import { CompareQueriesConfigCtrl } from './config_ctrl';
4 | export {
5 |   CompareQueriesDatasource as Datasource,
6 |   CompareQueriesQueryCtrl as QueryCtrl,
7 |   CompareQueriesConfigCtrl as ConfigCtrl,
8 | };
9 | 
--------------------------------------------------------------------------------
/src/partials/config.html:
--------------------------------------------------------------------------------
1 | 
2 | 
--------------------------------------------------------------------------------
/src/partials/query.editor.html:
--------------------------------------------------------------------------------
 1 | 
 2 |   
25 | 
26 |   
91 | 
92 | 
--------------------------------------------------------------------------------
/src/plugin.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "CompareQueries",
 3 |   "id": "autohome-comparequeries-datasource",
 4 |   "type": "datasource",
 5 | 
 6 |   "metrics": true,
 7 |   "mixed": true,
 8 |   "hiddenQueries": true,
 9 |   "annotations": false,
10 | 
11 |   "queryOptions": {
12 |     "minInterval": true
13 |   },
14 | 
15 |   "info": {
16 |     "description": "This plugin is built as a datasource plugin that combines and contrasts different time shifts data.",
17 |     "version": "1.0.1",
18 |     "updated": "2021-12-10",
19 |     "keywords": [
20 |       "datasource",
21 |       "compare",
22 |       "time shift"
23 |     ],
24 |     "author": {
25 |       "name": "AutohomeCorp",
26 |       "url": "https://github.com/AutohomeCorp/autohome-compareQueries-datasource"
27 |     },
28 |     "links": [
29 |       {
30 |         "name": "GitHub",
31 |         "url": "https://github.com/AutohomeCorp/autohome-compareQueries-datasource#readme"
32 |       }
33 |     ],
34 |     "logos": {
35 |       "small": "img/logo.png",
36 |       "large": "img/logo.png"
37 |     }
38 |   },
39 |   "dependencies": {
40 |     "grafanaDependency": ">=6.3.0",
41 |     "plugins": []
42 |   }
43 | }
44 | 
--------------------------------------------------------------------------------
/src/query_ctrl.ts:
--------------------------------------------------------------------------------
 1 | import _ from 'lodash';
 2 | // import kbn from 'app/core/utils/kbn';
 3 | import { QueryCtrl } from 'grafana/app/plugins/sdk';
 4 | 
 5 | export class CompareQueriesQueryCtrl extends QueryCtrl {
 6 |   static templateUrl = 'partials/query.editor.html';
 7 |   errors: any;
 8 |   query: any;
 9 |   target: any;
10 |   aliasTypes = ['suffix', 'prefix', 'absolute'];
11 |   /** @ngInject **/
12 |   constructor($scope, $injector) {
13 |     super($scope, $injector);
14 |     if (!this.target.timeShifts) {
15 |       this.target.timeShifts = [];
16 |     }
17 |     if (this.target.timeShifts.length === 0) {
18 |       this.addTimeShifts();
19 |     }
20 |     if (typeof this.target.process === 'undefined') {
21 |       this.target.process = true;
22 |     }
23 |   }
24 | 
25 |   targetBlur() {
26 |     this.refresh();
27 |   }
28 |   onChangeInternal() {
29 |     this.refresh(); // Asks the panel to refresh data.
30 |   }
31 |   addTimeShifts() {
32 |     let id = this.getTimeShiftId();
33 |     this.target.timeShifts.push({ id: id });
34 |   }
35 |   removeTimeShift(timeShift) {
36 |     if (this.target.timeShifts && this.target.timeShifts.length <= 1) {
37 |       return;
38 |     }
39 |     var index = _.indexOf(this.target.timeShifts, timeShift);
40 |     this.target.timeShifts.splice(index, 1);
41 |     this.refreshTimeShifts();
42 |   }
43 |   refreshTimeShifts() {
44 |     this.refresh();
45 |   }
46 |   onAliasAsChange(aliasAs) {
47 |     console.error('timeShift.aliasAs=' + this.target.aliasAs);
48 |     console.error('aliasAs=' + aliasAs);
49 |   }
50 |   getTimeShiftId() {
51 |     let id = 0;
52 |     while (true) {
53 |       let notExits = _.every(this.target.timeShifts, function(timeShift) {
54 |         return timeShift.id !== id;
55 |       });
56 |       if (notExits) {
57 |         return id;
58 |       } else {
59 |         id++;
60 |       }
61 |     }
62 |   }
63 | }
64 | 
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "extends": "@grafana/toolkit/src/config/tsconfig.plugin.json",
 3 |     "include": ["src", "types"],
 4 |     "compilerOptions": {
 5 |       "rootDir": "./src",
 6 |       "baseUrl": "./src",
 7 |       "typeRoots": ["./node_modules/@types"],
 8 |       "noImplicitAny": false,
 9 |       "noImplicitThis": false
10 |     }
11 |   }
--------------------------------------------------------------------------------