Fix(google drive): google sheets creating a file (#1476)

* fixed upload to google drive with sheets

* cleanup, remove unneeded scope
This commit is contained in:
Adam Gough
2025-09-27 20:48:01 -07:00
committed by GitHub
parent dd1985c99c
commit 5ee6345120
2 changed files with 95 additions and 6 deletions
+22 -6
View File
@@ -1,6 +1,10 @@
import { createLogger } from '@/lib/logs/console/logger'
import type { GoogleDriveToolParams, GoogleDriveUploadResponse } from '@/tools/google_drive/types'
import { GOOGLE_WORKSPACE_MIME_TYPES, SOURCE_MIME_TYPES } from '@/tools/google_drive/utils'
import {
GOOGLE_WORKSPACE_MIME_TYPES,
handleSheetsFormat,
SOURCE_MIME_TYPES,
} from '@/tools/google_drive/utils'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('GoogleDriveUploadTool')
@@ -96,13 +100,27 @@ export const uploadTool: ToolConfig<GoogleDriveToolParams, GoogleDriveUploadResp
throw new Error(data.error?.message || 'Failed to create file in Google Drive')
}
// Now upload content to the created file
const fileId = data.id
const requestedMimeType = params?.mimeType || 'text/plain'
const authHeader =
response.headers.get('Authorization') || `Bearer ${params?.accessToken || ''}`
// For Google Workspace formats, use the appropriate source MIME type for content upload
let preparedContent: string | undefined =
typeof params?.content === 'string' ? (params?.content as string) : undefined
if (requestedMimeType === 'application/vnd.google-apps.spreadsheet' && params?.content) {
const { csv, rowCount, columnCount } = handleSheetsFormat(params.content as unknown)
if (csv !== undefined) {
preparedContent = csv
logger.info('Prepared CSV content for Google Sheets upload', {
fileId,
fileName: params?.fileName,
rowCount,
columnCount,
})
}
}
const uploadMimeType = GOOGLE_WORKSPACE_MIME_TYPES.includes(requestedMimeType)
? SOURCE_MIME_TYPES[requestedMimeType] || 'text/plain'
: requestedMimeType
@@ -122,7 +140,7 @@ export const uploadTool: ToolConfig<GoogleDriveToolParams, GoogleDriveUploadResp
Authorization: authHeader,
'Content-Type': uploadMimeType,
},
body: params?.content || '',
body: preparedContent !== undefined ? preparedContent : params?.content || '',
}
)
@@ -136,7 +154,6 @@ export const uploadTool: ToolConfig<GoogleDriveToolParams, GoogleDriveUploadResp
throw new Error(uploadError.error?.message || 'Failed to upload content to file')
}
// For Google Workspace documents, update the name again to ensure it sticks after conversion
if (GOOGLE_WORKSPACE_MIME_TYPES.includes(requestedMimeType)) {
logger.info('Updating file name to ensure it persists after conversion', {
fileId,
@@ -165,7 +182,6 @@ export const uploadTool: ToolConfig<GoogleDriveToolParams, GoogleDriveUploadResp
}
}
// Get the final file data
const finalFileResponse = await fetch(
`https://www.googleapis.com/drive/v3/files/${fileId}?supportsAllDrives=true&fields=id,name,mimeType,webViewLink,webContentLink,size,createdTime,modifiedTime,parents`,
{
+73
View File
@@ -21,3 +21,76 @@ export const SOURCE_MIME_TYPES: Record<string, string> = {
'application/vnd.google-apps.spreadsheet': 'text/csv',
'application/vnd.google-apps.presentation': 'application/vnd.ms-powerpoint',
}
export function handleSheetsFormat(input: unknown): {
csv?: string
rowCount: number
columnCount: number
} {
let workingValue: unknown = input
if (typeof workingValue === 'string') {
try {
workingValue = JSON.parse(workingValue)
} catch (_error) {
const csvString = workingValue as string
return { csv: csvString, rowCount: 0, columnCount: 0 }
}
}
if (!Array.isArray(workingValue)) {
return { rowCount: 0, columnCount: 0 }
}
let table: unknown[] = workingValue
if (
table.length > 0 &&
typeof (table as any)[0] === 'object' &&
(table as any)[0] !== null &&
!Array.isArray((table as any)[0])
) {
const allKeys = new Set<string>()
;(table as any[]).forEach((obj) => {
if (obj && typeof obj === 'object') {
Object.keys(obj).forEach((key) => allKeys.add(key))
}
})
const headers = Array.from(allKeys)
const rows = (table as any[]).map((obj) => {
if (!obj || typeof obj !== 'object') {
return Array(headers.length).fill('')
}
return headers.map((key) => {
const value = (obj as Record<string, unknown>)[key]
if (value !== null && typeof value === 'object') {
return JSON.stringify(value)
}
return value === undefined ? '' : (value as any)
})
})
table = [headers, ...rows]
}
const escapeCell = (cell: unknown): string => {
if (cell === null || cell === undefined) return ''
const stringValue = String(cell)
const mustQuote = /[",\n\r]/.test(stringValue)
const doubledQuotes = stringValue.replace(/"/g, '""')
return mustQuote ? `"${doubledQuotes}"` : doubledQuotes
}
const rowsAsStrings = (table as unknown[]).map((row) => {
if (!Array.isArray(row)) {
return escapeCell(row)
}
return row.map((cell) => escapeCell(cell)).join(',')
})
const csv = rowsAsStrings.join('\r\n')
const rowCount = Array.isArray(table) ? (table as any[]).length : 0
const columnCount =
Array.isArray(table) && Array.isArray((table as any[])[0]) ? (table as any[])[0].length : 0
return { csv, rowCount, columnCount }
}