Skip to content

Commit 0f32c38

Browse files
committed
feat(excel): expand workbook operations and align Graph contracts
1 parent 9ef474c commit 0f32c38

39 files changed

Lines changed: 3102 additions & 561 deletions

‎apps/docs/content/docs/integrations/microsoft_excel.mdx‎

Lines changed: 482 additions & 20 deletions
Large diffs are not rendered by default.

‎apps/sim/blocks/blocks/microsoft_excel.ts‎

Lines changed: 864 additions & 109 deletions
Large diffs are not rendered by default.

‎apps/sim/lib/oauth/oauth.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,15 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
434434
providerId: 'microsoft-excel',
435435
icon: MicrosoftExcelIcon,
436436
baseProviderIcon: MicrosoftIcon,
437-
scopes: ['openid', 'profile', 'email', 'Files.Read', 'Files.ReadWrite', 'offline_access'],
437+
scopes: [
438+
'openid',
439+
'profile',
440+
'email',
441+
'Files.Read',
442+
'Files.ReadWrite',
443+
'Sites.Read.All',
444+
'offline_access',
445+
],
438446
},
439447
'microsoft-intune': {
440448
name: 'Microsoft Intune',

‎apps/sim/lib/workflows/migrations/subblock-migrations.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ function isFieldProjection(value: unknown): boolean {
112112
* secret onto a live subblock.
113113
*/
114114
export const SUBBLOCK_ID_MIGRATIONS: Record<string, readonly SubblockIdMigration[]> = {
115+
microsoft_excel: [{ from: 'valueInputOption', to: '_removed_valueInputOption' }],
116+
microsoft_excel_v2: [{ from: 'valueInputOption', to: '_removed_valueInputOption' }],
115117
/** MCP normalization selects the active replacement before this rename pass. */
116118
mcp: [
117119
{ from: 'server', to: 'serverSelector' },

‎apps/sim/tools/generated/tool-ids.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎apps/sim/tools/generated/tool-metadata.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎apps/sim/tools/generated/tool-outputs.ts‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { ErrorExtractorId } from '@/tools/error-extractors'
2+
import {
3+
assertExcelSuccess,
4+
EXCEL_WORKBOOK_PARAMS,
5+
excelHeaders,
6+
excelWorkbookUrl,
7+
} from '@/tools/microsoft_excel/shared'
8+
import type { ExcelCalculateParams, ExcelCalculateResponse } from '@/tools/microsoft_excel/types'
9+
import type { ToolConfig } from '@/tools/types'
10+
export const calculateTool: ToolConfig<ExcelCalculateParams, ExcelCalculateResponse> = {
11+
id: 'microsoft_excel_calculate',
12+
name: 'Microsoft Excel Calculate',
13+
description: 'Calculate in a Microsoft Excel workbook.',
14+
version: '1.0.0',
15+
oauth: { required: true, provider: 'microsoft-excel' },
16+
errorExtractor: ErrorExtractorId.MICROSOFT_GRAPH_ERRORS,
17+
params: {
18+
...EXCEL_WORKBOOK_PARAMS,
19+
calculationType: {
20+
type: 'string',
21+
required: true,
22+
visibility: 'user-or-llm',
23+
description:
24+
'Recalculate recalculates dirty cells; Full recalculates all cells; FullRebuild rebuilds dependencies and recalculates all cells.',
25+
},
26+
},
27+
request: {
28+
url: (params) => `${excelWorkbookUrl(params)}/application/calculate`,
29+
method: 'POST',
30+
headers: excelHeaders,
31+
body: (params) => {
32+
if (!['Recalculate', 'Full', 'FullRebuild'].includes(params.calculationType))
33+
throw new Error('Invalid calculation type')
34+
return { calculationType: params.calculationType }
35+
},
36+
},
37+
transformResponse: async (response) => {
38+
await assertExcelSuccess(response)
39+
return { success: true, output: { calculated: true } }
40+
},
41+
outputs: {
42+
calculated: { type: 'boolean', description: 'Whether workbook calculation completed.' },
43+
},
44+
}

‎apps/sim/tools/microsoft_excel/clear_range.ts‎

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
import { ErrorExtractorId } from '@/tools/error-extractors'
2+
import {
3+
assertExcelSuccess,
4+
EXCEL_WORKBOOK_PARAMS,
5+
excelHeaders,
6+
} from '@/tools/microsoft_excel/shared'
27
import type {
38
MicrosoftExcelClearRangeParams,
49
MicrosoftExcelClearRangeResponse,
@@ -8,7 +13,7 @@ import {
813
getItemBasePath,
914
getSpreadsheetWebUrl,
1015
} from '@/tools/microsoft_excel/utils'
11-
import type { ToolConfig } from '@/tools/types'
16+
import type { ToolConfig, ToolResponseContext } from '@/tools/types'
1217

1318
/**
1419
* Clears the contents and/or formatting of a worksheet range.
@@ -30,6 +35,7 @@ export const clearRangeTool: ToolConfig<
3035
},
3136

3237
params: {
38+
sessionId: EXCEL_WORKBOOK_PARAMS.sessionId,
3339
accessToken: {
3440
type: 'string',
3541
required: true,
@@ -47,7 +53,7 @@ export const clearRangeTool: ToolConfig<
4753
required: false,
4854
visibility: 'user-or-llm',
4955
description:
50-
'The ID of the drive containing the spreadsheet. Required for SharePoint files. If omitted, uses personal OneDrive.',
56+
'The ID of the drive containing the spreadsheet. Required for SharePoint files. If omitted, uses the signed-in user’s OneDrive for Business.',
5157
},
5258
sheetName: {
5359
type: 'string',
@@ -80,29 +86,26 @@ export const clearRangeTool: ToolConfig<
8086
return `${buildWorksheetRangeUrl(basePath, params.range, params.sheetName)}/clear`
8187
},
8288
method: 'POST',
83-
headers: (params) => {
84-
if (!params.accessToken) {
85-
throw new Error('Access token is required')
86-
}
87-
return {
88-
Authorization: `Bearer ${params.accessToken}`,
89-
'Content-Type': 'application/json',
90-
}
91-
},
89+
headers: excelHeaders,
9290
body: (params) => ({
9391
applyTo: params.applyTo || 'All',
9492
}),
9593
},
9694

97-
transformResponse: async (_response: Response, params?: MicrosoftExcelClearRangeParams) => {
95+
transformResponse: async (
96+
response: Response,
97+
params?: MicrosoftExcelClearRangeParams,
98+
context?: ToolResponseContext
99+
) => {
100+
await assertExcelSuccess(response)
98101
const spreadsheetId = params?.spreadsheetId?.trim() || ''
99102
const driveId = params?.driveId
100103

101104
const accessToken = params?.accessToken
102105
if (!accessToken) {
103106
throw new Error('Access token is required')
104107
}
105-
const webUrl = await getSpreadsheetWebUrl(spreadsheetId, accessToken, driveId)
108+
const webUrl = await getSpreadsheetWebUrl(spreadsheetId, accessToken, driveId, context?.signal)
106109

107110
return {
108111
success: true,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { ErrorExtractorId } from '@/tools/error-extractors'
2+
import {
3+
assertExcelSuccess,
4+
EXCEL_WORKBOOK_PARAMS,
5+
excelHeaders,
6+
excelWorkbookUrl,
7+
} from '@/tools/microsoft_excel/shared'
8+
import type { ExcelCloseSessionResponse, ExcelSessionParams } from '@/tools/microsoft_excel/types'
9+
import type { ToolConfig } from '@/tools/types'
10+
export const closeSessionTool: ToolConfig<ExcelSessionParams, ExcelCloseSessionResponse> = {
11+
id: 'microsoft_excel_close_session',
12+
name: 'Microsoft Excel Close Session',
13+
description: 'Close Session in a Microsoft Excel workbook.',
14+
version: '1.0.0',
15+
oauth: { required: true, provider: 'microsoft-excel' },
16+
errorExtractor: ErrorExtractorId.MICROSOFT_GRAPH_ERRORS,
17+
params: {
18+
...EXCEL_WORKBOOK_PARAMS,
19+
sessionId: { ...EXCEL_WORKBOOK_PARAMS.sessionId, required: true },
20+
},
21+
request: {
22+
url: (params) => {
23+
if (!params.sessionId?.trim()) throw new Error('Session ID is required')
24+
return `${excelWorkbookUrl(params)}/closeSession`
25+
},
26+
method: 'POST',
27+
headers: excelHeaders,
28+
},
29+
transformResponse: async (response) => {
30+
await assertExcelSuccess(response)
31+
return { success: true, output: { closed: true } }
32+
},
33+
outputs: { closed: { type: 'boolean', description: 'Whether the workbook session was closed.' } },
34+
}

0 commit comments

Comments
 (0)