Skip to content

Commit cbebb5f

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(quickbooks): close remaining response gaps
1 parent c0baea8 commit cbebb5f

5 files changed

Lines changed: 56 additions & 4 deletions

File tree

apps/sim/app/api/tools/quickbooks/upload-attachment/route.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ describe('POST /api/tools/quickbooks/upload-attachment', () => {
8989
expect(mockFetch).not.toHaveBeenCalled()
9090
})
9191

92+
it('rejects whitespace-only access tokens at the API boundary', async () => {
93+
const response = await POST(
94+
createMockRequest('POST', {
95+
...baseBody,
96+
accessToken: ' ',
97+
})
98+
)
99+
100+
expect(response.status).toBe(400)
101+
expect(mockProcessFilesToUserFiles).not.toHaveBeenCalled()
102+
expect(mockFetch).not.toHaveBeenCalled()
103+
})
104+
92105
it('uploads an authorized file using QuickBooks multipart field names', async () => {
93106
const response = await POST(createMockRequest('POST', baseBody))
94107
expect(response.status).toBe(200)

apps/sim/lib/api/contracts/tools/quickbooks.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ export const quickBooksUploadAttachmentBodySchema = z.object({
88
.string()
99
.min(1, 'Access token is required')
1010
.max(4096, 'Access token must be 4096 characters or less')
11-
.regex(/^[^\r\n]+$/, 'Access token contains invalid characters'),
11+
.regex(/^[^\r\n]+$/, 'Access token contains invalid characters')
12+
.refine((value) => value.trim().length > 0, 'Access token is required'),
1213
realmId: z
1314
.string()
1415
.min(1, 'QuickBooks company ID is required')

apps/sim/tools/quickbooks/generic_operations.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ describe('QuickBooks generic operations', () => {
107107
})
108108

109109
it('builds current locale and SKU-dependent entity endpoints', () => {
110+
expect(
111+
buildQuickBooksRecordUrl({
112+
realmId: '123145',
113+
entity: 'Budget',
114+
operation: 'read',
115+
recordId: '40',
116+
}).url
117+
).toBe('https://quickbooks.api.intuit.com/v3/company/123145/budget/40?minorversion=75')
110118
expect(
111119
buildQuickBooksRecordUrl({
112120
realmId: '123145',
@@ -644,6 +652,19 @@ describe('QuickBooks generic operations', () => {
644652
})
645653
})
646654

655+
it('rejects successful report responses without report sections', async () => {
656+
await expect(
657+
quickBooksRunReportTool.transformResponse?.(
658+
new Response(JSON.stringify({ time: '2026-01-20T10:00:00-08:00' }), { status: 200 }),
659+
{
660+
accessToken: 'token',
661+
realmId: '123145',
662+
report: 'AgedPayables',
663+
}
664+
)
665+
).rejects.toThrow('QuickBooks report response did not include Header, Columns, and Rows')
666+
})
667+
647668
it('transforms QuickBooks CDC entity groups and deleted records', async () => {
648669
const response = new Response(
649670
JSON.stringify({

apps/sim/tools/quickbooks/run_report.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,21 @@ export const quickBooksRunReportTool: ToolConfig<QuickBooksReportParams, QuickBo
6464
if (!params) throw new Error('QuickBooks report parameters are required')
6565
const { report } = buildQuickBooksReportUrl(params)
6666
const data = await parseQuickBooksJson(response)
67+
if (
68+
!isQuickBooksReportSection(data.Header) ||
69+
!isQuickBooksReportSection(data.Columns) ||
70+
!isQuickBooksReportSection(data.Rows)
71+
) {
72+
throw new Error('QuickBooks report response did not include Header, Columns, and Rows')
73+
}
6774

6875
return {
6976
success: true,
7077
output: {
7178
report,
72-
header: data.Header ?? {},
73-
columns: data.Columns ?? {},
74-
rows: data.Rows ?? {},
79+
header: data.Header,
80+
columns: data.Columns,
81+
rows: data.Rows,
7582
time: typeof data.time === 'string' ? data.time : null,
7683
},
7784
}
@@ -98,3 +105,12 @@ export const quickBooksRunReportTool: ToolConfig<QuickBooksReportParams, QuickBo
98105
},
99106
},
100107
}
108+
109+
function isQuickBooksReportSection(value: unknown): value is Record<string, unknown> {
110+
return (
111+
value != null &&
112+
typeof value === 'object' &&
113+
!Array.isArray(value) &&
114+
Object.keys(value).length > 0
115+
)
116+
}

apps/sim/tools/quickbooks/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export const QUICKBOOKS_READABLE_ENTITIES = [
4343
'Attachable',
4444
'Bill',
4545
'BillPayment',
46+
'Budget',
4647
'Class',
4748
'CompanyCurrency',
4849
'CompanyInfo',

0 commit comments

Comments
 (0)