diff --git a/.changeset/googledrive-root-browse.md b/.changeset/googledrive-root-browse.md new file mode 100644 index 0000000..4d92f99 --- /dev/null +++ b/.changeset/googledrive-root-browse.md @@ -0,0 +1,5 @@ +--- +"@churchapps/content-providers": patch +--- + +Fix Google Drive browsing returning an empty list: the file query no longer combines `includeItemsFromAllDrives` with a `root` parent lookup, which Google rejected, and failed API requests now log the response body (ChurchAppsSupport #944). diff --git a/content-providers/src/helpers/ApiHelper.ts b/content-providers/src/helpers/ApiHelper.ts index ef1e8be..31f846d 100644 --- a/content-providers/src/helpers/ApiHelper.ts +++ b/content-providers/src/helpers/ApiHelper.ts @@ -17,7 +17,9 @@ export class ApiHelper { const response = await fetch(url, options); if (!response.ok) { - console.warn(`[${providerId}] apiRequest failed: ${method} ${url} → HTTP ${response.status} ${response.statusText}`); + // Body carries the provider's actual reason (bad param, expired token); truncate so a long HTML error page doesn't flood the log + const detail = await response.text().catch(() => ""); + console.warn(`[${providerId}] apiRequest failed: ${method} ${url} → HTTP ${response.status} ${response.statusText}${detail ? ` ${detail.slice(0, 500)}` : ""}`); return null; } return await response.json(); diff --git a/content-providers/src/providers/googledrive/GoogleDriveProvider.ts b/content-providers/src/providers/googledrive/GoogleDriveProvider.ts index 077c0dd..1569c50 100644 --- a/content-providers/src/providers/googledrive/GoogleDriveProvider.ts +++ b/content-providers/src/providers/googledrive/GoogleDriveProvider.ts @@ -67,7 +67,9 @@ export class GoogleDriveProvider extends BaseProvider { orderBy: "folder,name_natural", pageSize: "1000", supportsAllDrives: "true", - includeItemsFromAllDrives: "true" + // corpora=user (not includeItemsFromAllDrives) — "root" is only meaningful in the user's own corpus, and + // combining it with an all-drives search makes Google reject the query, which browse saw as an empty folder. + corpora: "user" }); if (pageToken) params.set("pageToken", pageToken); const response = await this.apiRequest(`/files?${params.toString()}`, auth); diff --git a/content-providers/tests/googleDriveProvider.test.ts b/content-providers/tests/googleDriveProvider.test.ts index 6b1f364..c8066e3 100644 --- a/content-providers/tests/googleDriveProvider.test.ts +++ b/content-providers/tests/googleDriveProvider.test.ts @@ -70,7 +70,13 @@ test("browse treats empty path as root and rejects malformed folder ids without const restore = mockFetch((url) => { requests.push(url); return { files: [] }; }); try { await new GoogleDriveProvider().browse(null, auth); - assert.ok(new URL(requests[0]).searchParams.get("q")?.includes("'root' in parents")); + const params = new URL(requests[0]).searchParams; + assert.ok(params.get("q")?.includes("'root' in parents")); + // "root" only resolves within the user's own corpus; includeItemsFromAllDrives makes Google reject the query + assert.equal(params.get("supportsAllDrives"), "true"); + assert.equal(params.get("corpora"), "user"); + assert.equal(params.get("includeItemsFromAllDrives"), null); + assert.equal(params.get("orderBy"), "folder,name_natural"); requests.length = 0; const items = await new GoogleDriveProvider().browse("/x' or 'a", auth); @@ -81,6 +87,21 @@ test("browse treats empty path as root and rejects malformed folder ids without } }); +test("browse returns an empty list when Drive rejects the request", async () => { + const realFetch = globalThis.fetch; + globalThis.fetch = (async () => ({ + ok: false, + status: 400, + statusText: "Bad Request", + text: async () => "{\"error\":{\"message\":\"Invalid Value\"}}" + }) as Response) as typeof fetch; + try { + assert.deepEqual(await new GoogleDriveProvider().browse("/parent1", auth), []); + } finally { + globalThis.fetch = realFetch; + } +}); + test("getPlaylist returns media files for a folder and null at root or when empty", async () => { const restore = mockFetch(() => listing); try {