├── .eslintrc.js ├── .github └── workflows │ ├── release.yml │ └── validate-build.yml ├── .gitignore ├── .prettierrc.js ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Magefile.go ├── Makefile ├── README.md ├── go.mod ├── go.sum ├── jest.config.js ├── package.json ├── pkg ├── main.go └── plugin │ ├── config_editor.go │ ├── plugin.go │ ├── plugin_test.go │ └── ws_data_proxy.go ├── src ├── components │ ├── ConfigEditor.tsx │ ├── FieldEditor.tsx │ ├── KeyValueEditor.tsx │ ├── QueryEditor.tsx │ ├── TabbedQueryEditor.tsx │ └── fields │ │ ├── AliasField.tsx │ │ ├── CustomHeadersField.tsx │ │ ├── CustomQueryParamsField.tsx │ │ ├── JsonPathField.tsx │ │ └── PathField.tsx ├── datasource.ts ├── detectFieldType.ts ├── hooks │ └── useDebounce.tsx ├── img │ ├── assets │ │ ├── golioth-grafana-websockets-plugin-datasource.png │ │ ├── grafana-websockets-graphing.png │ │ └── grafana-websockets-plugin-streaming.png │ └── logo.svg ├── module.ts ├── parseValues.ts ├── plugin.json ├── suggestions.ts └── types.ts ├── tsconfig.json └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', // Specifies the ESLint parser 3 | parserOptions: { 4 | ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features 5 | sourceType: 'module', // Allows for the use of imports 6 | ecmaFeatures: { 7 | jsx: true, // Allows for the parsing of JSX 8 | }, 9 | }, 10 | settings: { 11 | react: { 12 | version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use 13 | }, 14 | }, 15 | extends: [ 16 | 'plugin:react/recommended', // Uses the recommended rules from @eslint-plugin-react 17 | 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin 18 | 'prettier', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier 19 | 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. 20 | ], 21 | rules: { 22 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs 23 | // e.g. "@typescript-eslint/explicit-function-return-type": "off", 24 | 'react/prop-types': 'off', 25 | 'react-hooks/exhaustive-deps': 'off', 26 | '@typescript-eslint/explicit-function-return-type': 'off', 27 | '@typescript-eslint/explicit-module-boundary-types': 'off', 28 | '@typescript-eslint/no-non-null-assertion': 'off', // linter sucks sometimes 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | - name: Setup Node.js environment 16 | uses: actions/setup-node@v2.1.2 17 | with: 18 | node-version: '14.x' 19 | 20 | - name: Get yarn cache directory path 21 | id: yarn-cache-dir-path 22 | run: echo "::set-output name=dir::$(yarn cache dir)" 23 | 24 | - name: Cache yarn cache 25 | uses: actions/cache@v2 26 | id: cache-yarn-cache 27 | with: 28 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 29 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 30 | restore-keys: | 31 | ${{ runner.os }}-yarn- 32 | 33 | - name: Cache node_modules 34 | id: cache-node-modules 35 | uses: actions/cache@v2 36 | with: 37 | path: node_modules 38 | key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }} 39 | restore-keys: | 40 | ${{ runner.os }}-${{ matrix.node-version }}-nodemodules- 41 | 42 | - name: Install dependencies 43 | run: yarn install --frozen-lockfile; 44 | if: | 45 | steps.cache-yarn-cache.outputs.cache-hit != 'true' || 46 | steps.cache-node-modules.outputs.cache-hit != 'true' 47 | 48 | - name: Build and test frontend 49 | run: yarn build 50 | 51 | - name: Check for backend 52 | id: check-for-backend 53 | run: | 54 | if [ -f "Magefile.go" ] 55 | then 56 | echo "::set-output name=has-backend::true" 57 | fi 58 | 59 | - name: Setup Go environment 60 | if: steps.check-for-backend.outputs.has-backend == 'true' 61 | uses: actions/setup-go@v2 62 | with: 63 | go-version: '1.19' 64 | 65 | - name: Test backend 66 | if: steps.check-for-backend.outputs.has-backend == 'true' 67 | uses: magefile/mage-action@v2 68 | with: 69 | version: v1.14.0 70 | args: coverage 71 | 72 | - name: Build backend 73 | if: steps.check-for-backend.outputs.has-backend == 'true' 74 | uses: magefile/mage-action@v2 75 | with: 76 | version: v1.14.0 77 | args: buildAll 78 | 79 | - name: Sign plugin 80 | run: yarn sign 81 | env: 82 | GRAFANA_API_KEY: ${{ secrets.GRAFANA_API_KEY }} # Requires a Grafana API key from Grafana.com. 83 | 84 | - name: Get plugin metadata 85 | id: metadata 86 | run: | 87 | sudo apt-get install jq 88 | export GRAFANA_PLUGIN_ID=$(cat dist/plugin.json | jq -r .id) 89 | export GRAFANA_PLUGIN_VERSION=$(cat dist/plugin.json | jq -r .info.version) 90 | export GRAFANA_PLUGIN_TYPE=$(cat dist/plugin.json | jq -r .type) 91 | export GRAFANA_PLUGIN_ARTIFACT=${GRAFANA_PLUGIN_ID}-${GRAFANA_PLUGIN_VERSION}.zip 92 | export GRAFANA_PLUGIN_ARTIFACT_CHECKSUM=${GRAFANA_PLUGIN_ARTIFACT}.md5 93 | echo "::set-output name=plugin-id::${GRAFANA_PLUGIN_ID}" 94 | echo "::set-output name=plugin-version::${GRAFANA_PLUGIN_VERSION}" 95 | echo "::set-output name=plugin-type::${GRAFANA_PLUGIN_TYPE}" 96 | echo "::set-output name=archive::${GRAFANA_PLUGIN_ARTIFACT}" 97 | echo "::set-output name=archive-checksum::${GRAFANA_PLUGIN_ARTIFACT_CHECKSUM}" 98 | echo ::set-output name=github-tag::${GITHUB_REF#refs/*/} 99 | 100 | - name: Read changelog 101 | id: changelog 102 | run: | 103 | awk '/^## / {s++} s == 1 {print}' CHANGELOG.md > release_notes.md 104 | echo "::set-output name=path::release_notes.md" 105 | 106 | - name: Check package version 107 | run: if [ "v${{ steps.metadata.outputs.plugin-version }}" != "${{ steps.metadata.outputs.github-tag }}" ]; then printf "\033[0;31mPlugin version doesn't match tag name\033[0m\n"; exit 1; fi 108 | 109 | - name: Package plugin 110 | id: package-plugin 111 | run: | 112 | mv dist ${{ steps.metadata.outputs.plugin-id }} 113 | zip ${{ steps.metadata.outputs.archive }} ${{ steps.metadata.outputs.plugin-id }} -r 114 | md5sum ${{ steps.metadata.outputs.archive }} > ${{ steps.metadata.outputs.archive-checksum }} 115 | echo "::set-output name=checksum::$(cat ./${{ steps.metadata.outputs.archive-checksum }} | cut -d' ' -f1)" 116 | 117 | - name: Lint plugin 118 | run: | 119 | git clone https://github.com/grafana/plugin-validator 120 | pushd ./plugin-validator/pkg/cmd/plugincheck2 121 | go install 122 | popd 123 | plugincheck2 ${{ steps.metadata.outputs.archive }} 124 | 125 | - name: Create release 126 | id: create_release 127 | uses: actions/create-release@v1 128 | env: 129 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 130 | with: 131 | tag_name: ${{ github.ref }} 132 | release_name: Release ${{ github.ref }} 133 | body_path: ${{ steps.changelog.outputs.path }} 134 | draft: true 135 | 136 | - name: Add plugin to release 137 | id: upload-plugin-asset 138 | uses: actions/upload-release-asset@v1 139 | env: 140 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 141 | with: 142 | upload_url: ${{ steps.create_release.outputs.upload_url }} 143 | asset_path: ./${{ steps.metadata.outputs.archive }} 144 | asset_name: ${{ steps.metadata.outputs.archive }} 145 | asset_content_type: application/zip 146 | 147 | - name: Add checksum to release 148 | id: upload-checksum-asset 149 | uses: actions/upload-release-asset@v1 150 | env: 151 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 152 | with: 153 | upload_url: ${{ steps.create_release.outputs.upload_url }} 154 | asset_path: ./${{ steps.metadata.outputs.archive-checksum }} 155 | asset_name: ${{ steps.metadata.outputs.archive-checksum }} 156 | asset_content_type: text/plain 157 | 158 | - name: Publish to Grafana.com 159 | run: | 160 | echo A draft release has been created for your plugin. Please review and publish it. Then submit your plugin to grafana.com/plugins by opening a PR to https://github.com/grafana/grafana-plugin-repository with the following entry: 161 | echo 162 | echo '{ "id": "${{ steps.metadata.outputs.plugin-id }}", "type": "${{ steps.metadata.outputs.plugin-type }}", "url": "https://github.com/${{ github.repository }}", "versions": [ { "version": "${{ steps.metadata.outputs.plugin-version }}", "commit": "${{ github.sha }}", "url": "https://github.com/${{ github.repository }}", "download": { "any": { "url": "https://github.com/${{ github.repository }}/releases/download/v${{ steps.metadata.outputs.plugin-version }}/${{ steps.metadata.outputs.archive }}", "md5": "${{ steps.package-plugin.outputs.checksum }}" } } } ] }' | jq . -------------------------------------------------------------------------------- /.github/workflows/validate-build.yml: -------------------------------------------------------------------------------- 1 | name: validate-build 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - develop 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Setup Node.js environment 19 | uses: actions/setup-node@v2.1.2 20 | with: 21 | node-version: '14.x' 22 | 23 | - name: Get yarn cache directory path 24 | id: yarn-cache-dir-path 25 | run: echo "::set-output name=dir::$(yarn cache dir)" 26 | 27 | - name: Cache yarn cache 28 | uses: actions/cache@v2 29 | id: cache-yarn-cache 30 | with: 31 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 32 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 33 | restore-keys: | 34 | ${{ runner.os }}-yarn- 35 | - name: Cache node_modules 36 | id: cache-node-modules 37 | uses: actions/cache@v2 38 | with: 39 | path: node_modules 40 | key: ${{ runner.os }}-${{ matrix.node-version }}-nodemodules-${{ hashFiles('**/yarn.lock') }} 41 | restore-keys: | 42 | ${{ runner.os }}-${{ matrix.node-version }}-nodemodules- 43 | - name: Install dependencies 44 | run: yarn install --frozen-lockfile 45 | 46 | - name: Build and test frontend 47 | run: yarn build 48 | 49 | - name: Check for backend 50 | id: check-for-backend 51 | run: | 52 | if [ -f "Magefile.go" ] 53 | then 54 | echo "::set-output name=has-backend::true" 55 | fi 56 | - name: Setup Go environment 57 | if: steps.check-for-backend.outputs.has-backend == 'true' 58 | uses: actions/setup-go@v2 59 | with: 60 | go-version: '1.19' 61 | 62 | - name: Test backend 63 | if: steps.check-for-backend.outputs.has-backend == 'true' 64 | uses: magefile/mage-action@v2 65 | with: 66 | version: v1.14.0 67 | args: coverage 68 | 69 | - name: Build backend 70 | if: steps.check-for-backend.outputs.has-backend == 'true' 71 | uses: magefile/mage-action@v2 72 | with: 73 | version: v1.14.0 74 | args: buildAll 75 | 76 | - name: Sign plugin 77 | run: yarn sign 78 | env: 79 | GRAFANA_API_KEY: ${{ secrets.GRAFANA_API_KEY }} # Requires a Grafana API key from Grafana.com. 80 | 81 | - name: Upload plugin artifacts 82 | uses: actions/upload-artifact@v4 83 | with: 84 | name: golioth-websocket-datasource 85 | path: ./dist 86 | retention-days: 1 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | node_modules/ 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # Compiled binary addons (https://nodejs.org/api/addons.html) 23 | dist/ 24 | artifacts/ 25 | work/ 26 | ci/ 27 | e2e-results/ 28 | 29 | # Editor 30 | .idea 31 | 32 | # Plugin Validator 33 | golioth-websocket-datasource/ 34 | golioth-websocket-datasource.zip -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | endOfLine: 'lf', 3 | semi: false, 4 | singleQuote: true, 5 | arrowParens: 'avoid', 6 | jsxSingleQuote: true, 7 | trailingComma: 'all', 8 | } 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.1.0 4 | 5 | - support for Grafana provisioning configuration 6 | 7 | ## 1.0.2 8 | 9 | - update plugin's description and bump toolkit version 10 | 11 | ## 1.0.1 12 | 13 | - add links at the plugin.jon pointing to the license and to the repo. 14 | 15 | ## 1.0.0 16 | 17 | Golioth's WebSocket Data Source Plugin Launch 18 | 19 | The main focus on this release is to launch the new websocket data source plugin for the Grafana Community. 20 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | coc@golioth.io. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # What is Grafana Data Source Backend Plugin? 2 | 3 | Grafana supports a wide range of data sources, including Prometheus, MySQL, and even Datadog. There’s a good chance you can already visualize metrics from the systems you have set up. In some cases, though, you already have an in-house metrics solution that you’d like to add to your Grafana dashboards. Grafana Data Source Plugins enables integrating such solutions with Grafana. 4 | 5 | For more information about backend plugins, refer to the documentation on [Backend plugins](https://grafana.com/docs/grafana/latest/developers/plugins/backend/). 6 | 7 | A backend data source plugin consists of both frontend and backend components. 8 | 9 | In this case, the frontend is written in javascript and the backend is written in golang. 10 | 11 | # Summary 12 | 13 | - [Commits format](#commit) 14 | - [Building and running locally](#run-locally) 15 | - [Learn More related contents](#learn-more) 16 | 17 | # Commit Message Format 18 | 19 | We have very precise rules over how our Git commit messages must be formatted. 20 | This format leads to **easier to read commit history**. 21 | 22 | Each commit message consists of a **header**, a **body**, and a **footer**. 23 | 24 | ``` 25 |
26 | 27 | 28 | 29 |