├── .dockerignore ├── .gitignore ├── Dockerfile ├── Dockerfile-dev ├── LICENSE ├── README.md ├── docker-compose.yml ├── package-lock.json ├── package.json ├── server.js └── settings.env.example /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | settings.env -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | npm-debug.log* 4 | node_modules 5 | .npm 6 | settings.env -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10.15-alpine 2 | WORKDIR /app 3 | COPY package*.json ./ 4 | RUN npm install 5 | COPY . . 6 | EXPOSE 3000 7 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /Dockerfile-dev: -------------------------------------------------------------------------------- 1 | FROM node:10.15-alpine 2 | WORKDIR /app 3 | EXPOSE 3000 4 | CMD [ "npm", "start" ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Mike Frank 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jira Grafana JSON Datasource Plugin 2 | ### Connect Grafana to Jira Cloud to retrieve metrics on your Jira issues. 3 | 4 | The original intention for this project was to allow me to show a singlestat count of open issues for my team's Jira Service Desk. 5 | 6 | ![Example](https://preview.ibb.co/hD2txU/screenshot_2018_09_23_094255.png) 7 | 8 | ## Requirements 9 | This project only requires your Grafana instance to have the [SimpleJson plugin](https://grafana.com/plugins/grafana-simple-json-datasource/installation) installed. 10 | 11 | ## Installation 12 | 13 | Run the Docker container (replace environment variables with your Jira Cloud credentials): 14 | ```bash 15 | $ docker run -d -p 3000:3000 --name jira-grafana -e JIRA_HOST=myjira.atlassian.net -e JIRA_USER=mike@bluefrog.ca -e JIRA_PASS=XXXX bluefrg/jira-grafana-json-datasource 16 | ``` 17 | 18 | Confirm running by visiting: http://localhost:3000 19 | 20 | *Note: This project has only been tested locally, and as a ECS task using Jira Cloud. Jira On-Prem is not supported by the dependencies used in this project.* 21 | 22 | ### Atlassian credentials 23 | It's recommended to use API tokens from your [Atlassian API token page](https://id.atlassian.com/manage/api-tokens) for authentication instead of your primary account username and password. 24 | 25 | ### Testing Jira connection 26 | You can validate the connection to Jira is authenticating properly by visiting the `test-jira` endpoint at: http://localhost:3000/test-jira 27 | 28 | A successful authentication will return the current user, a failure will return the HTTP response and detailed error message from Jira. 29 | 30 | ### docker-compose.yml 31 | The docker-compose is included as an example of how to test Grafana and the Jira Grafana JSON Datasource integration. Not intended for production use. 32 | 33 | ## Setup 34 | To add this as a data source in Grafana, go to Configuration -> Data Sources and click Add. For the Type choose SimpleJson. Enter the root URL for this project (e.g. http://localhost:3000). 35 | 36 | When adding a panel to a dashboard, choose the newly created data source. Under the metrics tab, you will see your Jira filters as an option to plot on your panel. 37 | 38 | ![Example](https://thumb.ibb.co/fxS7rp/screenshot_2018_09_23_093243.png) 39 | 40 | ### Authentication 41 | You can require authentication by adding a `HTTP_USER` and `HTTP_PASS` environment variable. 42 | 43 | You will need to reconfigure your data source to use "Basic Auth". 44 | 45 | ## License 46 | 47 | This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info. 48 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | networks: 4 | web: 5 | driver: "bridge" 6 | services: 7 | jira-grafana: 8 | # Copy settings.env.example to settings.env 9 | env_file: settings.env 10 | build: 11 | context: . 12 | dockerfile: Dockerfile-dev 13 | volumes: 14 | - ".:/app" 15 | ports: 16 | - "3000:3000" 17 | networks: 18 | - "web" 19 | grafana: 20 | image: grafana/grafana 21 | environment: 22 | - "GF_INSTALL_PLUGINS=grafana-simple-json-datasource" 23 | ports: 24 | - "3010:3000" 25 | networks: 26 | - "web" -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jira-grafana-json-datasource", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "requires": { 12 | "mime-types": "~2.1.18", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "ajv": { 17 | "version": "6.10.0", 18 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", 19 | "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", 20 | "requires": { 21 | "fast-deep-equal": "^2.0.1", 22 | "fast-json-stable-stringify": "^2.0.0", 23 | "json-schema-traverse": "^0.4.1", 24 | "uri-js": "^4.2.2" 25 | } 26 | }, 27 | "array-flatten": { 28 | "version": "1.1.1", 29 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 30 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 31 | }, 32 | "asn1": { 33 | "version": "0.2.4", 34 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 35 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 36 | "requires": { 37 | "safer-buffer": "~2.1.0" 38 | } 39 | }, 40 | "assert-plus": { 41 | "version": "1.0.0", 42 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 43 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 44 | }, 45 | "asynckit": { 46 | "version": "0.4.0", 47 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 48 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 49 | }, 50 | "aws-sign2": { 51 | "version": "0.7.0", 52 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 53 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 54 | }, 55 | "aws4": { 56 | "version": "1.8.0", 57 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 58 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" 59 | }, 60 | "basic-auth": { 61 | "version": "2.0.1", 62 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 63 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 64 | "requires": { 65 | "safe-buffer": "5.1.2" 66 | } 67 | }, 68 | "bcrypt-pbkdf": { 69 | "version": "1.0.2", 70 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 71 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 72 | "requires": { 73 | "tweetnacl": "^0.14.3" 74 | } 75 | }, 76 | "body-parser": { 77 | "version": "1.18.3", 78 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 79 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 80 | "requires": { 81 | "bytes": "3.0.0", 82 | "content-type": "~1.0.4", 83 | "debug": "2.6.9", 84 | "depd": "~1.1.2", 85 | "http-errors": "~1.6.3", 86 | "iconv-lite": "0.4.23", 87 | "on-finished": "~2.3.0", 88 | "qs": "6.5.2", 89 | "raw-body": "2.3.3", 90 | "type-is": "~1.6.16" 91 | } 92 | }, 93 | "bytes": { 94 | "version": "3.0.0", 95 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 96 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 97 | }, 98 | "caseless": { 99 | "version": "0.12.0", 100 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 101 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 102 | }, 103 | "combined-stream": { 104 | "version": "1.0.7", 105 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", 106 | "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", 107 | "requires": { 108 | "delayed-stream": "~1.0.0" 109 | } 110 | }, 111 | "content-disposition": { 112 | "version": "0.5.2", 113 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 114 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 115 | }, 116 | "content-type": { 117 | "version": "1.0.4", 118 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 119 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 120 | }, 121 | "cookie": { 122 | "version": "0.3.1", 123 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 124 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 125 | }, 126 | "cookie-signature": { 127 | "version": "1.0.6", 128 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 129 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 130 | }, 131 | "core-util-is": { 132 | "version": "1.0.2", 133 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 134 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 135 | }, 136 | "dashdash": { 137 | "version": "1.14.1", 138 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 139 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 140 | "requires": { 141 | "assert-plus": "^1.0.0" 142 | } 143 | }, 144 | "debug": { 145 | "version": "2.6.9", 146 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 147 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 148 | "requires": { 149 | "ms": "2.0.0" 150 | } 151 | }, 152 | "delayed-stream": { 153 | "version": "1.0.0", 154 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 155 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 156 | }, 157 | "depd": { 158 | "version": "1.1.2", 159 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 160 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 161 | }, 162 | "destroy": { 163 | "version": "1.0.4", 164 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 165 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 166 | }, 167 | "ecc-jsbn": { 168 | "version": "0.1.2", 169 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 170 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 171 | "requires": { 172 | "jsbn": "~0.1.0", 173 | "safer-buffer": "^2.1.0" 174 | } 175 | }, 176 | "ee-first": { 177 | "version": "1.1.1", 178 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 179 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 180 | }, 181 | "encodeurl": { 182 | "version": "1.0.2", 183 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 184 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 185 | }, 186 | "escape-html": { 187 | "version": "1.0.3", 188 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 189 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 190 | }, 191 | "etag": { 192 | "version": "1.8.1", 193 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 194 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 195 | }, 196 | "express": { 197 | "version": "4.16.4", 198 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", 199 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", 200 | "requires": { 201 | "accepts": "~1.3.5", 202 | "array-flatten": "1.1.1", 203 | "body-parser": "1.18.3", 204 | "content-disposition": "0.5.2", 205 | "content-type": "~1.0.4", 206 | "cookie": "0.3.1", 207 | "cookie-signature": "1.0.6", 208 | "debug": "2.6.9", 209 | "depd": "~1.1.2", 210 | "encodeurl": "~1.0.2", 211 | "escape-html": "~1.0.3", 212 | "etag": "~1.8.1", 213 | "finalhandler": "1.1.1", 214 | "fresh": "0.5.2", 215 | "merge-descriptors": "1.0.1", 216 | "methods": "~1.1.2", 217 | "on-finished": "~2.3.0", 218 | "parseurl": "~1.3.2", 219 | "path-to-regexp": "0.1.7", 220 | "proxy-addr": "~2.0.4", 221 | "qs": "6.5.2", 222 | "range-parser": "~1.2.0", 223 | "safe-buffer": "5.1.2", 224 | "send": "0.16.2", 225 | "serve-static": "1.13.2", 226 | "setprototypeof": "1.1.0", 227 | "statuses": "~1.4.0", 228 | "type-is": "~1.6.16", 229 | "utils-merge": "1.0.1", 230 | "vary": "~1.1.2" 231 | }, 232 | "dependencies": { 233 | "statuses": { 234 | "version": "1.4.0", 235 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 236 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 237 | } 238 | } 239 | }, 240 | "extend": { 241 | "version": "3.0.2", 242 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 243 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 244 | }, 245 | "extsprintf": { 246 | "version": "1.3.0", 247 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 248 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 249 | }, 250 | "fast-deep-equal": { 251 | "version": "2.0.1", 252 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 253 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" 254 | }, 255 | "fast-json-stable-stringify": { 256 | "version": "2.0.0", 257 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 258 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 259 | }, 260 | "finalhandler": { 261 | "version": "1.1.1", 262 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 263 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 264 | "requires": { 265 | "debug": "2.6.9", 266 | "encodeurl": "~1.0.2", 267 | "escape-html": "~1.0.3", 268 | "on-finished": "~2.3.0", 269 | "parseurl": "~1.3.2", 270 | "statuses": "~1.4.0", 271 | "unpipe": "~1.0.0" 272 | }, 273 | "dependencies": { 274 | "statuses": { 275 | "version": "1.4.0", 276 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 277 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 278 | } 279 | } 280 | }, 281 | "forever-agent": { 282 | "version": "0.6.1", 283 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 284 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 285 | }, 286 | "form-data": { 287 | "version": "2.3.3", 288 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 289 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 290 | "requires": { 291 | "asynckit": "^0.4.0", 292 | "combined-stream": "^1.0.6", 293 | "mime-types": "^2.1.12" 294 | } 295 | }, 296 | "forwarded": { 297 | "version": "0.1.2", 298 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 299 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 300 | }, 301 | "fresh": { 302 | "version": "0.5.2", 303 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 304 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 305 | }, 306 | "getpass": { 307 | "version": "0.1.7", 308 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 309 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 310 | "requires": { 311 | "assert-plus": "^1.0.0" 312 | } 313 | }, 314 | "har-schema": { 315 | "version": "2.0.0", 316 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 317 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 318 | }, 319 | "har-validator": { 320 | "version": "5.1.3", 321 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", 322 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", 323 | "requires": { 324 | "ajv": "^6.5.5", 325 | "har-schema": "^2.0.0" 326 | } 327 | }, 328 | "http-errors": { 329 | "version": "1.6.3", 330 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 331 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 332 | "requires": { 333 | "depd": "~1.1.2", 334 | "inherits": "2.0.3", 335 | "setprototypeof": "1.1.0", 336 | "statuses": ">= 1.4.0 < 2" 337 | } 338 | }, 339 | "http-signature": { 340 | "version": "1.2.0", 341 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 342 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 343 | "requires": { 344 | "assert-plus": "^1.0.0", 345 | "jsprim": "^1.2.2", 346 | "sshpk": "^1.7.0" 347 | } 348 | }, 349 | "iconv-lite": { 350 | "version": "0.4.23", 351 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 352 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 353 | "requires": { 354 | "safer-buffer": ">= 2.1.2 < 3" 355 | } 356 | }, 357 | "inherits": { 358 | "version": "2.0.3", 359 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 360 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 361 | }, 362 | "ipaddr.js": { 363 | "version": "1.8.0", 364 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", 365 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" 366 | }, 367 | "is-typedarray": { 368 | "version": "1.0.0", 369 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 370 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 371 | }, 372 | "isstream": { 373 | "version": "0.1.2", 374 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 375 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 376 | }, 377 | "jira-connector": { 378 | "version": "2.10.0", 379 | "resolved": "https://registry.npmjs.org/jira-connector/-/jira-connector-2.10.0.tgz", 380 | "integrity": "sha512-nqRq2iEn0w/shTkM2jubQJfnTZxQvSMdmAkNZFB6eW3hn96MgKuHfQ8/CaGnZACrbqRaFTXhPe85GP+y0pemdA==", 381 | "requires": { 382 | "oauth": "^0.9.12", 383 | "request": "^2.83.0" 384 | } 385 | }, 386 | "jsbn": { 387 | "version": "0.1.1", 388 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 389 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 390 | }, 391 | "json-schema": { 392 | "version": "0.2.3", 393 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 394 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 395 | }, 396 | "json-schema-traverse": { 397 | "version": "0.4.1", 398 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 399 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 400 | }, 401 | "json-stringify-safe": { 402 | "version": "5.0.1", 403 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 404 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 405 | }, 406 | "jsprim": { 407 | "version": "1.4.1", 408 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 409 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 410 | "requires": { 411 | "assert-plus": "1.0.0", 412 | "extsprintf": "1.3.0", 413 | "json-schema": "0.2.3", 414 | "verror": "1.10.0" 415 | } 416 | }, 417 | "media-typer": { 418 | "version": "0.3.0", 419 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 420 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 421 | }, 422 | "merge-descriptors": { 423 | "version": "1.0.1", 424 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 425 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 426 | }, 427 | "methods": { 428 | "version": "1.1.2", 429 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 430 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 431 | }, 432 | "mime": { 433 | "version": "1.4.1", 434 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 435 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 436 | }, 437 | "mime-db": { 438 | "version": "1.38.0", 439 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", 440 | "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" 441 | }, 442 | "mime-types": { 443 | "version": "2.1.22", 444 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", 445 | "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", 446 | "requires": { 447 | "mime-db": "~1.38.0" 448 | } 449 | }, 450 | "morgan": { 451 | "version": "1.9.1", 452 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", 453 | "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", 454 | "requires": { 455 | "basic-auth": "~2.0.0", 456 | "debug": "2.6.9", 457 | "depd": "~1.1.2", 458 | "on-finished": "~2.3.0", 459 | "on-headers": "~1.0.1" 460 | } 461 | }, 462 | "ms": { 463 | "version": "2.0.0", 464 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 465 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 466 | }, 467 | "negotiator": { 468 | "version": "0.6.1", 469 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 470 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 471 | }, 472 | "oauth": { 473 | "version": "0.9.15", 474 | "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", 475 | "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" 476 | }, 477 | "oauth-sign": { 478 | "version": "0.9.0", 479 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 480 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 481 | }, 482 | "on-finished": { 483 | "version": "2.3.0", 484 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 485 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 486 | "requires": { 487 | "ee-first": "1.1.1" 488 | } 489 | }, 490 | "on-headers": { 491 | "version": "1.0.2", 492 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 493 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" 494 | }, 495 | "parseurl": { 496 | "version": "1.3.2", 497 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 498 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 499 | }, 500 | "passport": { 501 | "version": "0.4.0", 502 | "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", 503 | "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", 504 | "requires": { 505 | "passport-strategy": "1.x.x", 506 | "pause": "0.0.1" 507 | } 508 | }, 509 | "passport-anonymous": { 510 | "version": "1.0.1", 511 | "resolved": "https://registry.npmjs.org/passport-anonymous/-/passport-anonymous-1.0.1.tgz", 512 | "integrity": "sha1-JB43J07ETft/bK0jS0HEODhrwRc=", 513 | "requires": { 514 | "passport-strategy": "1.x.x" 515 | } 516 | }, 517 | "passport-http": { 518 | "version": "0.3.0", 519 | "resolved": "https://registry.npmjs.org/passport-http/-/passport-http-0.3.0.tgz", 520 | "integrity": "sha1-juU9Q4C+nGDfIVGSUCmCb3cRVgM=", 521 | "requires": { 522 | "passport-strategy": "1.x.x" 523 | } 524 | }, 525 | "passport-strategy": { 526 | "version": "1.0.0", 527 | "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", 528 | "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" 529 | }, 530 | "path-to-regexp": { 531 | "version": "0.1.7", 532 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 533 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 534 | }, 535 | "pause": { 536 | "version": "0.0.1", 537 | "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", 538 | "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" 539 | }, 540 | "performance-now": { 541 | "version": "2.1.0", 542 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 543 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 544 | }, 545 | "proxy-addr": { 546 | "version": "2.0.4", 547 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", 548 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", 549 | "requires": { 550 | "forwarded": "~0.1.2", 551 | "ipaddr.js": "1.8.0" 552 | } 553 | }, 554 | "psl": { 555 | "version": "1.1.31", 556 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", 557 | "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" 558 | }, 559 | "punycode": { 560 | "version": "2.1.1", 561 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 562 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 563 | }, 564 | "qs": { 565 | "version": "6.5.2", 566 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 567 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 568 | }, 569 | "range-parser": { 570 | "version": "1.2.0", 571 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 572 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 573 | }, 574 | "raw-body": { 575 | "version": "2.3.3", 576 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 577 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 578 | "requires": { 579 | "bytes": "3.0.0", 580 | "http-errors": "1.6.3", 581 | "iconv-lite": "0.4.23", 582 | "unpipe": "1.0.0" 583 | } 584 | }, 585 | "request": { 586 | "version": "2.88.0", 587 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 588 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 589 | "requires": { 590 | "aws-sign2": "~0.7.0", 591 | "aws4": "^1.8.0", 592 | "caseless": "~0.12.0", 593 | "combined-stream": "~1.0.6", 594 | "extend": "~3.0.2", 595 | "forever-agent": "~0.6.1", 596 | "form-data": "~2.3.2", 597 | "har-validator": "~5.1.0", 598 | "http-signature": "~1.2.0", 599 | "is-typedarray": "~1.0.0", 600 | "isstream": "~0.1.2", 601 | "json-stringify-safe": "~5.0.1", 602 | "mime-types": "~2.1.19", 603 | "oauth-sign": "~0.9.0", 604 | "performance-now": "^2.1.0", 605 | "qs": "~6.5.2", 606 | "safe-buffer": "^5.1.2", 607 | "tough-cookie": "~2.4.3", 608 | "tunnel-agent": "^0.6.0", 609 | "uuid": "^3.3.2" 610 | } 611 | }, 612 | "safe-buffer": { 613 | "version": "5.1.2", 614 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 615 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 616 | }, 617 | "safer-buffer": { 618 | "version": "2.1.2", 619 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 620 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 621 | }, 622 | "send": { 623 | "version": "0.16.2", 624 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 625 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 626 | "requires": { 627 | "debug": "2.6.9", 628 | "depd": "~1.1.2", 629 | "destroy": "~1.0.4", 630 | "encodeurl": "~1.0.2", 631 | "escape-html": "~1.0.3", 632 | "etag": "~1.8.1", 633 | "fresh": "0.5.2", 634 | "http-errors": "~1.6.2", 635 | "mime": "1.4.1", 636 | "ms": "2.0.0", 637 | "on-finished": "~2.3.0", 638 | "range-parser": "~1.2.0", 639 | "statuses": "~1.4.0" 640 | }, 641 | "dependencies": { 642 | "statuses": { 643 | "version": "1.4.0", 644 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 645 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 646 | } 647 | } 648 | }, 649 | "serve-static": { 650 | "version": "1.13.2", 651 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 652 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 653 | "requires": { 654 | "encodeurl": "~1.0.2", 655 | "escape-html": "~1.0.3", 656 | "parseurl": "~1.3.2", 657 | "send": "0.16.2" 658 | } 659 | }, 660 | "setprototypeof": { 661 | "version": "1.1.0", 662 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 663 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 664 | }, 665 | "sshpk": { 666 | "version": "1.16.1", 667 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", 668 | "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", 669 | "requires": { 670 | "asn1": "~0.2.3", 671 | "assert-plus": "^1.0.0", 672 | "bcrypt-pbkdf": "^1.0.0", 673 | "dashdash": "^1.12.0", 674 | "ecc-jsbn": "~0.1.1", 675 | "getpass": "^0.1.1", 676 | "jsbn": "~0.1.0", 677 | "safer-buffer": "^2.0.2", 678 | "tweetnacl": "~0.14.0" 679 | } 680 | }, 681 | "statuses": { 682 | "version": "1.5.0", 683 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 684 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 685 | }, 686 | "tough-cookie": { 687 | "version": "2.4.3", 688 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 689 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 690 | "requires": { 691 | "psl": "^1.1.24", 692 | "punycode": "^1.4.1" 693 | }, 694 | "dependencies": { 695 | "punycode": { 696 | "version": "1.4.1", 697 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 698 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 699 | } 700 | } 701 | }, 702 | "tunnel-agent": { 703 | "version": "0.6.0", 704 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 705 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 706 | "requires": { 707 | "safe-buffer": "^5.0.1" 708 | } 709 | }, 710 | "tweetnacl": { 711 | "version": "0.14.5", 712 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 713 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 714 | }, 715 | "type-is": { 716 | "version": "1.6.16", 717 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 718 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 719 | "requires": { 720 | "media-typer": "0.3.0", 721 | "mime-types": "~2.1.18" 722 | } 723 | }, 724 | "unpipe": { 725 | "version": "1.0.0", 726 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 727 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 728 | }, 729 | "uri-js": { 730 | "version": "4.2.2", 731 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 732 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 733 | "requires": { 734 | "punycode": "^2.1.0" 735 | } 736 | }, 737 | "utils-merge": { 738 | "version": "1.0.1", 739 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 740 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 741 | }, 742 | "uuid": { 743 | "version": "3.3.2", 744 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 745 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 746 | }, 747 | "vary": { 748 | "version": "1.1.2", 749 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 750 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 751 | }, 752 | "verror": { 753 | "version": "1.10.0", 754 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 755 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 756 | "requires": { 757 | "assert-plus": "^1.0.0", 758 | "core-util-is": "1.0.2", 759 | "extsprintf": "^1.2.0" 760 | } 761 | } 762 | } 763 | } 764 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jira-grafana-json-datasource", 3 | "version": "1.0.0", 4 | "description": "Jira Grafana JSON Datasource", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.15.1", 14 | "express": "^4.16", 15 | "jira-connector": "^2.10", 16 | "morgan": "^1.9.1", 17 | "passport": "^0.4.0", 18 | "passport-anonymous": "^1.0.1", 19 | "passport-http": "^0.3.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const app = express() 4 | const JiraClient = require('jira-connector') 5 | const morgan = require('morgan') 6 | const passport = require('passport') 7 | const BasicStrategy = require('passport-http').BasicStrategy 8 | const AnonymousStrategy = require('passport-anonymous') 9 | 10 | 11 | // Instantiate our Jira client 12 | const jira = new JiraClient({ 13 | host: process.env.JIRA_HOST, 14 | basic_auth: { 15 | username: process.env.JIRA_USER, 16 | password: process.env.JIRA_PASS 17 | } 18 | }) 19 | 20 | 21 | // Setup an authentication strategy 22 | let authenticationStrategy = null 23 | if (process.env.HTTP_USER) { 24 | passport.use(new BasicStrategy( 25 | function (username, password, done) { 26 | 27 | if (process.env.HTTP_USER == username && 28 | process.env.HTTP_PASS == password) { 29 | return done(null, true) 30 | } 31 | 32 | return done(null, false) 33 | } 34 | )) 35 | 36 | authenticationStrategy = 'basic' 37 | } 38 | else { 39 | // Default ot allowing anonymous access 40 | passport.use(new AnonymousStrategy()) 41 | authenticationStrategy = 'anonymous' 42 | } 43 | 44 | 45 | app.use(bodyParser.json()) 46 | app.use(morgan('combined')) // We want to log all HTTP requests 47 | app.use(passport.initialize()) 48 | 49 | 50 | // Should return 200 ok. Used for "Test connection" on the datasource config page. 51 | app.get('/', 52 | passport.authenticate(authenticationStrategy, { session: false }), 53 | (httpReq, httpRes) => { 54 | httpRes.set('Content-Type', 'text/plain') 55 | httpRes.send(new Date() + ': OK') 56 | }) 57 | 58 | 59 | // Test the connection between Jira and this project 60 | app.get('/test-jira', 61 | passport.authenticate(authenticationStrategy, { session: false }), 62 | (httpReq, httpRes) => { 63 | 64 | jira.myself.getMyself().then((jiraRes) => { 65 | httpRes.json(jiraRes) 66 | }).catch((jiraErr) => { 67 | httpRes.json(JSON.parse(jiraErr)) 68 | }) 69 | 70 | }) 71 | 72 | 73 | // Used by the find metric options on the query tab in panels. 74 | app.all('/search', 75 | passport.authenticate(authenticationStrategy, { session: false }), 76 | (httpReq, httpRes) => { 77 | 78 | // The JiraClient doesn't have any way to list filters so we need to do a custom query 79 | jira.makeRequest({ 80 | uri: jira.buildURL('/filter') 81 | }).then((jiraRes) => { 82 | 83 | let result = jiraRes.map(filter => { 84 | return { 85 | text: filter.name, 86 | value: filter.id 87 | } 88 | }) 89 | 90 | httpRes.json(result) 91 | }) 92 | 93 | }) 94 | 95 | 96 | // Should return metrics based on input. 97 | app.post('/query', 98 | passport.authenticate(authenticationStrategy, { session: false }), 99 | (httpReq, httpRes) => { 100 | 101 | let result = [] 102 | 103 | // Convert proper formatted Grafana data into the Jira mess 104 | let from = new Date(httpReq.body.range.from).toISOString().replace(/T/, ' ').replace(/\:([^:]*)$/, '') 105 | let to = new Date(httpReq.body.range.to).toISOString().replace(/T/, ' ').replace(/\:([^:]*)$/, '') 106 | 107 | let p = httpReq.body.targets.map(target => { 108 | 109 | // Default jql with time range 110 | let jql = [`created >= "${from}"`, `created <= "${to}"`] 111 | 112 | // Additional jql for targets 113 | if ( target.target ) { 114 | jql.push(`filter = "${target.target}"`) 115 | } 116 | 117 | return jira.search.search({ 118 | jql: jql.join(' AND ') 119 | }).then((jiraRes) => { 120 | 121 | if (target.type == 'timeserie') { 122 | 123 | let datapoints = jiraRes.issues.map(issue => { 124 | timestamp = Math.floor(new Date(issue.fields.created)) 125 | return [1, timestamp] 126 | }) 127 | 128 | result.push({ 129 | target: target.target, 130 | datapoints: datapoints 131 | }) 132 | 133 | } 134 | else if (target.type == 'table') { 135 | 136 | let rows = jiraRes.issues.map(issue => { 137 | return [ 138 | issue.key, 139 | issue.fields.summary, 140 | issue.fields.assignee ? issue.fields.assignee.displayName : '', 141 | issue.fields.status ? issue.fields.status.name : '', 142 | issue.fields.created 143 | ] 144 | }) 145 | 146 | result.push({ 147 | columns: [ 148 | { text: 'Key', 'type': 'string' }, 149 | { text: 'Summary', 'type': 'string' }, 150 | { text: 'Assignee', 'type': 'string' }, 151 | { text: 'Status', 'type': 'string' }, 152 | { text: 'Created', 'type': 'time' } 153 | ], 154 | type: 'table', 155 | rows: rows 156 | }) 157 | } 158 | 159 | 160 | }) 161 | }) 162 | 163 | // Once all promises resolve, return result 164 | Promise.all(p).then(() => { 165 | httpRes.json(result) 166 | }) 167 | 168 | }) 169 | 170 | 171 | app.listen(3000) 172 | 173 | 174 | console.log('Server is listening to port 3000') -------------------------------------------------------------------------------- /settings.env.example: -------------------------------------------------------------------------------- 1 | # Your Jira Cloud instance 2 | JIRA_HOST=myjira.atlassian.net 3 | JIRA_USER=mike@bluefrog.ca 4 | JIRA_PASS=XXXX 5 | 6 | # Uncomment to enable Basic Auth 7 | #HTTP_USER=mike 8 | #HTTP_PASS=1234 --------------------------------------------------------------------------------