mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-28 03:00:29 -04:00
feat(create-excel): onedrive create excel (#1745)
* added onedrive upload excel * added * updated docs * lint * cleaned * use lib --------- Co-authored-by: Adam Gough <adamgough@Adams-MacBook-Pro.local>
This commit is contained in:
@@ -51,7 +51,7 @@ In Sim, the OneDrive integration enables your agents to directly interact with y
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate OneDrive into the workflow. Can create, upload, and list files.
|
||||
Integrate OneDrive into the workflow. Can create text and Excel files, upload files, and list files.
|
||||
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ Upload a file to OneDrive
|
||||
| `fileName` | string | Yes | The name of the file to upload |
|
||||
| `file` | file | No | The file to upload \(binary\) |
|
||||
| `content` | string | No | The text content to upload \(if no file is provided\) |
|
||||
| `mimeType` | string | No | The MIME type of the file to create \(e.g., text/plain for .txt, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet for .xlsx\) |
|
||||
| `folderSelector` | string | No | Select the folder to upload the file to |
|
||||
| `manualFolderId` | string | No | Manually entered folder ID \(advanced mode\) |
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import * as XLSX from 'xlsx'
|
||||
import { z } from 'zod'
|
||||
import { checkHybridAuth } from '@/lib/auth/hybrid'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
@@ -14,8 +15,11 @@ const MICROSOFT_GRAPH_BASE = 'https://graph.microsoft.com/v1.0'
|
||||
const OneDriveUploadSchema = z.object({
|
||||
accessToken: z.string().min(1, 'Access token is required'),
|
||||
fileName: z.string().min(1, 'File name is required'),
|
||||
file: z.any(), // UserFile object
|
||||
file: z.any().optional(), // UserFile object (optional for blank Excel creation)
|
||||
folderId: z.string().optional().nullable(),
|
||||
mimeType: z.string().optional(),
|
||||
// Optional Excel write-after-create inputs
|
||||
values: z.array(z.array(z.union([z.string(), z.number(), z.boolean(), z.null()]))).optional(),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
@@ -42,17 +46,30 @@ export async function POST(request: NextRequest) {
|
||||
const body = await request.json()
|
||||
const validatedData = OneDriveUploadSchema.parse(body)
|
||||
|
||||
logger.info(`[${requestId}] Uploading file to OneDrive`, {
|
||||
fileName: validatedData.fileName,
|
||||
folderId: validatedData.folderId || 'root',
|
||||
})
|
||||
let fileBuffer: Buffer
|
||||
let mimeType: string
|
||||
|
||||
// Handle array or single file
|
||||
const rawFile = validatedData.file
|
||||
let fileToProcess
|
||||
// Check if we're creating a blank Excel file
|
||||
const isExcelCreation =
|
||||
validatedData.mimeType ===
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' && !validatedData.file
|
||||
|
||||
if (Array.isArray(rawFile)) {
|
||||
if (rawFile.length === 0) {
|
||||
if (isExcelCreation) {
|
||||
// Create a blank Excel workbook
|
||||
|
||||
const workbook = XLSX.utils.book_new()
|
||||
const worksheet = XLSX.utils.aoa_to_sheet([[]])
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
|
||||
|
||||
// Generate XLSX file as buffer
|
||||
const xlsxBuffer = XLSX.write(workbook, { type: 'buffer', bookType: 'xlsx' })
|
||||
fileBuffer = Buffer.from(xlsxBuffer)
|
||||
mimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
} else {
|
||||
// Handle regular file upload
|
||||
const rawFile = validatedData.file
|
||||
|
||||
if (!rawFile) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
@@ -61,40 +78,51 @@ export async function POST(request: NextRequest) {
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
fileToProcess = rawFile[0]
|
||||
} else {
|
||||
fileToProcess = rawFile
|
||||
}
|
||||
|
||||
// Convert to UserFile format
|
||||
let userFile
|
||||
try {
|
||||
userFile = processSingleFileToUserFile(fileToProcess, requestId, logger)
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to process file',
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
let fileToProcess
|
||||
if (Array.isArray(rawFile)) {
|
||||
if (rawFile.length === 0) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'No file provided',
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
fileToProcess = rawFile[0]
|
||||
} else {
|
||||
fileToProcess = rawFile
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Downloading file from storage: ${userFile.key}`)
|
||||
// Convert to UserFile format
|
||||
let userFile
|
||||
try {
|
||||
userFile = processSingleFileToUserFile(fileToProcess, requestId, logger)
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to process file',
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
let fileBuffer: Buffer
|
||||
try {
|
||||
fileBuffer = await downloadFileFromStorage(userFile, requestId, logger)
|
||||
} catch (error) {
|
||||
logger.error(`[${requestId}] Failed to download file from storage:`, error)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: `Failed to download file: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
fileBuffer = await downloadFileFromStorage(userFile, requestId, logger)
|
||||
} catch (error) {
|
||||
logger.error(`[${requestId}] Failed to download file from storage:`, error)
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: `Failed to download file: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
mimeType = userFile.type || 'application/octet-stream'
|
||||
}
|
||||
|
||||
const maxSize = 250 * 1024 * 1024 // 250MB
|
||||
@@ -110,7 +138,11 @@ export async function POST(request: NextRequest) {
|
||||
)
|
||||
}
|
||||
|
||||
const fileName = validatedData.fileName || userFile.name
|
||||
// Ensure file name has correct extension for Excel files
|
||||
let fileName = validatedData.fileName
|
||||
if (isExcelCreation && !fileName.endsWith('.xlsx')) {
|
||||
fileName = `${fileName.replace(/\.[^.]*$/, '')}.xlsx`
|
||||
}
|
||||
|
||||
let uploadUrl: string
|
||||
const folderId = validatedData.folderId?.trim()
|
||||
@@ -121,10 +153,6 @@ export async function POST(request: NextRequest) {
|
||||
uploadUrl = `${MICROSOFT_GRAPH_BASE}/me/drive/root:/${encodeURIComponent(fileName)}:/content`
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Uploading to OneDrive: ${uploadUrl}`)
|
||||
|
||||
const mimeType = userFile.type || 'application/octet-stream'
|
||||
|
||||
const uploadResponse = await fetch(uploadUrl, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
@@ -136,11 +164,6 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
if (!uploadResponse.ok) {
|
||||
const errorText = await uploadResponse.text()
|
||||
logger.error(`[${requestId}] OneDrive upload failed:`, {
|
||||
status: uploadResponse.status,
|
||||
statusText: uploadResponse.statusText,
|
||||
error: errorText,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
@@ -153,11 +176,174 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const fileData = await uploadResponse.json()
|
||||
|
||||
logger.info(`[${requestId}] File uploaded successfully to OneDrive`, {
|
||||
fileId: fileData.id,
|
||||
fileName: fileData.name,
|
||||
size: fileData.size,
|
||||
})
|
||||
// If this is an Excel creation and values were provided, write them using the Excel API
|
||||
let excelWriteResult: any | undefined
|
||||
const shouldWriteExcelContent =
|
||||
isExcelCreation && Array.isArray(validatedData.values) && validatedData.values.length > 0
|
||||
|
||||
if (shouldWriteExcelContent) {
|
||||
try {
|
||||
// Create a workbook session to ensure reliability and persistence of changes
|
||||
let workbookSessionId: string | undefined
|
||||
const sessionResp = await fetch(
|
||||
`${MICROSOFT_GRAPH_BASE}/me/drive/items/${encodeURIComponent(fileData.id)}/workbook/createSession`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${validatedData.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ persistChanges: true }),
|
||||
}
|
||||
)
|
||||
|
||||
if (sessionResp.ok) {
|
||||
const sessionData = await sessionResp.json()
|
||||
workbookSessionId = sessionData?.id
|
||||
}
|
||||
|
||||
// Determine the first worksheet name
|
||||
let sheetName = 'Sheet1'
|
||||
try {
|
||||
const listUrl = `${MICROSOFT_GRAPH_BASE}/me/drive/items/${encodeURIComponent(
|
||||
fileData.id
|
||||
)}/workbook/worksheets?$select=name&$orderby=position&$top=1`
|
||||
const listResp = await fetch(listUrl, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${validatedData.accessToken}`,
|
||||
...(workbookSessionId ? { 'workbook-session-id': workbookSessionId } : {}),
|
||||
},
|
||||
})
|
||||
if (listResp.ok) {
|
||||
const listData = await listResp.json()
|
||||
const firstSheetName = listData?.value?.[0]?.name
|
||||
if (firstSheetName) {
|
||||
sheetName = firstSheetName
|
||||
}
|
||||
} else {
|
||||
const listErr = await listResp.text()
|
||||
logger.warn(`[${requestId}] Failed to list worksheets, using default Sheet1`, {
|
||||
status: listResp.status,
|
||||
error: listErr,
|
||||
})
|
||||
}
|
||||
} catch (listError) {
|
||||
logger.warn(`[${requestId}] Error listing worksheets, using default Sheet1`, listError)
|
||||
}
|
||||
|
||||
let processedValues: any = validatedData.values || []
|
||||
|
||||
if (
|
||||
Array.isArray(processedValues) &&
|
||||
processedValues.length > 0 &&
|
||||
typeof processedValues[0] === 'object' &&
|
||||
!Array.isArray(processedValues[0])
|
||||
) {
|
||||
const ws = XLSX.utils.json_to_sheet(processedValues)
|
||||
processedValues = XLSX.utils.sheet_to_json(ws, { header: 1, defval: '' })
|
||||
}
|
||||
|
||||
const rowsCount = processedValues.length
|
||||
const colsCount = Math.max(...processedValues.map((row: any[]) => row.length), 0)
|
||||
processedValues = processedValues.map((row: any[]) => {
|
||||
const paddedRow = [...row]
|
||||
while (paddedRow.length < colsCount) paddedRow.push('')
|
||||
return paddedRow
|
||||
})
|
||||
|
||||
// Compute concise end range from A1 and matrix size (no network round-trip)
|
||||
const indexToColLetters = (index: number): string => {
|
||||
let n = index
|
||||
let s = ''
|
||||
while (n > 0) {
|
||||
const rem = (n - 1) % 26
|
||||
s = String.fromCharCode(65 + rem) + s
|
||||
n = Math.floor((n - 1) / 26)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
const endColLetters = colsCount > 0 ? indexToColLetters(colsCount) : 'A'
|
||||
const endRow = rowsCount > 0 ? rowsCount : 1
|
||||
const computedRangeAddress = `A1:${endColLetters}${endRow}`
|
||||
|
||||
const url = new URL(
|
||||
`${MICROSOFT_GRAPH_BASE}/me/drive/items/${encodeURIComponent(
|
||||
fileData.id
|
||||
)}/workbook/worksheets('${encodeURIComponent(
|
||||
sheetName
|
||||
)}')/range(address='${encodeURIComponent(computedRangeAddress)}')`
|
||||
)
|
||||
|
||||
const excelWriteResponse = await fetch(url.toString(), {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
Authorization: `Bearer ${validatedData.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
...(workbookSessionId ? { 'workbook-session-id': workbookSessionId } : {}),
|
||||
},
|
||||
body: JSON.stringify({ values: processedValues }),
|
||||
})
|
||||
|
||||
if (!excelWriteResponse || !excelWriteResponse.ok) {
|
||||
const errorText = excelWriteResponse ? await excelWriteResponse.text() : 'no response'
|
||||
logger.error(`[${requestId}] Excel content write failed`, {
|
||||
status: excelWriteResponse?.status,
|
||||
statusText: excelWriteResponse?.statusText,
|
||||
error: errorText,
|
||||
})
|
||||
// Do not fail the entire request; return upload success with write error details
|
||||
excelWriteResult = {
|
||||
success: false,
|
||||
error: `Excel write failed: ${excelWriteResponse?.statusText || 'unknown'}`,
|
||||
details: errorText,
|
||||
}
|
||||
} else {
|
||||
const writeData = await excelWriteResponse.json()
|
||||
// The Range PATCH returns a Range object; log address and values length
|
||||
const addr = writeData.address || writeData.addressLocal
|
||||
const v = writeData.values || []
|
||||
excelWriteResult = {
|
||||
success: true,
|
||||
updatedRange: addr,
|
||||
updatedRows: Array.isArray(v) ? v.length : undefined,
|
||||
updatedColumns: Array.isArray(v) && v[0] ? v[0].length : undefined,
|
||||
updatedCells: Array.isArray(v) && v[0] ? v.length * (v[0] as any[]).length : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to close the workbook session if one was created
|
||||
if (workbookSessionId) {
|
||||
try {
|
||||
const closeResp = await fetch(
|
||||
`${MICROSOFT_GRAPH_BASE}/me/drive/items/${encodeURIComponent(fileData.id)}/workbook/closeSession`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${validatedData.accessToken}`,
|
||||
'workbook-session-id': workbookSessionId,
|
||||
},
|
||||
}
|
||||
)
|
||||
if (!closeResp.ok) {
|
||||
const closeText = await closeResp.text()
|
||||
logger.warn(`[${requestId}] Failed to close Excel session`, {
|
||||
status: closeResp.status,
|
||||
error: closeText,
|
||||
})
|
||||
}
|
||||
} catch (closeErr) {
|
||||
logger.warn(`[${requestId}] Error closing Excel session`, closeErr)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`[${requestId}] Exception during Excel content write`, err)
|
||||
excelWriteResult = {
|
||||
success: false,
|
||||
error: err instanceof Error ? err.message : 'Unknown error during Excel write',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
@@ -173,6 +359,7 @@ export async function POST(request: NextRequest) {
|
||||
modifiedTime: fileData.lastModifiedDateTime,
|
||||
parentReference: fileData.parentReference,
|
||||
},
|
||||
...(excelWriteResult ? { excelWriteResult } : {}),
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
@@ -8,7 +8,8 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
name: 'OneDrive',
|
||||
description: 'Create, upload, and list files',
|
||||
authMode: AuthMode.OAuth,
|
||||
longDescription: 'Integrate OneDrive into the workflow. Can create, upload, and list files.',
|
||||
longDescription:
|
||||
'Integrate OneDrive into the workflow. Can create text and Excel files, upload files, and list files.',
|
||||
docsLink: 'https://docs.sim.ai/tools/onedrive',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
@@ -51,10 +52,45 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
title: 'File Name',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Name of the file (e.g., document.txt)',
|
||||
placeholder: 'Name of the file',
|
||||
condition: { field: 'operation', value: ['create_file', 'upload'] },
|
||||
required: true,
|
||||
},
|
||||
// File Type selector for create_file operation
|
||||
{
|
||||
id: 'mimeType',
|
||||
title: 'File Type',
|
||||
type: 'dropdown',
|
||||
layout: 'full',
|
||||
options: [
|
||||
{ label: 'Text File (.txt)', id: 'text/plain' },
|
||||
{
|
||||
label: 'Excel File (.xlsx)',
|
||||
id: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
},
|
||||
],
|
||||
placeholder: 'Select file type',
|
||||
condition: { field: 'operation', value: 'create_file' },
|
||||
required: true,
|
||||
},
|
||||
// Excel values input when creating an .xlsx file
|
||||
{
|
||||
id: 'values',
|
||||
title: 'Values',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder:
|
||||
'Enter values as JSON array of arrays (e.g., [["A1","B1"],["A2","B2"]]) or an array of objects',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'create_file',
|
||||
and: {
|
||||
field: 'mimeType',
|
||||
value: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
},
|
||||
},
|
||||
required: false,
|
||||
},
|
||||
// File upload (basic mode)
|
||||
{
|
||||
id: 'file',
|
||||
@@ -86,7 +122,14 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Text content for the file',
|
||||
condition: { field: 'operation', value: 'create_file' },
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'create_file',
|
||||
and: {
|
||||
field: 'mimeType',
|
||||
value: 'text/plain',
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
},
|
||||
|
||||
@@ -234,14 +277,22 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { credential, folderSelector, manualFolderId, mimeType, ...rest } = params
|
||||
const { credential, folderSelector, manualFolderId, mimeType, values, ...rest } = params
|
||||
|
||||
// Use folderSelector if provided, otherwise use manualFolderId
|
||||
const effectiveFolderId = (folderSelector || manualFolderId || '').trim()
|
||||
|
||||
let parsedValues
|
||||
try {
|
||||
parsedValues = values ? JSON.parse(values as string) : undefined
|
||||
} catch (error) {
|
||||
throw new Error('Invalid JSON format for values')
|
||||
}
|
||||
|
||||
return {
|
||||
credential,
|
||||
...rest,
|
||||
values: parsedValues,
|
||||
folderId: effectiveFolderId || undefined,
|
||||
pageSize: rest.pageSize ? Number.parseInt(rest.pageSize as string, 10) : undefined,
|
||||
mimeType: mimeType,
|
||||
@@ -257,6 +308,8 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
file: { type: 'json', description: 'File to upload (UserFile object)' },
|
||||
fileReference: { type: 'json', description: 'File reference from previous block' },
|
||||
content: { type: 'string', description: 'Text content to upload' },
|
||||
mimeType: { type: 'string', description: 'MIME type of file to create' },
|
||||
values: { type: 'string', description: 'Cell values for new Excel as JSON' },
|
||||
// Get Content operation inputs
|
||||
// fileId: { type: 'string', required: false },
|
||||
// List operation inputs
|
||||
|
||||
@@ -43,6 +43,15 @@ export interface OneDriveListResponse extends ToolResponse {
|
||||
export interface OneDriveUploadResponse extends ToolResponse {
|
||||
output: {
|
||||
file: OneDriveFile
|
||||
excelWriteResult?: {
|
||||
success: boolean
|
||||
updatedRange?: string
|
||||
updatedRows?: number
|
||||
updatedColumns?: number
|
||||
updatedCells?: number
|
||||
error?: string
|
||||
details?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +69,8 @@ export interface OneDriveToolParams {
|
||||
pageSize?: number
|
||||
pageToken?: string
|
||||
exportMimeType?: string
|
||||
// Optional Excel write parameters (used when creating an .xlsx without file content)
|
||||
values?: (string | number | boolean | null)[][]
|
||||
}
|
||||
|
||||
export type OneDriveResponse = OneDriveUploadResponse | OneDriveListResponse
|
||||
|
||||
@@ -48,6 +48,13 @@ export const uploadTool: ToolConfig<OneDriveToolParams, OneDriveUploadResponse>
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The text content to upload (if no file is provided)',
|
||||
},
|
||||
mimeType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'The MIME type of the file to create (e.g., text/plain for .txt, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet for .xlsx)',
|
||||
},
|
||||
folderSelector: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -64,15 +71,17 @@ export const uploadTool: ToolConfig<OneDriveToolParams, OneDriveUploadResponse>
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
// If file is provided, use custom API route for binary upload
|
||||
if (params.file) {
|
||||
// If file is provided OR Excel file is being created, use custom API route
|
||||
const isExcelFile =
|
||||
params.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
if (params.file || isExcelFile) {
|
||||
return '/api/tools/onedrive/upload'
|
||||
}
|
||||
|
||||
// Text-only upload - use direct Microsoft Graph API
|
||||
// Direct upload for text files - use Microsoft Graph API
|
||||
let fileName = params.fileName || 'untitled'
|
||||
|
||||
// Always create .txt files for text content
|
||||
// For text files, ensure .txt extension
|
||||
if (!fileName.endsWith('.txt')) {
|
||||
// Remove any existing extensions and add .txt
|
||||
fileName = `${fileName.replace(/\.[^.]*$/, '')}.txt`
|
||||
@@ -87,32 +96,44 @@ export const uploadTool: ToolConfig<OneDriveToolParams, OneDriveUploadResponse>
|
||||
return `https://graph.microsoft.com/v1.0/me/drive/root:/${fileName}:/content`
|
||||
},
|
||||
method: (params) => {
|
||||
// Use POST for custom API route, PUT for direct upload
|
||||
return params.file ? 'POST' : 'PUT'
|
||||
// Use POST for custom API route (file uploads or Excel creation), PUT for direct text upload
|
||||
const isExcelFile =
|
||||
params.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
return params.file || isExcelFile ? 'POST' : 'PUT'
|
||||
},
|
||||
headers: (params) => {
|
||||
const headers: Record<string, string> = {}
|
||||
// For file uploads via custom API, send JSON
|
||||
if (params.file) {
|
||||
const isExcelFile =
|
||||
params.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
|
||||
// For file uploads or Excel creation via custom API, send JSON
|
||||
if (params.file || isExcelFile) {
|
||||
headers['Content-Type'] = 'application/json'
|
||||
} else {
|
||||
// For text-only uploads, use direct PUT with access token
|
||||
// For direct text uploads, use direct PUT with access token
|
||||
headers.Authorization = `Bearer ${params.accessToken}`
|
||||
headers['Content-Type'] = 'text/plain'
|
||||
}
|
||||
return headers
|
||||
},
|
||||
body: (params) => {
|
||||
// For file uploads, send all params as JSON to custom API route
|
||||
if (params.file) {
|
||||
const isExcelFile =
|
||||
params.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
|
||||
// For file uploads or Excel creation, send all params as JSON to custom API route
|
||||
if (params.file || isExcelFile) {
|
||||
return {
|
||||
accessToken: params.accessToken,
|
||||
fileName: params.fileName,
|
||||
file: params.file,
|
||||
folderId: params.manualFolderId || params.folderSelector,
|
||||
mimeType: params.mimeType,
|
||||
// Optional Excel content write-after-create
|
||||
values: params.values,
|
||||
}
|
||||
}
|
||||
// For text-only uploads, send content directly
|
||||
|
||||
// For text files, send content directly
|
||||
return (params.content || '') as unknown as Record<string, unknown>
|
||||
},
|
||||
},
|
||||
@@ -120,8 +141,11 @@ export const uploadTool: ToolConfig<OneDriveToolParams, OneDriveUploadResponse>
|
||||
transformResponse: async (response: Response, params?: OneDriveToolParams) => {
|
||||
const data = await response.json()
|
||||
|
||||
// Handle response from custom API route (for file uploads)
|
||||
if (params?.file && data.success !== undefined) {
|
||||
const isExcelFile =
|
||||
params?.mimeType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
|
||||
// Handle response from custom API route (for file uploads or Excel creation)
|
||||
if ((params?.file || isExcelFile) && data.success !== undefined) {
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || 'Failed to upload file')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user