fix custom widget review findings

This commit is contained in:
ajnart committed 2026-08-14 10:35:21 +02:00
1 parent 0482c040ec
commit afa54755fd
17 files changed
+207 -26

No files matched your search

@@ -8,12 +8,20 @@ import {
recordPreviewJournal,
resolvePreviewRequestParams,
} from "./preview-procedure-helpers";
import { executeCustomWidgetRequest } from "./request-executor";
import { executeCustomWidgetRequest, invalidateCustomWidgetResponseCache } from "./request-executor";
import { hashRuntimeParams, renderRequestBody, renderRequestTarget } from "./request-manifest";
import { acquireCustomWidgetRequestLimit } from "./request-limits";
import { getPreviewJournal, getPreviewSession, setPreviewSessionLiveActions } from "./preview-sessions";
export const previewQueryProcedures = {
previewRefresh: permissionRequiredProcedure
.requiresPermission("admin")
.input(z.object({ sessionId: z.string().min(1) }))
.mutation(async ({ ctx, input }) => {
const session = await getPreviewSession(input.sessionId, ctx.session.user.id);
invalidateCustomWidgetResponseCache([`custom-jsx:preview:${session.id}:`]);
}),
previewQuery: permissionRequiredProcedure
.requiresPermission("admin")
.meta({
@@ -10,11 +10,12 @@ import type { HomarrCustomWidgetV2 } from "@homarr/custom-widgets/core";
const mocks = vi.hoisted(() => ({
executeRequest: vi.fn(async () => ({ ok: true, status: 200, statusText: "OK", data: { value: 42 } })),
invalidateResponseCache: vi.fn(),
}));
vi.mock("../../custom-widget/request-executor", () => ({
executeCustomWidgetRequest: mocks.executeRequest,
invalidateCustomWidgetResponseCache: vi.fn(),
invalidateCustomWidgetResponseCache: mocks.invalidateResponseCache,
}));
vi.mock("../../custom-widget/request-limits", () => ({
@@ -111,7 +112,21 @@ async function setup(
}
describe("Custom JSX v2 board router", () => {
beforeEach(() => mocks.executeRequest.mockClear());
beforeEach(() => {
mocks.executeRequest.mockClear();
mocks.invalidateResponseCache.mockClear();
});
test("refreshes the server response cache for a placed widget", async () => {
const { db, itemId } = await setup();
const caller = customApiRouter.createCaller({ db, deviceType: undefined, session: null });
await caller.refresh({ itemId });
expect(mocks.invalidateResponseCache).toHaveBeenCalledWith([
expect.stringMatching(new RegExp(`^custom-jsx:${itemId}:\\d+:$`, "u")),
]);
});
test("loads placed widgets and public queries for anonymous visitors on public boards", async () => {
const { db, itemId } = await setup();
@@ -195,6 +195,13 @@ const executeRequest = async (
};
export const customApiRouter = createTRPCRouter({
refresh: publicProcedure.input(itemInputSchema).mutation(async ({ ctx, input }) => {
const resolved = await resolvePlacedDefinitionAsync(ctx, input.itemId);
invalidateCustomWidgetResponseCache([
`custom-jsx:${resolved.item.id}:${getCustomWidgetCacheVersion(resolved.stored)}:`,
]);
}),
getData: publicProcedure.input(itemInputSchema).query(async ({ ctx, input }) => {
const resolved = await resolvePlacedDefinitionAsync(ctx, input.itemId);
const loadRequests = Object.entries(resolved.definition.requests).filter(