Compare commits

...

23 Commits

  1. 95
      README.md
  2. 4
      package.json
  3. BIN
      src/img/task-creation.png
  4. BIN
      src/img/task-list.png
  5. 8
      src/panels/corpglory-dataexporter-panel/components/Panel.tsx
  6. 15
      src/plugin.json
  7. 24
      src/plugin_state.ts
  8. 7
      yarn.lock

95
README.md

@ -1,79 +1,36 @@
# Grafana app plugin template
# Grafana Data Exporter App
This template is a starting point for building an app plugin for Grafana.
Grafana plugin for exporting data from Grafana panels as CSV.
## What are Grafana app plugins?
Supported datasources:
- MySQL
- PostgreSQL
App plugins can let you create a custom out-of-the-box monitoring experience by custom pages, nested datasources and panel plugins.
We work on expanding this list. If you would like us to support any particular datasource -- please let us know at ping@corpglory.com
## Getting started
## Prerequisites
- [Grafana 9.0.0+](https://grafana.com/grafana/download)
- [Grafana Data Exporter](https://code.corpglory.net/corpglory/grafana-data-exporter)
### Frontend
## Plugin Configuration
1. Install dependencies
- Make sure [Grafana Data Exporter](https://code.corpglory.net/corpglory/grafana-data-exporter) is running, and accessible from Grafana Server
- In Grafana, go to Configuration -> Plugins -> Data Exporter App
- Fill "DataExporter backend URL" field with the Data Exporter URL (Please note: the URL should be accessible from Grafana Server)
- Click Connect
- If Grafana connects to the Data Exporter successfully, you'll see this message: "Plugin is connected! You can now go to a dashboard and add the DataExporter panel there."
```bash
yarn install
```
## Plugin Usage
- go to a dashboard you'd like to export data from
- click "Add panel"
- select Data Exporter Panel
- click Add Task
- select timerange and query
- click Export
2. Build plugin in development mode or run in watch mode
## Support and Consulting
```bash
yarn dev
Commercial support, professional services **or any help** — send us your inquiry at ping@corpglory.com
# or
yarn watch
```
3. Build plugin in production mode
```bash
yarn build
```
4. Run the tests (using Jest)
```bash
# Runs the tests and watches for changes
yarn test
# Exists after running all the tests
yarn lint:ci
```
5. Spin up a Grafana instance and run the plugin inside it (using Docker)
```bash
yarn server
```
6. Run the E2E tests (using Cypress)
```bash
# Spin up a Grafana instance first that we tests against
yarn server
# Start the tests
yarn e2e
```
7. Run the linter
```bash
yarn lint
# or
yarn lint:fix
```
## Learn more
Below you can find source code for existing app plugins and other related documentation.
- [Basic app plugin example](https://github.com/grafana/grafana-plugin-examples/tree/master/examples/app-basic#readme)
- [Plugin.json documentation](https://grafana.com/docs/grafana/latest/developers/plugins/metadata/)
- [How to sign a plugin?](https://grafana.com/docs/grafana/latest/developers/plugins/sign-a-plugin/)
## About CorpGlory Inc.
Grafana Data Exporter is developed by [CorpGlory Inc.](https://corpglory.com/), a company which provides high quality software development, data visualization, Grafana and monitoring consulting.

4
package.json

@ -1,6 +1,6 @@
{
"name": "corpglory-dataexporter-app",
"version": "1.0.5",
"version": "1.0.10",
"description": "",
"scripts": {
"lint": "eslint --cache --ext .js,.jsx,.ts,.tsx --max-warnings=0 ./src",
@ -55,6 +55,7 @@
"@types/node": "^17.0.19",
"@types/react-copy-to-clipboard": "^5.0.4",
"@types/react-router-dom": "^5.3.3",
"@types/uuid": "^9.0.2",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"axios": "^1.2.1",
@ -84,6 +85,7 @@
"ts-node": "^10.5.0",
"tsconfig-paths": "^3.12.0",
"typescript": "^4.4.0",
"uuid": "^9.0.0",
"webpack": "^5.69.1",
"webpack-cli": "^4.9.2",
"webpack-livereload-plugin": "^3.0.2"

BIN
src/img/task-creation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
src/img/task-list.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

8
src/panels/corpglory-dataexporter-panel/components/Panel.tsx

@ -55,6 +55,7 @@ import { RefreshEvent } from '@grafana/runtime';
import React, { useState, useEffect } from 'react';
import * as _ from 'lodash';
import PluginState from 'plugin_state';
const PANEL_ID = 'corpglory-dataexporter-panel';
const APP_ID = 'corpglory-dataexporter-app';
@ -215,6 +216,7 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {
queries: selectedQueries,
csvDelimiter,
};
const token = await PluginState.createGrafanaToken();
// TODO: move this function to API Service
await queryApi('/task', {
method: 'POST',
@ -222,6 +224,7 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {
task,
url: window.location.toString(),
timeZoneName,
apiKey: token.key,
},
});
@ -261,6 +264,7 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {
custom: {
filterable: false,
displayMode: 'image',
cellOptions: { type: 'image' },
},
links: [
{
@ -330,6 +334,7 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {
custom: {
filterable: false,
displayMode: 'image',
cellOptions: { type: 'image' },
},
links: [
{
@ -394,6 +399,7 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {
custom: {
filterable: false,
displayMode: 'image',
cellOptions: { type: 'image' },
},
links: [
{
@ -493,7 +499,7 @@ export function Panel({ width, height, timeRange, eventBus, timeZone }: Props) {
);
const mainDiv = (
<div>
<Table width={width} height={height - 40} data={tasksDataFrame as DataFrame} />
{tasksDataFrame && <Table width={width} height={height - 40} data={tasksDataFrame as DataFrame} />}
<HorizontalGroup justify="flex-end">
<Button variant="primary" icon="plus" style={{ marginTop: '8px' }} onClick={openDatasourceModal}>
Add Task

15
src/plugin.json

@ -4,7 +4,7 @@
"name": "Data Exporter App",
"id": "corpglory-dataexporter-app",
"info": {
"description": "",
"description": "Export dashboard data into CSV",
"author": {
"name": "CorpGlory Inc."
},
@ -13,7 +13,16 @@
"large": "img/logo.svg"
},
"keywords": ["export", "csv"],
"screenshots": [],
"screenshots": [
{
"name": "Task List",
"path": "img/task-list.png"
},
{
"name": "Task Creation",
"path": "img/task-creation.png"
}
],
"version": "%VERSION%",
"updated": "%TODAY%"
},
@ -31,7 +40,7 @@
}
],
"dependencies": {
"grafanaDependency": ">=7.0.0",
"grafanaDependency": ">=9.0.0",
"plugins": []
}
}

24
src/plugin_state.ts

@ -7,8 +7,12 @@ import {
import { getBackendSrv } from '@grafana/runtime';
import { v4 as uuidv4 } from 'uuid';
import axios from 'axios';
import * as _ from 'lodash';
export type UpdateGrafanaPluginSettingsProps = {
jsonData?: Partial<DataExporterPluginMetaJSONData>;
secureJsonData?: Partial<DataExporterPluginMetaSecureJSONData>;
@ -97,19 +101,25 @@ class PluginState {
static createGrafanaToken = async () => {
const baseUrl = '/api/auth/keys';
const keys = await this.grafanaBackend.get(baseUrl);
const existingKey = keys.find((key: { id: number; name: string; role: string }) => key.name === 'DataExporter');
const keys = await this.grafanaBackend.get(baseUrl, { includeExpired: true });
const existingKeys = keys.filter((key: { id: number; name: string; role: string }) =>
_.includes(key.name, 'DataExporter')
);
if (existingKey) {
// @ts-ignore
// for some reason, there is no `options` argument in Grafana's public types for BackendSrv but it exists
await this.grafanaBackend.delete(`${baseUrl}/${existingKey.id}`, undefined, { showSuccessAlert: false });
console.log('existingKeys', existingKeys);
if (!_.isEmpty(existingKeys)) {
for (let key of existingKeys) {
// @ts-ignore
// for some reason, there is no `options` argument in Grafana's public types for BackendSrv but it exists
await this.grafanaBackend.delete(`${baseUrl}/${key.id}`, undefined, { showSuccessAlert: false });
}
}
return await this.grafanaBackend.post(
baseUrl,
{
name: 'DataExporter',
name: `DataExporter_${uuidv4()}`,
role: 'Admin',
secondsToLive: null,
},

7
yarn.lock

@ -3945,6 +3945,11 @@
dependencies:
source-map "^0.6.1"
"@types/uuid@^9.0.2":
version "9.0.2"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.2.tgz#ede1d1b1e451548d44919dc226253e32a6952c4b"
integrity sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==
"@types/webpack-dev-server@3":
version "3.11.6"
resolved "https://registry.yarnpkg.com/@types/webpack-dev-server/-/webpack-dev-server-3.11.6.tgz#d8888cfd2f0630203e13d3ed7833a4d11b8a34dc"
@ -12944,7 +12949,7 @@ uuid@8.3.2, uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
uuid@9.0.0:
uuid@9.0.0, uuid@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==

Loading…
Cancel
Save