Skip to content

Commit dd765f7

Browse files
committed
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.
1 parent 28cdf97 commit dd765f7

2 files changed

Lines changed: 22 additions & 12 deletions

File tree

apps/sim/tools/microsoft_excel/read.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
MicrosoftExcelV2ToolParams,
88
} from '@/tools/microsoft_excel/types'
99
import {
10+
escapeODataString,
1011
getItemBasePath,
1112
getSpreadsheetWebUrl,
1213
parseGraphErrorMessage,
@@ -79,7 +80,7 @@ export const readTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelReadRe
7980
const rangeInput = params.range.trim()
8081

8182
if (!rangeInput.includes('!')) {
82-
const sheetOnly = encodeURIComponent(rangeInput)
83+
const sheetOnly = encodeURIComponent(escapeODataString(rangeInput))
8384
return `${basePath}/workbook/worksheets('${sheetOnly}')/usedRange(valuesOnly=true)`
8485
}
8586

@@ -91,7 +92,7 @@ export const readTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelReadRe
9192
)
9293
}
9394

94-
const sheetName = encodeURIComponent(match[1])
95+
const sheetName = encodeURIComponent(escapeODataString(match[1]))
9596
const address = encodeURIComponent(match[2])
9697

9798
return `${basePath}/workbook/worksheets('${sheetName}')/range(address='${address}')`
@@ -128,7 +129,7 @@ export const readTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelReadRe
128129
}
129130

130131
const basePath = getItemBasePath(spreadsheetId, driveId)
131-
const rangeUrl = `${basePath}/workbook/worksheets('${encodeURIComponent(firstSheetName)}')/usedRange(valuesOnly=true)`
132+
const rangeUrl = `${basePath}/workbook/worksheets('${encodeURIComponent(escapeODataString(firstSheetName))}')/usedRange(valuesOnly=true)`
132133

133134
const rangeResp = await fetch(rangeUrl, {
134135
headers: { Authorization: `Bearer ${accessToken}` },
@@ -278,7 +279,7 @@ export const readV2Tool: ToolConfig<MicrosoftExcelV2ToolParams, MicrosoftExcelV2
278279
}
279280

280281
const basePath = getItemBasePath(spreadsheetId, params.driveId)
281-
const encodedSheetName = encodeURIComponent(sheetName)
282+
const encodedSheetName = encodeURIComponent(escapeODataString(sheetName))
282283

283284
if (!params.cellRange) {
284285
return `${basePath}/workbook/worksheets('${encodedSheetName}')/usedRange(valuesOnly=true)`

apps/sim/tools/microsoft_excel/write.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import type {
55
MicrosoftExcelV2WriteResponse,
66
MicrosoftExcelWriteResponse,
77
} from '@/tools/microsoft_excel/types'
8-
import { getItemBasePath, getSpreadsheetWebUrl } from '@/tools/microsoft_excel/utils'
8+
import {
9+
escapeODataString,
10+
getItemBasePath,
11+
getSpreadsheetWebUrl,
12+
} from '@/tools/microsoft_excel/utils'
913
import type { ToolConfig } from '@/tools/types'
1014

1115
/**
@@ -82,17 +86,22 @@ export const writeTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelWrite
8286

8387
request: {
8488
url: (params) => {
89+
const spreadsheetId = params.spreadsheetId?.trim()
90+
if (!spreadsheetId) {
91+
throw new Error('Spreadsheet ID is required')
92+
}
93+
8594
const rangeInput = params.range?.trim()
8695
const match = rangeInput?.match(/^([^!]+)!(.+)$/)
8796

8897
if (!match) {
8998
throw new Error(`Invalid range format: "${params.range}". Use the format "Sheet1!A1:B2"`)
9099
}
91100

92-
const sheetName = encodeURIComponent(match[1])
101+
const sheetName = encodeURIComponent(escapeODataString(match[1]))
93102
const address = encodeURIComponent(match[2])
94103

95-
const basePath = getItemBasePath(params.spreadsheetId!, params.driveId)
104+
const basePath = getItemBasePath(spreadsheetId, params.driveId)
96105
const url = new URL(
97106
`${basePath}/workbook/worksheets('${sheetName}')/range(address='${address}')`
98107
)
@@ -173,10 +182,10 @@ export const writeTool: ToolConfig<MicrosoftExcelToolParams, MicrosoftExcelWrite
173182
return {
174183
success: true,
175184
output: {
176-
updatedRange: data.updatedRange,
177-
updatedRows: data.updatedRows,
178-
updatedColumns: data.updatedColumns,
179-
updatedCells: data.updatedCells,
185+
updatedRange: data.address ?? '',
186+
updatedRows: data.rowCount ?? 0,
187+
updatedColumns: data.columnCount ?? 0,
188+
updatedCells: (data.rowCount ?? 0) * (data.columnCount ?? 0),
180189
metadata: {
181190
spreadsheetId,
182191
spreadsheetUrl: webUrl,
@@ -280,7 +289,7 @@ export const writeV2Tool: ToolConfig<MicrosoftExcelV2ToolParams, MicrosoftExcelV
280289
}
281290

282291
const cellRange = params.cellRange?.trim() || 'A1'
283-
const encodedSheetName = encodeURIComponent(sheetName)
292+
const encodedSheetName = encodeURIComponent(escapeODataString(sheetName))
284293
const encodedAddress = encodeURIComponent(cellRange)
285294

286295
const basePath = getItemBasePath(spreadsheetId, params.driveId)

0 commit comments

Comments
 (0)