mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(microsoft-excel): clean up dead code found during integration audit (#5454)
* fix(microsoft-excel): clean up dead code found during integration audit
- Remove unreachable 'update' operation subblocks (Google Sheets
copy-paste leftover — never a selectable dropdown option and not
handled by the tool selector or any registered tool)
- Fix microsoft_excel_table_add to trim spreadsheetId and OData-escape
tableName, matching every other tool in this file
- Drop phantom/dead type fields: Google-Sheets-only insertDataOption
and responseValueRenderOption (never used), never-populated
sheetId/sheetName/title/sheets metadata fields, unused rowIndex, and
the unconfigurable majorDimension param (hardcoded to 'ROWS' now
that it's inlined)
* fix(microsoft-excel): fix write output field mapping + OData escaping gaps
Found in an independent second-pass audit against the live Graph API
docs:
- microsoft_excel_write (v1) transformResponse read
updatedRange/updatedRows/updatedColumns/updatedCells from the Graph
response — none of these fields exist on the workbookRange resource
(the real fields are address/rowCount/columnCount), so these four
output fields were always undefined. Fixed to read the actual
fields, matching what the v2 write tool already does correctly.
- read.ts and write.ts (v1 and v2) built worksheets('name') OData
URLs with only encodeURIComponent, skipping escapeODataString — a
worksheet name containing an apostrophe would produce a malformed
request. Every other tool in this integration already escapes
correctly; read/write were the outliers.
- write.ts (v1) also wasn't trimming spreadsheetId before use,
inconsistent with every other tool here.
This commit is contained in:
@@ -234,41 +234,6 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
|
||||
],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
},
|
||||
{
|
||||
id: 'values',
|
||||
title: 'Values',
|
||||
type: 'long-input',
|
||||
placeholder:
|
||||
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
required: true,
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate Microsoft Excel data as a JSON array based on the user's description.
|
||||
|
||||
Format options:
|
||||
1. Array of arrays: [["Header1", "Header2"], ["Value1", "Value2"]]
|
||||
2. Array of objects: [{"column1": "value1", "column2": "value2"}]
|
||||
|
||||
Examples:
|
||||
- "update with new prices" -> [["Product", "Price"], ["Widget A", 29.99], ["Widget B", 49.99]]
|
||||
- "quarterly targets" -> [{"Q1": 10000, "Q2": 12000, "Q3": 15000, "Q4": 18000}]
|
||||
|
||||
Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
|
||||
placeholder: 'Describe the data you want to update...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'valueInputOption',
|
||||
title: 'Value Input Option',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'User Entered (Parse formulas)', id: 'USER_ENTERED' },
|
||||
{ label: "Raw (Don't parse formulas)", id: 'RAW' },
|
||||
],
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
},
|
||||
{
|
||||
id: 'values',
|
||||
title: 'Values',
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
MicrosoftExcelV2ToolParams,
|
||||
} from '@/tools/microsoft_excel/types'
|
||||
import {
|
||||
escapeODataString,
|
||||
getItemBasePath,
|
||||
getSpreadsheetWebUrl,
|
||||
parseGraphErrorMessage,
|
||||
@@ -79,7 +80,7 @@ export const readTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelReadRe
|
||||
const rangeInput = params.range.trim()
|
||||
|
||||
if (!rangeInput.includes('!')) {
|
||||
const sheetOnly = encodeURIComponent(rangeInput)
|
||||
const sheetOnly = encodeURIComponent(escapeODataString(rangeInput))
|
||||
return `${basePath}/workbook/worksheets('${sheetOnly}')/usedRange(valuesOnly=true)`
|
||||
}
|
||||
|
||||
@@ -91,7 +92,7 @@ export const readTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelReadRe
|
||||
)
|
||||
}
|
||||
|
||||
const sheetName = encodeURIComponent(match[1])
|
||||
const sheetName = encodeURIComponent(escapeODataString(match[1]))
|
||||
const address = encodeURIComponent(match[2])
|
||||
|
||||
return `${basePath}/workbook/worksheets('${sheetName}')/range(address='${address}')`
|
||||
@@ -128,7 +129,7 @@ export const readTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelReadRe
|
||||
}
|
||||
|
||||
const basePath = getItemBasePath(spreadsheetId, driveId)
|
||||
const rangeUrl = `${basePath}/workbook/worksheets('${encodeURIComponent(firstSheetName)}')/usedRange(valuesOnly=true)`
|
||||
const rangeUrl = `${basePath}/workbook/worksheets('${encodeURIComponent(escapeODataString(firstSheetName))}')/usedRange(valuesOnly=true)`
|
||||
|
||||
const rangeResp = await fetch(rangeUrl, {
|
||||
headers: { Authorization: `Bearer ${accessToken}` },
|
||||
@@ -278,7 +279,7 @@ export const readV2Tool: ToolConfig<MicrosoftExcelV2ToolParams, MicrosoftExcelV2
|
||||
}
|
||||
|
||||
const basePath = getItemBasePath(spreadsheetId, params.driveId)
|
||||
const encodedSheetName = encodeURIComponent(sheetName)
|
||||
const encodedSheetName = encodeURIComponent(escapeODataString(sheetName))
|
||||
|
||||
if (!params.cellRange) {
|
||||
return `${basePath}/workbook/worksheets('${encodedSheetName}')/usedRange(valuesOnly=true)`
|
||||
|
||||
@@ -3,7 +3,11 @@ import type {
|
||||
MicrosoftExcelTableAddResponse,
|
||||
MicrosoftExcelTableToolParams,
|
||||
} from '@/tools/microsoft_excel/types'
|
||||
import { getItemBasePath, getSpreadsheetWebUrl } from '@/tools/microsoft_excel/utils'
|
||||
import {
|
||||
escapeODataString,
|
||||
getItemBasePath,
|
||||
getSpreadsheetWebUrl,
|
||||
} from '@/tools/microsoft_excel/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const tableAddTool: ToolConfig<
|
||||
@@ -59,15 +63,27 @@ export const tableAddTool: ToolConfig<
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const tableName = encodeURIComponent(params.tableName)
|
||||
const basePath = getItemBasePath(params.spreadsheetId, params.driveId)
|
||||
return `${basePath}/workbook/tables('${tableName}')/rows/add`
|
||||
const spreadsheetId = params.spreadsheetId?.trim()
|
||||
if (!spreadsheetId) {
|
||||
throw new Error('Spreadsheet ID is required')
|
||||
}
|
||||
const tableName = params.tableName?.trim()
|
||||
if (!tableName) {
|
||||
throw new Error('Table name is required')
|
||||
}
|
||||
const basePath = getItemBasePath(spreadsheetId, params.driveId)
|
||||
return `${basePath}/workbook/tables('${encodeURIComponent(escapeODataString(tableName))}')/rows/add`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
headers: (params) => {
|
||||
if (!params.accessToken) {
|
||||
throw new Error('Access token is required')
|
||||
}
|
||||
return {
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
},
|
||||
body: (params) => {
|
||||
let processedValues: any = params.values || []
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@ import type { ToolResponse } from '@/tools/types'
|
||||
export type ExcelCellValue = string | number | boolean | null
|
||||
|
||||
interface MicrosoftExcelRange {
|
||||
sheetId?: number
|
||||
sheetName?: string
|
||||
range: string
|
||||
values: ExcelCellValue[][]
|
||||
}
|
||||
@@ -13,14 +11,6 @@ interface MicrosoftExcelRange {
|
||||
interface MicrosoftExcelMetadata {
|
||||
spreadsheetId: string
|
||||
spreadsheetUrl?: string
|
||||
title?: string
|
||||
sheets?: {
|
||||
sheetId: number
|
||||
title: string
|
||||
index: number
|
||||
rowCount?: number
|
||||
columnCount?: number
|
||||
}[]
|
||||
}
|
||||
|
||||
export interface MicrosoftExcelReadResponse extends ToolResponse {
|
||||
@@ -67,10 +57,7 @@ export interface MicrosoftExcelToolParams {
|
||||
range?: string
|
||||
values?: ExcelCellValue[][]
|
||||
valueInputOption?: 'RAW' | 'USER_ENTERED'
|
||||
insertDataOption?: 'OVERWRITE' | 'INSERT_ROWS'
|
||||
includeValuesInResponse?: boolean
|
||||
responseValueRenderOption?: 'FORMATTED_VALUE' | 'UNFORMATTED_VALUE' | 'FORMULA'
|
||||
majorDimension?: 'ROWS' | 'COLUMNS'
|
||||
}
|
||||
|
||||
export interface MicrosoftExcelTableToolParams {
|
||||
@@ -79,7 +66,6 @@ export interface MicrosoftExcelTableToolParams {
|
||||
driveId?: string
|
||||
tableName: string
|
||||
values: ExcelCellValue[][]
|
||||
rowIndex?: number
|
||||
}
|
||||
|
||||
export interface MicrosoftExcelWorksheetToolParams {
|
||||
@@ -217,7 +203,6 @@ export interface MicrosoftExcelV2ToolParams {
|
||||
values?: ExcelCellValue[][]
|
||||
valueInputOption?: 'RAW' | 'USER_ENTERED'
|
||||
includeValuesInResponse?: boolean
|
||||
majorDimension?: 'ROWS' | 'COLUMNS'
|
||||
}
|
||||
|
||||
export interface MicrosoftExcelV2ReadResponse extends ToolResponse {
|
||||
|
||||
@@ -5,7 +5,11 @@ import type {
|
||||
MicrosoftExcelV2WriteResponse,
|
||||
MicrosoftExcelWriteResponse,
|
||||
} from '@/tools/microsoft_excel/types'
|
||||
import { getItemBasePath, getSpreadsheetWebUrl } from '@/tools/microsoft_excel/utils'
|
||||
import {
|
||||
escapeODataString,
|
||||
getItemBasePath,
|
||||
getSpreadsheetWebUrl,
|
||||
} from '@/tools/microsoft_excel/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
/**
|
||||
@@ -82,6 +86,11 @@ export const writeTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelWrite
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const spreadsheetId = params.spreadsheetId?.trim()
|
||||
if (!spreadsheetId) {
|
||||
throw new Error('Spreadsheet ID is required')
|
||||
}
|
||||
|
||||
const rangeInput = params.range?.trim()
|
||||
const match = rangeInput?.match(/^([^!]+)!(.+)$/)
|
||||
|
||||
@@ -89,10 +98,10 @@ export const writeTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelWrite
|
||||
throw new Error(`Invalid range format: "${params.range}". Use the format "Sheet1!A1:B2"`)
|
||||
}
|
||||
|
||||
const sheetName = encodeURIComponent(match[1])
|
||||
const sheetName = encodeURIComponent(escapeODataString(match[1]))
|
||||
const address = encodeURIComponent(match[2])
|
||||
|
||||
const basePath = getItemBasePath(params.spreadsheetId!, params.driveId)
|
||||
const basePath = getItemBasePath(spreadsheetId, params.driveId)
|
||||
const url = new URL(
|
||||
`${basePath}/workbook/worksheets('${sheetName}')/range(address='${address}')`
|
||||
)
|
||||
@@ -145,7 +154,7 @@ export const writeTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelWrite
|
||||
}
|
||||
|
||||
const body: Record<string, any> = {
|
||||
majorDimension: params.majorDimension || 'ROWS',
|
||||
majorDimension: 'ROWS',
|
||||
values: processedValues,
|
||||
}
|
||||
|
||||
@@ -173,10 +182,10 @@ export const writeTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelWrite
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
updatedRange: data.updatedRange,
|
||||
updatedRows: data.updatedRows,
|
||||
updatedColumns: data.updatedColumns,
|
||||
updatedCells: data.updatedCells,
|
||||
updatedRange: data.address ?? '',
|
||||
updatedRows: data.rowCount ?? 0,
|
||||
updatedColumns: data.columnCount ?? 0,
|
||||
updatedCells: (data.rowCount ?? 0) * (data.columnCount ?? 0),
|
||||
metadata: {
|
||||
spreadsheetId,
|
||||
spreadsheetUrl: webUrl,
|
||||
@@ -280,7 +289,7 @@ export const writeV2Tool: ToolConfig<MicrosoftExcelV2ToolParams, MicrosoftExcelV
|
||||
}
|
||||
|
||||
const cellRange = params.cellRange?.trim() || 'A1'
|
||||
const encodedSheetName = encodeURIComponent(sheetName)
|
||||
const encodedSheetName = encodeURIComponent(escapeODataString(sheetName))
|
||||
const encodedAddress = encodeURIComponent(cellRange)
|
||||
|
||||
const basePath = getItemBasePath(spreadsheetId, params.driveId)
|
||||
@@ -337,7 +346,7 @@ export const writeV2Tool: ToolConfig<MicrosoftExcelV2ToolParams, MicrosoftExcelV
|
||||
}
|
||||
|
||||
const body: Record<string, any> = {
|
||||
majorDimension: params.majorDimension || 'ROWS',
|
||||
majorDimension: 'ROWS',
|
||||
values: processedValues,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user