├── .config ├── .cprc.json ├── .eslintrc ├── .prettierrc.js ├── Dockerfile ├── README.md ├── docker-compose-base.yaml ├── entrypoint.sh ├── jest-setup.js ├── jest.config.js ├── jest │ ├── mocks │ │ └── react-inlinesvg.tsx │ └── utils.js ├── supervisord │ └── supervisord.conf ├── tsconfig.json ├── types │ ├── bundler-rules.d.ts │ └── webpack-plugins.d.ts └── webpack │ ├── BuildModeWebpackPlugin.ts │ ├── constants.ts │ ├── utils.ts │ └── webpack.config.ts ├── .eslintcache ├── .eslintrc ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .nvmrc ├── .prettierrc.js ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docker-compose.yaml ├── jest-setup.js ├── jest.config.js ├── package.json ├── renovate.json ├── setup-demo-environment.sh ├── src ├── ConfigEditor.tsx ├── QueryEditor.tsx ├── README.md ├── datasource.ts ├── img │ ├── logo.svg │ └── screenshot.png ├── module.ts ├── plugin.json ├── shared │ ├── constants.ts │ ├── hooks │ │ ├── useFetchContexts.test.ts │ │ ├── useFetchContexts.ts │ │ ├── useFetchDimensions.test.ts │ │ ├── useFetchDimensions.ts │ │ ├── useFetchNodes.test.ts │ │ ├── useFetchNodes.ts │ │ ├── useFetchRooms.test.ts │ │ ├── useFetchRooms.ts │ │ ├── useFetchSpaces.test.ts │ │ ├── useFetchSpaces.ts │ │ └── useGetChartData.ts │ ├── types │ │ ├── dropdown.interface.ts │ │ ├── index.ts │ │ └── space.interface.ts │ └── utils │ │ ├── request.ts │ │ └── transformations.ts └── styles.css ├── tsconfig.json └── yarn.lock /.config/.cprc.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "5.19.8" 3 | } 4 | -------------------------------------------------------------------------------- /.config/.eslintrc: -------------------------------------------------------------------------------- 1 | /* 2 | * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ 3 | * 4 | * In order to extend the configuration follow the steps in 5 | * https://grafana.com/developers/plugin-tools/get-started/set-up-development-environment#extend-the-eslint-config 6 | */ 7 | { 8 | "extends": ["@grafana/eslint-config"], 9 | "root": true, 10 | "rules": { 11 | "react/prop-types": "off" 12 | }, 13 | "overrides": [ 14 | { 15 | "plugins": ["deprecation"], 16 | "files": ["src/**/*.{ts,tsx}"], 17 | "rules": { 18 | "deprecation/deprecation": "warn" 19 | }, 20 | "parserOptions": { 21 | "project": "./tsconfig.json" 22 | } 23 | }, 24 | { 25 | "files": ["./tests/**/*"], 26 | "rules": { 27 | "react-hooks/rules-of-hooks": "off", 28 | }, 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /.config/.prettierrc.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ 3 | * 4 | * In order to extend the configuration follow the steps in .config/README.md 5 | */ 6 | 7 | module.exports = { 8 | endOfLine: 'auto', 9 | printWidth: 120, 10 | trailingComma: 'es5', 11 | semi: true, 12 | jsxSingleQuote: false, 13 | singleQuote: true, 14 | useTabs: false, 15 | tabWidth: 2, 16 | }; 17 | -------------------------------------------------------------------------------- /.config/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG grafana_version=latest 2 | ARG grafana_image=grafana-enterprise 3 | 4 | FROM grafana/${grafana_image}:${grafana_version} 5 | 6 | ARG anonymous_auth_enabled=true 7 | ARG development=false 8 | ARG TARGETARCH 9 | 10 | 11 | ENV DEV "${development}" 12 | 13 | # Make it as simple as possible to access the grafana instance for development purposes 14 | # Do NOT enable these settings in a public facing / production grafana instance 15 | ENV GF_AUTH_ANONYMOUS_ORG_ROLE "Admin" 16 | ENV GF_AUTH_ANONYMOUS_ENABLED "${anonymous_auth_enabled}" 17 | ENV GF_AUTH_BASIC_ENABLED "false" 18 | # Set development mode so plugins can be loaded without the need to sign 19 | ENV GF_DEFAULT_APP_MODE "development" 20 | 21 | 22 | LABEL maintainer="Grafana Labs " 23 | 24 | ENV GF_PATHS_HOME="/usr/share/grafana" 25 | WORKDIR $GF_PATHS_HOME 26 | 27 | USER root 28 | 29 | # Installing supervisor and inotify-tools 30 | RUN if [ "${development}" = "true" ]; then \ 31 | if grep -i -q alpine /etc/issue; then \ 32 | apk add supervisor inotify-tools git; \ 33 | elif grep -i -q ubuntu /etc/issue; then \ 34 | DEBIAN_FRONTEND=noninteractive && \ 35 | apt-get update && \ 36 | apt-get install -y supervisor inotify-tools git && \ 37 | rm -rf /var/lib/apt/lists/*; \ 38 | else \ 39 | echo 'ERROR: Unsupported base image' && /bin/false; \ 40 | fi \ 41 | fi 42 | 43 | COPY supervisord/supervisord.conf /etc/supervisor.d/supervisord.ini 44 | COPY supervisord/supervisord.conf /etc/supervisor/conf.d/supervisord.conf 45 | 46 | 47 | 48 | # Inject livereload script into grafana index.html 49 | RUN sed -i 's|||g' /usr/share/grafana/public/views/index.html 50 | 51 | 52 | COPY entrypoint.sh /entrypoint.sh 53 | RUN chmod +x /entrypoint.sh 54 | ENTRYPOINT ["/entrypoint.sh"] 55 | -------------------------------------------------------------------------------- /.config/README.md: -------------------------------------------------------------------------------- 1 | # Default build configuration by Grafana 2 | 3 | **This is an auto-generated directory and is not intended to be changed! ⚠️** 4 | 5 | The `.config/` directory holds basic configuration for the different tools 6 | that are used to develop, test and build the project. In order to make it updates easier we ask you to 7 | not edit files in this folder to extend configuration. 8 | 9 | ## How to extend the basic configs? 10 | 11 | Bear in mind that you are doing it at your own risk, and that extending any of the basic configuration can lead 12 | to issues around working with the project. 13 | 14 | ### Extending the ESLint config 15 | 16 | Edit the `.eslintrc` file in the project root in order to extend the ESLint configuration. 17 | 18 | **Example:** 19 | 20 | ```json 21 | { 22 | "extends": "./.config/.eslintrc", 23 | "rules": { 24 | "react/prop-types": "off" 25 | } 26 | } 27 | ``` 28 | 29 | --- 30 | 31 | ### Extending the Prettier config 32 | 33 | Edit the `.prettierrc.js` file in the project root in order to extend the Prettier configuration. 34 | 35 | **Example:** 36 | 37 | ```javascript 38 | module.exports = { 39 | // Prettier configuration provided by Grafana scaffolding 40 | ...require('./.config/.prettierrc.js'), 41 | 42 | semi: false, 43 | }; 44 | ``` 45 | 46 | --- 47 | 48 | ### Extending the Jest config 49 | 50 | There are two configuration in the project root that belong to Jest: `jest-setup.js` and `jest.config.js`. 51 | 52 | **`jest-setup.js`:** A file that is run before each test file in the suite is executed. We are using it to 53 | set up the Jest DOM for the testing library and to apply some polyfills. ([link to Jest docs](https://jestjs.io/docs/configuration#setupfilesafterenv-array)) 54 | 55 | **`jest.config.js`:** The main Jest configuration file that extends the Grafana recommended setup. ([link to Jest docs](https://jestjs.io/docs/configuration)) 56 | 57 | #### ESM errors with Jest 58 | 59 | A common issue with the current jest config involves importing an npm package that only offers an ESM build. These packages cause jest to error with `SyntaxError: Cannot use import statement outside a module`. To work around this, we provide a list of known packages to pass to the `[transformIgnorePatterns](https://jestjs.io/docs/configuration#transformignorepatterns-arraystring)` jest configuration property. If need be, this can be extended in the following way: 60 | 61 | ```javascript 62 | process.env.TZ = 'UTC'; 63 | const { grafanaESModules, nodeModulesToTransform } = require('./config/jest/utils'); 64 | 65 | module.exports = { 66 | // Jest configuration provided by Grafana 67 | ...require('./.config/jest.config'), 68 | // Inform jest to only transform specific node_module packages. 69 | transformIgnorePatterns: [nodeModulesToTransform([...grafanaESModules, 'packageName'])], 70 | }; 71 | ``` 72 | 73 | --- 74 | 75 | ### Extending the TypeScript config 76 | 77 | Edit the `tsconfig.json` file in the project root in order to extend the TypeScript configuration. 78 | 79 | **Example:** 80 | 81 | ```json 82 | { 83 | "extends": "./.config/tsconfig.json", 84 | "compilerOptions": { 85 | "preserveConstEnums": true 86 | } 87 | } 88 | ``` 89 | 90 | --- 91 | 92 | ### Extending the Webpack config 93 | 94 | Follow these steps to extend the basic Webpack configuration that lives under `.config/`: 95 | 96 | #### 1. Create a new Webpack configuration file 97 | 98 | Create a new config file that is going to extend the basic one provided by Grafana. 99 | It can live in the project root, e.g. `webpack.config.ts`. 100 | 101 | #### 2. Merge the basic config provided by Grafana and your custom setup 102 | 103 | We are going to use [`webpack-merge`](https://github.com/survivejs/webpack-merge) for this. 104 | 105 | ```typescript 106 | // webpack.config.ts 107 | import type { Configuration } from 'webpack'; 108 | import { merge } from 'webpack-merge'; 109 | import grafanaConfig, { type Env } from './.config/webpack/webpack.config'; 110 | 111 | const config = async (env: Env): Promise => { 112 | const baseConfig = await grafanaConfig(env); 113 | 114 | return merge(baseConfig, { 115 | // Add custom config here... 116 | output: { 117 | asyncChunks: true, 118 | }, 119 | }); 120 | }; 121 | 122 | export default config; 123 | ``` 124 | 125 | #### 3. Update the `package.json` to use the new Webpack config 126 | 127 | We need to update the `scripts` in the `package.json` to use the extended Webpack configuration. 128 | 129 | **Update for `build`:** 130 | 131 | ```diff 132 | -"build": "webpack -c ./.config/webpack/webpack.config.ts --env production", 133 | +"build": "webpack -c ./webpack.config.ts --env production", 134 | ``` 135 | 136 | **Update for `dev`:** 137 | 138 | ```diff 139 | -"dev": "webpack -w -c ./.config/webpack/webpack.config.ts --env development", 140 | +"dev": "webpack -w -c ./webpack.config.ts --env development", 141 | ``` 142 | 143 | ### Configure grafana image to use when running docker 144 | 145 | By default, `grafana-enterprise` will be used as the docker image for all docker related commands. If you want to override this behavior, simply alter the `docker-compose.yaml` by adding the following build arg `grafana_image`. 146 | 147 | **Example:** 148 | 149 | ```yaml 150 | version: '3.7' 151 | 152 | services: 153 | grafana: 154 | extends: 155 | file: .config/docker-compose-base.yaml 156 | service: grafana 157 | build: 158 | args: 159 | grafana_version: ${GRAFANA_VERSION:-9.1.2} 160 | grafana_image: ${GRAFANA_IMAGE:-grafana} 161 | ``` 162 | 163 | In this example, we assign the environment variable `GRAFANA_IMAGE` to the build arg `grafana_image` with a default value of `grafana`. This will allow you to set the value while running the docker compose commands, which might be convenient in some scenarios. 164 | 165 | --- 166 | -------------------------------------------------------------------------------- /.config/docker-compose-base.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | grafana: 3 | user: root 4 | container_name: 'netdatacloud-netdata-datasource' 5 | 6 | build: 7 | context: . 8 | args: 9 | grafana_image: ${GRAFANA_IMAGE:-grafana-enterprise} 10 | grafana_version: ${GRAFANA_VERSION:-11.5.3} 11 | development: ${DEVELOPMENT:-false} 12 | anonymous_auth_enabled: ${ANONYMOUS_AUTH_ENABLED:-true} 13 | ports: 14 | - 3000:3000/tcp 15 | volumes: 16 | - ../dist:/var/lib/grafana/plugins/netdatacloud-netdata-datasource 17 | - ../provisioning:/etc/grafana/provisioning 18 | - ..:/root/netdatacloud-netdata-datasource 19 | 20 | environment: 21 | NODE_ENV: development 22 | GF_LOG_FILTERS: plugin.netdatacloud-netdata-datasource:debug 23 | GF_LOG_LEVEL: debug 24 | GF_DATAPROXY_LOGGING: 1 25 | GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS: netdatacloud-netdata-datasource 26 | -------------------------------------------------------------------------------- /.config/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ "${DEV}" = "false" ]; then 4 | echo "Starting test mode" 5 | exec /run.sh 6 | fi 7 | 8 | echo "Starting development mode" 9 | 10 | if grep -i -q alpine /etc/issue; then 11 | exec /usr/bin/supervisord -c /etc/supervisord.conf 12 | elif grep -i -q ubuntu /etc/issue; then 13 | exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf 14 | else 15 | echo 'ERROR: Unsupported base image' 16 | exit 1 17 | fi 18 | 19 | -------------------------------------------------------------------------------- /.config/jest-setup.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ 3 | * 4 | * In order to extend the configuration follow the steps in 5 | * https://grafana.com/developers/plugin-tools/get-started/set-up-development-environment#extend-the-jest-config 6 | */ 7 | 8 | import '@testing-library/jest-dom'; 9 | import { TextEncoder, TextDecoder } from 'util'; 10 | 11 | Object.assign(global, { TextDecoder, TextEncoder }); 12 | 13 | // https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom 14 | Object.defineProperty(global, 'matchMedia', { 15 | writable: true, 16 | value: (query) => ({ 17 | matches: false, 18 | media: query, 19 | onchange: null, 20 | addListener: jest.fn(), // deprecated 21 | removeListener: jest.fn(), // deprecated 22 | addEventListener: jest.fn(), 23 | removeEventListener: jest.fn(), 24 | dispatchEvent: jest.fn(), 25 | }), 26 | }); 27 | 28 | HTMLCanvasElement.prototype.getContext = () => {}; 29 | -------------------------------------------------------------------------------- /.config/jest.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ 3 | * 4 | * In order to extend the configuration follow the steps in 5 | * https://grafana.com/developers/plugin-tools/get-started/set-up-development-environment#extend-the-jest-config 6 | */ 7 | 8 | const path = require('path'); 9 | const { grafanaESModules, nodeModulesToTransform } = require('./jest/utils'); 10 | 11 | module.exports = { 12 | moduleNameMapper: { 13 | '\\.(css|scss|sass)$': 'identity-obj-proxy', 14 | 'react-inlinesvg': path.resolve(__dirname, 'jest', 'mocks', 'react-inlinesvg.tsx'), 15 | }, 16 | modulePaths: ['/src'], 17 | setupFilesAfterEnv: ['/jest-setup.js'], 18 | testEnvironment: 'jest-environment-jsdom', 19 | testMatch: [ 20 | '/src/**/__tests__/**/*.{js,jsx,ts,tsx}', 21 | '/src/**/*.{spec,test,jest}.{js,jsx,ts,tsx}', 22 | '/src/**/*.{spec,test,jest}.{js,jsx,ts,tsx}', 23 | ], 24 | transform: { 25 | '^.+\\.(t|j)sx?$': [ 26 | '@swc/jest', 27 | { 28 | sourceMaps: 'inline', 29 | jsc: { 30 | parser: { 31 | syntax: 'typescript', 32 | tsx: true, 33 | decorators: false, 34 | dynamicImport: true, 35 | }, 36 | }, 37 | }, 38 | ], 39 | }, 40 | // Jest will throw `Cannot use import statement outside module` if it tries to load an 41 | // ES module without it being transformed first. ./config/README.md#esm-errors-with-jest 42 | transformIgnorePatterns: [nodeModulesToTransform(grafanaESModules)], 43 | }; 44 | -------------------------------------------------------------------------------- /.config/jest/mocks/react-inlinesvg.tsx: -------------------------------------------------------------------------------- 1 | // Due to the grafana/ui Icon component making fetch requests to 2 | // `/public/img/icon/.svg` we need to mock react-inlinesvg to prevent 3 | // the failed fetch requests from displaying errors in console. 4 | 5 | import React from 'react'; 6 | 7 | type Callback = (...args: any[]) => void; 8 | 9 | export interface StorageItem { 10 | content: string; 11 | queue: Callback[]; 12 | status: string; 13 | } 14 | 15 | export const cacheStore: { [key: string]: StorageItem } = Object.create(null); 16 | 17 | const SVG_FILE_NAME_REGEX = /(.+)\/(.+)\.svg$/; 18 | 19 | const InlineSVG = ({ src }: { src: string }) => { 20 | // testId will be the file name without extension (e.g. `public/img/icons/angle-double-down.svg` -> `angle-double-down`) 21 | const testId = src.replace(SVG_FILE_NAME_REGEX, '$2'); 22 | return ; 23 | }; 24 | 25 | export default InlineSVG; 26 | -------------------------------------------------------------------------------- /.config/jest/utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ 3 | * 4 | * In order to extend the configuration follow the steps in .config/README.md 5 | */ 6 | 7 | /* 8 | * This utility function is useful in combination with jest `transformIgnorePatterns` config 9 | * to transform specific packages (e.g.ES modules) in a projects node_modules folder. 10 | */ 11 | const nodeModulesToTransform = (moduleNames) => `node_modules\/(?!.*(${moduleNames.join('|')})\/.*)`; 12 | 13 | // Array of known nested grafana package dependencies that only bundle an ESM version 14 | const grafanaESModules = [ 15 | '.pnpm', // Support using pnpm symlinked packages 16 | '@grafana/schema', 17 | 'd3', 18 | 'd3-color', 19 | 'd3-force', 20 | 'd3-interpolate', 21 | 'd3-scale-chromatic', 22 | 'ol', 23 | 'react-colorful', 24 | 'rxjs', 25 | 'uuid', 26 | ]; 27 | 28 | module.exports = { 29 | nodeModulesToTransform, 30 | grafanaESModules, 31 | }; 32 | -------------------------------------------------------------------------------- /.config/supervisord/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | user=root 4 | 5 | [program:grafana] 6 | user=root 7 | directory=/var/lib/grafana 8 | command=/run.sh 9 | stdout_logfile=/dev/fd/1 10 | stdout_logfile_maxbytes=0 11 | redirect_stderr=true 12 | killasgroup=true 13 | stopasgroup=true 14 | autostart=true 15 | 16 | -------------------------------------------------------------------------------- /.config/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* 2 | * ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️ 3 | * 4 | * In order to extend the configuration follow the steps in 5 | * https://grafana.com/developers/plugin-tools/get-started/set-up-development-environment#extend-the-typescript-config 6 | */ 7 | { 8 | "compilerOptions": { 9 | "alwaysStrict": true, 10 | "declaration": false, 11 | "rootDir": "../src", 12 | "baseUrl": "../src", 13 | "typeRoots": ["../node_modules/@types"], 14 | "resolveJsonModule": true 15 | }, 16 | "ts-node": { 17 | "compilerOptions": { 18 | "module": "commonjs", 19 | "target": "es5", 20 | "esModuleInterop": true 21 | }, 22 | "transpileOnly": true 23 | }, 24 | "include": ["../src", "./types"], 25 | "extends": "@grafana/tsconfig" 26 | } 27 | -------------------------------------------------------------------------------- /.config/types/bundler-rules.d.ts: -------------------------------------------------------------------------------- 1 | // Image declarations 2 | declare module '*.gif' { 3 | const src: string; 4 | export default src; 5 | } 6 | 7 | declare module '*.jpg' { 8 | const src: string; 9 | export default src; 10 | } 11 | 12 | declare module '*.jpeg' { 13 | const src: string; 14 | export default src; 15 | } 16 | 17 | declare module '*.png' { 18 | const src: string; 19 | export default src; 20 | } 21 | 22 | declare module '*.webp' { 23 | const src: string; 24 | export default src; 25 | } 26 | 27 | declare module '*.svg' { 28 | const src: string; 29 | export default src; 30 | } 31 | 32 | // Font declarations 33 | declare module '*.woff'; 34 | declare module '*.woff2'; 35 | declare module '*.eot'; 36 | declare module '*.ttf'; 37 | declare module '*.otf'; 38 | -------------------------------------------------------------------------------- /.config/types/webpack-plugins.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'replace-in-file-webpack-plugin' { 2 | import { Compiler, Plugin } from 'webpack'; 3 | 4 | interface ReplaceRule { 5 | search: string | RegExp; 6 | replace: string | ((match: string) => string); 7 | } 8 | 9 | interface ReplaceOption { 10 | dir?: string; 11 | files?: string[]; 12 | test?: RegExp | RegExp[]; 13 | rules: ReplaceRule[]; 14 | } 15 | 16 | class ReplaceInFilePlugin extends Plugin { 17 | constructor(options?: ReplaceOption[]); 18 | options: ReplaceOption[]; 19 | apply(compiler: Compiler): void; 20 | } 21 | 22 | export = ReplaceInFilePlugin; 23 | } 24 | 25 | declare module 'webpack-livereload-plugin' { 26 | import { ServerOptions } from 'https'; 27 | import { Compiler, Plugin, Stats, Compilation } from 'webpack'; 28 | 29 | interface Options extends Pick { 30 | /** 31 | * protocol for livereload `