From 0349c9fccb571a27a6bd622cf727a5c9d37228a9 Mon Sep 17 00:00:00 2001 From: asimonok Date: Mon, 22 Apr 2024 17:05:56 +0300 Subject: [PATCH] Add server-based form elements example --- provisioning/dashboards/server-based.json | 234 ++++++++++++++++++++++ provisioning/datasources/datasources.yaml | 9 + server-json/Dockerfile | 10 +- server-json/server.ts | 82 +++++++- server-json/tsconfig.json | 4 + 5 files changed, 336 insertions(+), 3 deletions(-) create mode 100644 provisioning/dashboards/server-based.json create mode 100644 server-json/tsconfig.json diff --git a/provisioning/dashboards/server-based.json b/provisioning/dashboards/server-based.json new file mode 100644 index 00000000..ba0cfbd0 --- /dev/null +++ b/provisioning/dashboards/server-based.json @@ -0,0 +1,234 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "marcusolsson-json-datasource", + "uid": "fecd2733-2fd2-42e5-88aa-93f4831faf5f" + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "buttonGroup": { + "orientation": "center", + "size": "md" + }, + "confirmModal": { + "body": "Please confirm to update changed values", + "cancel": "Cancel", + "columns": { + "include": [ + "name", + "oldValue", + "newValue" + ], + "name": "Label", + "newValue": "New Value", + "oldValue": "Old Value" + }, + "confirm": "Confirm", + "title": "Confirm update request" + }, + "elementValueChanged": "/**\n * Update device variable\n */\nif (context.element.id === 'device') {\n context.grafana.locationService.partial({\n 'var-device': context.element.value,\n })\n}", + "elements": [ + { + "id": "device", + "options": [ + { + "id": "device1", + "label": "device1", + "type": "string", + "value": "device1" + }, + { + "id": "device2", + "label": "device2", + "type": "string", + "value": "device2" + } + ], + "optionsSource": "Custom", + "title": "Device", + "type": "select", + "uid": "device", + "value": "device1" + }, + { + "id": "device1Field", + "max": 10, + "min": 0, + "title": "Device 1 Field", + "type": "number", + "uid": "device1Field", + "value": 0 + }, + { + "id": "comment", + "title": "Comment", + "type": "textarea", + "uid": "comment", + "value": "" + } + ], + "initial": { + "code": "/**\n * Convert JSON to form elements array\n */\nconst formElements = JSON.parse(context.panel.data.series[0].fields[0].values[0])\n\n/**\n * Set elements with helpers\n */\ncontext.panel.onChangeElements(formElements.map((element) => {\n const elementInForm = context.panel.elements.find((item) => item.uid === element.uid);\n let value = element.value;\n\n if (element.uid === 'comment' && elementInForm) {\n value = elementInForm.value;\n }\n\n return {\n ...element,\n value,\n helpers: {\n showIf: () => true,\n disableIf: () => false,\n getOptions: () => element.options,\n }\n }\n}),\n);\n", + "contentType": "application/json", + "datasource": "PostgreSQL", + "getPayload": "return {\n rawSql: '',\n format: 'table',\n}", + "highlight": false, + "highlightColor": "red", + "method": "query" + }, + "layout": { + "orientation": "horizontal", + "padding": 10, + "variant": "single" + }, + "reset": { + "backgroundColor": "purple", + "foregroundColor": "yellow", + "icon": "process", + "text": "Reset", + "variant": "hidden" + }, + "resetAction": { + "code": "if (response && response.ok) {\n notifySuccess(['Update', 'Values updated successfully.']);\n locationService.reload();\n} else {\n notifyError(['Update', 'An error occured updating values.']);\n}", + "confirm": false, + "getPayload": "return {\n rawSql: '',\n format: 'table',\n}", + "mode": "initial" + }, + "saveDefault": { + "icon": "save", + "text": "Save Default", + "variant": "hidden" + }, + "submit": { + "backgroundColor": "purple", + "foregroundColor": "yellow", + "icon": "cloud-upload", + "text": "Submit", + "variant": "primary" + }, + "sync": true, + "update": { + "code": "if (response && response.ok) {\n notifySuccess(['Update', 'Values updated successfully.']);\n locationService.reload();\n} else {\n notifyError(['Update', 'An error occured updating values.']);\n}", + "confirm": false, + "contentType": "application/json", + "getPayload": "const payload = {};\n\nelements.forEach((element) => {\n if (!element.value) {\n return;\n }\n\n payload[element.id] = element.value;\n})\n\nreturn payload;\n\n/**\n * Data Source payload\n */ \nreturn {\n rawSql: '',\n format: 'table',\n};", + "method": "-", + "payloadMode": "all" + }, + "updateEnabled": "auto" + }, + "pluginVersion": "4.0.0", + "targets": [ + { + "cacheDurationSeconds": 300, + "datasource": { + "type": "marcusolsson-json-datasource", + "uid": "fecd2733-2fd2-42e5-88aa-93f4831faf5f" + }, + "fields": [ + { + "jsonPath": "$", + "language": "jsonpath", + "name": "elements" + } + ], + "method": "GET", + "params": [ + [ + "", + "" + ] + ], + "queryParams": "", + "refId": "A", + "urlPath": "/form?device=$device" + } + ], + "title": "Server-based Elements", + "type": "volkovlabs-form-panel" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "device1", + "value": "device1" + }, + "datasource": { + "type": "marcusolsson-static-datasource", + "uid": "P1D2C73DC01F2359B" + }, + "definition": "", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "device", + "options": [], + "query": { + "frame": { + "fields": [ + { + "config": {}, + "name": "name", + "type": "string", + "values": [ + "device1", + "device2" + ] + } + ], + "meta": {} + } + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Server-based", + "uid": "f430fe0c-2487-4a02-a495-941c85b1e21d", + "version": 5, + "weekStart": "" +} diff --git a/provisioning/datasources/datasources.yaml b/provisioning/datasources/datasources.yaml index 0a99a817..29773f14 100644 --- a/provisioning/datasources/datasources.yaml +++ b/provisioning/datasources/datasources.yaml @@ -30,3 +30,12 @@ datasources: orgId: 1 version: 1 editable: true + - name: Server API + uid: serverapi + type: marcusolsson-json-datasource + access: proxy + url: http://host.docker.internal:3001 + isDefault: false + orgId: 1 + version: 1 + editable: true diff --git a/server-json/Dockerfile b/server-json/Dockerfile index 2df5b685..83e6bc87 100644 --- a/server-json/Dockerfile +++ b/server-json/Dockerfile @@ -6,5 +6,13 @@ WORKDIR /app ## Copy Server COPY server.ts . +## Add packages +RUN npm install -g ts-node + +## Add the wait script to the image +ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.9.0/wait /wait +RUN chmod +x /wait + EXPOSE 3001 -CMD [ "node", "server.ts" ] \ No newline at end of file + +CMD /wait && ts-node server.ts diff --git a/server-json/server.ts b/server-json/server.ts index 344fbd52..86634a0a 100644 --- a/server-json/server.ts +++ b/server-json/server.ts @@ -13,7 +13,7 @@ let values = { name: 'Name', amount: 30, updated: false, step: 4 }; /** * Create Server */ -const server = http.createServer(function (req, res) { +const server = http.createServer(function (req: any, res: any) { /** * Set CORS headers */ @@ -28,6 +28,84 @@ const server = http.createServer(function (req, res) { return; } + /** + * Get form + */ + const urlObject = new URL(`http://localhost${req.url}`); + if (urlObject.pathname === '/form') { + const device = urlObject.searchParams.get('device') || ''; + const formElements: unknown[] = [ + { + uid: 'device', + id: 'device', + title: 'Device', + type: 'select', + value: device, + options: [ + { + id: 'device1', + label: 'device1', + type: 'string', + value: 'device1', + }, + { + id: 'device2', + label: 'device2', + type: 'string', + value: 'device2', + }, + ], + optionsSource: 'Custom', + }, + ]; + + /** + * Add device1 elements + */ + if (device === 'device1') { + formElements.push({ + uid: 'device1Field', + id: 'device1Field', + title: 'Device 1 Field', + type: 'number', + value: 0, + min: 0, + max: 10, + }); + } + + /** + * Add device2 elements + */ + if (device === 'device2') { + formElements.push({ + uid: 'device2Field', + id: 'device2Field', + title: 'Device 2 Field', + type: 'number', + value: 0, + min: 0, + max: 10, + }); + } + + /** + * Add common elements + */ + formElements.push({ + uid: 'comment', + id: 'comment', + title: 'Comment', + type: 'textarea', + value: '', + }); + + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.write(JSON.stringify(formElements)); + res.end(); + return; + } + /** * GET */ @@ -49,7 +127,7 @@ const server = http.createServer(function (req, res) { if (req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH') { setTimeout(() => { let body = ''; - req.on('data', function (chunk) { + req.on('data', function (chunk: any) { body += chunk; }); diff --git a/server-json/tsconfig.json b/server-json/tsconfig.json new file mode 100644 index 00000000..6b46b833 --- /dev/null +++ b/server-json/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../.config/tsconfig.json", + "include": ["./*.ts"] +}