Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/googledrive-root-browse.md
Original file line number Diff line number Diff line change
@@ -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).
4 changes: 3 additions & 1 deletion content-providers/src/helpers/ApiHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<DriveFileListResponse>(`/files?${params.toString()}`, auth);
Expand Down
23 changes: 22 additions & 1 deletion content-providers/tests/googleDriveProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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 {
Expand Down
Loading