Skip to content
Open
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
16 changes: 15 additions & 1 deletion workspace-server/src/__tests__/services/DocsService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,12 @@ describe('DocsService', () => {
markdown: '# Hello',
});

expect(mockDriveAPI.files.create).toHaveBeenCalled();
expect(mockDriveAPI.files.create).toHaveBeenCalledWith(
expect.objectContaining({
supportsAllDrives: true,
}),
expect.any(Object),
);
expect(JSON.parse(result.content[0].text)).toEqual({
documentId: 'test-doc-id',
title: 'Test Title',
Expand Down Expand Up @@ -157,6 +162,7 @@ describe('DocsService', () => {
addParents: 'test-folder-id',
removeParents: 'root',
fields: 'id, parents',
supportsAllDrives: true,
});
});

Expand Down Expand Up @@ -269,6 +275,8 @@ describe('DocsService', () => {
expect(mockDriveAPI.files.list).toHaveBeenCalledWith(
expect.objectContaining({
q: expect.stringContaining("fullText contains 'Test'"),
supportsAllDrives: true,
includeItemsFromAllDrives: true,
}),
);
expect(JSON.parse(result.content[0].text)).toEqual({
Expand Down Expand Up @@ -336,11 +344,17 @@ describe('DocsService', () => {
expect(mockDriveService.findFolder).toHaveBeenCalledWith({
folderName: 'Test Folder',
});
expect(mockDriveAPI.files.get).toHaveBeenCalledWith(
expect.objectContaining({
supportsAllDrives: true,
}),
);
expect(mockDriveAPI.files.update).toHaveBeenCalledWith({
fileId: 'test-doc-id',
addParents: 'test-folder-id',
removeParents: 'root',
fields: 'id, parents',
supportsAllDrives: true,
});
expect(result.content[0].text).toBe(
'Moved document test-doc-id to folder Test Folder',
Expand Down
45 changes: 45 additions & 0 deletions workspace-server/src/__tests__/services/DriveService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,51 @@ describe('DriveService', () => {
);
});

it('should download files when provided with a full Drive URL', async () => {
const mockFileId = 'file-id-from-url';
const mockUrl = `https://drive.google.com/file/d/${mockFileId}/view`;
const mockContent = 'Hello, World!';
const mockBuffer = Buffer.from(mockContent);
const mockLocalPath = 'downloads/test.txt';

mockDriveAPI.files.get.mockImplementation((params: any) => {
if (params.alt === 'media') {
return Promise.resolve({
data: mockBuffer,
});
}
return Promise.resolve({
data: { id: mockFileId, name: 'test.txt', mimeType: 'text/plain' },
});
});

const result = await driveService.downloadFile({
fileId: mockUrl,
localPath: mockLocalPath,
});

expect(mockDriveAPI.files.get).toHaveBeenCalledWith(
expect.objectContaining({
fileId: mockFileId,
fields: 'id, name, mimeType',
supportsAllDrives: true,
}),
);

expect(mockDriveAPI.files.get).toHaveBeenCalledWith(
expect.objectContaining({
fileId: mockFileId,
alt: 'media',
supportsAllDrives: true,
}),
expect.any(Object),
);

expect(result.content[0].text).toContain(
`Successfully downloaded file test.txt`,
);
});

it('should suggest specialized tools for workspace types', async () => {
const mockFileId = 'doc-id';
mockDriveAPI.files.get.mockResolvedValue({
Expand Down
2 changes: 2 additions & 0 deletions workspace-server/src/__tests__/services/SheetsService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ describe('SheetsService', () => {
fields: 'nextPageToken, files(id, name)',
q: "mimeType='application/vnd.google-apps.spreadsheet' and fullText contains 'budget'",
pageToken: undefined,
supportsAllDrives: true,
includeItemsFromAllDrives: true,
});

expect(response.files).toHaveLength(2);
Expand Down
2 changes: 2 additions & 0 deletions workspace-server/src/__tests__/services/SlidesService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ describe('SlidesService', () => {
fields: 'nextPageToken, files(id, name)',
q: "mimeType='application/vnd.google-apps.presentation' and fullText contains 'test query'",
pageToken: undefined,
supportsAllDrives: true,
includeItemsFromAllDrives: true,
});

expect(response.files).toHaveLength(2);
Expand Down
5 changes: 5 additions & 0 deletions workspace-server/src/services/DocsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export class DocsService {
requestBody: fileMetadata,
media: media,
fields: 'id, name',
supportsAllDrives: true,
},
mediaUploadOptions,
);
Expand Down Expand Up @@ -235,6 +236,8 @@ export class DocsService {
fields: 'nextPageToken, files(id, name)',
q: q,
pageToken: pageToken,
supportsAllDrives: true,
includeItemsFromAllDrives: true,
});

const files = res.data.files || [];
Expand Down Expand Up @@ -798,6 +801,7 @@ export class DocsService {
const file = await drive.files.get({
fileId: documentId,
fields: 'parents',
supportsAllDrives: true,
});

const previousParents = file.data.parents?.join(',');
Expand All @@ -807,6 +811,7 @@ export class DocsService {
addParents: folderId,
removeParents: previousParents,
fields: 'id, parents',
supportsAllDrives: true,
});
} catch (error) {
if (error instanceof Error) {
Expand Down
8 changes: 5 additions & 3 deletions workspace-server/src/services/DriveService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { AuthManager } from '../auth/AuthManager';
import { logToFile } from '../utils/logger';
import { gaxiosOptions } from '../utils/GaxiosConfig';
import { escapeQueryString } from '../utils/DriveQueryBuilder';
import { extractDocumentId } from '../utils/validation';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { PROJECT_ROOT } from '../utils/paths';
Expand Down Expand Up @@ -355,10 +356,11 @@ export class DriveService {
logToFile(`Downloading Drive file ${fileId} to ${localPath}`);
try {
const drive = await this.getDriveClient();
const id = extractDocumentId(fileId);

// 1. Check if it's a Google Doc (special handling required, export instead of download)
const metadata = await drive.files.get({
fileId: fileId,
fileId: id,
fields: 'id, name, mimeType',
supportsAllDrives: true,
});
Copy link
Owner Author

@raybell-md raybell-md Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jules Shouldn't this have includeItemsFromAllDrives: true?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, drive.files.get (which is used here) only supports supportsAllDrives, not includeItemsFromAllDrives. The latter is used for list operations to include items from all drives in results. I have ensured supportsAllDrives: true is present for all get calls, which is what the API requires for retrieving a specific file from a shared drive.

Expand Down Expand Up @@ -391,7 +393,7 @@ export class DriveService {
content: [
{
type: 'text' as const,
text: `This is a ${fileInfo.type}. Direct download is not supported. Please use the '${fileInfo.tool}' tool with ${fileInfo.idName}: ${fileId}`,
text: `This is a ${fileInfo.type}. Direct download is not supported. Please use the '${fileInfo.tool}' tool with ${fileInfo.idName}: ${id}`,
},
],
};
Expand All @@ -411,7 +413,7 @@ export class DriveService {
// 2. Download media
const response = await drive.files.get(
{
fileId: fileId,
fileId: id,
alt: 'media',
supportsAllDrives: true,
},
Expand Down
2 changes: 2 additions & 0 deletions workspace-server/src/services/SheetsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ export class SheetsService {
fields: 'nextPageToken, files(id, name)',
q: q,
pageToken: pageToken,
supportsAllDrives: true,
includeItemsFromAllDrives: true,
});

const files = res.data.files || [];
Expand Down
2 changes: 2 additions & 0 deletions workspace-server/src/services/SlidesService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ export class SlidesService {
fields: 'nextPageToken, files(id, name)',
q: q,
pageToken: pageToken,
supportsAllDrives: true,
includeItemsFromAllDrives: true,
});

const files = res.data.files || [];
Expand Down