├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── dist ├── LICENSE ├── README.md ├── config_ctrl.d.ts ├── config_ctrl.js ├── config_ctrl.js.map ├── config_ctrl.ts ├── css │ └── query_editor.css ├── datasource.d.ts ├── datasource.js ├── datasource.js.map ├── datasource.ts ├── druid.d.ts ├── img │ └── druid_logo.png ├── module.d.ts ├── module.js ├── module.js.map ├── module.ts ├── partials │ ├── config.html │ └── query.editor.html ├── plugin.json ├── query_ctrl.d.ts ├── query_ctrl.js ├── query_ctrl.js.map └── query_ctrl.ts ├── headers ├── common.d.ts ├── es6-shim │ └── es6-shim.d.ts ├── mocha │ └── mocha.d.ts └── zone │ └── zone.d.ts ├── img ├── AddDataSource.png ├── DruidPanel.png ├── ListDataSource.png └── druid_logo.png ├── karma.conf.js ├── package-lock.json ├── package.json ├── specs ├── datasource_specs.ts ├── lib │ ├── common.ts │ ├── context_srv_stub.ts │ ├── template_srv_stub.ts │ └── time_srv_stub.ts └── query_ctrl_specs.ts ├── src ├── config_ctrl.ts ├── css │ └── query_editor.css ├── datasource.d.ts ├── datasource.ts ├── druid.d.ts ├── img │ └── druid_logo.png ├── module.ts ├── partials │ ├── config.html │ └── query.editor.html ├── plugin.json └── query_ctrl.ts ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | .DS_Store 4 | .vscode 5 | .tscache 6 | .idea/ 7 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | require('load-grunt-tasks')(grunt); 3 | 4 | var pkgJson = require('./package.json'); 5 | 6 | grunt.loadNpmTasks('grunt-contrib-clean'); 7 | grunt.loadNpmTasks('grunt-ts'); 8 | grunt.loadNpmTasks('grunt-contrib-watch'); 9 | grunt.loadNpmTasks('grunt-string-replace'); 10 | 11 | grunt.initConfig({ 12 | clean: ['dist'], 13 | 14 | copy: { 15 | dist_js: { 16 | expand: true, 17 | cwd: 'src', 18 | src: ['**/*.ts', '**/*.d.ts'], 19 | dest: 'dist' 20 | }, 21 | dist_html: { 22 | expand: true, 23 | flatten: true, 24 | cwd: 'src/partials', 25 | src: ['*.html'], 26 | dest: 'dist/partials/' 27 | }, 28 | dist_css: { 29 | expand: true, 30 | flatten: true, 31 | cwd: 'src/css', 32 | src: ['*.css'], 33 | dest: 'dist/css/' 34 | }, 35 | dist_img: { 36 | expand: true, 37 | flatten: true, 38 | cwd: 'src/img', 39 | src: ['*.*'], 40 | dest: 'dist/img/' 41 | }, 42 | dist_statics: { 43 | expand: true, 44 | flatten: true, 45 | src: ['src/plugin.json', 'LICENSE', 'README.md'], 46 | dest: 'dist/' 47 | } 48 | }, 49 | 50 | ts: { 51 | default: { 52 | src: ['dist/**/*.ts', '!**/*.d.ts'], 53 | dest: 'dist', 54 | options: { 55 | module: 'system', 56 | target: 'es5', 57 | rootDir: 'dist/', 58 | declaration: true, 59 | emitDecoratorMetadata: true, 60 | experimentalDecorators: true, 61 | sourceMap: true, 62 | noImplicitAny: false, 63 | skipLibCheck: true 64 | } 65 | }, 66 | }, 67 | 68 | 'string-replace': { 69 | dist: { 70 | files: [{ 71 | cwd: 'src', 72 | expand: true, 73 | src: ["**/plugin.json"], 74 | dest: 'dist' 75 | }], 76 | options: { 77 | replacements: [{ 78 | pattern: '%VERSION%', 79 | replacement: pkgJson.version 80 | },{ 81 | pattern: '%TODAY%', 82 | replacement: '<%= grunt.template.today("yyyy-mm-dd") %>' 83 | }] 84 | } 85 | } 86 | }, 87 | 88 | watch: { 89 | files: ['src/**/*.ts', 'src/**/*.html', 'src/**/*.css', 'src/img/*.*', 'src/plugin.json', 'README.md'], 90 | tasks: ['default'], 91 | options: { 92 | debounceDelay: 250, 93 | }, 94 | } 95 | }); 96 | 97 | grunt.registerTask('default', [ 98 | 'clean', 99 | 'copy:dist_js', 100 | 'ts', 101 | 'copy:dist_html', 102 | 'copy:dist_css', 103 | 'copy:dist_img', 104 | 'copy:dist_statics', 105 | 'string-replace' 106 | ]); 107 | }; 108 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {2018} {PayPal} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UPDATE 2 | 3 | We are working to refresh this codebase. Please see the [milestones](https://github.com/grafana-druid-plugin/druidplugin/milestones) for current estimated dates and related issues / projects for planned work. 4 | 5 | ## Grafana Version Compatibility: 6 | **Druid plugin version 0.0.3 and below are supported for Grafana: 3.x.x** 7 | 8 | **Druid plugin 0.0.4 and above are supported for Grafana: 4.x.x** 9 | 10 | **For latest versions, see https://github.com/grafadruid/druid-grafana** 11 | 12 | # Grafana plugin for [Druid](http://druid.io/) real-time OLAP database 13 | 14 | ![Screenshot](https://raw.githubusercontent.com/grafana-druid-plugin/druidplugin/master/img/AddDataSource.png) 15 | ![Screenshot](https://raw.githubusercontent.com/grafana-druid-plugin/druidplugin/master/img/ListDataSource.png) 16 | ![Screenshot](https://raw.githubusercontent.com/grafana-druid-plugin/druidplugin/master/img/DruidPanel.png) 17 | 18 | ## Status 19 | 20 | This plugin is built on the top of an existing Druid plugin (https://github.com/grafana/grafana-plugins) which used to work on older Grafana versions. With the UI changes done on Grafana-3.0 the existing plugin stopped working. Lot of changes have been made to have it work on Grafana 3.0. It supports timeseries, group by, topN and Select queries. 21 | 22 | Lot of features might still not be implemented. Your contributions are welcome. 23 | 24 | ## Plugin development history 25 | 26 | This plugin was originally developed by Quantiply Corporation (Supported for Grafana versions < 2.5): https://github.com/grafana/grafana-plugins/tree/master/datasources/druid 27 | 28 | This plugin was further enhanced by Carl Bergquist (https://github.com/grafana/grafana/pull/3328) (to support it on Grafana version 2.5 & 2.6). 29 | 30 | I cloned the source from the Pull Request by Carl Bergquist and changed the plugin to have it work on Grafana-3.0. 31 | 32 | All the credits for the original code and enahcement to 2.5 goes to Quantiply and Carl Bergquist. 33 | 34 | Opensourcing all the changes done to the plugin to support Grafana-3.0. 35 | -------------------------------------------------------------------------------- /dist/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {2018} {PayPal} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /dist/README.md: -------------------------------------------------------------------------------- 1 | # UPDATE 2 | 3 | We are working to refresh this codebase. Please see the [milestones](https://github.com/grafana-druid-plugin/druidplugin/milestones) for current estimated dates and related issues / projects for planned work. 4 | 5 | ## Grafana Version Compatibility: 6 | **Druid plugin version 0.0.3 and below are supported for Grafana: 3.x.x** 7 | 8 | **Druid plugin 0.0.4 and above are supported for Grafana: 4.x.x** 9 | 10 | # Grafana plugin for [Druid](http://druid.io/) real-time OLAP database 11 | 12 | ![Screenshot](https://raw.githubusercontent.com/grafana-druid-plugin/druidplugin/master/img/AddDataSource.png) 13 | ![Screenshot](https://raw.githubusercontent.com/grafana-druid-plugin/druidplugin/master/img/ListDataSource.png) 14 | ![Screenshot](https://raw.githubusercontent.com/grafana-druid-plugin/druidplugin/master/img/DruidPanel.png) 15 | 16 | ## Status 17 | 18 | This plugin is built on the top of an existing Druid plugin (https://github.com/grafana/grafana-plugins) which used to work on older Grafana versions. With the UI changes done on Grafana-3.0 the existing plugin stopped working. Lot of changes have been made to have it work on Grafana 3.0. It supports timeseries, group by, topN and Select queries. 19 | 20 | Lot of features might still not be implemented. Your contributions are welcome. 21 | 22 | ## Plugin development history 23 | 24 | This plugin was originally developed by Quantiply Corporation (Supported for Grafana versions < 2.5): https://github.com/grafana/grafana-plugins/tree/master/datasources/druid 25 | 26 | This plugin was further enhanced by Carl Bergquist (https://github.com/grafana/grafana/pull/3328) (to support it on Grafana version 2.5 & 2.6). 27 | 28 | I cloned the source from the Pull Request by Carl Bergquist and changed the plugin to have it work on Grafana-3.0. 29 | 30 | All the credits for the original code and enahcement to 2.5 goes to Quantiply and Carl Bergquist. 31 | 32 | Opensourcing all the changes done to the plugin to support Grafana-3.0. -------------------------------------------------------------------------------- /dist/config_ctrl.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export declare class DruidConfigCtrl { 3 | static templateUrl: string; 4 | current: any; 5 | constructor($scope: any); 6 | } 7 | -------------------------------------------------------------------------------- /dist/config_ctrl.js: -------------------------------------------------------------------------------- 1 | System.register([], function (exports_1, context_1) { 2 | "use strict"; 3 | var DruidConfigCtrl; 4 | var __moduleName = context_1 && context_1.id; 5 | return { 6 | setters: [], 7 | execute: function () { 8 | DruidConfigCtrl = (function () { 9 | function DruidConfigCtrl($scope) { 10 | } 11 | DruidConfigCtrl.templateUrl = 'partials/config.html'; 12 | return DruidConfigCtrl; 13 | }()); 14 | exports_1("DruidConfigCtrl", DruidConfigCtrl); 15 | } 16 | }; 17 | }); 18 | //# sourceMappingURL=config_ctrl.js.map -------------------------------------------------------------------------------- /dist/config_ctrl.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"config_ctrl.js","sourceRoot":"","sources":["config_ctrl.ts"],"names":[],"mappings":";;;;;;;;gBAME,yBAAY,MAAM;gBAClB,CAAC;gBAJM,2BAAW,GAAG,sBAAsB,CAAC;gBAK9C,sBAAC;aAAA,AAND;;QAOA,CAAC"} -------------------------------------------------------------------------------- /dist/config_ctrl.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export class DruidConfigCtrl { 4 | static templateUrl = 'partials/config.html'; 5 | current: any; 6 | 7 | constructor($scope) { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /dist/css/query_editor.css: -------------------------------------------------------------------------------- 1 | .min-width-10 { 2 | min-width: 10rem; 3 | } 4 | 5 | .min-width-12 { 6 | min-width: 12rem; 7 | } 8 | 9 | .min-width-20 { 10 | min-width: 20rem; 11 | } 12 | 13 | .gf-form-select-wrapper select.gf-form-input { 14 | height: 2.64rem; 15 | } 16 | 17 | .gf-form-select-wrapper--caret-indent.gf-form-select-wrapper::after { 18 | right: 0.775rem 19 | } 20 | -------------------------------------------------------------------------------- /dist/datasource.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import * as Druid from 'druid.d'; 3 | export default class DruidDatasource { 4 | id: number; 5 | name: string; 6 | url: string; 7 | q: any; 8 | backendSrv: any; 9 | templateSrv: any; 10 | basicAuth: any; 11 | supportMetrics: any; 12 | periodGranularity: any; 13 | GRANULARITIES: any[][]; 14 | filterTemplateExpanders: { 15 | "selector": string[]; 16 | "regex": string[]; 17 | "javascript": string[]; 18 | "search": any[]; 19 | }; 20 | constructor(instanceSettings: any, $q: any, backendSrv: any, templateSrv: any); 21 | query(options: any): any; 22 | doQuery(from: any, to: any, granularity: any, target: any): any; 23 | splitCardinalityFields(aggregator: any): any; 24 | selectQuery(datasource: string, intervals: Array, granularity: Druid.Granularity, dimensions: Array, metric: Array, filters: Array, selectThreshold: Object): any; 25 | timeSeriesQuery(datasource: string, intervals: Array, granularity: Druid.Granularity, filters: Array, aggregators: Object, postAggregators: Object): any; 26 | topNQuery(datasource: string, intervals: Array, granularity: Druid.Granularity, filters: Array, aggregators: Object, postAggregators: Object, threshold: number, metric: string | Object, dimension: string | Object): any; 27 | groupByQuery(datasource: string, intervals: Array, granularity: Druid.Granularity, filters: Array, aggregators: Object, postAggregators: Object, groupBy: Array, limitSpec: Druid.LimitSpec): any; 28 | druidQuery(query: Druid.AbstractDruidQuery): any; 29 | getLimitSpec(limitNum: any, orderBy: any): { 30 | "type": string; 31 | "limit": any; 32 | "columns": any; 33 | }; 34 | testDatasource(): any; 35 | getDataSources(): any; 36 | getDimensionsAndMetrics(datasource: any): any; 37 | getFilterValues(target: any, panelRange: any, query: any): any; 38 | get(relativeUrl: any, params?: any): any; 39 | buildFilterTree(filters: any): Druid.DruidFilter; 40 | getQueryIntervals(from: any, to: any): string[]; 41 | getMetricNames(aggregators: any, postAggregators: any): any; 42 | formatTimestamp(ts: any): number; 43 | convertTimeSeriesData(md: any, metrics: any): any; 44 | getGroupName(groupBy: any, metric: any): any; 45 | convertTopNData(md: any, dimension: any, metric: any): any; 46 | convertGroupByData(md: any, groupBy: any, metrics: any): any; 47 | convertSelectData(data: any): any; 48 | dateToMoment(date: any, roundUp: any): any; 49 | computeGranularity(from: any, to: any, maxDataPoints: any): any; 50 | roundUpStartTime(from: any, granularity: any): any; 51 | replaceTemplateValues(obj: any, attrList: any): any; 52 | } 53 | -------------------------------------------------------------------------------- /dist/datasource.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"datasource.js","sourceRoot":"","sources":["datasource.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/datasource.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import _ from 'lodash'; 4 | import moment from 'moment'; 5 | import * as dateMath from 'app/core/utils/datemath'; 6 | import * as Druid from 'druid.d' 7 | 8 | const DRUID_DATASOURCE_PATH = '/druid/v2/datasources'; 9 | 10 | export default class DruidDatasource { 11 | id: number; 12 | name: string; 13 | url: string; 14 | q: any; 15 | backendSrv: any; 16 | templateSrv: any; 17 | basicAuth: any; 18 | supportMetrics: any; 19 | periodGranularity: any; 20 | GRANULARITIES = [ 21 | ['second', moment.duration(1, 'second')], 22 | ['minute', moment.duration(1, 'minute')], 23 | ['fifteen_minute', moment.duration(15, 'minute')], 24 | ['thirty_minute', moment.duration(30, 'minute')], 25 | ['hour', moment.duration(1, 'hour')], 26 | ['day', moment.duration(1, 'day')], 27 | ['week', moment.duration(1, 'week')], 28 | ['month', moment.duration(1, 'month')], 29 | ['quarter', moment.duration(1, 'quarter')], 30 | ['year', moment.duration(1, 'year')] 31 | ]; 32 | filterTemplateExpanders = { 33 | "selector": ['value'], 34 | "regex": ['pattern'], 35 | "javascript": ['function'], 36 | "search": [] 37 | }; 38 | 39 | 40 | /** @ngInject */ 41 | constructor(instanceSettings, $q, backendSrv, templateSrv) { 42 | this.name = instanceSettings.name; 43 | this.id = instanceSettings.id; 44 | this.url = instanceSettings.url; 45 | this.backendSrv = backendSrv; 46 | this.q = $q; 47 | this.templateSrv = templateSrv; 48 | this.basicAuth = instanceSettings.basicAuth; 49 | instanceSettings.jsonData = instanceSettings.jsonData || {}; 50 | this.supportMetrics = true; 51 | this.periodGranularity = instanceSettings.jsonData.periodGranularity; 52 | } 53 | 54 | query(options) { 55 | const from = this.dateToMoment(options.range.from, false); 56 | const to = this.dateToMoment(options.range.to, true); 57 | 58 | let promises = options.targets.map(target => { 59 | if (target.hide === true || _.isEmpty(target.druidDS) || (_.isEmpty(target.aggregators) && target.queryType !== "select")) { 60 | const d = this.q.defer(); 61 | d.resolve([]); 62 | return d.promise; 63 | } 64 | const maxDataPointsByResolution = options.maxDataPoints; 65 | const maxDataPointsByConfig = target.maxDataPoints ? target.maxDataPoints : Number.MAX_VALUE; 66 | const maxDataPoints = Math.min(maxDataPointsByResolution, maxDataPointsByConfig); 67 | let granularity = target.shouldOverrideGranularity ? this.templateSrv.replace(target.customGranularity) : this.computeGranularity(from, to, maxDataPoints); 68 | //Round up to start of an interval 69 | //Width of bar chars in Grafana is determined by size of the smallest interval 70 | const roundedFrom = granularity === "all" ? from : this.roundUpStartTime(from, granularity); 71 | if (this.periodGranularity != "") { 72 | if (granularity === 'day') { 73 | granularity = { "type": "period", "period": "P1D", "timeZone": this.periodGranularity } 74 | } 75 | } 76 | return this.doQuery(roundedFrom, to, granularity, target); 77 | }); 78 | 79 | return this.q.all(promises).then(results => { 80 | return { data: _.flatten(results) }; 81 | }); 82 | } 83 | 84 | doQuery(from, to, granularity, target) { 85 | let datasource = target.druidDS; 86 | let filters = target.filters; 87 | let aggregators = target.aggregators.map(this.splitCardinalityFields); 88 | let postAggregators = target.postAggregators; 89 | let groupBy = _.map(target.groupBy, (e) => { return this.templateSrv.replace(e) }); 90 | let limitSpec = null; 91 | let metricNames = this.getMetricNames(aggregators, postAggregators); 92 | let intervals = this.getQueryIntervals(from, to); 93 | let promise = null; 94 | 95 | let selectMetrics = target.selectMetrics; 96 | let selectDimensions = target.selectDimensions; 97 | let selectThreshold = target.selectThreshold; 98 | if (!selectThreshold) { 99 | selectThreshold = 5; 100 | } 101 | 102 | if (target.queryType === 'topN') { 103 | let threshold = target.limit; 104 | let metric = target.druidMetric; 105 | let dimension = this.templateSrv.replace(target.dimension); 106 | promise = this.topNQuery(datasource, intervals, granularity, filters, aggregators, postAggregators, threshold, metric, dimension) 107 | .then(response => { 108 | return this.convertTopNData(response.data, dimension, metric); 109 | }); 110 | } 111 | else if (target.queryType === 'groupBy') { 112 | limitSpec = this.getLimitSpec(target.limit, target.orderBy); 113 | promise = this.groupByQuery(datasource, intervals, granularity, filters, aggregators, postAggregators, groupBy, limitSpec) 114 | .then(response => { 115 | return this.convertGroupByData(response.data, groupBy, metricNames); 116 | }); 117 | } 118 | else if (target.queryType === 'select') { 119 | promise = this.selectQuery(datasource, intervals, granularity, selectDimensions, selectMetrics, filters, selectThreshold); 120 | return promise.then(response => { 121 | return this.convertSelectData(response.data); 122 | }); 123 | } 124 | else { 125 | promise = this.timeSeriesQuery(datasource, intervals, granularity, filters, aggregators, postAggregators) 126 | .then(response => { 127 | return this.convertTimeSeriesData(response.data, metricNames); 128 | }); 129 | } 130 | /* 131 | At this point the promise will return an list of time series of this form 132 | [ 133 | { 134 | target: , 135 | datapoints: [ 136 | [, ], 137 | ... 138 | ] 139 | }, 140 | ... 141 | ] 142 | 143 | Druid calculates metrics based on the intervals specified in the query but returns a timestamp rounded down. 144 | We need to adjust the first timestamp in each time series 145 | */ 146 | return promise.then(metrics => { 147 | let fromMs = this.formatTimestamp(from); 148 | metrics.forEach(metric => { 149 | if (!_.isEmpty(metric.datapoints[0]) && metric.datapoints[0][1] < fromMs) { 150 | metric.datapoints[0][1] = fromMs; 151 | } 152 | }); 153 | return metrics; 154 | }); 155 | }; 156 | 157 | splitCardinalityFields(aggregator) { 158 | if (aggregator.type === 'cardinality' && typeof aggregator.fieldNames === 'string') { 159 | aggregator.fieldNames = aggregator.fieldNames.split(',') 160 | } 161 | return aggregator; 162 | } 163 | 164 | selectQuery(datasource: string, intervals: Array, granularity: Druid.Granularity, 165 | dimensions: Array, metric: Array, filters: Array, 166 | selectThreshold: Object) { 167 | let query: Druid.DruidSelectQuery = { 168 | "queryType": "select", 169 | "dataSource": datasource, 170 | "granularity": granularity, 171 | "pagingSpec": { "pagingIdentifiers": {}, "threshold": selectThreshold }, 172 | "dimensions": dimensions, 173 | "metrics": metric, 174 | "intervals": intervals 175 | }; 176 | 177 | if (filters && filters.length > 0) { 178 | query.filter = this.buildFilterTree(filters); 179 | } 180 | 181 | return this.druidQuery(query); 182 | }; 183 | 184 | timeSeriesQuery(datasource: string, intervals: Array, granularity: Druid.Granularity, 185 | filters: Array, aggregators: Object, postAggregators: Object) { 186 | let query: Druid.DruidTimeSeriesQuery = { 187 | queryType: "timeseries", 188 | dataSource: datasource, 189 | granularity: granularity, 190 | aggregations: aggregators, 191 | postAggregations: postAggregators, 192 | intervals: intervals 193 | }; 194 | 195 | if (filters && filters.length > 0) { 196 | query.filter = this.buildFilterTree(filters); 197 | } 198 | 199 | return this.druidQuery(query); 200 | }; 201 | 202 | topNQuery(datasource: string, intervals: Array, granularity: Druid.Granularity, 203 | filters: Array, aggregators: Object, postAggregators: Object, 204 | threshold: number, metric: string | Object, dimension: string | Object) { 205 | const query: Druid.DruidTopNQuery = { 206 | queryType: "topN", 207 | dataSource: datasource, 208 | granularity: granularity, 209 | threshold: threshold, 210 | dimension: dimension, 211 | metric: metric, 212 | aggregations: aggregators, 213 | postAggregations: postAggregators, 214 | intervals: intervals 215 | }; 216 | 217 | if (filters && filters.length > 0) { 218 | query.filter = this.buildFilterTree(filters); 219 | } 220 | 221 | return this.druidQuery(query); 222 | }; 223 | 224 | groupByQuery(datasource: string, intervals: Array, granularity: Druid.Granularity, 225 | filters: Array, aggregators: Object, postAggregators: Object, groupBy: Array, 226 | limitSpec: Druid.LimitSpec) { 227 | const query: Druid.DruidGroupByQuery = { 228 | queryType: "groupBy", 229 | dataSource: datasource, 230 | granularity: granularity, 231 | dimensions: groupBy, 232 | aggregations: aggregators, 233 | postAggregations: postAggregators, 234 | intervals: intervals, 235 | limitSpec: limitSpec, 236 | }; 237 | 238 | if (filters && filters.length > 0) { 239 | query.filter = this.buildFilterTree(filters); 240 | } 241 | 242 | return this.druidQuery(query); 243 | }; 244 | 245 | druidQuery(query: Druid.AbstractDruidQuery) { 246 | const options = { 247 | method: 'POST', 248 | url: this.url + '/druid/v2/', 249 | data: query 250 | }; 251 | return this.backendSrv.datasourceRequest(options); 252 | }; 253 | 254 | getLimitSpec(limitNum, orderBy) { 255 | return { 256 | "type": "default", 257 | "limit": limitNum, 258 | "columns": !orderBy ? null : orderBy.map(col => { 259 | return { "dimension": col, "direction": "DESCENDING" }; 260 | }) 261 | }; 262 | } 263 | 264 | testDatasource() { 265 | return this.get(DRUID_DATASOURCE_PATH).then(() => { 266 | return { status: "success", message: "Druid Data source is working", title: "Success" }; 267 | }); 268 | } 269 | 270 | //Get list of available datasources 271 | getDataSources() { 272 | return this.get(DRUID_DATASOURCE_PATH).then(response => { 273 | return response.data; 274 | }); 275 | }; 276 | 277 | getDimensionsAndMetrics(datasource) { 278 | return this.get(DRUID_DATASOURCE_PATH + datasource).then(response => { 279 | return response.data; 280 | }); 281 | }; 282 | 283 | getFilterValues(target, panelRange, query) { 284 | const topNquery: any = { 285 | "queryType": "topN", 286 | "dataSource": target.druidDS, 287 | "granularity": 'all', 288 | "threshold": 10, 289 | "dimension": target.currentFilter.dimension, 290 | "metric": "count", 291 | "aggregations": [{ "type": "count", "name": "count" }], 292 | "intervals": this.getQueryIntervals(panelRange.from, panelRange.to) 293 | }; 294 | 295 | let filters = []; 296 | if (target.filters) { 297 | filters = 298 | filters = _.cloneDeep(target.filters); 299 | } 300 | filters.push({ 301 | "type": "search", 302 | "dimension": target.currentFilter.dimension, 303 | "query": { 304 | "type": "insensitive_contains", 305 | "value": query 306 | } 307 | }); 308 | topNquery.filter = this.buildFilterTree(filters); 309 | 310 | return this.druidQuery(topNquery); 311 | }; 312 | 313 | get(relativeUrl, params?) { 314 | return this.backendSrv.datasourceRequest({ 315 | method: 'GET', 316 | url: this.url + relativeUrl, 317 | params: params, 318 | }); 319 | }; 320 | 321 | buildFilterTree(filters): Druid.DruidFilter { 322 | //Do template variable replacement 323 | const replacedFilters = filters.map(filter => { 324 | return this.replaceTemplateValues(filter, this.filterTemplateExpanders[filter.type]); 325 | }) 326 | .map(filter => { 327 | const finalFilter = _.omit(filter, 'negate'); 328 | if (filter.negate) { 329 | return { "type": "not", "field": finalFilter }; 330 | } 331 | return finalFilter; 332 | }); 333 | if (replacedFilters) { 334 | if (replacedFilters.length === 1) { 335 | return replacedFilters[0]; 336 | } 337 | return { 338 | "type": "and", 339 | "fields": replacedFilters 340 | }; 341 | } 342 | return null; 343 | } 344 | 345 | getQueryIntervals(from, to) { 346 | return [from.toISOString() + '/' + to.toISOString()]; 347 | } 348 | 349 | getMetricNames(aggregators, postAggregators) { 350 | const displayAggs = _.filter(aggregators, agg => { 351 | return agg.type !== 'approxHistogramFold' && agg.hidden != true; 352 | }); 353 | return _.union(_.map(displayAggs, 'name'), _.map(postAggregators, 'name')); 354 | } 355 | 356 | formatTimestamp(ts) { 357 | return moment(ts).format('X') * 1000; 358 | } 359 | 360 | convertTimeSeriesData(md, metrics) { 361 | return metrics.map(metric => { 362 | return { 363 | target: metric, 364 | datapoints: md.map(item => { 365 | return [ 366 | item.result[metric], 367 | this.formatTimestamp(item.timestamp) 368 | ]; 369 | }) 370 | }; 371 | }); 372 | } 373 | 374 | getGroupName(groupBy, metric) { 375 | return groupBy.map(dim => { 376 | return metric.event[dim]; 377 | }) 378 | .join("-"); 379 | } 380 | 381 | convertTopNData(md, dimension, metric) { 382 | /* 383 | Druid topN results look like this: 384 | [ 385 | { 386 | "timestamp": "ts1", 387 | "result": [ 388 | {"": d1, "": mv1}, 389 | {"": d2, "": mv2} 390 | ] 391 | }, 392 | { 393 | "timestamp": "ts2", 394 | "result": [ 395 | {"": d1, "": mv3}, 396 | {"": d2, "": mv4} 397 | ] 398 | }, 399 | ... 400 | ] 401 | */ 402 | 403 | /* 404 | First, we need make sure that the result for each 405 | timestamp contains entries for all distinct dimension values 406 | in the entire list of results. 407 | 408 | Otherwise, if we do a stacked bar chart, Grafana doesn't sum 409 | the metrics correctly. 410 | */ 411 | 412 | //Get the list of all distinct dimension values for the entire result set 413 | const dVals = md.reduce((dValsSoFar, tsItem) => { 414 | const dValsForTs = _.map(tsItem.result, dimension); 415 | return _.union(dValsSoFar, dValsForTs); 416 | }, {}); 417 | 418 | //Add null for the metric for any missing dimension values per timestamp result 419 | md.forEach(tsItem => { 420 | const dValsPresent = _.map(tsItem.result, dimension); 421 | const dValsMissing = _.difference(dVals, dValsPresent); 422 | dValsMissing.forEach(dVal => { 423 | const nullPoint = {}; 424 | nullPoint[dimension] = dVal; 425 | nullPoint[metric] = null; 426 | tsItem.result.push(nullPoint); 427 | }); 428 | return tsItem; 429 | }); 430 | 431 | //Re-index the results by dimension value instead of time interval 432 | const mergedData = md.map(item => { 433 | /* 434 | This first map() transforms this into a list of objects 435 | where the keys are dimension values 436 | and the values are [metricValue, unixTime] so that we get this: 437 | [ 438 | { 439 | "d1": [mv1, ts1], 440 | "d2": [mv2, ts1] 441 | }, 442 | { 443 | "d1": [mv3, ts2], 444 | "d2": [mv4, ts2] 445 | }, 446 | ... 447 | ] 448 | */ 449 | const timestamp = this.formatTimestamp(item.timestamp); 450 | const keys = _.map(item.result, dimension); 451 | const vals = _.map(item.result, metric).map(val => { return [val, timestamp]; }); 452 | return _.zipObject(keys, vals); 453 | }) 454 | .reduce((prev, curr) => { 455 | /* 456 | Reduce() collapses all of the mapped objects into a single 457 | object. The keys are dimension values 458 | and the values are arrays of all the values for the same key. 459 | The _.assign() function merges objects together and it's callback 460 | gets invoked for every key,value pair in the source (2nd argument). 461 | Since our initial value for reduce() is an empty object, 462 | the _.assign() callback will get called for every new val 463 | that we add to the final object. 464 | */ 465 | return _.assignWith(prev, curr, (pVal, cVal) => { 466 | if (pVal) { 467 | pVal.push(cVal); 468 | return pVal; 469 | } 470 | return [cVal]; 471 | }); 472 | }, {}); 473 | 474 | //Convert object keyed by dimension values into an array 475 | //of objects {target: , datapoints: } 476 | return _.map(mergedData, (vals, key) => { 477 | return { 478 | target: key, 479 | datapoints: vals 480 | }; 481 | }); 482 | } 483 | 484 | convertGroupByData(md, groupBy, metrics) { 485 | const mergedData = md.map(item => { 486 | /* 487 | The first map() transforms the list Druid events into a list of objects 488 | with keys of the form ":" and values 489 | of the form [metricValue, unixTime] 490 | */ 491 | const groupName = this.getGroupName(groupBy, item); 492 | const keys = metrics.map(metric => { 493 | return groupName + ":" + metric; 494 | }); 495 | const vals = metrics.map(metric => { 496 | return [ 497 | item.event[metric], 498 | this.formatTimestamp(item.timestamp) 499 | ]; 500 | }); 501 | return _.zipObject(keys, vals); 502 | }) 503 | .reduce((prev, curr) => { 504 | /* 505 | Reduce() collapses all of the mapped objects into a single 506 | object. The keys are still of the form ":" 507 | and the values are arrays of all the values for the same key. 508 | The _.assign() function merges objects together and it's callback 509 | gets invoked for every key,value pair in the source (2nd argument). 510 | Since our initial value for reduce() is an empty object, 511 | the _.assign() callback will get called for every new val 512 | that we add to the final object. 513 | */ 514 | return _.assignWith(prev, curr, (pVal, cVal) => { 515 | if (pVal) { 516 | pVal.push(cVal); 517 | return pVal; 518 | } 519 | return [cVal]; 520 | }); 521 | }, {}); 522 | 523 | return _.map(mergedData, (vals, key) => { 524 | /* 525 | Second map converts the aggregated object into an array 526 | */ 527 | return { 528 | target: key, 529 | datapoints: vals 530 | }; 531 | }); 532 | } 533 | 534 | convertSelectData(data) { 535 | const resultList = _.map(data, "result"); 536 | const eventsList = _.map(resultList, "events"); 537 | const eventList = _.flatten(eventsList); 538 | const result = {}; 539 | for (let i = 0; i < eventList.length; i++) { 540 | const event = eventList[i].event; 541 | const timestamp = event.timestamp; 542 | if (_.isEmpty(timestamp)) { 543 | continue; 544 | } 545 | for (const key in event) { 546 | if (key !== "timestamp") { 547 | if (!result[key]) { 548 | result[key] = { "target": key, "datapoints": [] }; 549 | } 550 | result[key].datapoints.push([event[key], timestamp]); 551 | } 552 | } 553 | } 554 | return _.values(result); 555 | } 556 | 557 | dateToMoment(date, roundUp) { 558 | if (date === 'now') { 559 | return moment(); 560 | } 561 | date = dateMath.parse(date, roundUp); 562 | return moment(date.valueOf()); 563 | } 564 | 565 | computeGranularity(from, to, maxDataPoints) { 566 | const intervalSecs = to.unix() - from.unix(); 567 | /* 568 | Find the smallest granularity for which there 569 | will be fewer than maxDataPoints 570 | */ 571 | const granularityEntry = _.find(this.GRANULARITIES, gEntry => { 572 | return Math.ceil(intervalSecs / gEntry[1].asSeconds()) <= maxDataPoints; 573 | }); 574 | 575 | return granularityEntry[0]; 576 | } 577 | 578 | roundUpStartTime(from, granularity) { 579 | const duration = _.find(this.GRANULARITIES, gEntry => { 580 | return gEntry[0] === granularity; 581 | })[1]; 582 | let rounded = null; 583 | if (granularity === 'day') { 584 | rounded = moment(+from).startOf('day'); 585 | } else { 586 | rounded = moment(Math.ceil((+from) / (+duration)) * (+duration)); 587 | } 588 | return rounded; 589 | } 590 | 591 | replaceTemplateValues(obj, attrList) { 592 | const substitutedVals = attrList.map(attr => { 593 | return this.templateSrv.replace(obj[attr]); 594 | }); 595 | return _.assign(_.clone(obj, true), _.zipObject(attrList, substitutedVals)); 596 | } 597 | } 598 | -------------------------------------------------------------------------------- /dist/druid.d.ts: -------------------------------------------------------------------------------- 1 | export interface AbstractDruidQuery { 2 | dataSource: string; 3 | intervals: Array; 4 | granularity: Granularity; 5 | queryType: string; 6 | filter?: DruidFilter; 7 | aggregations?: Object; 8 | postAggregations?: Object; 9 | context?: Object; 10 | } 11 | 12 | export enum Granularity { 13 | all = 'all', 14 | none = 'none', 15 | second = 'second', 16 | minute = 'minute', 17 | fifteen_minute = 'fifteen_minute', 18 | thirty_minute = 'thirty_minute', 19 | hour = 'hour', 20 | day = 'day', 21 | week = 'week', 22 | month = 'month', 23 | quarter = 'quarter', 24 | year = 'year' 25 | } 26 | 27 | export interface LimitSpec { 28 | type: 'default'; 29 | limit: number; 30 | columns: Array; 31 | } 32 | 33 | export interface OrderByColumnSpec { 34 | dimension: string; 35 | direction: 'ascending' | 'descending'; 36 | dimensionOrder: 'lexicographic' | 'alphanumeric' | 'strlen' | 'numeric'; 37 | } 38 | 39 | export interface DruidGroupByQuery extends AbstractDruidQuery { 40 | queryType: 'groupBy'; 41 | dimensions: Array; 42 | limitSpec?: LimitSpec; 43 | having?: Object; 44 | } 45 | 46 | export interface DruidTimeSeriesQuery extends AbstractDruidQuery { 47 | queryType: 'timeseries'; 48 | descending?: 'true' | 'false'; 49 | } 50 | 51 | export interface DruidTopNQuery extends AbstractDruidQuery { 52 | queryType: 'topN'; 53 | dimension: string | Object; 54 | threshold: number; 55 | metric: string | Object; 56 | } 57 | 58 | export interface DruidSelectQuery extends AbstractDruidQuery { 59 | pagingSpec: { 60 | pagingIdentifiers: {}; 61 | threshold: Object; 62 | }; 63 | dimensions: Array; 64 | metrics: Array; 65 | } 66 | 67 | export interface DruidFilterLogical { 68 | type: 'or' | 'and'; 69 | fields: DruidFilter[]; 70 | } 71 | 72 | export interface DruidFilterNot { 73 | type: 'not'; 74 | field: DruidFilter; 75 | } 76 | 77 | export interface DruidFilterSelect { 78 | type: 'selector'; 79 | dimension: string; 80 | value: string; 81 | } 82 | 83 | export interface DruidFilterIn { 84 | type: string; 85 | dimension: string; 86 | values: Array; 87 | } 88 | 89 | export interface DruidFilterRegex { 90 | type: 'regex'; 91 | dimension: string; 92 | pattern: string; 93 | } 94 | 95 | export type DruidFilter = DruidFilterLogical | DruidFilterSelect | DruidFilterRegex | DruidFilterIn | DruidFilterNot; -------------------------------------------------------------------------------- /dist/img/druid_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana-druid-plugin/druidplugin/33af5d544755a7bbcf7d72fcbd9673ef66a2840c/dist/img/druid_logo.png -------------------------------------------------------------------------------- /dist/module.d.ts: -------------------------------------------------------------------------------- 1 | import DruidDatasource from './datasource'; 2 | import { DruidQueryCtrl } from './query_ctrl'; 3 | import { DruidConfigCtrl } from './config_ctrl'; 4 | export { DruidDatasource as Datasource, DruidQueryCtrl as QueryCtrl, DruidConfigCtrl as ConfigCtrl }; 5 | -------------------------------------------------------------------------------- /dist/module.js: -------------------------------------------------------------------------------- 1 | System.register(["./datasource", "./query_ctrl", "./config_ctrl"], function (exports_1, context_1) { 2 | "use strict"; 3 | var datasource_1, query_ctrl_1, config_ctrl_1, DruidAnnotationsQueryCtrl; 4 | var __moduleName = context_1 && context_1.id; 5 | return { 6 | setters: [ 7 | function (datasource_1_1) { 8 | datasource_1 = datasource_1_1; 9 | }, 10 | function (query_ctrl_1_1) { 11 | query_ctrl_1 = query_ctrl_1_1; 12 | }, 13 | function (config_ctrl_1_1) { 14 | config_ctrl_1 = config_ctrl_1_1; 15 | } 16 | ], 17 | execute: function () { 18 | exports_1("Datasource", datasource_1.default); 19 | exports_1("QueryCtrl", query_ctrl_1.DruidQueryCtrl); 20 | exports_1("ConfigCtrl", config_ctrl_1.DruidConfigCtrl); 21 | DruidAnnotationsQueryCtrl = (function () { 22 | function DruidAnnotationsQueryCtrl() { 23 | } 24 | DruidAnnotationsQueryCtrl.templateUrl = 'partials/annotations.editor.html'; 25 | return DruidAnnotationsQueryCtrl; 26 | }()); 27 | } 28 | }; 29 | }); 30 | //# sourceMappingURL=module.js.map -------------------------------------------------------------------------------- /dist/module.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"module.js","sourceRoot":"","sources":["module.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;oCAAO,oBAAe;mCACd,2BAAc;oCACd,6BAAe;;gBAEvB;gBAEA,CAAC;gBADQ,qCAAW,GAAG,kCAAkC,CAAC;gBAC1D,gCAAC;aAAA,AAFD;QASA,CAAC"} -------------------------------------------------------------------------------- /dist/module.ts: -------------------------------------------------------------------------------- 1 | import DruidDatasource from './datasource'; 2 | import {DruidQueryCtrl} from './query_ctrl'; 3 | import {DruidConfigCtrl} from './config_ctrl'; 4 | 5 | class DruidAnnotationsQueryCtrl { 6 | static templateUrl = 'partials/annotations.editor.html'; 7 | } 8 | 9 | export { 10 | DruidDatasource as Datasource, 11 | DruidQueryCtrl as QueryCtrl, 12 | DruidConfigCtrl as ConfigCtrl 13 | }; 14 | -------------------------------------------------------------------------------- /dist/partials/config.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Druid Settings

4 |
5 | Period Granularity 6 |
7 | 119 | This enables the to query hourly data in different timezone. 120 |
121 |
122 |
123 | 124 | 125 | -------------------------------------------------------------------------------- /dist/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "datasource", 3 | "name": "Druid", 4 | "id": "paypal-druid-datasource", 5 | 6 | "metrics": true, 7 | 8 | "info": { 9 | "version": "0.0.6", 10 | "author": { 11 | "name": "Abhishek Sant", 12 | "url": "https://github.com/grafana-druid-plugin/druidplugin" 13 | }, 14 | "links":[ 15 | {"name": "GitHub", "url": "https://github.com/grafana-druid-plugin/druidplugin"} 16 | ], 17 | "logos": { 18 | "small": "img/druid_logo.png", 19 | "large": "img/druid_logo.png" 20 | }, 21 | "dependencies": { 22 | "grafanaVersion": "4.x.x", 23 | "plugins": [ ] 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /dist/query_ctrl.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { QueryCtrl } from 'app/plugins/sdk'; 3 | import './css/query_editor.css!'; 4 | export declare class DruidQueryCtrl extends QueryCtrl { 5 | static templateUrl: string; 6 | errors: any; 7 | addFilterMode: boolean; 8 | addAggregatorMode: boolean; 9 | addPostAggregatorMode: boolean; 10 | addDimensionsMode: boolean; 11 | addMetricsMode: boolean; 12 | listDataSources: any; 13 | getDimensionsAndMetrics: any; 14 | getMetrics: any; 15 | getMetricsPlusDimensions: any; 16 | getDimensions: any; 17 | getFilterValues: any; 18 | queryTypes: any; 19 | filterTypes: any; 20 | aggregatorTypes: any; 21 | postAggregatorTypes: any; 22 | arithmeticPostAggregator: any; 23 | customGranularity: any; 24 | target: any; 25 | datasource: any; 26 | queryTypeValidators: { 27 | "timeseries": any; 28 | "groupBy": any; 29 | "topN": any; 30 | "select": any; 31 | }; 32 | filterValidators: { 33 | "selector": any; 34 | "regex": any; 35 | "javascript": any; 36 | }; 37 | aggregatorValidators: { 38 | "count": (target: any) => string; 39 | "cardinality": any; 40 | "longSum": any; 41 | "doubleSum": any; 42 | "approxHistogramFold": any; 43 | "hyperUnique": any; 44 | "thetaSketch": any; 45 | }; 46 | postAggregatorValidators: { 47 | "arithmetic": any; 48 | "max": any; 49 | "min": any; 50 | "quantile": any; 51 | }; 52 | arithmeticPostAggregatorFns: { 53 | '+': any; 54 | '-': any; 55 | '*': any; 56 | '/': any; 57 | }; 58 | defaultQueryType: string; 59 | defaultFilterType: string; 60 | defaultAggregatorType: string; 61 | defaultPostAggregator: { 62 | type: string; 63 | 'fn': string; 64 | }; 65 | customGranularities: string[]; 66 | defaultCustomGranularity: string; 67 | defaultSelectDimension: string; 68 | defaultSelectMetric: string; 69 | defaultLimit: number; 70 | constructor($scope: any, $injector: any, $q: any); 71 | cachedAndCoalesced(ioFn: any, $scope: any, cacheName: any): any; 72 | targetBlur(): void; 73 | addFilter(): void; 74 | editFilter(index: any): void; 75 | removeFilter(index: any): void; 76 | clearCurrentFilter(): void; 77 | addSelectDimensions(): void; 78 | removeSelectDimension(index: any): void; 79 | clearCurrentSelectDimension(): void; 80 | addSelectMetrics(): void; 81 | removeSelectMetric(index: any): void; 82 | clearCurrentSelectMetric(): void; 83 | addAggregator(): void; 84 | editAggregator(index: any): void; 85 | removeAggregator(index: any): void; 86 | clearCurrentAggregator(): void; 87 | addPostAggregator(): void; 88 | removePostAggregator(index: any): void; 89 | clearCurrentPostAggregator(): void; 90 | isValidFilterType(type: any): any; 91 | isValidAggregatorType(type: any): any; 92 | isValidPostAggregatorType(type: any): any; 93 | isValidQueryType(type: any): any; 94 | isValidArithmeticPostAggregatorFn(fn: any): any; 95 | validateMaxDataPoints(target: any, errs: any): boolean; 96 | validateLimit(target: any, errs: any): boolean; 97 | validateOrderBy(target: any): boolean; 98 | validateGroupByQuery(target: any, errs: any): boolean; 99 | validateTopNQuery(target: any, errs: any): boolean; 100 | validateSelectQuery(target: any, errs: any): boolean; 101 | validateSelectorFilter(target: any): "Must provide dimension name for selector filter." | "Must provide dimension value for selector filter."; 102 | validateJavascriptFilter(target: any): "Must provide dimension name for javascript filter." | "Must provide func value for javascript filter."; 103 | validateRegexFilter(target: any): "Must provide dimension name for regex filter." | "Must provide pattern for regex filter."; 104 | validateCountAggregator(target: any): string; 105 | validateCardinalityAggregator(type: any, target: any): string; 106 | validateSimpleAggregator(type: any, target: any): string; 107 | validateApproxHistogramFoldAggregator(target: any): string; 108 | validateThetaSketchAggregator(target: any): string; 109 | validateSimplePostAggregator(type: any, target: any): string; 110 | validateMaxPostAggregator(target: any): string; 111 | validateMinPostAggregator(target: any): string; 112 | validateQuantilePostAggregator(target: any): string; 113 | validateArithmeticPostAggregator(target: any): "Must provide an output name for arithmetic post aggregator." | "Must provide a function for arithmetic post aggregator." | "Invalid arithmetic function" | "Must provide a list of fields for arithmetic post aggregator." | "Must provide at least two fields for arithmetic post aggregator."; 114 | validateTarget(): any; 115 | } 116 | -------------------------------------------------------------------------------- /dist/query_ctrl.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"query_ctrl.js","sourceRoot":"","sources":["query_ctrl.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/query_ctrl.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import _ from 'lodash'; 4 | import { QueryCtrl } from 'app/plugins/sdk'; 5 | import './css/query_editor.css!'; 6 | 7 | export class DruidQueryCtrl extends QueryCtrl { 8 | static templateUrl = 'partials/query.editor.html'; 9 | 10 | errors: any; 11 | addFilterMode: boolean; 12 | addAggregatorMode: boolean; 13 | addPostAggregatorMode: boolean; 14 | addDimensionsMode: boolean; 15 | addMetricsMode: boolean; 16 | listDataSources: any; 17 | getDimensionsAndMetrics: any; 18 | getMetrics: any; 19 | getMetricsPlusDimensions: any; 20 | getDimensions: any; 21 | getFilterValues: any; 22 | queryTypes: any; 23 | filterTypes: any; 24 | aggregatorTypes: any; 25 | postAggregatorTypes: any; 26 | arithmeticPostAggregator: any; 27 | customGranularity: any; 28 | target: any; 29 | datasource: any; 30 | 31 | queryTypeValidators = { 32 | "timeseries": _.noop.bind(this), 33 | "groupBy": this.validateGroupByQuery.bind(this), 34 | "topN": this.validateTopNQuery.bind(this), 35 | "select": this.validateSelectQuery.bind(this) 36 | }; 37 | filterValidators = { 38 | "selector": this.validateSelectorFilter.bind(this), 39 | "regex": this.validateRegexFilter.bind(this), 40 | "javascript": this.validateJavascriptFilter.bind(this) 41 | }; 42 | aggregatorValidators = { 43 | "count": this.validateCountAggregator, 44 | "cardinality": _.partial(this.validateCardinalityAggregator.bind(this), 'cardinality'), 45 | "longSum": _.partial(this.validateSimpleAggregator.bind(this), 'longSum'), 46 | "doubleSum": _.partial(this.validateSimpleAggregator.bind(this), 'doubleSum'), 47 | "approxHistogramFold": this.validateApproxHistogramFoldAggregator.bind(this), 48 | "hyperUnique": _.partial(this.validateSimpleAggregator.bind(this), 'hyperUnique'), 49 | "thetaSketch": this.validateThetaSketchAggregator.bind(this) 50 | }; 51 | postAggregatorValidators = { 52 | "arithmetic": this.validateArithmeticPostAggregator.bind(this), 53 | "max": this.validateMaxPostAggregator.bind(this), 54 | "min": this.validateMinPostAggregator.bind(this), 55 | "quantile": this.validateQuantilePostAggregator.bind(this) 56 | }; 57 | 58 | arithmeticPostAggregatorFns = { '+': null, '-': null, '*': null, '/': null }; 59 | defaultQueryType = "timeseries"; 60 | defaultFilterType = "selector"; 61 | defaultAggregatorType = "count"; 62 | defaultPostAggregator = { type: 'arithmetic', 'fn': '+' }; 63 | customGranularities = ['second', 'minute', 'fifteen_minute', 'thirty_minute', 'hour', 'day', 'week', 'month', 'quarter', 'year', 'all']; 64 | defaultCustomGranularity = 'minute'; 65 | defaultSelectDimension = ""; 66 | defaultSelectMetric = ""; 67 | defaultLimit = 5; 68 | 69 | /** @ngInject **/ 70 | constructor($scope, $injector, $q) { 71 | super($scope, $injector); 72 | if (!this.target.queryType) { 73 | this.target.queryType = this.defaultQueryType; 74 | } 75 | 76 | this.queryTypes = _.keys(this.queryTypeValidators); 77 | this.filterTypes = _.keys(this.filterValidators); 78 | this.aggregatorTypes = _.keys(this.aggregatorValidators); 79 | this.postAggregatorTypes = _.keys(this.postAggregatorValidators); 80 | this.arithmeticPostAggregator = _.keys(this.arithmeticPostAggregatorFns); 81 | this.customGranularity = this.customGranularities; 82 | 83 | this.errors = this.validateTarget(); 84 | if (!this.target.currentFilter) { 85 | this.clearCurrentFilter(); 86 | } 87 | 88 | if (!this.target.currentSelect) { 89 | this.target.currentSelect = {}; 90 | this.clearCurrentSelectDimension(); 91 | this.clearCurrentSelectMetric(); 92 | } 93 | 94 | if (!this.target.currentAggregator) { 95 | this.clearCurrentAggregator(); 96 | } 97 | 98 | if (!this.target.currentPostAggregator) { 99 | this.clearCurrentPostAggregator(); 100 | } 101 | 102 | if (!this.target.customGranularity) { 103 | this.target.customGranularity = this.defaultCustomGranularity; 104 | } 105 | 106 | if (!this.target.limit) { 107 | this.target.limit = this.defaultLimit; 108 | } 109 | 110 | // needs to be defined here as it is called from typeahead 111 | this.listDataSources = (query, callback) => { 112 | this.datasource.getDataSources() 113 | .then(callback); 114 | }; 115 | 116 | this.getDimensions = (query, callback) => { 117 | return this.datasource.getDimensionsAndMetrics(this.target.druidDS) 118 | .then(function (dimsAndMetrics) { 119 | callback(dimsAndMetrics.dimensions); 120 | }); 121 | }; 122 | 123 | this.getMetrics = (query, callback) => { 124 | return this.datasource.getDimensionsAndMetrics(this.target.druidDS) 125 | .then(function (dimsAndMetrics) { 126 | callback(dimsAndMetrics.metrics); 127 | }); 128 | }; 129 | 130 | this.getMetricsPlusDimensions = (query, callback) => { 131 | return this.datasource.getDimensionsAndMetrics(this.target.druidDS) 132 | .then(function (dimsAndMetrics) { 133 | callback([].concat(dimsAndMetrics.metrics).concat(dimsAndMetrics.dimensions)); 134 | }); 135 | }; 136 | 137 | this.getDimensionsAndMetrics = (query, callback) => { 138 | this.datasource.getDimensionsAndMetrics(this.target.druidDS) 139 | .then(callback); 140 | }; 141 | 142 | this.getFilterValues = (query, callback) => { 143 | let dimension = this.target.currentFilter.dimension; 144 | this.datasource.getFilterValues(this.target, this.panelCtrl.range, query) 145 | .then(function (results) { 146 | callback(results.data[0].result.map(function (datum) { return datum[dimension]; })); 147 | }); 148 | }; 149 | 150 | //this.$on('typeahead-updated', function() { 151 | // $timeout(this.targetBlur); 152 | //}); 153 | } 154 | 155 | cachedAndCoalesced(ioFn, $scope, cacheName) { 156 | const promiseName = cacheName + "Promise"; 157 | if (!$scope[cacheName]) { 158 | if (!$scope[promiseName]) { 159 | $scope[promiseName] = ioFn() 160 | .then(function (result) { 161 | $scope[promiseName] = null; 162 | $scope[cacheName] = result; 163 | return $scope[cacheName]; 164 | }); 165 | } 166 | return $scope[promiseName]; 167 | } else { 168 | let deferred;// = $q.defer(); 169 | deferred.resolve($scope[cacheName]); 170 | return deferred.promise; 171 | } 172 | } 173 | 174 | targetBlur() { 175 | this.errors = this.validateTarget(); 176 | this.refresh(); 177 | } 178 | 179 | addFilter() { 180 | if (!this.addFilterMode) { 181 | //Enabling this mode will display the filter inputs 182 | this.addFilterMode = true; 183 | return; 184 | } 185 | 186 | if (!this.target.filters) { 187 | this.target.filters = []; 188 | } 189 | 190 | this.target.errors = this.validateTarget(); 191 | if (!this.target.errors.currentFilter) { 192 | //Add new filter to the list 193 | this.target.filters.push(this.target.currentFilter); 194 | this.clearCurrentFilter(); 195 | this.addFilterMode = false; 196 | } 197 | 198 | this.targetBlur(); 199 | } 200 | 201 | editFilter(index) { 202 | this.addFilterMode = true; 203 | const delFilter = this.target.filters.splice(index, 1); 204 | this.target.currentFilter = delFilter[0]; 205 | } 206 | 207 | removeFilter(index) { 208 | this.target.filters.splice(index, 1); 209 | this.targetBlur(); 210 | } 211 | 212 | clearCurrentFilter() { 213 | this.target.currentFilter = { type: this.defaultFilterType }; 214 | this.addFilterMode = false; 215 | this.targetBlur(); 216 | } 217 | 218 | addSelectDimensions() { 219 | if (!this.addDimensionsMode) { 220 | this.addDimensionsMode = true; 221 | return; 222 | } 223 | if (!this.target.selectDimensions) { 224 | this.target.selectDimensions = []; 225 | } 226 | this.target.selectDimensions.push(this.target.currentSelect.dimension); 227 | this.clearCurrentSelectDimension(); 228 | } 229 | 230 | removeSelectDimension(index) { 231 | this.target.selectDimensions.splice(index, 1); 232 | this.targetBlur(); 233 | } 234 | 235 | clearCurrentSelectDimension() { 236 | this.target.currentSelect.dimension = this.defaultSelectDimension; 237 | this.addDimensionsMode = false; 238 | this.targetBlur(); 239 | } 240 | 241 | addSelectMetrics() { 242 | if (!this.addMetricsMode) { 243 | this.addMetricsMode = true; 244 | return; 245 | } 246 | if (!this.target.selectMetrics) { 247 | this.target.selectMetrics = []; 248 | } 249 | this.target.selectMetrics.push(this.target.currentSelect.metric); 250 | this.clearCurrentSelectMetric(); 251 | } 252 | 253 | removeSelectMetric(index) { 254 | this.target.selectMetrics.splice(index, 1); 255 | this.targetBlur(); 256 | } 257 | 258 | clearCurrentSelectMetric() { 259 | this.target.currentSelect.metric = this.defaultSelectMetric; 260 | this.addMetricsMode = false; 261 | this.targetBlur(); 262 | } 263 | 264 | addAggregator() { 265 | if (!this.addAggregatorMode) { 266 | this.addAggregatorMode = true; 267 | return; 268 | } 269 | 270 | if (!this.target.aggregators) { 271 | this.target.aggregators = []; 272 | } 273 | 274 | this.target.errors = this.validateTarget(); 275 | if (!this.target.errors.currentAggregator) { 276 | //Add new aggregator to the list 277 | this.target.aggregators.push(this.target.currentAggregator); 278 | this.clearCurrentAggregator(); 279 | this.addAggregatorMode = false; 280 | } 281 | 282 | this.targetBlur(); 283 | } 284 | 285 | editAggregator(index) { 286 | this.addAggregatorMode = true; 287 | const delAggregator = this.target.aggregators.splice(index, 1); 288 | this.target.currentAggregator = delAggregator[0]; 289 | } 290 | removeAggregator(index) { 291 | this.target.aggregators.splice(index, 1); 292 | this.targetBlur(); 293 | } 294 | 295 | clearCurrentAggregator() { 296 | this.target.currentAggregator = { type: this.defaultAggregatorType }; 297 | this.addAggregatorMode = false; 298 | this.targetBlur(); 299 | } 300 | 301 | addPostAggregator() { 302 | if (!this.addPostAggregatorMode) { 303 | this.addPostAggregatorMode = true; 304 | return; 305 | } 306 | 307 | if (!this.target.postAggregators) { 308 | this.target.postAggregators = []; 309 | } 310 | 311 | this.target.errors = this.validateTarget(); 312 | if (!this.target.errors.currentPostAggregator) { 313 | //Add new post aggregator to the list 314 | this.target.postAggregators.push(this.target.currentPostAggregator); 315 | this.clearCurrentPostAggregator(); 316 | this.addPostAggregatorMode = false; 317 | } 318 | 319 | this.targetBlur(); 320 | } 321 | 322 | removePostAggregator(index) { 323 | this.target.postAggregators.splice(index, 1); 324 | this.targetBlur(); 325 | } 326 | 327 | clearCurrentPostAggregator() { 328 | this.target.currentPostAggregator = _.clone(this.defaultPostAggregator);; 329 | this.addPostAggregatorMode = false; 330 | this.targetBlur(); 331 | } 332 | 333 | isValidFilterType(type) { 334 | return _.has(this.filterValidators, type); 335 | } 336 | 337 | isValidAggregatorType(type) { 338 | return _.has(this.aggregatorValidators, type); 339 | } 340 | 341 | isValidPostAggregatorType(type) { 342 | return _.has(this.postAggregatorValidators, type); 343 | } 344 | 345 | isValidQueryType(type) { 346 | return _.has(this.queryTypeValidators, type); 347 | } 348 | 349 | isValidArithmeticPostAggregatorFn(fn) { 350 | return _.includes(this.arithmeticPostAggregator, fn); 351 | } 352 | 353 | validateMaxDataPoints(target, errs) { 354 | if (target.maxDataPoints) { 355 | const intMax = parseInt(target.maxDataPoints); 356 | if (isNaN(intMax) || intMax <= 0) { 357 | errs.maxDataPoints = "Must be a positive integer"; 358 | return false; 359 | } 360 | target.maxDataPoints = intMax; 361 | } 362 | return true; 363 | } 364 | 365 | validateLimit(target, errs) { 366 | if (!target.limit) { 367 | errs.limit = "Must specify a limit"; 368 | return false; 369 | } 370 | const intLimit = parseInt(target.limit); 371 | if (isNaN(intLimit)) { 372 | errs.limit = "Limit must be a integer"; 373 | return false; 374 | } 375 | target.limit = intLimit; 376 | return true; 377 | } 378 | 379 | validateOrderBy(target) { 380 | if (target.orderBy && !Array.isArray(target.orderBy)) { 381 | target.orderBy = target.orderBy.split(","); 382 | } 383 | return true; 384 | } 385 | 386 | validateGroupByQuery(target, errs) { 387 | if (target.groupBy && !Array.isArray(target.groupBy)) { 388 | target.groupBy = target.groupBy.split(","); 389 | } 390 | if (!target.groupBy) { 391 | errs.groupBy = "Must list dimensions to group by."; 392 | return false; 393 | } 394 | if (!this.validateLimit(target, errs) || !this.validateOrderBy(target)) { 395 | return false; 396 | } 397 | return true; 398 | } 399 | 400 | validateTopNQuery(target, errs) { 401 | if (!target.dimension) { 402 | errs.dimension = "Must specify a dimension"; 403 | return false; 404 | } 405 | if (!target.druidMetric) { 406 | errs.druidMetric = "Must specify a metric"; 407 | return false; 408 | } 409 | if (!this.validateLimit(target, errs)) { 410 | return false; 411 | } 412 | return true; 413 | } 414 | 415 | validateSelectQuery(target, errs) { 416 | if (!target.selectThreshold && target.selectThreshold <= 0) { 417 | errs.selectThreshold = "Must specify a positive number"; 418 | return false; 419 | } 420 | return true; 421 | } 422 | 423 | validateSelectorFilter(target) { 424 | if (!target.currentFilter.dimension) { 425 | return "Must provide dimension name for selector filter."; 426 | } 427 | if (!target.currentFilter.value) { 428 | // TODO Empty string is how you match null or empty in Druid 429 | return "Must provide dimension value for selector filter."; 430 | } 431 | return null; 432 | } 433 | 434 | validateJavascriptFilter(target) { 435 | if (!target.currentFilter.dimension) { 436 | return "Must provide dimension name for javascript filter."; 437 | } 438 | if (!target.currentFilter["function"]) { 439 | return "Must provide func value for javascript filter."; 440 | } 441 | return null; 442 | } 443 | 444 | validateRegexFilter(target) { 445 | if (!target.currentFilter.dimension) { 446 | return "Must provide dimension name for regex filter."; 447 | } 448 | if (!target.currentFilter.pattern) { 449 | return "Must provide pattern for regex filter."; 450 | } 451 | return null; 452 | } 453 | 454 | validateCountAggregator(target) { 455 | if (!target.currentAggregator.name) { 456 | return "Must provide an output name for count aggregator."; 457 | } 458 | return null; 459 | } 460 | 461 | validateCardinalityAggregator(type, target) { 462 | if (!target.currentAggregator.name) { 463 | return "Must provide an output name for " + type + " aggregator."; 464 | } 465 | 466 | return null; 467 | } 468 | 469 | validateSimpleAggregator(type, target) { 470 | if (!target.currentAggregator.name) { 471 | return "Must provide an output name for " + type + " aggregator."; 472 | } 473 | if (!target.currentAggregator.fieldName) { 474 | return "Must provide a metric name for " + type + " aggregator."; 475 | } 476 | //TODO - check that fieldName is a valid metric (exists and of correct type) 477 | return null; 478 | } 479 | 480 | validateApproxHistogramFoldAggregator(target) { 481 | const err = this.validateSimpleAggregator('approxHistogramFold', target); 482 | if (err) { return err; } 483 | //TODO - check that resolution and numBuckets are ints (if given) 484 | //TODO - check that lowerLimit and upperLimit are flots (if given) 485 | return null; 486 | } 487 | 488 | validateThetaSketchAggregator(target) { 489 | const err = this.validateSimpleAggregator('thetaSketch', target); 490 | if (err) { return err; } 491 | return null; 492 | } 493 | 494 | validateSimplePostAggregator(type, target) { 495 | if (!target.currentPostAggregator.name) { 496 | return "Must provide an output name for " + type + " post aggregator."; 497 | } 498 | if (!target.currentPostAggregator.fieldName) { 499 | return "Must provide an aggregator name for " + type + " post aggregator."; 500 | } 501 | //TODO - check that fieldName is a valid aggregation (exists and of correct type) 502 | return null; 503 | } 504 | 505 | validateMaxPostAggregator(target) { 506 | const err = this.validateSimplePostAggregator('max', target); 507 | if (err) { return err; } 508 | return null; 509 | } 510 | 511 | validateMinPostAggregator(target) { 512 | const err = this.validateSimplePostAggregator('min', target); 513 | if (err) { return err; } 514 | return null; 515 | } 516 | 517 | validateQuantilePostAggregator(target) { 518 | const err = this.validateSimplePostAggregator('quantile', target); 519 | if (err) { return err; } 520 | if (!target.currentPostAggregator.probability) { 521 | return "Must provide a probability for the quantile post aggregator."; 522 | } 523 | return null; 524 | } 525 | 526 | validateArithmeticPostAggregator(target) { 527 | if (!target.currentPostAggregator.name) { 528 | return "Must provide an output name for arithmetic post aggregator."; 529 | } 530 | if (!target.currentPostAggregator.fn) { 531 | return "Must provide a function for arithmetic post aggregator."; 532 | } 533 | if (!this.isValidArithmeticPostAggregatorFn(target.currentPostAggregator.fn)) { 534 | return "Invalid arithmetic function"; 535 | } 536 | if (!target.currentPostAggregator.fields) { 537 | return "Must provide a list of fields for arithmetic post aggregator."; 538 | } else { 539 | if (!Array.isArray(target.currentPostAggregator.fields)) { 540 | target.currentPostAggregator.fields = target.currentPostAggregator.fields 541 | .split(",") 542 | .map(function (f) { return f.trim(); }) 543 | .map(function (f) { return { type: "fieldAccess", fieldName: f }; }); 544 | } 545 | if (target.currentPostAggregator.fields.length < 2) { 546 | return "Must provide at least two fields for arithmetic post aggregator."; 547 | } 548 | } 549 | return null; 550 | } 551 | 552 | validateTarget() { 553 | let validatorOut, errs: any = {}; 554 | if (!this.target.druidDS) { 555 | errs.druidDS = "You must supply a druidDS name."; 556 | } 557 | 558 | if (!this.target.queryType) { 559 | errs.queryType = "You must supply a query type."; 560 | } else if (!this.isValidQueryType(this.target.queryType)) { 561 | errs.queryType = "Unknown query type: " + this.target.queryType + "."; 562 | } else { 563 | this.queryTypeValidators[this.target.queryType](this.target, errs); 564 | } 565 | 566 | if (this.target.shouldOverrideGranularity) { 567 | if (this.target.customGranularity) { 568 | if (!_.includes(this.customGranularity, this.target.customGranularity)) { 569 | errs.customGranularity = "Invalid granularity."; 570 | } 571 | } else { 572 | errs.customGranularity = "You must choose a granularity."; 573 | } 574 | } else { 575 | this.validateMaxDataPoints(this.target, errs); 576 | } 577 | 578 | if (this.addFilterMode) { 579 | if (!this.isValidFilterType(this.target.currentFilter.type)) { 580 | errs.currentFilter = "Invalid filter type: " + this.target.currentFilter.type + "."; 581 | } else { 582 | validatorOut = this.filterValidators[this.target.currentFilter.type](this.target); 583 | if (validatorOut) { 584 | errs.currentFilter = validatorOut; 585 | } 586 | } 587 | } 588 | 589 | if (this.addAggregatorMode) { 590 | if (!this.isValidAggregatorType(this.target.currentAggregator.type)) { 591 | errs.currentAggregator = "Invalid aggregator type: " + this.target.currentAggregator.type + "."; 592 | } else { 593 | validatorOut = this.aggregatorValidators[this.target.currentAggregator.type](this.target); 594 | if (validatorOut) { 595 | errs.currentAggregator = validatorOut; 596 | } 597 | } 598 | } 599 | 600 | if (_.isEmpty(this.target.aggregators) && !_.isEqual(this.target.queryType, "select")) { 601 | errs.aggregators = "You must supply at least one aggregator"; 602 | } 603 | 604 | if (this.addPostAggregatorMode) { 605 | if (!this.isValidPostAggregatorType(this.target.currentPostAggregator.type)) { 606 | errs.currentPostAggregator = "Invalid post aggregator type: " + this.target.currentPostAggregator.type + "."; 607 | } else { 608 | validatorOut = this.postAggregatorValidators[this.target.currentPostAggregator.type](this.target); 609 | if (validatorOut) { 610 | errs.currentPostAggregator = validatorOut; 611 | } 612 | } 613 | } 614 | 615 | return errs; 616 | } 617 | } 618 | -------------------------------------------------------------------------------- /headers/common.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare var System: any; 4 | 5 | // dummy modules 6 | declare module 'app/core/config' { 7 | var config: any; 8 | export default config; 9 | } 10 | 11 | declare module 'lodash' { 12 | var lodash: any; 13 | export default lodash; 14 | } 15 | 16 | declare module 'moment' { 17 | var moment: any; 18 | export default moment; 19 | } 20 | 21 | declare module 'angular' { 22 | var angular: any; 23 | export default angular; 24 | } 25 | 26 | declare module 'jquery' { 27 | var jquery: any; 28 | export default jquery; 29 | } 30 | 31 | declare module 'app/core/utils/kbn' { 32 | var kbn: any; 33 | export default kbn; 34 | } 35 | 36 | // Hack for datemath module 37 | declare module 'app/core/utils/datemath' { 38 | export var parse: any; 39 | export var isValid: any; 40 | export var parseDateMath: any; 41 | } 42 | 43 | declare module 'app/core/store' { 44 | var store: any; 45 | export default store; 46 | } 47 | 48 | declare module 'tether' { 49 | var config: any; 50 | export default config; 51 | } 52 | 53 | declare module 'tether-drop' { 54 | var config: any; 55 | export default config; 56 | } 57 | 58 | declare module 'eventemitter3' { 59 | var config: any; 60 | export default config; 61 | } 62 | -------------------------------------------------------------------------------- /headers/mocha/mocha.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for mocha 2.0.1 2 | // Project: http://mochajs.org/ 3 | // Definitions by: Kazi Manzur Rashid , otiai10 , jt000 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | interface Mocha { 7 | // Setup mocha with the given setting options. 8 | setup(options: MochaSetupOptions): Mocha; 9 | 10 | //Run tests and invoke `fn()` when complete. 11 | run(callback?: () => void): void; 12 | 13 | // Set reporter as function 14 | reporter(reporter: () => void): Mocha; 15 | 16 | // Set reporter, defaults to "dot" 17 | reporter(reporter: string): Mocha; 18 | 19 | // Enable growl support. 20 | growl(): Mocha 21 | } 22 | 23 | interface MochaSetupOptions { 24 | //milliseconds to wait before considering a test slow 25 | slow?: number; 26 | 27 | // timeout in milliseconds 28 | timeout?: number; 29 | 30 | // ui name "bdd", "tdd", "exports" etc 31 | ui?: string; 32 | 33 | //array of accepted globals 34 | globals?: any[]; 35 | 36 | // reporter instance (function or string), defaults to `mocha.reporters.Dot` 37 | reporter?: any; 38 | 39 | // bail on the first test failure 40 | bail?: boolean; 41 | 42 | // ignore global leaks 43 | ignoreLeaks?: boolean; 44 | 45 | // grep string or regexp to filter tests with 46 | grep?: any; 47 | } 48 | 49 | interface MochaDone { 50 | (error?: Error): void; 51 | } 52 | 53 | declare var mocha: Mocha; 54 | 55 | declare var describe : { 56 | (description: string, spec: () => void): void; 57 | only(description: string, spec: () => void): void; 58 | skip(description: string, spec: () => void): void; 59 | timeout(ms: number): void; 60 | } 61 | 62 | // alias for `describe` 63 | declare var context : { 64 | (contextTitle: string, spec: () => void): void; 65 | only(contextTitle: string, spec: () => void): void; 66 | skip(contextTitle: string, spec: () => void): void; 67 | timeout(ms: number): void; 68 | } 69 | 70 | declare var it: { 71 | (expectation: string, assertion?: () => void): void; 72 | (expectation: string, assertion?: (done: MochaDone) => void): void; 73 | only(expectation: string, assertion?: () => void): void; 74 | only(expectation: string, assertion?: (done: MochaDone) => void): void; 75 | skip(expectation: string, assertion?: () => void): void; 76 | skip(expectation: string, assertion?: (done: MochaDone) => void): void; 77 | timeout(ms: number): void; 78 | }; 79 | 80 | declare function before(action: () => void): void; 81 | 82 | declare function before(action: (done: MochaDone) => void): void; 83 | 84 | declare function setup(action: () => void): void; 85 | 86 | declare function setup(action: (done: MochaDone) => void): void; 87 | 88 | declare function after(action: () => void): void; 89 | 90 | declare function after(action: (done: MochaDone) => void): void; 91 | 92 | declare function teardown(action: () => void): void; 93 | 94 | declare function teardown(action: (done: MochaDone) => void): void; 95 | 96 | declare function beforeEach(action: () => void): void; 97 | 98 | declare function beforeEach(action: (done: MochaDone) => void): void; 99 | 100 | declare function suiteSetup(action: () => void): void; 101 | 102 | declare function suiteSetup(action: (done: MochaDone) => void): void; 103 | 104 | declare function afterEach(action: () => void): void; 105 | 106 | declare function afterEach(action: (done: MochaDone) => void): void; 107 | 108 | declare function suiteTeardown(action: () => void): void; 109 | 110 | declare function suiteTeardown(action: (done: MochaDone) => void): void; 111 | 112 | declare module "mocha" { 113 | 114 | class Mocha { 115 | constructor(options?: { 116 | grep?: RegExp; 117 | ui?: string; 118 | reporter?: string; 119 | timeout?: number; 120 | bail?: boolean; 121 | }); 122 | 123 | bail(value?: boolean): Mocha; 124 | addFile(file: string): Mocha; 125 | reporter(value: string): Mocha; 126 | ui(value: string): Mocha; 127 | grep(value: string): Mocha; 128 | grep(value: RegExp): Mocha; 129 | invert(): Mocha; 130 | ignoreLeaks(value: boolean): Mocha; 131 | checkLeaks(): Mocha; 132 | growl(): Mocha; 133 | globals(value: string): Mocha; 134 | globals(values: string[]): Mocha; 135 | useColors(value: boolean): Mocha; 136 | useInlineDiffs(value: boolean): Mocha; 137 | timeout(value: number): Mocha; 138 | slow(value: number): Mocha; 139 | enableTimeouts(value: boolean): Mocha; 140 | asyncOnly(value: boolean): Mocha; 141 | noHighlighting(value: boolean): Mocha; 142 | 143 | run(onComplete?: (failures: number) => void): void; 144 | } 145 | 146 | export = Mocha; 147 | } 148 | -------------------------------------------------------------------------------- /headers/zone/zone.d.ts: -------------------------------------------------------------------------------- 1 | declare module Zone { 2 | export class Stacktrace { 3 | constructor(e: Error); 4 | get(): string; 5 | } 6 | } 7 | 8 | 9 | declare class Zone { 10 | constructor(parentZone: Zone, data: any); 11 | fork(locals: any): Zone; 12 | bind(fn, skipEnqueue): void; 13 | bindOnce(fn): any; 14 | run(fn, applyTo?, applyWith?): void; 15 | beforeTask(): void; 16 | onZoneCreated(): void; 17 | afterTask(): void; 18 | enqueueTask(): void; 19 | dequeueTask(): void; 20 | 21 | static patchSetClearFn(obj, fnNames): string; 22 | static patchPrototype(obj, fnNames): any; 23 | static bindArguments(args: any[]): any; 24 | static bindArgumentsOnce(args: any[]): any; 25 | static patchableFn(obj, fnNames): any 26 | static patchProperty(obj, prop): void; 27 | static patchProperties(obj, properties): void; 28 | static patchEventTargetMethods(obj): void; 29 | static patch(): void; 30 | static canPatchViaPropertyDescriptor(): boolean; 31 | static patchViaPropertyDescriptor(): void; 32 | static patchViaCapturingAllTheEvents(): void; 33 | static patchWebSocket(): void; 34 | static patchClass(className: string): void; 35 | static patchMutationObserverClass(className: string): void; 36 | static patchDefineProperty(): void; 37 | static patchRegisterElement(): void; 38 | static eventNames: string; 39 | static onEventNames: string; 40 | static init(): void; 41 | static exceptZone: { 42 | boringZone: Zone; 43 | interestingZone: Zone, 44 | beforeTask: () => void; 45 | afterTask: () => void; 46 | fork: (ops: any) => Zone; 47 | }; 48 | static longStackTraceZone: { 49 | getLongStacktrace(exception: any): string; 50 | stackFramesFilter(line: string): boolean; 51 | onError(exception): void; 52 | fork(locals): Zone; 53 | }; 54 | static getStacktrace(): Zone.Stacktrace; 55 | static countingZone: { 56 | '+enqueueTask': () => void; 57 | '-dequeueTask': () => void; 58 | '+afterTask': () => void; 59 | counter: () => void; 60 | data: { 61 | count: number; 62 | flushed: boolean; 63 | }; 64 | onFlush: () => void; 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /img/AddDataSource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana-druid-plugin/druidplugin/33af5d544755a7bbcf7d72fcbd9673ef66a2840c/img/AddDataSource.png -------------------------------------------------------------------------------- /img/DruidPanel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana-druid-plugin/druidplugin/33af5d544755a7bbcf7d72fcbd9673ef66a2840c/img/DruidPanel.png -------------------------------------------------------------------------------- /img/ListDataSource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana-druid-plugin/druidplugin/33af5d544755a7bbcf7d72fcbd9673ef66a2840c/img/ListDataSource.png -------------------------------------------------------------------------------- /img/druid_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana-druid-plugin/druidplugin/33af5d544755a7bbcf7d72fcbd9673ef66a2840c/img/druid_logo.png -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = function(config) { 3 | config.set({ 4 | frameworks: ['systemjs', 'mocha', 'expect', 'sinon'], 5 | 6 | files: [ 7 | 'specs/*specs.ts', 8 | 'specs/**/*specs.ts', 9 | 'specs/lib/*.ts', 10 | { pattern: 'src/**/*.ts', included: false }, 11 | { pattern: 'src/css/*.css', included: false }, 12 | { pattern: 'node_modules/grafana-sdk-mocks/**/*.ts', included: false }, 13 | { pattern: 'node_modules/grafana-sdk-mocks/**/*.js', included: false }, 14 | { pattern: 'node_modules/typescript/lib/typescript.js', included: false }, 15 | { pattern: 'node_modules/systemjs-plugin-css/css.js', included: false }, 16 | { pattern: 'node_modules/lodash/lodash.js', included: false }, 17 | { pattern: 'node_modules/moment/moment.js', included: false }, 18 | { pattern: 'node_modules/q/q.js', included: false }, 19 | ], 20 | 21 | systemjs: { 22 | // // SystemJS configuration specifically for tests, added after your config file. 23 | // // Good for adding test libraries and mock modules 24 | config: { 25 | // Set path for third-party libraries as modules 26 | paths: { 27 | 'systemjs': 'node_modules/systemjs/dist/system.js', 28 | 'system-polyfills': 'node_modules/systemjs/dist/system-polyfills.js', 29 | 'lodash': 'node_modules/lodash/lodash.js', 30 | 'moment': 'node_modules/moment/moment.js', 31 | 'q': 'node_modules/q/q.js', 32 | 'typescript': 'node_modules/typescript/lib/typescript.js', 33 | 'plugin-typescript': 'node_modules/plugin-typescript/lib/plugin.js', 34 | 'css': 'node_modules/systemjs-plugin-css/css.js', 35 | 'app/': 'node_modules/grafana-sdk-mocks/app/', 36 | }, 37 | 38 | map: { 39 | 'plugin-typescript': 'node_modules/plugin-typescript/lib/', 40 | css: 'node_modules/systemjs-plugin-css/css.js', 41 | 'typescript': 'node_modules/typescript/', 42 | 'app/core/utils/kbn': 'node_modules/grafana-sdk-mocks/app/core/utils/kbn.js' 43 | }, 44 | 45 | packages: { 46 | 'plugin-typescript': { 47 | 'main': 'plugin.js' 48 | }, 49 | 'typescript': { 50 | 'main': 'lib/typescript.js', 51 | 'meta': { 52 | 'lib/typescript.js': { 53 | 'exports': 'ts' 54 | } 55 | } 56 | }, 57 | 'app': { 58 | 'defaultExtension': 'ts', 59 | 'meta': { 60 | '*.js': { 61 | 'loader': 'typescript' 62 | } 63 | } 64 | }, 65 | 'src': { 66 | 'defaultExtension': 'ts', 67 | meta: { 68 | '*.css': { loader: 'css' } 69 | } 70 | }, 71 | 'specs': { 72 | 'defaultExtension': 'ts', 73 | 'meta': { 74 | '*.js': { 75 | 'loader': 'typescript' 76 | } 77 | } 78 | }, 79 | }, 80 | 81 | transpiler: 'plugin-typescript', 82 | } 83 | }, 84 | 85 | reporters: ['dots'], 86 | 87 | logLevel: config.LOG_INFO, 88 | 89 | browsers: ['PhantomJS'] 90 | }); 91 | }; 92 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paypal-druid-plugin", 3 | "version": "4.0.0", 4 | "description": "Grafana plugin for Druid real-time OLAP database", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "grunt", 8 | "test": "echo \\\"Error: no test specified\\\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/grafana-druid-plugin/druidplugin.git" 13 | }, 14 | "keywords": [ 15 | "Druid" 16 | ], 17 | "author": "Abhishek Sant", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/grafana-druid-plugin/druidplugin/issues" 21 | }, 22 | "devDependencies": { 23 | "@types/lodash": "^4.14.74", 24 | "grafana-sdk-mocks": "github:grafana/grafana-sdk-mocks", 25 | "grunt": "^1.0.3", 26 | "grunt-contrib-clean": "^2.0.0", 27 | "grunt-contrib-copy": "^1.0.0", 28 | "grunt-contrib-watch": "^1.0.0", 29 | "grunt-string-replace": "^1.3", 30 | "grunt-ts": "^6.0.0-beta.21", 31 | "karma": "^3.0.0", 32 | "karma-chrome-launcher": "^2.2.0", 33 | "karma-expect": "^1.1.3", 34 | "karma-mocha": "^1.3.0", 35 | "karma-phantomjs-launcher": "^1.0.4", 36 | "karma-sinon": "^1.0.5", 37 | "karma-systemjs": "^0.16.0", 38 | "load-grunt-tasks": "^4.0.0", 39 | "mocha": "^5.1.1", 40 | "plugin-typescript": "^8.0.0", 41 | "sinon": "^6.3.1", 42 | "systemjs-plugin-css": "^0.1.35", 43 | "typescript": "^3.0.3" 44 | }, 45 | "homepage": "https://github.com/grafana-druid-plugin/druidplugin#readme" 46 | } 47 | -------------------------------------------------------------------------------- /specs/datasource_specs.ts: -------------------------------------------------------------------------------- 1 | import {describe, beforeEach, it, sinon, expect, angularMocks} from './lib/common'; 2 | import DruidDatasource from '../src/datasource'; 3 | import TemplateSrvStub from './lib/template_srv_stub'; 4 | import Q from 'q'; 5 | import moment from 'moment'; 6 | 7 | describe('ChangeMyNameDatasource', function() { 8 | let ctx: any = { 9 | backendSrv: {}, 10 | templateSrv: new TemplateSrvStub() 11 | }; 12 | 13 | beforeEach(function() { 14 | ctx.$q = Q; 15 | ctx.instanceSettings = {}; 16 | 17 | ctx.ds = new DruidDatasource(ctx.instanceSettings, ctx.backendSrv, ctx.templateSrv, ctx.$q); 18 | }); 19 | 20 | describe('When performing testDatasource', function() { 21 | describe('and an error is returned', function() { 22 | const error = { 23 | data: { 24 | error: { 25 | code: 'Error Code', 26 | message: `An error message.` 27 | } 28 | }, 29 | status: 400, 30 | statusText: 'Bad Request' 31 | }; 32 | 33 | beforeEach(function() { 34 | ctx.backendSrv.datasourceRequest = function(options) { 35 | return ctx.$q.reject(error); 36 | }; 37 | }); 38 | 39 | it('should return error status and a detailed error message', function() { 40 | return ctx.ds.testDatasource().then(function(results) { 41 | expect(results.status).to.equal('error'); 42 | expect(results.message).to.equal('Data Source is just a template and has not been implemented yet.'); 43 | }); 44 | }); 45 | }); 46 | 47 | describe('and the response works', function() { 48 | const response = { 49 | data: { 50 | }, 51 | status: 200, 52 | statusText: 'OK' 53 | }; 54 | 55 | beforeEach(function() { 56 | ctx.backendSrv.datasourceRequest = function(options) { 57 | return ctx.$q.when({data: response, status: 200}); 58 | }; 59 | }); 60 | 61 | it('should return success status', function() { 62 | return ctx.ds.testDatasource().then(function(results) { 63 | expect(results.status).to.equal('success'); 64 | }); 65 | }); 66 | }); 67 | }); 68 | 69 | describe('When performing query', function() { 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /specs/lib/common.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | var _global = (window); 4 | var beforeEach = _global.beforeEach; 5 | var before = _global.before; 6 | var describe = _global.describe; 7 | var it = _global.it; 8 | var sinon = _global.sinon; 9 | var expect = _global.expect; 10 | 11 | var angularMocks = { 12 | module: _global.module, 13 | inject: _global.inject, 14 | }; 15 | 16 | export { 17 | beforeEach, 18 | before, 19 | describe, 20 | it, 21 | sinon, 22 | expect, 23 | angularMocks, 24 | }; 25 | -------------------------------------------------------------------------------- /specs/lib/context_srv_stub.ts: -------------------------------------------------------------------------------- 1 | export default class ContextSrvStub { 2 | hasRole() { 3 | return true; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /specs/lib/template_srv_stub.ts: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | export default class TemplateSrvStub { 4 | variables = []; 5 | templateSettings = { interpolate : /\[\[([\s\S]+?)\]\]/g }; 6 | data = {}; 7 | 8 | replace(text) { 9 | return _.template(text, this.templateSettings)(this.data); 10 | } 11 | 12 | getAdhocFilters() { 13 | return []; 14 | } 15 | 16 | variableExists() { 17 | return false; 18 | } 19 | 20 | highlightVariablesAsHtml(str) { 21 | return str; 22 | } 23 | 24 | setGrafanaVariable(name, value) { 25 | this.data[name] = value; 26 | } 27 | 28 | init() {} 29 | fillVariableValuesForUrl() {} 30 | updateTemplateData() {} 31 | } 32 | -------------------------------------------------------------------------------- /specs/lib/time_srv_stub.ts: -------------------------------------------------------------------------------- 1 | import {sinon} from '../lib/common'; 2 | import dateMath from 'app/core/utils/datemath'; 3 | 4 | export default class TimeSrvStub { 5 | init = sinon.spy(); 6 | time = { from: 'now-1h', to: 'now'}; 7 | 8 | timeRange(parse) { 9 | if (parse === false) { 10 | return this.time; 11 | } 12 | 13 | return { 14 | from : dateMath.parse(this.time.from, false), 15 | to : dateMath.parse(this.time.to, true) 16 | }; 17 | } 18 | 19 | replace(target) { 20 | return target; 21 | } 22 | 23 | setTime(time) { 24 | this.time = time; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /specs/query_ctrl_specs.ts: -------------------------------------------------------------------------------- 1 | import {describe, beforeEach, it, sinon, expect} from './lib/common'; 2 | import {DruidQueryCtrl} from '../src/query_ctrl'; 3 | import TemplateSrvStub from './lib/template_srv_stub'; 4 | import Q from 'q'; 5 | import moment from 'moment'; 6 | 7 | describe('ChangeMyNameQueryCtrl', function() { 8 | let queryCtrl; 9 | 10 | beforeEach(function() { 11 | queryCtrl = new DruidQueryCtrl({}, {}, new TemplateSrvStub()); 12 | queryCtrl.datasource = {$q: Q}; 13 | }); 14 | 15 | describe('init query_ctrl variables', function() { 16 | it('defaults should be initialized', function() { 17 | // Replace with test for defaults that should be set in the query ctrl. 18 | expect(queryCtrl.target.target).to.be(''); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/config_ctrl.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | export class DruidConfigCtrl { 4 | static templateUrl = 'partials/config.html'; 5 | current: any; 6 | 7 | constructor($scope) { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/css/query_editor.css: -------------------------------------------------------------------------------- 1 | .min-width-10 { 2 | min-width: 10rem; 3 | } 4 | 5 | .min-width-12 { 6 | min-width: 12rem; 7 | } 8 | 9 | .min-width-20 { 10 | min-width: 20rem; 11 | } 12 | 13 | .gf-form-select-wrapper select.gf-form-input { 14 | height: 2.64rem; 15 | } 16 | 17 | .gf-form-select-wrapper--caret-indent.gf-form-select-wrapper::after { 18 | right: 0.775rem 19 | } 20 | -------------------------------------------------------------------------------- /src/datasource.d.ts: -------------------------------------------------------------------------------- 1 | declare var DruidDatasource: any; 2 | export {DruidDatasource}; 3 | 4 | -------------------------------------------------------------------------------- /src/datasource.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import _ from 'lodash'; 4 | import moment from 'moment'; 5 | import * as dateMath from 'app/core/utils/datemath'; 6 | import * as Druid from 'druid.d' 7 | 8 | const DRUID_DATASOURCE_PATH = '/druid/v2/datasources'; 9 | 10 | export default class DruidDatasource { 11 | id: number; 12 | name: string; 13 | url: string; 14 | q: any; 15 | backendSrv: any; 16 | templateSrv: any; 17 | basicAuth: any; 18 | supportMetrics: any; 19 | periodGranularity: any; 20 | GRANULARITIES = [ 21 | ['second', moment.duration(1, 'second')], 22 | ['minute', moment.duration(1, 'minute')], 23 | ['fifteen_minute', moment.duration(15, 'minute')], 24 | ['thirty_minute', moment.duration(30, 'minute')], 25 | ['hour', moment.duration(1, 'hour')], 26 | ['day', moment.duration(1, 'day')], 27 | ['week', moment.duration(1, 'week')], 28 | ['month', moment.duration(1, 'month')], 29 | ['quarter', moment.duration(1, 'quarter')], 30 | ['year', moment.duration(1, 'year')] 31 | ]; 32 | filterTemplateExpanders = { 33 | "selector": ['value'], 34 | "regex": ['pattern'], 35 | "javascript": ['function'], 36 | "search": [] 37 | }; 38 | 39 | 40 | /** @ngInject */ 41 | constructor(instanceSettings, $q, backendSrv, templateSrv) { 42 | this.name = instanceSettings.name; 43 | this.id = instanceSettings.id; 44 | this.url = instanceSettings.url; 45 | this.backendSrv = backendSrv; 46 | this.q = $q; 47 | this.templateSrv = templateSrv; 48 | this.basicAuth = instanceSettings.basicAuth; 49 | instanceSettings.jsonData = instanceSettings.jsonData || {}; 50 | this.supportMetrics = true; 51 | this.periodGranularity = instanceSettings.jsonData.periodGranularity; 52 | } 53 | 54 | query(options) { 55 | const from = this.dateToMoment(options.range.from, false); 56 | const to = this.dateToMoment(options.range.to, true); 57 | 58 | let promises = options.targets.map(target => { 59 | if (target.hide === true || _.isEmpty(target.druidDS) || (_.isEmpty(target.aggregators) && target.queryType !== "select")) { 60 | const d = this.q.defer(); 61 | d.resolve([]); 62 | return d.promise; 63 | } 64 | const maxDataPointsByResolution = options.maxDataPoints; 65 | const maxDataPointsByConfig = target.maxDataPoints ? target.maxDataPoints : Number.MAX_VALUE; 66 | const maxDataPoints = Math.min(maxDataPointsByResolution, maxDataPointsByConfig); 67 | let granularity = target.shouldOverrideGranularity ? this.templateSrv.replace(target.customGranularity) : this.computeGranularity(from, to, maxDataPoints); 68 | //Round up to start of an interval 69 | //Width of bar chars in Grafana is determined by size of the smallest interval 70 | const roundedFrom = granularity === "all" ? from : this.roundUpStartTime(from, granularity); 71 | if (this.periodGranularity != "") { 72 | if (granularity === 'day') { 73 | granularity = { "type": "period", "period": "P1D", "timeZone": this.periodGranularity } 74 | } 75 | } 76 | return this.doQuery(roundedFrom, to, granularity, target); 77 | }); 78 | 79 | return this.q.all(promises).then(results => { 80 | return { data: _.flatten(results) }; 81 | }); 82 | } 83 | 84 | doQuery(from, to, granularity, target) { 85 | let datasource = target.druidDS; 86 | let filters = target.filters; 87 | let aggregators = target.aggregators.map(this.splitCardinalityFields); 88 | let postAggregators = target.postAggregators; 89 | let groupBy = _.map(target.groupBy, (e) => { return this.templateSrv.replace(e) }); 90 | let limitSpec = null; 91 | let metricNames = this.getMetricNames(aggregators, postAggregators); 92 | let intervals = this.getQueryIntervals(from, to); 93 | let promise = null; 94 | 95 | let selectMetrics = target.selectMetrics; 96 | let selectDimensions = target.selectDimensions; 97 | let selectThreshold = target.selectThreshold; 98 | if (!selectThreshold) { 99 | selectThreshold = 5; 100 | } 101 | 102 | if (target.queryType === 'topN') { 103 | let threshold = target.limit; 104 | let metric = target.druidMetric; 105 | let dimension = this.templateSrv.replace(target.dimension); 106 | promise = this.topNQuery(datasource, intervals, granularity, filters, aggregators, postAggregators, threshold, metric, dimension) 107 | .then(response => { 108 | return this.convertTopNData(response.data, dimension, metric); 109 | }); 110 | } 111 | else if (target.queryType === 'groupBy') { 112 | limitSpec = this.getLimitSpec(target.limit, target.orderBy); 113 | promise = this.groupByQuery(datasource, intervals, granularity, filters, aggregators, postAggregators, groupBy, limitSpec) 114 | .then(response => { 115 | return this.convertGroupByData(response.data, groupBy, metricNames); 116 | }); 117 | } 118 | else if (target.queryType === 'select') { 119 | promise = this.selectQuery(datasource, intervals, granularity, selectDimensions, selectMetrics, filters, selectThreshold); 120 | return promise.then(response => { 121 | return this.convertSelectData(response.data); 122 | }); 123 | } 124 | else { 125 | promise = this.timeSeriesQuery(datasource, intervals, granularity, filters, aggregators, postAggregators) 126 | .then(response => { 127 | return this.convertTimeSeriesData(response.data, metricNames); 128 | }); 129 | } 130 | /* 131 | At this point the promise will return an list of time series of this form 132 | [ 133 | { 134 | target: , 135 | datapoints: [ 136 | [, ], 137 | ... 138 | ] 139 | }, 140 | ... 141 | ] 142 | 143 | Druid calculates metrics based on the intervals specified in the query but returns a timestamp rounded down. 144 | We need to adjust the first timestamp in each time series 145 | */ 146 | return promise.then(metrics => { 147 | let fromMs = this.formatTimestamp(from); 148 | metrics.forEach(metric => { 149 | if (!_.isEmpty(metric.datapoints[0]) && metric.datapoints[0][1] < fromMs) { 150 | metric.datapoints[0][1] = fromMs; 151 | } 152 | }); 153 | return metrics; 154 | }); 155 | }; 156 | 157 | splitCardinalityFields(aggregator) { 158 | if (aggregator.type === 'cardinality' && typeof aggregator.fieldNames === 'string') { 159 | aggregator.fieldNames = aggregator.fieldNames.split(',') 160 | } 161 | return aggregator; 162 | } 163 | 164 | selectQuery(datasource: string, intervals: Array, granularity: Druid.Granularity, 165 | dimensions: Array, metric: Array, filters: Array, 166 | selectThreshold: Object) { 167 | let query: Druid.DruidSelectQuery = { 168 | "queryType": "select", 169 | "dataSource": datasource, 170 | "granularity": granularity, 171 | "pagingSpec": { "pagingIdentifiers": {}, "threshold": selectThreshold }, 172 | "dimensions": dimensions, 173 | "metrics": metric, 174 | "intervals": intervals 175 | }; 176 | 177 | if (filters && filters.length > 0) { 178 | query.filter = this.buildFilterTree(filters); 179 | } 180 | 181 | return this.druidQuery(query); 182 | }; 183 | 184 | timeSeriesQuery(datasource: string, intervals: Array, granularity: Druid.Granularity, 185 | filters: Array, aggregators: Object, postAggregators: Object) { 186 | let query: Druid.DruidTimeSeriesQuery = { 187 | queryType: "timeseries", 188 | dataSource: datasource, 189 | granularity: granularity, 190 | aggregations: aggregators, 191 | postAggregations: postAggregators, 192 | intervals: intervals 193 | }; 194 | 195 | if (filters && filters.length > 0) { 196 | query.filter = this.buildFilterTree(filters); 197 | } 198 | 199 | return this.druidQuery(query); 200 | }; 201 | 202 | topNQuery(datasource: string, intervals: Array, granularity: Druid.Granularity, 203 | filters: Array, aggregators: Object, postAggregators: Object, 204 | threshold: number, metric: string | Object, dimension: string | Object) { 205 | const query: Druid.DruidTopNQuery = { 206 | queryType: "topN", 207 | dataSource: datasource, 208 | granularity: granularity, 209 | threshold: threshold, 210 | dimension: dimension, 211 | metric: metric, 212 | aggregations: aggregators, 213 | postAggregations: postAggregators, 214 | intervals: intervals 215 | }; 216 | 217 | if (filters && filters.length > 0) { 218 | query.filter = this.buildFilterTree(filters); 219 | } 220 | 221 | return this.druidQuery(query); 222 | }; 223 | 224 | groupByQuery(datasource: string, intervals: Array, granularity: Druid.Granularity, 225 | filters: Array, aggregators: Object, postAggregators: Object, groupBy: Array, 226 | limitSpec: Druid.LimitSpec) { 227 | const query: Druid.DruidGroupByQuery = { 228 | queryType: "groupBy", 229 | dataSource: datasource, 230 | granularity: granularity, 231 | dimensions: groupBy, 232 | aggregations: aggregators, 233 | postAggregations: postAggregators, 234 | intervals: intervals, 235 | limitSpec: limitSpec, 236 | }; 237 | 238 | if (filters && filters.length > 0) { 239 | query.filter = this.buildFilterTree(filters); 240 | } 241 | 242 | return this.druidQuery(query); 243 | }; 244 | 245 | druidQuery(query: Druid.AbstractDruidQuery) { 246 | const options = { 247 | method: 'POST', 248 | url: this.url + '/druid/v2/', 249 | data: query 250 | }; 251 | return this.backendSrv.datasourceRequest(options); 252 | }; 253 | 254 | getLimitSpec(limitNum, orderBy) { 255 | return { 256 | "type": "default", 257 | "limit": limitNum, 258 | "columns": !orderBy ? null : orderBy.map(col => { 259 | return { "dimension": col, "direction": "DESCENDING" }; 260 | }) 261 | }; 262 | } 263 | 264 | testDatasource() { 265 | return this.get(DRUID_DATASOURCE_PATH).then(() => { 266 | return { status: "success", message: "Druid Data source is working", title: "Success" }; 267 | }); 268 | } 269 | 270 | //Get list of available datasources 271 | getDataSources() { 272 | return this.get(DRUID_DATASOURCE_PATH).then(response => { 273 | return response.data; 274 | }); 275 | }; 276 | 277 | getDimensionsAndMetrics(datasource) { 278 | return this.get(DRUID_DATASOURCE_PATH + datasource).then(response => { 279 | return response.data; 280 | }); 281 | }; 282 | 283 | getFilterValues(target, panelRange, query) { 284 | const topNquery: any = { 285 | "queryType": "topN", 286 | "dataSource": target.druidDS, 287 | "granularity": 'all', 288 | "threshold": 10, 289 | "dimension": target.currentFilter.dimension, 290 | "metric": "count", 291 | "aggregations": [{ "type": "count", "name": "count" }], 292 | "intervals": this.getQueryIntervals(panelRange.from, panelRange.to) 293 | }; 294 | 295 | let filters = []; 296 | if (target.filters) { 297 | filters = 298 | filters = _.cloneDeep(target.filters); 299 | } 300 | filters.push({ 301 | "type": "search", 302 | "dimension": target.currentFilter.dimension, 303 | "query": { 304 | "type": "insensitive_contains", 305 | "value": query 306 | } 307 | }); 308 | topNquery.filter = this.buildFilterTree(filters); 309 | 310 | return this.druidQuery(topNquery); 311 | }; 312 | 313 | get(relativeUrl, params?) { 314 | return this.backendSrv.datasourceRequest({ 315 | method: 'GET', 316 | url: this.url + relativeUrl, 317 | params: params, 318 | }); 319 | }; 320 | 321 | buildFilterTree(filters): Druid.DruidFilter { 322 | //Do template variable replacement 323 | const replacedFilters = filters.map(filter => { 324 | return this.replaceTemplateValues(filter, this.filterTemplateExpanders[filter.type]); 325 | }) 326 | .map(filter => { 327 | const finalFilter = _.omit(filter, 'negate'); 328 | if (filter.negate) { 329 | return { "type": "not", "field": finalFilter }; 330 | } 331 | return finalFilter; 332 | }); 333 | if (replacedFilters) { 334 | if (replacedFilters.length === 1) { 335 | return replacedFilters[0]; 336 | } 337 | return { 338 | "type": "and", 339 | "fields": replacedFilters 340 | }; 341 | } 342 | return null; 343 | } 344 | 345 | getQueryIntervals(from, to) { 346 | return [from.toISOString() + '/' + to.toISOString()]; 347 | } 348 | 349 | getMetricNames(aggregators, postAggregators) { 350 | const displayAggs = _.filter(aggregators, agg => { 351 | return agg.type !== 'approxHistogramFold' && agg.hidden != true; 352 | }); 353 | return _.union(_.map(displayAggs, 'name'), _.map(postAggregators, 'name')); 354 | } 355 | 356 | formatTimestamp(ts) { 357 | return moment(ts).format('X') * 1000; 358 | } 359 | 360 | convertTimeSeriesData(md, metrics) { 361 | return metrics.map(metric => { 362 | return { 363 | target: metric, 364 | datapoints: md.map(item => { 365 | return [ 366 | item.result[metric], 367 | this.formatTimestamp(item.timestamp) 368 | ]; 369 | }) 370 | }; 371 | }); 372 | } 373 | 374 | getGroupName(groupBy, metric) { 375 | return groupBy.map(dim => { 376 | return metric.event[dim]; 377 | }) 378 | .join("-"); 379 | } 380 | 381 | convertTopNData(md, dimension, metric) { 382 | /* 383 | Druid topN results look like this: 384 | [ 385 | { 386 | "timestamp": "ts1", 387 | "result": [ 388 | {"": d1, "": mv1}, 389 | {"": d2, "": mv2} 390 | ] 391 | }, 392 | { 393 | "timestamp": "ts2", 394 | "result": [ 395 | {"": d1, "": mv3}, 396 | {"": d2, "": mv4} 397 | ] 398 | }, 399 | ... 400 | ] 401 | */ 402 | 403 | /* 404 | First, we need make sure that the result for each 405 | timestamp contains entries for all distinct dimension values 406 | in the entire list of results. 407 | 408 | Otherwise, if we do a stacked bar chart, Grafana doesn't sum 409 | the metrics correctly. 410 | */ 411 | 412 | //Get the list of all distinct dimension values for the entire result set 413 | const dVals = md.reduce((dValsSoFar, tsItem) => { 414 | const dValsForTs = _.map(tsItem.result, dimension); 415 | return _.union(dValsSoFar, dValsForTs); 416 | }, {}); 417 | 418 | //Add null for the metric for any missing dimension values per timestamp result 419 | md.forEach(tsItem => { 420 | const dValsPresent = _.map(tsItem.result, dimension); 421 | const dValsMissing = _.difference(dVals, dValsPresent); 422 | dValsMissing.forEach(dVal => { 423 | const nullPoint = {}; 424 | nullPoint[dimension] = dVal; 425 | nullPoint[metric] = null; 426 | tsItem.result.push(nullPoint); 427 | }); 428 | return tsItem; 429 | }); 430 | 431 | //Re-index the results by dimension value instead of time interval 432 | const mergedData = md.map(item => { 433 | /* 434 | This first map() transforms this into a list of objects 435 | where the keys are dimension values 436 | and the values are [metricValue, unixTime] so that we get this: 437 | [ 438 | { 439 | "d1": [mv1, ts1], 440 | "d2": [mv2, ts1] 441 | }, 442 | { 443 | "d1": [mv3, ts2], 444 | "d2": [mv4, ts2] 445 | }, 446 | ... 447 | ] 448 | */ 449 | const timestamp = this.formatTimestamp(item.timestamp); 450 | const keys = _.map(item.result, dimension); 451 | const vals = _.map(item.result, metric).map(val => { return [val, timestamp]; }); 452 | return _.zipObject(keys, vals); 453 | }) 454 | .reduce((prev, curr) => { 455 | /* 456 | Reduce() collapses all of the mapped objects into a single 457 | object. The keys are dimension values 458 | and the values are arrays of all the values for the same key. 459 | The _.assign() function merges objects together and it's callback 460 | gets invoked for every key,value pair in the source (2nd argument). 461 | Since our initial value for reduce() is an empty object, 462 | the _.assign() callback will get called for every new val 463 | that we add to the final object. 464 | */ 465 | return _.assignWith(prev, curr, (pVal, cVal) => { 466 | if (pVal) { 467 | pVal.push(cVal); 468 | return pVal; 469 | } 470 | return [cVal]; 471 | }); 472 | }, {}); 473 | 474 | //Convert object keyed by dimension values into an array 475 | //of objects {target: , datapoints: } 476 | return _.map(mergedData, (vals, key) => { 477 | return { 478 | target: key, 479 | datapoints: vals 480 | }; 481 | }); 482 | } 483 | 484 | convertGroupByData(md, groupBy, metrics) { 485 | const mergedData = md.map(item => { 486 | /* 487 | The first map() transforms the list Druid events into a list of objects 488 | with keys of the form ":" and values 489 | of the form [metricValue, unixTime] 490 | */ 491 | const groupName = this.getGroupName(groupBy, item); 492 | const keys = metrics.map(metric => { 493 | return groupName + ":" + metric; 494 | }); 495 | const vals = metrics.map(metric => { 496 | return [ 497 | item.event[metric], 498 | this.formatTimestamp(item.timestamp) 499 | ]; 500 | }); 501 | return _.zipObject(keys, vals); 502 | }) 503 | .reduce((prev, curr) => { 504 | /* 505 | Reduce() collapses all of the mapped objects into a single 506 | object. The keys are still of the form ":" 507 | and the values are arrays of all the values for the same key. 508 | The _.assign() function merges objects together and it's callback 509 | gets invoked for every key,value pair in the source (2nd argument). 510 | Since our initial value for reduce() is an empty object, 511 | the _.assign() callback will get called for every new val 512 | that we add to the final object. 513 | */ 514 | return _.assignWith(prev, curr, (pVal, cVal) => { 515 | if (pVal) { 516 | pVal.push(cVal); 517 | return pVal; 518 | } 519 | return [cVal]; 520 | }); 521 | }, {}); 522 | 523 | return _.map(mergedData, (vals, key) => { 524 | /* 525 | Second map converts the aggregated object into an array 526 | */ 527 | return { 528 | target: key, 529 | datapoints: vals 530 | }; 531 | }); 532 | } 533 | 534 | convertSelectData(data) { 535 | const resultList = _.map(data, "result"); 536 | const eventsList = _.map(resultList, "events"); 537 | const eventList = _.flatten(eventsList); 538 | const result = {}; 539 | for (let i = 0; i < eventList.length; i++) { 540 | const event = eventList[i].event; 541 | const timestamp = event.timestamp; 542 | if (_.isEmpty(timestamp)) { 543 | continue; 544 | } 545 | for (const key in event) { 546 | if (key !== "timestamp") { 547 | if (!result[key]) { 548 | result[key] = { "target": key, "datapoints": [] }; 549 | } 550 | result[key].datapoints.push([event[key], timestamp]); 551 | } 552 | } 553 | } 554 | return _.values(result); 555 | } 556 | 557 | dateToMoment(date, roundUp) { 558 | if (date === 'now') { 559 | return moment(); 560 | } 561 | date = dateMath.parse(date, roundUp); 562 | return moment(date.valueOf()); 563 | } 564 | 565 | computeGranularity(from, to, maxDataPoints) { 566 | const intervalSecs = to.unix() - from.unix(); 567 | /* 568 | Find the smallest granularity for which there 569 | will be fewer than maxDataPoints 570 | */ 571 | const granularityEntry = _.find(this.GRANULARITIES, gEntry => { 572 | return Math.ceil(intervalSecs / gEntry[1].asSeconds()) <= maxDataPoints; 573 | }); 574 | 575 | return granularityEntry[0]; 576 | } 577 | 578 | roundUpStartTime(from, granularity) { 579 | const duration = _.find(this.GRANULARITIES, gEntry => { 580 | return gEntry[0] === granularity; 581 | })[1]; 582 | let rounded = null; 583 | if (granularity === 'day') { 584 | rounded = moment(+from).startOf('day'); 585 | } else { 586 | rounded = moment(Math.ceil((+from) / (+duration)) * (+duration)); 587 | } 588 | return rounded; 589 | } 590 | 591 | replaceTemplateValues(obj, attrList) { 592 | const substitutedVals = attrList.map(attr => { 593 | return this.templateSrv.replace(obj[attr]); 594 | }); 595 | return _.assign(_.clone(obj, true), _.zipObject(attrList, substitutedVals)); 596 | } 597 | } 598 | -------------------------------------------------------------------------------- /src/druid.d.ts: -------------------------------------------------------------------------------- 1 | export interface AbstractDruidQuery { 2 | dataSource: string; 3 | intervals: Array; 4 | granularity: Granularity; 5 | queryType: string; 6 | filter?: DruidFilter; 7 | aggregations?: Object; 8 | postAggregations?: Object; 9 | context?: Object; 10 | } 11 | 12 | export enum Granularity { 13 | all = 'all', 14 | none = 'none', 15 | second = 'second', 16 | minute = 'minute', 17 | fifteen_minute = 'fifteen_minute', 18 | thirty_minute = 'thirty_minute', 19 | hour = 'hour', 20 | day = 'day', 21 | week = 'week', 22 | month = 'month', 23 | quarter = 'quarter', 24 | year = 'year' 25 | } 26 | 27 | export interface LimitSpec { 28 | type: 'default'; 29 | limit: number; 30 | columns: Array; 31 | } 32 | 33 | export interface OrderByColumnSpec { 34 | dimension: string; 35 | direction: 'ascending' | 'descending'; 36 | dimensionOrder: 'lexicographic' | 'alphanumeric' | 'strlen' | 'numeric'; 37 | } 38 | 39 | export interface DruidGroupByQuery extends AbstractDruidQuery { 40 | queryType: 'groupBy'; 41 | dimensions: Array; 42 | limitSpec?: LimitSpec; 43 | having?: Object; 44 | } 45 | 46 | export interface DruidTimeSeriesQuery extends AbstractDruidQuery { 47 | queryType: 'timeseries'; 48 | descending?: 'true' | 'false'; 49 | } 50 | 51 | export interface DruidTopNQuery extends AbstractDruidQuery { 52 | queryType: 'topN'; 53 | dimension: string | Object; 54 | threshold: number; 55 | metric: string | Object; 56 | } 57 | 58 | export interface DruidSelectQuery extends AbstractDruidQuery { 59 | pagingSpec: { 60 | pagingIdentifiers: {}; 61 | threshold: Object; 62 | }; 63 | dimensions: Array; 64 | metrics: Array; 65 | } 66 | 67 | export interface DruidFilterLogical { 68 | type: 'or' | 'and'; 69 | fields: DruidFilter[]; 70 | } 71 | 72 | export interface DruidFilterNot { 73 | type: 'not'; 74 | field: DruidFilter; 75 | } 76 | 77 | export interface DruidFilterSelect { 78 | type: 'selector'; 79 | dimension: string; 80 | value: string; 81 | } 82 | 83 | export interface DruidFilterIn { 84 | type: string; 85 | dimension: string; 86 | values: Array; 87 | } 88 | 89 | export interface DruidFilterRegex { 90 | type: 'regex'; 91 | dimension: string; 92 | pattern: string; 93 | } 94 | 95 | export type DruidFilter = DruidFilterLogical | DruidFilterSelect | DruidFilterRegex | DruidFilterIn | DruidFilterNot; -------------------------------------------------------------------------------- /src/img/druid_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grafana-druid-plugin/druidplugin/33af5d544755a7bbcf7d72fcbd9673ef66a2840c/src/img/druid_logo.png -------------------------------------------------------------------------------- /src/module.ts: -------------------------------------------------------------------------------- 1 | import DruidDatasource from './datasource'; 2 | import {DruidQueryCtrl} from './query_ctrl'; 3 | import {DruidConfigCtrl} from './config_ctrl'; 4 | 5 | class DruidAnnotationsQueryCtrl { 6 | static templateUrl = 'partials/annotations.editor.html'; 7 | } 8 | 9 | export { 10 | DruidDatasource as Datasource, 11 | DruidQueryCtrl as QueryCtrl, 12 | DruidConfigCtrl as ConfigCtrl 13 | }; 14 | -------------------------------------------------------------------------------- /src/partials/config.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Druid Settings

4 |
5 | Period Granularity 6 |
7 | 119 | This enables the to query hourly data in different timezone. 120 |
121 |
122 |
123 | 124 | 125 | -------------------------------------------------------------------------------- /src/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "datasource", 3 | "name": "Druid", 4 | "id": "paypal-druid-datasource", 5 | 6 | "metrics": true, 7 | 8 | "info": { 9 | "version": "0.0.6", 10 | "author": { 11 | "name": "Abhishek Sant", 12 | "url": "https://github.com/grafana-druid-plugin/druidplugin" 13 | }, 14 | "links":[ 15 | {"name": "GitHub", "url": "https://github.com/grafana-druid-plugin/druidplugin"} 16 | ], 17 | "logos": { 18 | "small": "img/druid_logo.png", 19 | "large": "img/druid_logo.png" 20 | }, 21 | "dependencies": { 22 | "grafanaVersion": "4.x.x", 23 | "plugins": [ ] 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/query_ctrl.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import _ from 'lodash'; 4 | import { QueryCtrl } from 'app/plugins/sdk'; 5 | import './css/query_editor.css!'; 6 | 7 | export class DruidQueryCtrl extends QueryCtrl { 8 | static templateUrl = 'partials/query.editor.html'; 9 | 10 | errors: any; 11 | addFilterMode: boolean; 12 | addAggregatorMode: boolean; 13 | addPostAggregatorMode: boolean; 14 | addDimensionsMode: boolean; 15 | addMetricsMode: boolean; 16 | listDataSources: any; 17 | getDimensionsAndMetrics: any; 18 | getMetrics: any; 19 | getMetricsPlusDimensions: any; 20 | getDimensions: any; 21 | getFilterValues: any; 22 | queryTypes: any; 23 | filterTypes: any; 24 | aggregatorTypes: any; 25 | postAggregatorTypes: any; 26 | arithmeticPostAggregator: any; 27 | customGranularity: any; 28 | target: any; 29 | datasource: any; 30 | 31 | queryTypeValidators = { 32 | "timeseries": _.noop.bind(this), 33 | "groupBy": this.validateGroupByQuery.bind(this), 34 | "topN": this.validateTopNQuery.bind(this), 35 | "select": this.validateSelectQuery.bind(this) 36 | }; 37 | filterValidators = { 38 | "selector": this.validateSelectorFilter.bind(this), 39 | "regex": this.validateRegexFilter.bind(this), 40 | "javascript": this.validateJavascriptFilter.bind(this) 41 | }; 42 | aggregatorValidators = { 43 | "count": this.validateCountAggregator, 44 | "cardinality": _.partial(this.validateCardinalityAggregator.bind(this), 'cardinality'), 45 | "longSum": _.partial(this.validateSimpleAggregator.bind(this), 'longSum'), 46 | "doubleSum": _.partial(this.validateSimpleAggregator.bind(this), 'doubleSum'), 47 | "approxHistogramFold": this.validateApproxHistogramFoldAggregator.bind(this), 48 | "hyperUnique": _.partial(this.validateSimpleAggregator.bind(this), 'hyperUnique'), 49 | "thetaSketch": this.validateThetaSketchAggregator.bind(this) 50 | }; 51 | postAggregatorValidators = { 52 | "arithmetic": this.validateArithmeticPostAggregator.bind(this), 53 | "max": this.validateMaxPostAggregator.bind(this), 54 | "min": this.validateMinPostAggregator.bind(this), 55 | "quantile": this.validateQuantilePostAggregator.bind(this) 56 | }; 57 | 58 | arithmeticPostAggregatorFns = { '+': null, '-': null, '*': null, '/': null }; 59 | defaultQueryType = "timeseries"; 60 | defaultFilterType = "selector"; 61 | defaultAggregatorType = "count"; 62 | defaultPostAggregator = { type: 'arithmetic', 'fn': '+' }; 63 | customGranularities = ['second', 'minute', 'fifteen_minute', 'thirty_minute', 'hour', 'day', 'week', 'month', 'quarter', 'year', 'all']; 64 | defaultCustomGranularity = 'minute'; 65 | defaultSelectDimension = ""; 66 | defaultSelectMetric = ""; 67 | defaultLimit = 5; 68 | 69 | /** @ngInject **/ 70 | constructor($scope, $injector, $q) { 71 | super($scope, $injector); 72 | if (!this.target.queryType) { 73 | this.target.queryType = this.defaultQueryType; 74 | } 75 | 76 | this.queryTypes = _.keys(this.queryTypeValidators); 77 | this.filterTypes = _.keys(this.filterValidators); 78 | this.aggregatorTypes = _.keys(this.aggregatorValidators); 79 | this.postAggregatorTypes = _.keys(this.postAggregatorValidators); 80 | this.arithmeticPostAggregator = _.keys(this.arithmeticPostAggregatorFns); 81 | this.customGranularity = this.customGranularities; 82 | 83 | this.errors = this.validateTarget(); 84 | if (!this.target.currentFilter) { 85 | this.clearCurrentFilter(); 86 | } 87 | 88 | if (!this.target.currentSelect) { 89 | this.target.currentSelect = {}; 90 | this.clearCurrentSelectDimension(); 91 | this.clearCurrentSelectMetric(); 92 | } 93 | 94 | if (!this.target.currentAggregator) { 95 | this.clearCurrentAggregator(); 96 | } 97 | 98 | if (!this.target.currentPostAggregator) { 99 | this.clearCurrentPostAggregator(); 100 | } 101 | 102 | if (!this.target.customGranularity) { 103 | this.target.customGranularity = this.defaultCustomGranularity; 104 | } 105 | 106 | if (!this.target.limit) { 107 | this.target.limit = this.defaultLimit; 108 | } 109 | 110 | // needs to be defined here as it is called from typeahead 111 | this.listDataSources = (query, callback) => { 112 | this.datasource.getDataSources() 113 | .then(callback); 114 | }; 115 | 116 | this.getDimensions = (query, callback) => { 117 | return this.datasource.getDimensionsAndMetrics(this.target.druidDS) 118 | .then(function (dimsAndMetrics) { 119 | callback(dimsAndMetrics.dimensions); 120 | }); 121 | }; 122 | 123 | this.getMetrics = (query, callback) => { 124 | return this.datasource.getDimensionsAndMetrics(this.target.druidDS) 125 | .then(function (dimsAndMetrics) { 126 | callback(dimsAndMetrics.metrics); 127 | }); 128 | }; 129 | 130 | this.getMetricsPlusDimensions = (query, callback) => { 131 | return this.datasource.getDimensionsAndMetrics(this.target.druidDS) 132 | .then(function (dimsAndMetrics) { 133 | callback([].concat(dimsAndMetrics.metrics).concat(dimsAndMetrics.dimensions)); 134 | }); 135 | }; 136 | 137 | this.getDimensionsAndMetrics = (query, callback) => { 138 | this.datasource.getDimensionsAndMetrics(this.target.druidDS) 139 | .then(callback); 140 | }; 141 | 142 | this.getFilterValues = (query, callback) => { 143 | let dimension = this.target.currentFilter.dimension; 144 | this.datasource.getFilterValues(this.target, this.panelCtrl.range, query) 145 | .then(function (results) { 146 | callback(results.data[0].result.map(function (datum) { return datum[dimension]; })); 147 | }); 148 | }; 149 | 150 | //this.$on('typeahead-updated', function() { 151 | // $timeout(this.targetBlur); 152 | //}); 153 | } 154 | 155 | cachedAndCoalesced(ioFn, $scope, cacheName) { 156 | const promiseName = cacheName + "Promise"; 157 | if (!$scope[cacheName]) { 158 | if (!$scope[promiseName]) { 159 | $scope[promiseName] = ioFn() 160 | .then(function (result) { 161 | $scope[promiseName] = null; 162 | $scope[cacheName] = result; 163 | return $scope[cacheName]; 164 | }); 165 | } 166 | return $scope[promiseName]; 167 | } else { 168 | let deferred;// = $q.defer(); 169 | deferred.resolve($scope[cacheName]); 170 | return deferred.promise; 171 | } 172 | } 173 | 174 | targetBlur() { 175 | this.errors = this.validateTarget(); 176 | this.refresh(); 177 | } 178 | 179 | addFilter() { 180 | if (!this.addFilterMode) { 181 | //Enabling this mode will display the filter inputs 182 | this.addFilterMode = true; 183 | return; 184 | } 185 | 186 | if (!this.target.filters) { 187 | this.target.filters = []; 188 | } 189 | 190 | this.target.errors = this.validateTarget(); 191 | if (!this.target.errors.currentFilter) { 192 | //Add new filter to the list 193 | this.target.filters.push(this.target.currentFilter); 194 | this.clearCurrentFilter(); 195 | this.addFilterMode = false; 196 | } 197 | 198 | this.targetBlur(); 199 | } 200 | 201 | editFilter(index) { 202 | this.addFilterMode = true; 203 | const delFilter = this.target.filters.splice(index, 1); 204 | this.target.currentFilter = delFilter[0]; 205 | } 206 | 207 | removeFilter(index) { 208 | this.target.filters.splice(index, 1); 209 | this.targetBlur(); 210 | } 211 | 212 | clearCurrentFilter() { 213 | this.target.currentFilter = { type: this.defaultFilterType }; 214 | this.addFilterMode = false; 215 | this.targetBlur(); 216 | } 217 | 218 | addSelectDimensions() { 219 | if (!this.addDimensionsMode) { 220 | this.addDimensionsMode = true; 221 | return; 222 | } 223 | if (!this.target.selectDimensions) { 224 | this.target.selectDimensions = []; 225 | } 226 | this.target.selectDimensions.push(this.target.currentSelect.dimension); 227 | this.clearCurrentSelectDimension(); 228 | } 229 | 230 | removeSelectDimension(index) { 231 | this.target.selectDimensions.splice(index, 1); 232 | this.targetBlur(); 233 | } 234 | 235 | clearCurrentSelectDimension() { 236 | this.target.currentSelect.dimension = this.defaultSelectDimension; 237 | this.addDimensionsMode = false; 238 | this.targetBlur(); 239 | } 240 | 241 | addSelectMetrics() { 242 | if (!this.addMetricsMode) { 243 | this.addMetricsMode = true; 244 | return; 245 | } 246 | if (!this.target.selectMetrics) { 247 | this.target.selectMetrics = []; 248 | } 249 | this.target.selectMetrics.push(this.target.currentSelect.metric); 250 | this.clearCurrentSelectMetric(); 251 | } 252 | 253 | removeSelectMetric(index) { 254 | this.target.selectMetrics.splice(index, 1); 255 | this.targetBlur(); 256 | } 257 | 258 | clearCurrentSelectMetric() { 259 | this.target.currentSelect.metric = this.defaultSelectMetric; 260 | this.addMetricsMode = false; 261 | this.targetBlur(); 262 | } 263 | 264 | addAggregator() { 265 | if (!this.addAggregatorMode) { 266 | this.addAggregatorMode = true; 267 | return; 268 | } 269 | 270 | if (!this.target.aggregators) { 271 | this.target.aggregators = []; 272 | } 273 | 274 | this.target.errors = this.validateTarget(); 275 | if (!this.target.errors.currentAggregator) { 276 | //Add new aggregator to the list 277 | this.target.aggregators.push(this.target.currentAggregator); 278 | this.clearCurrentAggregator(); 279 | this.addAggregatorMode = false; 280 | } 281 | 282 | this.targetBlur(); 283 | } 284 | 285 | editAggregator(index) { 286 | this.addAggregatorMode = true; 287 | const delAggregator = this.target.aggregators.splice(index, 1); 288 | this.target.currentAggregator = delAggregator[0]; 289 | } 290 | removeAggregator(index) { 291 | this.target.aggregators.splice(index, 1); 292 | this.targetBlur(); 293 | } 294 | 295 | clearCurrentAggregator() { 296 | this.target.currentAggregator = { type: this.defaultAggregatorType }; 297 | this.addAggregatorMode = false; 298 | this.targetBlur(); 299 | } 300 | 301 | addPostAggregator() { 302 | if (!this.addPostAggregatorMode) { 303 | this.addPostAggregatorMode = true; 304 | return; 305 | } 306 | 307 | if (!this.target.postAggregators) { 308 | this.target.postAggregators = []; 309 | } 310 | 311 | this.target.errors = this.validateTarget(); 312 | if (!this.target.errors.currentPostAggregator) { 313 | //Add new post aggregator to the list 314 | this.target.postAggregators.push(this.target.currentPostAggregator); 315 | this.clearCurrentPostAggregator(); 316 | this.addPostAggregatorMode = false; 317 | } 318 | 319 | this.targetBlur(); 320 | } 321 | 322 | removePostAggregator(index) { 323 | this.target.postAggregators.splice(index, 1); 324 | this.targetBlur(); 325 | } 326 | 327 | clearCurrentPostAggregator() { 328 | this.target.currentPostAggregator = _.clone(this.defaultPostAggregator);; 329 | this.addPostAggregatorMode = false; 330 | this.targetBlur(); 331 | } 332 | 333 | isValidFilterType(type) { 334 | return _.has(this.filterValidators, type); 335 | } 336 | 337 | isValidAggregatorType(type) { 338 | return _.has(this.aggregatorValidators, type); 339 | } 340 | 341 | isValidPostAggregatorType(type) { 342 | return _.has(this.postAggregatorValidators, type); 343 | } 344 | 345 | isValidQueryType(type) { 346 | return _.has(this.queryTypeValidators, type); 347 | } 348 | 349 | isValidArithmeticPostAggregatorFn(fn) { 350 | return _.includes(this.arithmeticPostAggregator, fn); 351 | } 352 | 353 | validateMaxDataPoints(target, errs) { 354 | if (target.maxDataPoints) { 355 | const intMax = parseInt(target.maxDataPoints); 356 | if (isNaN(intMax) || intMax <= 0) { 357 | errs.maxDataPoints = "Must be a positive integer"; 358 | return false; 359 | } 360 | target.maxDataPoints = intMax; 361 | } 362 | return true; 363 | } 364 | 365 | validateLimit(target, errs) { 366 | if (!target.limit) { 367 | errs.limit = "Must specify a limit"; 368 | return false; 369 | } 370 | const intLimit = parseInt(target.limit); 371 | if (isNaN(intLimit)) { 372 | errs.limit = "Limit must be a integer"; 373 | return false; 374 | } 375 | target.limit = intLimit; 376 | return true; 377 | } 378 | 379 | validateOrderBy(target) { 380 | if (target.orderBy && !Array.isArray(target.orderBy)) { 381 | target.orderBy = target.orderBy.split(","); 382 | } 383 | return true; 384 | } 385 | 386 | validateGroupByQuery(target, errs) { 387 | if (target.groupBy && !Array.isArray(target.groupBy)) { 388 | target.groupBy = target.groupBy.split(","); 389 | } 390 | if (!target.groupBy) { 391 | errs.groupBy = "Must list dimensions to group by."; 392 | return false; 393 | } 394 | if (!this.validateLimit(target, errs) || !this.validateOrderBy(target)) { 395 | return false; 396 | } 397 | return true; 398 | } 399 | 400 | validateTopNQuery(target, errs) { 401 | if (!target.dimension) { 402 | errs.dimension = "Must specify a dimension"; 403 | return false; 404 | } 405 | if (!target.druidMetric) { 406 | errs.druidMetric = "Must specify a metric"; 407 | return false; 408 | } 409 | if (!this.validateLimit(target, errs)) { 410 | return false; 411 | } 412 | return true; 413 | } 414 | 415 | validateSelectQuery(target, errs) { 416 | if (!target.selectThreshold && target.selectThreshold <= 0) { 417 | errs.selectThreshold = "Must specify a positive number"; 418 | return false; 419 | } 420 | return true; 421 | } 422 | 423 | validateSelectorFilter(target) { 424 | if (!target.currentFilter.dimension) { 425 | return "Must provide dimension name for selector filter."; 426 | } 427 | if (!target.currentFilter.value) { 428 | // TODO Empty string is how you match null or empty in Druid 429 | return "Must provide dimension value for selector filter."; 430 | } 431 | return null; 432 | } 433 | 434 | validateJavascriptFilter(target) { 435 | if (!target.currentFilter.dimension) { 436 | return "Must provide dimension name for javascript filter."; 437 | } 438 | if (!target.currentFilter["function"]) { 439 | return "Must provide func value for javascript filter."; 440 | } 441 | return null; 442 | } 443 | 444 | validateRegexFilter(target) { 445 | if (!target.currentFilter.dimension) { 446 | return "Must provide dimension name for regex filter."; 447 | } 448 | if (!target.currentFilter.pattern) { 449 | return "Must provide pattern for regex filter."; 450 | } 451 | return null; 452 | } 453 | 454 | validateCountAggregator(target) { 455 | if (!target.currentAggregator.name) { 456 | return "Must provide an output name for count aggregator."; 457 | } 458 | return null; 459 | } 460 | 461 | validateCardinalityAggregator(type, target) { 462 | if (!target.currentAggregator.name) { 463 | return "Must provide an output name for " + type + " aggregator."; 464 | } 465 | 466 | return null; 467 | } 468 | 469 | validateSimpleAggregator(type, target) { 470 | if (!target.currentAggregator.name) { 471 | return "Must provide an output name for " + type + " aggregator."; 472 | } 473 | if (!target.currentAggregator.fieldName) { 474 | return "Must provide a metric name for " + type + " aggregator."; 475 | } 476 | //TODO - check that fieldName is a valid metric (exists and of correct type) 477 | return null; 478 | } 479 | 480 | validateApproxHistogramFoldAggregator(target) { 481 | const err = this.validateSimpleAggregator('approxHistogramFold', target); 482 | if (err) { return err; } 483 | //TODO - check that resolution and numBuckets are ints (if given) 484 | //TODO - check that lowerLimit and upperLimit are flots (if given) 485 | return null; 486 | } 487 | 488 | validateThetaSketchAggregator(target) { 489 | const err = this.validateSimpleAggregator('thetaSketch', target); 490 | if (err) { return err; } 491 | return null; 492 | } 493 | 494 | validateSimplePostAggregator(type, target) { 495 | if (!target.currentPostAggregator.name) { 496 | return "Must provide an output name for " + type + " post aggregator."; 497 | } 498 | if (!target.currentPostAggregator.fieldName) { 499 | return "Must provide an aggregator name for " + type + " post aggregator."; 500 | } 501 | //TODO - check that fieldName is a valid aggregation (exists and of correct type) 502 | return null; 503 | } 504 | 505 | validateMaxPostAggregator(target) { 506 | const err = this.validateSimplePostAggregator('max', target); 507 | if (err) { return err; } 508 | return null; 509 | } 510 | 511 | validateMinPostAggregator(target) { 512 | const err = this.validateSimplePostAggregator('min', target); 513 | if (err) { return err; } 514 | return null; 515 | } 516 | 517 | validateQuantilePostAggregator(target) { 518 | const err = this.validateSimplePostAggregator('quantile', target); 519 | if (err) { return err; } 520 | if (!target.currentPostAggregator.probability) { 521 | return "Must provide a probability for the quantile post aggregator."; 522 | } 523 | return null; 524 | } 525 | 526 | validateArithmeticPostAggregator(target) { 527 | if (!target.currentPostAggregator.name) { 528 | return "Must provide an output name for arithmetic post aggregator."; 529 | } 530 | if (!target.currentPostAggregator.fn) { 531 | return "Must provide a function for arithmetic post aggregator."; 532 | } 533 | if (!this.isValidArithmeticPostAggregatorFn(target.currentPostAggregator.fn)) { 534 | return "Invalid arithmetic function"; 535 | } 536 | if (!target.currentPostAggregator.fields) { 537 | return "Must provide a list of fields for arithmetic post aggregator."; 538 | } else { 539 | if (!Array.isArray(target.currentPostAggregator.fields)) { 540 | target.currentPostAggregator.fields = target.currentPostAggregator.fields 541 | .split(",") 542 | .map(function (f) { return f.trim(); }) 543 | .map(function (f) { return { type: "fieldAccess", fieldName: f }; }); 544 | } 545 | if (target.currentPostAggregator.fields.length < 2) { 546 | return "Must provide at least two fields for arithmetic post aggregator."; 547 | } 548 | } 549 | return null; 550 | } 551 | 552 | validateTarget() { 553 | let validatorOut, errs: any = {}; 554 | if (!this.target.druidDS) { 555 | errs.druidDS = "You must supply a druidDS name."; 556 | } 557 | 558 | if (!this.target.queryType) { 559 | errs.queryType = "You must supply a query type."; 560 | } else if (!this.isValidQueryType(this.target.queryType)) { 561 | errs.queryType = "Unknown query type: " + this.target.queryType + "."; 562 | } else { 563 | this.queryTypeValidators[this.target.queryType](this.target, errs); 564 | } 565 | 566 | if (this.target.shouldOverrideGranularity) { 567 | if (this.target.customGranularity) { 568 | if (!_.includes(this.customGranularity, this.target.customGranularity)) { 569 | errs.customGranularity = "Invalid granularity."; 570 | } 571 | } else { 572 | errs.customGranularity = "You must choose a granularity."; 573 | } 574 | } else { 575 | this.validateMaxDataPoints(this.target, errs); 576 | } 577 | 578 | if (this.addFilterMode) { 579 | if (!this.isValidFilterType(this.target.currentFilter.type)) { 580 | errs.currentFilter = "Invalid filter type: " + this.target.currentFilter.type + "."; 581 | } else { 582 | validatorOut = this.filterValidators[this.target.currentFilter.type](this.target); 583 | if (validatorOut) { 584 | errs.currentFilter = validatorOut; 585 | } 586 | } 587 | } 588 | 589 | if (this.addAggregatorMode) { 590 | if (!this.isValidAggregatorType(this.target.currentAggregator.type)) { 591 | errs.currentAggregator = "Invalid aggregator type: " + this.target.currentAggregator.type + "."; 592 | } else { 593 | validatorOut = this.aggregatorValidators[this.target.currentAggregator.type](this.target); 594 | if (validatorOut) { 595 | errs.currentAggregator = validatorOut; 596 | } 597 | } 598 | } 599 | 600 | if (_.isEmpty(this.target.aggregators) && !_.isEqual(this.target.queryType, "select")) { 601 | errs.aggregators = "You must supply at least one aggregator"; 602 | } 603 | 604 | if (this.addPostAggregatorMode) { 605 | if (!this.isValidPostAggregatorType(this.target.currentPostAggregator.type)) { 606 | errs.currentPostAggregator = "Invalid post aggregator type: " + this.target.currentPostAggregator.type + "."; 607 | } else { 608 | validatorOut = this.postAggregatorValidators[this.target.currentPostAggregator.type](this.target); 609 | if (validatorOut) { 610 | errs.currentPostAggregator = validatorOut; 611 | } 612 | } 613 | } 614 | 615 | return errs; 616 | } 617 | } 618 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "target": "ES6", 5 | "module": "system", 6 | "sourceMap": true, 7 | "declaration": true, 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "noImplicitAny": false, 11 | "outDir": "dist/" 12 | }, 13 | "exclude": [ 14 | "node_modules" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [false, "check-space"], 5 | "curly": true, 6 | "eofline": true, 7 | "forin": false, 8 | "indent": [true, "spaces"], 9 | "label-position": true, 10 | "label-undefined": true, 11 | "max-line-length": [true, 140], 12 | "member-access": false, 13 | "no-arg": true, 14 | "no-bitwise": true, 15 | "no-console": [true, 16 | "debug", 17 | "info", 18 | "time", 19 | "timeEnd", 20 | "trace" 21 | ], 22 | "no-construct": true, 23 | "no-debugger": true, 24 | "no-duplicate-key": true, 25 | "no-duplicate-variable": true, 26 | "no-empty": false, 27 | "no-eval": true, 28 | "no-inferrable-types": true, 29 | "no-shadowed-variable": false, 30 | "no-string-literal": false, 31 | "no-switch-case-fall-through": false, 32 | "no-trailing-whitespace": true, 33 | "no-unused-expression": false, 34 | "no-unused-variable": false, 35 | "no-unreachable": true, 36 | "no-use-before-declare": true, 37 | "no-var-keyword": false, 38 | "object-literal-sort-keys": false, 39 | "one-line": [true, 40 | "check-open-brace", 41 | "check-catch", 42 | "check-else" 43 | ], 44 | "radix": false, 45 | "semicolon": true, 46 | "triple-equals": [true, "allow-null-check"], 47 | "typedef-whitespace": [true, { 48 | "call-signature": "nospace", 49 | "index-signature": "nospace", 50 | "parameter": "nospace", 51 | "property-declaration": "nospace", 52 | "variable-declaration": "nospace" 53 | }], 54 | "variable-name": [true, "ban-keywords"], 55 | "whitespace": [true, 56 | "check-branch", 57 | "check-decl", 58 | "check-type" 59 | ] 60 | } 61 | } 62 | --------------------------------------------------------------------------------