From db480ca991fe9cc2b31ea20862ca737adb6760a2 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 23 Sep 2026 15:32:30 -0700 Subject: [PATCH] Performance: reduce json data transformations (#7175) --- src/widgets/gitea/widget.js | 11 +++++++---- src/widgets/gitea/widget.test.js | 11 ++++++++++- src/widgets/miniflux/widget.js | 11 +++++++---- src/widgets/miniflux/widget.test.js | 10 +++++++++- src/widgets/radarr/widget.js | 23 +++++++++++++---------- src/widgets/radarr/widget.test.js | 23 +++++++++++++++++++++++ src/widgets/unmanic/widget.js | 11 +++++++---- src/widgets/unmanic/widget.test.js | 10 +++++++++- 8 files changed, 85 insertions(+), 25 deletions(-) diff --git a/src/widgets/gitea/widget.js b/src/widgets/gitea/widget.js index b0420ccc6..003a6710d 100644 --- a/src/widgets/gitea/widget.js +++ b/src/widgets/gitea/widget.js @@ -11,10 +11,13 @@ const widget = { }, issues: { endpoint: "repos/issues/search", - map: (data) => ({ - pulls: asJson(data).filter((issue) => issue.pull_request), - issues: asJson(data).filter((issue) => !issue.pull_request), - }), + map: (data) => { + const items = asJson(data); + return { + pulls: items.filter((issue) => issue.pull_request), + issues: items.filter((issue) => !issue.pull_request), + }; + }, }, repositories: { endpoint: "repos/search", diff --git a/src/widgets/gitea/widget.test.js b/src/widgets/gitea/widget.test.js index 75395c126..1039ad7ce 100644 --- a/src/widgets/gitea/widget.test.js +++ b/src/widgets/gitea/widget.test.js @@ -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,13 @@ describe("gitea widget config", () => { it("exports a valid widget config", () => { expectWidgetConfigShape(widget); }); + + it("splits issues and pull requests", () => { + const result = widget.mappings.issues.map( + Buffer.from(JSON.stringify([{ id: 1, pull_request: {} }, { id: 2 }, { id: 3 }])), + ); + + expect(result.pulls.map((i) => i.id)).toEqual([1]); + expect(result.issues.map((i) => i.id)).toEqual([2, 3]); + }); }); diff --git a/src/widgets/miniflux/widget.js b/src/widgets/miniflux/widget.js index dbf4bbf37..aebe2cf97 100644 --- a/src/widgets/miniflux/widget.js +++ b/src/widgets/miniflux/widget.js @@ -8,10 +8,13 @@ const widget = { mappings: { counters: { endpoint: "feeds/counters", - map: (data) => ({ - read: Object.values(asJson(data).reads).reduce((acc, i) => acc + i, 0), - unread: Object.values(asJson(data).unreads).reduce((acc, i) => acc + i, 0), - }), + map: (data) => { + const { reads, unreads } = asJson(data); + return { + read: Object.values(reads).reduce((acc, i) => acc + i, 0), + unread: Object.values(unreads).reduce((acc, i) => acc + i, 0), + }; + }, }, }, }; diff --git a/src/widgets/miniflux/widget.test.js b/src/widgets/miniflux/widget.test.js index 62f7ad9b6..272d1fc64 100644 --- a/src/widgets/miniflux/widget.test.js +++ b/src/widgets/miniflux/widget.test.js @@ -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,12 @@ describe("miniflux widget config", () => { it("exports a valid widget config", () => { expectWidgetConfigShape(widget); }); + + it("sums read and unread counters", () => { + const result = widget.mappings.counters.map( + Buffer.from(JSON.stringify({ reads: { 1: 2, 2: 3 }, unreads: { 1: 4, 2: 1 } })), + ); + + expect(result).toEqual({ read: 5, unread: 5 }); + }); }); diff --git a/src/widgets/radarr/widget.js b/src/widgets/radarr/widget.js index 5170325d6..1f307430d 100644 --- a/src/widgets/radarr/widget.js +++ b/src/widgets/radarr/widget.js @@ -1,4 +1,4 @@ -import { asJson, jsonArrayFilter } from "utils/proxy/api-helpers"; +import { asJson } from "utils/proxy/api-helpers"; import genericProxyHandler from "utils/proxy/handlers/generic"; const widget = { @@ -8,15 +8,18 @@ const widget = { mappings: { movie: { endpoint: "movie", - map: (data) => ({ - wanted: jsonArrayFilter(data, (item) => item.monitored && !item.hasFile && item.isAvailable).length, - have: jsonArrayFilter(data, (item) => item.hasFile).length, - missing: jsonArrayFilter(data, (item) => item.monitored && !item.hasFile).length, - all: asJson(data).map((entry) => ({ - title: entry.title, - id: entry.id, - })), - }), + map: (data) => { + const movieData = asJson(data) ?? []; + return { + wanted: movieData.filter((item) => item.monitored && !item.hasFile && item.isAvailable).length, + have: movieData.filter((item) => item.hasFile).length, + missing: movieData.filter((item) => item.monitored && !item.hasFile).length, + all: movieData.map((entry) => ({ + title: entry.title, + id: entry.id, + })), + }; + }, }, "queue/status": { endpoint: "queue/status", diff --git a/src/widgets/radarr/widget.test.js b/src/widgets/radarr/widget.test.js index c7a19f39b..9e19aecfa 100644 --- a/src/widgets/radarr/widget.test.js +++ b/src/widgets/radarr/widget.test.js @@ -33,4 +33,27 @@ describe("radarr widget config", () => { expect(queue.map((entry) => entry.movieId)).toEqual([2, 1]); }); + + it("maps movie counts and titles", () => { + const movies = widget.mappings.movie.map( + Buffer.from( + JSON.stringify([ + { id: 1, title: "A", monitored: true, hasFile: false, isAvailable: true }, + { id: 2, title: "B", monitored: true, hasFile: true }, + { id: 3, title: "C", monitored: true, hasFile: false, isAvailable: false }, + ]), + ), + ); + + expect(movies).toEqual({ + wanted: 1, + have: 1, + missing: 2, + all: [ + { title: "A", id: 1 }, + { title: "B", id: 2 }, + { title: "C", id: 3 }, + ], + }); + }); }); diff --git a/src/widgets/unmanic/widget.js b/src/widgets/unmanic/widget.js index ef4493e9a..0544c0c68 100644 --- a/src/widgets/unmanic/widget.js +++ b/src/widgets/unmanic/widget.js @@ -8,10 +8,13 @@ const widget = { mappings: { workers: { endpoint: "workers/status", - map: (data) => ({ - total_workers: asJson(data).workers_status.length, - active_workers: asJson(data).workers_status.filter((worker) => !worker.idle).length, - }), + map: (data) => { + const workers = asJson(data).workers_status; + return { + total_workers: workers.length, + active_workers: workers.filter((worker) => !worker.idle).length, + }; + }, }, pending: { method: "POST", diff --git a/src/widgets/unmanic/widget.test.js b/src/widgets/unmanic/widget.test.js index 2997a66b6..f153a6032 100644 --- a/src/widgets/unmanic/widget.test.js +++ b/src/widgets/unmanic/widget.test.js @@ -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,12 @@ describe("unmanic widget config", () => { it("exports a valid widget config", () => { expectWidgetConfigShape(widget); }); + + it("counts total and active workers", () => { + const result = widget.mappings.workers.map( + Buffer.from(JSON.stringify({ workers_status: [{ idle: true }, { idle: false }, { idle: false }] })), + ); + + expect(result).toEqual({ total_workers: 3, active_workers: 2 }); + }); });