feat(swagger): Add Swagger API reference, and fixes #28
@@ -26,7 +26,7 @@ const DownloadMatcher = require('./DownloadMatcher');
|
||||
* @param {string} options.ombiBaseUrl - Ombi base URL for link generation (optional)
|
||||
* @returns {Array} Array of download objects for the user
|
||||
*/
|
||||
function buildUserDownloads(cacheSnapshot, { username, usernameSanitized, isAdmin, showAll, seriesMap, moviesMap, sonarrTagMap, radarrTagMap, embyUserMap, ombiRetriever, ombiBaseUrl }) {
|
||||
async function buildUserDownloads(cacheSnapshot, { username, usernameSanitized, isAdmin, showAll, seriesMap, moviesMap, sonarrTagMap, radarrTagMap, embyUserMap, ombiRetriever, ombiBaseUrl }) {
|
||||
// Input validation
|
||||
if (!cacheSnapshot || typeof cacheSnapshot !== 'object') {
|
||||
console.error('[DownloadBuilder] Invalid cacheSnapshot provided');
|
||||
@@ -74,7 +74,7 @@ function buildUserDownloads(cacheSnapshot, { username, usernameSanitized, isAdmi
|
||||
const seenDownloadKeys = new Set();
|
||||
|
||||
if (sabnzbdQueue.data?.queue?.slots) {
|
||||
const sabMatches = DownloadMatcher.matchSabSlots(sabnzbdQueue.data.queue.slots, context);
|
||||
const sabMatches = await DownloadMatcher.matchSabSlots(sabnzbdQueue.data.queue.slots, context);
|
||||
for (const dl of sabMatches) {
|
||||
const key = `${dl.type}:${dl.title}`;
|
||||
if (!seenDownloadKeys.has(key)) {
|
||||
@@ -85,7 +85,7 @@ function buildUserDownloads(cacheSnapshot, { username, usernameSanitized, isAdmi
|
||||
}
|
||||
|
||||
if (sabnzbdHistory.data?.history?.slots) {
|
||||
const sabHistoryMatches = DownloadMatcher.matchSabHistory(sabnzbdHistory.data.history.slots, context);
|
||||
const sabHistoryMatches = await DownloadMatcher.matchSabHistory(sabnzbdHistory.data.history.slots, context);
|
||||
for (const dl of sabHistoryMatches) {
|
||||
const key = `${dl.type}:${dl.title}`;
|
||||
if (!seenDownloadKeys.has(key)) {
|
||||
@@ -95,7 +95,7 @@ function buildUserDownloads(cacheSnapshot, { username, usernameSanitized, isAdmi
|
||||
}
|
||||
}
|
||||
|
||||
const torrentMatches = DownloadMatcher.matchTorrents(qbittorrentTorrents, context);
|
||||
const torrentMatches = await DownloadMatcher.matchTorrents(qbittorrentTorrents, context);
|
||||
for (const dl of torrentMatches) {
|
||||
const key = `${dl.type}:${dl.title}`;
|
||||
if (!seenDownloadKeys.has(key)) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import { buildUserDownloads } from '../../../server/services/DownloadBuilder.js';
|
||||
|
||||
describe('buildUserDownloads', () => {
|
||||
// All tests in this suite are async because buildUserDownloads is async
|
||||
const username = 'alice';
|
||||
const usernameSanitized = 'alice';
|
||||
const isAdmin = false;
|
||||
@@ -56,7 +57,7 @@ describe('buildUserDownloads', () => {
|
||||
}]
|
||||
]);
|
||||
|
||||
it('returns empty array when no downloads match user', () => {
|
||||
it('returns empty array when no downloads match user', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: { data: { queue: { slots: [] } } },
|
||||
sabnzbdHistory: { data: { history: { slots: [] } } },
|
||||
@@ -67,7 +68,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -82,7 +83,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns empty array for null/undefined cache data', () => {
|
||||
it('returns empty array for null/undefined cache data', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: null,
|
||||
sabnzbdHistory: null,
|
||||
@@ -93,7 +94,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: null
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -108,7 +109,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('matches SABnzbd queue slot to Sonarr series for tagged user', () => {
|
||||
it('matches SABnzbd queue slot to Sonarr series for tagged user', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -151,7 +152,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -178,7 +179,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result[0].episodes).toBeInstanceOf(Array);
|
||||
});
|
||||
|
||||
it('matches SABnzbd queue slot to Radarr movie for tagged user', () => {
|
||||
it('matches SABnzbd queue slot to Radarr movie for tagged user', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -220,7 +221,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -246,7 +247,7 @@ describe('buildUserDownloads', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('matches qBittorrent torrent to Sonarr series for tagged user', () => {
|
||||
it('matches qBittorrent torrent to Sonarr series for tagged user', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: { data: { queue: { slots: [] } } },
|
||||
sabnzbdHistory: { data: { history: { slots: [] } } },
|
||||
@@ -280,7 +281,7 @@ describe('buildUserDownloads', () => {
|
||||
}]
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -307,7 +308,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result[0]).toHaveProperty('eta');
|
||||
});
|
||||
|
||||
it('includes admin-specific fields when isAdmin is true', () => {
|
||||
it('includes admin-specific fields when isAdmin is true', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -350,7 +351,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin: true,
|
||||
@@ -377,7 +378,7 @@ describe('buildUserDownloads', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('filters by user tag when showAll is false', () => {
|
||||
it('filters by user tag when showAll is false', async () => {
|
||||
const bobSeriesMap = new Map([
|
||||
[2, {
|
||||
id: 2,
|
||||
@@ -429,7 +430,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username: 'alice',
|
||||
usernameSanitized: 'alice',
|
||||
isAdmin: false,
|
||||
@@ -445,7 +446,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('shows all tagged downloads when showAll is true (admin mode)', () => {
|
||||
it('shows all tagged downloads when showAll is true (admin mode)', async () => {
|
||||
const bobSeriesMap = new Map([
|
||||
[2, {
|
||||
id: 2,
|
||||
@@ -497,7 +498,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username: 'alice',
|
||||
usernameSanitized: 'alice',
|
||||
isAdmin: true,
|
||||
@@ -520,7 +521,7 @@ describe('buildUserDownloads', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('includes importIssues when present in queue record', () => {
|
||||
it('includes importIssues when present in queue record', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -567,7 +568,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -583,7 +584,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result[0].importIssues).toEqual(['Sample needs repack', 'Disk space low']);
|
||||
});
|
||||
|
||||
it('handles mixed series and movie downloads', () => {
|
||||
it('handles mixed series and movie downloads', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -651,7 +652,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -668,7 +669,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result[1].type).toBe('movie');
|
||||
});
|
||||
|
||||
it('prevents duplicate downloads when same item matches multiple sources', () => {
|
||||
it('prevents duplicate downloads when same item matches multiple sources', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -730,7 +731,7 @@ describe('buildUserDownloads', () => {
|
||||
}]
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -746,7 +747,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('matches SABnzbd history slots to completed downloads', () => {
|
||||
it('matches SABnzbd history slots to completed downloads', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: { data: { queue: { slots: [] } } },
|
||||
sabnzbdHistory: {
|
||||
@@ -782,7 +783,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin,
|
||||
@@ -803,7 +804,7 @@ describe('buildUserDownloads', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('does not display unmatched torrents', () => {
|
||||
it('does not display unmatched torrents', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: { data: { queue: { slots: [] } } },
|
||||
sabnzbdHistory: { data: { history: { slots: [] } } },
|
||||
@@ -824,7 +825,7 @@ describe('buildUserDownloads', () => {
|
||||
}]
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin: false,
|
||||
@@ -840,7 +841,7 @@ describe('buildUserDownloads', () => {
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('includes sonarrLink and radarrLink when available', () => {
|
||||
it('includes sonarrLink and radarrLink when available', async () => {
|
||||
const cacheSnapshot = {
|
||||
sabnzbdQueue: {
|
||||
data: {
|
||||
@@ -908,7 +909,7 @@ describe('buildUserDownloads', () => {
|
||||
qbittorrentTorrents: []
|
||||
};
|
||||
|
||||
const result = buildUserDownloads(cacheSnapshot, {
|
||||
const result = await buildUserDownloads(cacheSnapshot, {
|
||||
username,
|
||||
usernameSanitized,
|
||||
isAdmin: true,
|
||||
|
||||
Reference in New Issue
Block a user