Adopt buildConfig in dashboardapi adapter
This commit is contained in:
144
src/nodeClass.js
144
src/nodeClass.js
@@ -1,17 +1,11 @@
|
||||
const { outputUtils } = require('generalFunctions');
|
||||
const Specific = require('./specificClass');
|
||||
const { configManager } = require('generalFunctions');
|
||||
const DashboardApi = require('./specificClass');
|
||||
|
||||
/**
|
||||
* Node-RED wrapper for dashboard generation requests.
|
||||
* It listens for `registerChild` messages and emits Grafana upsert requests.
|
||||
*/
|
||||
class nodeClass {
|
||||
constructor(uiConfig, RED, nodeInstance, nameOfNode) {
|
||||
this.node = nodeInstance;
|
||||
this.RED = RED;
|
||||
this.name = nameOfNode;
|
||||
this.source = null;
|
||||
this.config = null;
|
||||
|
||||
this._loadConfig(uiConfig);
|
||||
this._setupSpecificClass();
|
||||
@@ -20,92 +14,98 @@ class nodeClass {
|
||||
}
|
||||
|
||||
_loadConfig(uiConfig) {
|
||||
this.config = {
|
||||
general: {
|
||||
name: uiConfig.name || this.name,
|
||||
logging: {
|
||||
enabled: uiConfig.enableLog,
|
||||
logLevel: uiConfig.logLevel || 'info',
|
||||
},
|
||||
const cfgMgr = new configManager();
|
||||
this.config = cfgMgr.buildConfig(this.name, uiConfig, this.node.id, {
|
||||
functionality: {
|
||||
softwareType: this.name.toLowerCase(),
|
||||
role: 'auto ui generator',
|
||||
},
|
||||
grafanaConnector: {
|
||||
protocol: uiConfig.protocol || 'http',
|
||||
host: uiConfig.host || 'localhost',
|
||||
port: Number(uiConfig.port || 3000),
|
||||
bearerToken: uiConfig.bearerToken || '',
|
||||
port: Number(uiConfig.port) || 3000,
|
||||
protocol: uiConfig.protocol || 'http',
|
||||
bearerToken: uiConfig.bearerToken || null,
|
||||
},
|
||||
defaultBucket: uiConfig.defaultBucket || process.env.INFLUXDB_BUCKET || '',
|
||||
};
|
||||
|
||||
this._output = new outputUtils();
|
||||
});
|
||||
}
|
||||
|
||||
_setupSpecificClass() {
|
||||
this.source = new Specific(this.config);
|
||||
this.source = new DashboardApi(this.config);
|
||||
this.node.source = this.source;
|
||||
}
|
||||
|
||||
_resolveChildNode(childId) {
|
||||
const runtimeNode = this.RED.nodes.getNode(childId);
|
||||
if (runtimeNode?.source?.config) {
|
||||
return runtimeNode;
|
||||
}
|
||||
|
||||
const flowNode = this.node._flow?.getNode?.(childId);
|
||||
if (flowNode?.source?.config) {
|
||||
return flowNode;
|
||||
}
|
||||
|
||||
return runtimeNode || flowNode || null;
|
||||
}
|
||||
|
||||
_resolveChildConfig(payload) {
|
||||
if (payload?.source?.config) {
|
||||
return payload.source.config;
|
||||
}
|
||||
|
||||
if (payload?.config) {
|
||||
return payload.config;
|
||||
}
|
||||
|
||||
if (typeof payload === 'string') {
|
||||
return this._resolveChildNode(payload)?.source?.config || null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
_attachInputHandler() {
|
||||
this.node.on('input', async (msg, send, done) => {
|
||||
try {
|
||||
if (msg.topic !== 'registerChild') {
|
||||
if (typeof done === 'function') done();
|
||||
return;
|
||||
switch (msg.topic) {
|
||||
case 'registerChild': {
|
||||
const childConfig = this._resolveChildConfig(msg.payload);
|
||||
if (!childConfig) {
|
||||
throw new Error('Missing or invalid child node');
|
||||
}
|
||||
|
||||
const payload = await this.source.generateDashB(childConfig);
|
||||
const authToken = process.env.GRAFANA_TOKEN || this.config.grafanaConnector.bearerToken || '';
|
||||
|
||||
send({
|
||||
...msg,
|
||||
topic: 'create',
|
||||
payload,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
},
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this.source.logger.warn(`Unknown topic: ${msg.topic}`);
|
||||
break;
|
||||
}
|
||||
|
||||
const childId = msg.payload;
|
||||
const childObj = this.RED.nodes.getNode(childId);
|
||||
const childSource = childObj?.source;
|
||||
if (!childSource?.config) {
|
||||
this.node.warn(`registerChild skipped: missing child source/config for id=${childId}`);
|
||||
if (typeof done === 'function') done();
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate one dashboard for the root source and optionally its registered children.
|
||||
const dashboards = this.source.generateDashboardsForGraph(childSource, {
|
||||
includeChildren: Boolean(msg.includeChildren ?? true),
|
||||
});
|
||||
|
||||
const url = this.source.grafanaUpsertUrl();
|
||||
const headers = {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
if (this.config.grafanaConnector.bearerToken) {
|
||||
headers.Authorization = `Bearer ${this.config.grafanaConnector.bearerToken}`;
|
||||
}
|
||||
|
||||
for (const dash of dashboards) {
|
||||
// Forward dashboard definitions to an HTTP request node configured for Grafana API.
|
||||
const payload = this.source.buildUpsertRequest({ dashboard: dash.dashboard, folderId: 0, overwrite: true });
|
||||
send({
|
||||
topic: 'grafana.dashboard.upsert',
|
||||
url,
|
||||
method: 'POST',
|
||||
headers,
|
||||
payload,
|
||||
meta: {
|
||||
nodeId: dash.nodeId,
|
||||
softwareType: dash.softwareType,
|
||||
uid: dash.uid,
|
||||
title: dash.title,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof done === 'function') done();
|
||||
done();
|
||||
} catch (error) {
|
||||
this.node.status({ fill: 'red', shape: 'ring', text: 'dashboardapi error' });
|
||||
this.node.error(error?.message || error, msg);
|
||||
if (typeof done === 'function') done();
|
||||
this.node.status({ fill: 'red', shape: 'ring', text: 'Bad request data' });
|
||||
this.node.error(`Bad request data: ${error.message}`, msg);
|
||||
done(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_attachCloseHandler() {
|
||||
this.node.on('close', (done) => {
|
||||
if (typeof done === 'function') done();
|
||||
done();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user