Skip to content
This repository was archived by the owner on Aug 4, 2025. It is now read-only.

Commit 067455c

Browse files
authored
Merge pull request #14 from hoptical/add-proxy
Use getBackendSrv/Proxy
2 parents 09e3ec8 + 354b647 commit 067455c

File tree

6 files changed

+32
-65
lines changed

6 files changed

+32
-65
lines changed

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Nodegraph API Plugin for Grafana
22

3-
[![Build](https://github.com/grafana/grafana-starter-datasource/workflows/CI/badge.svg)](https://github.com/grafana/grafana-starter-datasource/actions?query=workflow%3A%22CI%22)
3+
[![License](https://img.shields.io/github/license/hoptical/nodegraph-api-plugin)](LICENSE)
4+
[![CI](https://github.com/hoptical/nodegraph-api-plugin/actions/workflows/ci.yml/badge.svg)](https://github.com/hoptical/nodegraph-api-plugin/actions/workflows/ci.yml)
5+
[![Release](https://github.com/hoptical/nodegraph-api-plugin/actions/workflows/release.yml/badge.svg)](https://github.com/hoptical/nodegraph-api-plugin/actions/workflows/release.yml)
46

57
This plugin provides a data source to connect a REST API to [nodegraph](https://grafana.com/docs/grafana/latest/visualizations/node-graph/) panel of Grafana. It is [signed and published by Grafana](https://grafana.com/grafana/plugins/hamedkarbasi93-nodegraphapi-datasource/).
68

@@ -29,17 +31,17 @@ Alternatively, you can manually download the [latest](https://github.com/hoptica
2931

3032
You can now add the data source. Just enter the URL of your API app and push "Save & Test." You will get an error in case of connection failure.
3133

32-
> Important note: The browser should have access to the application, not the Grafana server.
33-
3434
![Add Datasource](https://raw.githubusercontent.com/hoptical/nodegraph-api-plugin/f447b74ecefd827b388e791a34792730e9a9a11d/src/img/add-datasource.png)
3535

3636
In the Grafana dashboard, pick the Nodegraph panel and visualize the graph.
3737

38-
## API Configuration
38+
> Note on Application Access:
39+
> - Versions 0.x.x work in *direct* mode. i.e., The browser must have access to the API application.
40+
> - Versions 1.x.x+ work in *proxy* mode. i.e., The Grafana server should have access to the API application.
3941
40-
The REST API application should return data in the following format:
42+
## API Configuration
4143

42-
> Note: Your API application should handle CORS policy. Otherwise, you will face a CORS-Policy error in Grafana.
44+
The REST API application should handle three requests: *fields*, *data*, and *health*. They are described below.
4345

4446
### Fetch Graph Fields
4547

@@ -174,7 +176,6 @@ In the `example` folder, you can find a simple API application in Python Flask.
174176
### Requirements:
175177

176178
- flask
177-
- flask-cors
178179

179180
### Run
180181

example/api/python/run.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from flask import Flask, jsonify
2-
from flask_cors import CORS
2+
33
app = Flask(__name__)
4-
CORS(app)
54

65

76
@app.route('/api/graph/fields')

src/ConfigEditor.tsx

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,39 @@
11
import React, { ChangeEvent, PureComponent } from 'react';
22
import { LegacyForms } from '@grafana/ui';
33
import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
4-
import { MyDataSourceOptions, MySecureJsonData } from './types';
4+
import { MyDataSourceOptions } from './types';
55

6-
const { SecretFormField, FormField } = LegacyForms;
6+
const { FormField } = LegacyForms;
77

88
interface Props extends DataSourcePluginOptionsEditorProps<MyDataSourceOptions> {}
99

1010
interface State {}
1111

1212
export class ConfigEditor extends PureComponent<Props, State> {
13-
// Secure field (only sent to the backend)
14-
onAPIKeyChange = (event: ChangeEvent<HTMLInputElement>) => {
15-
const { onOptionsChange, options } = this.props;
16-
onOptionsChange({
17-
...options,
18-
secureJsonData: {
19-
apiKey: event.target.value,
20-
},
21-
});
22-
};
23-
24-
onResetAPIKey = () => {
25-
const { onOptionsChange, options } = this.props;
26-
onOptionsChange({
27-
...options,
28-
secureJsonFields: {
29-
...options.secureJsonFields,
30-
apiKey: false,
31-
},
32-
secureJsonData: {
33-
...options.secureJsonData,
34-
apiKey: '',
35-
},
36-
});
37-
};
3813
onURLChange = (event: ChangeEvent<HTMLInputElement>) => {
3914
const { onOptionsChange, options } = this.props;
4015
const jsonData = {
4116
...options.jsonData,
42-
baseUrl: event.target.value,
17+
url: event.target.value,
4318
};
4419
onOptionsChange({ ...options, jsonData });
4520
};
4621

4722
render() {
4823
const { options } = this.props;
49-
const { jsonData, secureJsonFields } = options;
50-
const secureJsonData = (options.secureJsonData || {}) as MySecureJsonData;
24+
const { jsonData } = options;
25+
//const secureJsonData = (options.secureJsonData || {}) as MySecureJsonData;
5126

5227
return (
5328
<div className="gf-form-group">
5429
<div className="gf-form">
5530
<FormField
5631
label="URL"
5732
onChange={this.onURLChange}
58-
value={jsonData.baseUrl || ''}
33+
value={jsonData.url || ''}
5934
placeholder="http://localhost:5000"
6035
/>
6136
</div>
62-
<div className="gf-form-inline">
63-
<div className="gf-form">
64-
<SecretFormField
65-
isConfigured={(secureJsonFields && secureJsonFields.apiKey) as boolean}
66-
value={secureJsonData.apiKey || ''}
67-
label="API Key"
68-
placeholder="secure json field (backend only)"
69-
labelWidth={6}
70-
inputWidth={20}
71-
onReset={this.onResetAPIKey}
72-
onChange={this.onAPIKeyChange}
73-
/>
74-
</div>
75-
</div>
7637
</div>
7738
);
7839
}

src/datasource.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@ import { getTemplateSrv } from '@grafana/runtime';
1616

1717
import { MyQuery, MyDataSourceOptions, defaultQuery } from './types';
1818

19+
// proxy route
20+
const routePath = '/nodegraphds';
21+
1922
export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
20-
baseUrl: string; // base url of the api
23+
url: string;
2124
constructor(instanceSettings: DataSourceInstanceSettings<MyDataSourceOptions>) {
2225
super(instanceSettings);
2326

24-
this.baseUrl = instanceSettings.jsonData.baseUrl || '';
27+
// proxy url
28+
this.url = instanceSettings.url || '';
2529
}
2630

2731
async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
@@ -98,13 +102,11 @@ export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
98102
return Promise.all(promises).then(data => ({ data: data[0] }));
99103
}
100104
async doRequest(endpoint: string, params?: string) {
101-
// const result = await getBackendSrv().datasourceRequest({
102-
// method: 'GET',
103-
// url: `${this.baseUrl}${endpoint}${`?${params}`}`,
104-
// });
105+
// Do the request on proxy; the server will replace url + routePath with the url
106+
// defined in plugin.json
105107
const result = getBackendSrv().datasourceRequest({
106108
method: 'GET',
107-
url: `${this.baseUrl}${endpoint}${params?.length ? `?${params}` : ''}`,
109+
url: `${this.url}${routePath}${endpoint}${params?.length ? `?${params}` : ''}`,
108110
});
109111
return result;
110112
}

src/plugin.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
{
22
"$schema": "https://raw.githubusercontent.com/grafana/grafana/master/docs/sources/developers/plugins/plugin.schema.json",
3+
"routes": [
4+
{
5+
"path": "nodegraphds",
6+
"url": "{{ .JsonData.url }}"
7+
}
8+
],
39
"type": "datasource",
410
"name": "Node Graph API",
511
"id": "hamedkarbasi93-nodegraphapi-datasource",

src/types.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ export const defaultQuery: Partial<MyQuery> = {};
1010
* These are options configured for each DataSource instance
1111
*/
1212
export interface MyDataSourceOptions extends DataSourceJsonData {
13-
baseUrl?: string;
13+
url: string;
1414
}
1515

1616
/**
1717
* Value that is used in the backend, but never sent over HTTP to the frontend
1818
*/
19-
export interface MySecureJsonData {
20-
apiKey?: string;
21-
}
19+
export interface MySecureJsonData {}

0 commit comments

Comments
 (0)