Enhancement: widget support for Pulse v6 API changes (#6987)
Lint / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Release Drafter / Update Release Draft (push) Has been cancelled
Release Drafter / Auto Label PR (push) Has been cancelled
Tests / vitest (1) (push) Has been cancelled
Tests / vitest (2) (push) Has been cancelled
Tests / vitest (3) (push) Has been cancelled
Tests / vitest (4) (push) Has been cancelled

This commit is contained in:
shamoon
2026-08-15 00:11:08 -07:00
committed by GitHub
parent 27704f962e
commit 342afa2215
7 changed files with 46 additions and 2 deletions
+1
View File
@@ -12,5 +12,6 @@ widget:
type: pulse
url: http://pulse.host.or.ip:7655
key: your-api-token # `monitoring:read` scope is required
version: 2 # required for Pulse v6, defaults to 1
fields: ["nodes", "vms", "lxcs"] # optional
```
+1
View File
@@ -589,6 +589,7 @@ export function cleanServiceGroups(groups) {
"grafana",
"gluetun",
"vikunja",
"pulse",
].includes(type)
) {
widget.version = parseVersionForUrl(version);
+2
View File
@@ -266,6 +266,7 @@ describe("utils/config/service-helpers", () => {
widgets: [
{ type: "azuredevops", userEmail: "u@example.com", repositoryId: "r" },
{ type: "beszel", version: "2", systemId: "sys" },
{ type: "pulse", version: "2" },
{ type: "coinmarketcap", currency: "USD", symbols: "BTC", slugs: "bitcoin", defaultinterval: "1d" },
{ type: "crowdsec", limit24h: "true" },
{ type: "docker", server: "docker-local", container: "c1" },
@@ -345,6 +346,7 @@ describe("utils/config/service-helpers", () => {
expect.objectContaining({ userEmail: "u@example.com", repositoryId: "r" }),
);
expect(widgets.find((w) => w.type === "beszel")).toEqual(expect.objectContaining({ version: 2, systemId: "sys" }));
expect(widgets.find((w) => w.type === "pulse")).toEqual(expect.objectContaining({ version: 2 }));
expect(widgets.find((w) => w.type === "crowdsec")).toEqual(expect.objectContaining({ limit24h: true }));
expect(widgets.find((w) => w.type === "docker")).toEqual(
expect.objectContaining({ server: "docker-local", container: "c1" }),
+13 -1
View File
@@ -39,7 +39,8 @@ function formatResourceCount(total, active) {
export default function Component({ service }) {
const { widget } = service;
const { data: resourcesData, error: resourcesError } = useWidgetAPI(widget, "resources");
const version = widget.version ?? 1;
const { data: resourcesData, error: resourcesError } = useWidgetAPI(widget, version === 2 ? "summary" : "resources");
if (resourcesError) {
return <Container service={service} error={resourcesError} />;
@@ -55,6 +56,17 @@ export default function Component({ service }) {
);
}
if (version === 2) {
// pulse v6 (v2 widget) returns the counts directly in the summary
return (
<Container service={service}>
<Block label="pulse.nodes" value={resourcesData.nodes} />
<Block label="pulse.vms" value={resourcesData.vms} />
<Block label="pulse.lxcs" value={resourcesData.containers} />
</Container>
);
}
let resources = resourcesData.resources;
if (!resources && resourcesData.count === 0) {
resources = [];
+21
View File
@@ -27,6 +27,27 @@ describe("widgets/pulse/component", () => {
expect(screen.getByText("pulse.nodes")).toBeInTheDocument();
expect(screen.getByText("pulse.vms")).toBeInTheDocument();
expect(screen.getByText("pulse.lxcs")).toBeInTheDocument();
expect(useWidgetAPI).toHaveBeenCalledWith({ type: "pulse" }, "resources");
});
it("renders Pulse v6 summary counts", () => {
useWidgetAPI.mockReturnValue({
data: {
nodes: 2,
vms: 8,
containers: 12,
},
error: undefined,
});
const { container } = renderWithProviders(<Component service={{ widget: { type: "pulse", version: 2 } }} />, {
settings: { hideErrors: false },
});
expect(useWidgetAPI).toHaveBeenCalledWith({ type: "pulse", version: 2 }, "summary");
expectBlockValue(container, "pulse.nodes", 2);
expectBlockValue(container, "pulse.vms", 8);
expectBlockValue(container, "pulse.lxcs", 12);
});
it("renders error UI when the resources endpoint errors", () => {
+3
View File
@@ -8,6 +8,9 @@ const widget = {
resources: {
endpoint: "api/resources",
},
summary: {
endpoint: "api/state/summary",
},
},
};
+5 -1
View File
@@ -1,4 +1,4 @@
import { describe, it } from "vitest";
import { describe, expect, it } from "vitest";
import { expectWidgetConfigShape } from "test-utils/widget-config";
@@ -8,4 +8,8 @@ describe("pulse widget config", () => {
it("exports a valid widget config", () => {
expectWidgetConfigShape(widget);
});
it("maps the Pulse v6 summary endpoint", () => {
expect(widget.mappings.summary.endpoint).toBe("api/state/summary");
});
});