gapitio / gapit-htmlgraphics-panel

Grafana panel for displaying metric sensitive HTML or SVG graphics.
https://gapit-htmlgraphics-panel.gapit.io/
MIT License
65 stars 8 forks source link

Load SVG from file or url #167

Open clwhitney opened 11 months ago

clwhitney commented 11 months ago

What would you like to be added: I would like the ability to load a SVG from a file or url. The ability to load different svg files based on a user variable. (This could be a follow on.) Would like a single file first.

Why is this needed: I've been creating some very large svg files and it would be helpful to load from a file instead of cut and paste. Also sharing dashboards could be made easier. Dynamic dashboards which could pull up different svg based on a user variable.

This plugin has been a great help for what I've been doing. Thanks.

ZuperZee commented 11 months ago

Just a public url or through Grafana backend?

clwhitney commented 11 months ago

A public url, aka a third party server would be interesting but I have security concerns with that. There would probably need to be authentication handled some how.

Grafana backend. I file may have it's own security issues. Thus I was hoping for a url based upon the same grafana host server. Would be nice that this svg storage area would be persistent across grafana upgrades.

Like: https://grafana.domain.com/dashboards/f/f/dashname https://grafana.domain.com/storage/bigsvg.svg <- This would be the URL for the svg to load in.

storage would just be a directory accessible to the grafana server. Thus authentication has already happened.

Heck the way I wrote that, it would be kind of cool to just a storage plugin which one could maintain extra files needed by plugins. :-) Ignore this line.

ZuperZee commented 11 months ago

htmlgraphics-serve-html

I used the json datasource: https://grafana.com/grafana/plugins/marcusolsson-json-datasource/ To retrieve data from the backend file server.

The URL in the code is http://localhost:3100/api/datasources/proxy/uid/e20b750f-a072-4b4d-a543-bd06517ecc7a, but must be changed to the uid of "your" datasource.

To retrieve the URL: Go to "Data sources" in the "Administration" section. Select the data source. Open the network tab (f12 or ctrl+shift+i). Click "Save & test". image

The file server I created. It's really simple with no auth or anything (change it to something which fits better for your use case) https://github.com/ZuperZee/htmlgraphics-serve-html.

Panel JSON

{
  "datasource": {
    "type": "grafana",
    "uid": "grafana"
  },
  "fieldConfig": {
    "defaults": {
      "mappings": [],
      "thresholds": {
        "mode": "absolute",
        "steps": [
          {
            "color": "green",
            "value": null
          },
          {
            "color": "red",
            "value": 80
          }
        ]
      },
      "color": {
        "mode": "thresholds"
      }
    },
    "overrides": []
  },
  "gridPos": {
    "h": 18,
    "w": 24,
    "x": 0,
    "y": 0
  },
  "id": 1,
  "options": {
    "calcsMutation": "standard",
    "reduceOptions": {
      "calcs": [
        "lastNotNull",
        "last",
        "firstNotNull",
        "first",
        "min",
        "max",
        "mean",
        "sum",
        "count",
        "range",
        "delta",
        "step",
        "diff",
        "logmin",
        "allIsZero",
        "allIsNull",
        "diffperc"
      ]
    },
    "add100Percentage": true,
    "centerAlignContent": true,
    "overflow": "visible",
    "useGrafanaScrollbar": true,
    "SVGBaseFix": true,
    "codeData": "{\n  \"text\": \"Random text\"\n}",
    "rootCSS": "",
    "css": "* {\n  font-family: Open Sans;\n}\n\n",
    "html": "<div></div>",
    "renderOnMount": true,
    "onRender": "",
    "panelupdateOnMount": true,
    "dynamicHtmlGraphics": false,
    "dynamicData": false,
    "dynamicFieldDisplayValues": false,
    "dynamicProps": false,
    "onInitOnResize": false,
    "onInit": "function panelupdate() {\n  const firstDataElt = htmlNode.querySelector(\"#first > .data\");\n  const lastDataElt = htmlNode.querySelector(\"#last > .data\");\n\n  if (firstDataElt) {\n    const valueField = data.series[0]?.fields[1];\n    if (valueField) {\n      const length = valueField.values.length;\n      firstDataElt.textContent = valueField.values.get(0);\n      lastDataElt.textContent = valueField.values.get(length - 1);\n    } else {\n      firstDataElt.textContent = \"No data\"\n      lastDataElt.textContent = \"No data\"\n    }\n  }\n}\n\nasync function getHtml() {\n  const response = await fetch(\"http://localhost:3100/api/datasources/proxy/uid/e20b750f-a072-4b4d-a543-bd06517ecc7a\", {\n    \"mode\": \"cors\",\n    \"credentials\": \"include\"\n  });\n\n  if (!response.ok) {\n    throw new Error(`HTTP error: ${response.status}`);\n  }\n  const data = await response.text();\n  return data;\n}\n\nasync function updateHtml() {\n  const html = await getHtml();\n  htmlNode.innerHTML = html;\n}\n\nasync function main() {\n  await updateHtml();\n\n  panelupdate();\n  htmlNode.addEventListener(\"onpanelupdate\", panelupdate);\n}\n\nmain();\n"
  },
  "targets": [
    {
      "datasource": {
        "type": "datasource",
        "uid": "grafana"
      },
      "refId": "A"
    }
  ],
  "title": "Panel Title",
  "type": "gapit-htmlgraphics-panel"
}
ZuperZee commented 11 months ago

Accidentally closed the issue while writing the comment

ZuperZee commented 11 months ago

I could make it a little more integrated into the plugin, but it would more or less just be the updateHtml function which would be moved into HTMLGraphics.

clwhitney commented 11 months ago

Thank you. I think I see what you are doing. Creating a datasource of the file I want via the JSON API. Then including that in the oninit section to load. Thus a datasource per file. (I'll have to think about that.)

I was really trying to get around having to cut/paste 1+M svg files into the svg/canvas box. But I also found that the grafana server is pretty locked down. I can really use it to serve my images also. (I can't run another web server.) Thus I hoped there was a way for the plugin to access the files. I thought one of the other svg grafana plugins had an image directory. One could use one of their images or build your own. I remember dropping my image into their plugin image area and that worked. Just was a hassle when the plugin was upgraded.

I'll have to play more with this, running out of time before the Thanksgiving Holiday. Thanks again and have a happy holiday. (If your not from the US, go out with friends and have a good time.)

clwhitney commented 10 months ago

Ok, finally got back to this. Sorry about the wait. Nice clever hack there. I just noticed one thing, when the svg is placed into the panel, it seems to be bound differently. When I cut/paste into the plugin, the svg is bound within the panel nicely. Resizing flows really well. When I bring the svg in via this method, it was off center and I can resize the panel in ways that will chop the svg down. (I believe the views are set differently.)

In some way, if it is possible, I would still like having a filename or url added to the plugin. Kind of lazy but from a portability standpoint, moving the dashboard and datasource may be a little confusing. The url would be another way for me to document which svg I am using.

Also I figured I should load a couple of screen shots of what I am doing.

Screen Shot 2023-12-19 at 10 21 10 AM Screen Shot 2023-12-19 at 10 20 51 AM

Thank you.

ZuperZee commented 10 months ago

I just noticed one thing, when the svg is placed into the panel, it seems to be bound differently. When I cut/paste into the plugin, the svg is bound within the panel nicely. Resizing flows really well. When I bring the svg in via this method, it was off center and I can resize the panel in ways that will chop the svg down. (I believe the views are set differently.)

There shouldn't be any difference. Might be some code that is running before the SVG has rendered? No code should be inside onRender.

In some way, if it is possible, I would still like having a filename or url added to the plugin. Kind of lazy but from a portability standpoint, moving the dashboard and datasource may be a little confusing. The url would be another way for me to document which svg I am using.

Creating this would require having backend parts, which would require a decent amount of work. I might try it if I get some motivation to do it, but it will probably not be added in the near future.

clwhitney commented 10 months ago

Just thought of this also. With URL's one could include more than one svg in a panel. So yeah, I understand that this might be a bit big.

Thanks for your consideration.