Skip to content

Commit a010955

Browse files
committed
fix(oracle-fusion): preserve resource keys and tighten validation fixtures
1 parent ffc12e6 commit a010955

5 files changed

Lines changed: 54 additions & 38 deletions

File tree

‎apps/sim/lib/internal/oracle-fusion-financials/execute-tool.test.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,22 +112,23 @@ describe('Oracle Fusion Financials provider contract', () => {
112112
)
113113
})
114114

115-
it('keeps an explicit null patch distinct from an omitted field', async () => {
115+
it('preserves the opaque resource key and an explicit null patch', async () => {
116116
fetchProvider.mockResolvedValue(
117-
jsonResponse({ Purpose: null, links: self('expenseReports/report-key') })
117+
jsonResponse({ Purpose: null, links: self('expenseReports/%20report-key%20') })
118118
)
119119
const response = await execute('update_expense_report', {
120120
expenseReportUniqId: ' report-key ',
121121
purpose: null,
122122
})
123123
expect(response.status).toBe(200)
124+
expect(fetchProvider.mock.calls[0][0]).toBe(`${ROOT}expenseReports/%20report-key%20`)
124125
expect(fetchProvider.mock.calls[0][2]).toMatchObject({
125126
method: 'PATCH',
126127
body: '{"Purpose":null}',
127128
})
128129
expect((await response.json()).output.expenseReport).toEqual({
129130
Purpose: null,
130-
expenseReportUniqId: 'report-key',
131+
expenseReportUniqId: ' report-key ',
131132
})
132133
})
133134

‎apps/sim/lib/internal/oracle-fusion-financials/execute-tool.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { createLogger } from '@sim/logger'
2+
import { getErrorMessage } from '@sim/utils/errors'
13
import { ZodError } from 'zod'
24
import { OracleFusionProviderError } from '@/lib/internal/oracle-fusion/errors'
35
import {
@@ -6,10 +8,13 @@ import {
68
} from '@/lib/internal/oracle-fusion-financials/operations'
79
import type { InternalToolOperationHandler } from '@/lib/internal/tool-operations/types'
810

11+
const logger = createLogger('OracleFusionFinancialsToolExecution')
12+
913
export const executeOracleFusionFinancialsTool: InternalToolOperationHandler = async ({
1014
toolId,
1115
input,
1216
signal,
17+
requestId,
1318
}) => {
1419
signal?.throwIfAborted()
1520
if (!isOracleFusionFinancialsToolId(toolId)) {
@@ -30,6 +35,13 @@ export const executeOracleFusionFinancialsTool: InternalToolOperationHandler = a
3035
)
3136
}
3237
const validationFailure = error instanceof ZodError
38+
if (!validationFailure) {
39+
logger.error('Oracle Fusion Financials operation failed', {
40+
error: getErrorMessage(error, 'Unknown error'),
41+
requestId,
42+
toolId,
43+
})
44+
}
3345
return Response.json(
3446
{
3547
success: false,

‎apps/sim/lib/internal/oracle-fusion-financials/schema.ts‎

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -235,17 +235,14 @@ const listShape = {
235235
totalResults: z.boolean().default(false),
236236
}
237237

238-
const opaqueKeySchema = z
239-
.string()
240-
.trim()
241-
.refine((value) => {
242-
try {
243-
encodeOracleFusionPathSegment(value)
244-
return true
245-
} catch {
246-
return false
247-
}
248-
}, 'Oracle opaque key must be one URL path segment')
238+
const opaqueKeySchema = z.string().refine((value) => {
239+
try {
240+
encodeOracleFusionPathSegment(value)
241+
return true
242+
} catch {
243+
return false
244+
}
245+
}, 'Oracle opaque key must be one URL path segment')
249246

250247
const decimalIdSchema = z
251248
.string()

‎apps/sim/lib/oauth/token-resolution.test.ts‎

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ describe('resolveCredentialAccessToken', () => {
224224
authenticate.mockResolvedValue(INTERNAL_AUTH)
225225
resolveManagedPrincipal.mockResolvedValue(EXECUTOR_PRINCIPAL)
226226
mockGetToolMetadata.mockReturnValue({
227+
id: 'gmail_send',
228+
params: {},
227229
oauth: { required: true, provider: 'google', requiredScopes: ['scope-a'] },
228230
})
229231
})
@@ -288,7 +290,7 @@ describe('resolveCredentialAccessToken', () => {
288290

289291
it('rejects tools without managed OAuth support', async () => {
290292
mockResolveOAuthAccountId.mockResolvedValue(MANAGED_RESOLVED)
291-
mockGetToolMetadata.mockReturnValue({ oauth: undefined })
293+
mockGetToolMetadata.mockReturnValue({ id: 'http_request', params: {}, oauth: undefined })
292294

293295
const result = await resolveCredentialAccessToken({
294296
requestId: 'req-1',
@@ -309,7 +311,11 @@ describe('resolveCredentialAccessToken', () => {
309311

310312
it('rejects tools whose scope policy is empty', async () => {
311313
mockResolveOAuthAccountId.mockResolvedValue(MANAGED_RESOLVED)
312-
mockGetToolMetadata.mockReturnValue({ oauth: { required: true, provider: 'unknown-provider' } })
314+
mockGetToolMetadata.mockReturnValue({
315+
id: 'fixture-tool',
316+
params: {},
317+
oauth: { required: true, provider: 'oracle-fusion' },
318+
})
313319

314320
const result = await resolveCredentialAccessToken({
315321
requestId: 'req-1',
@@ -464,31 +470,34 @@ describe('ordinary tool credential binding', () => {
464470
['gmail', 'google-service-account'],
465471
['jira', 'atlassian-service-account'],
466472
['confluence', 'atlassian-service-account'],
467-
])('accepts the canonical %s service-account provider %s', async (provider, providerId) => {
468-
mockGetToolMetadata.mockReturnValue({
469-
id: 'fixture-tool',
470-
params: {},
471-
oauth: { required: true, provider },
472-
})
473-
mockResolveOAuthAccountId.mockResolvedValue({ ...serviceAccount, providerId })
474-
expect(
475-
await resolveCredentialAccessToken({
476-
requestId: 'binding-test',
477-
credentialId: 'credential-1',
478-
toolId: 'fixture-tool',
479-
authenticate,
473+
] as const)(
474+
'accepts the canonical %s service-account provider %s',
475+
async (provider, providerId) => {
476+
mockGetToolMetadata.mockReturnValue({
477+
id: 'fixture-tool',
478+
params: {},
479+
oauth: { required: true, provider },
480480
})
481-
).toMatchObject({
482-
ok: true,
483-
token: { accessToken: 'service-token', credentialType: 'service_account' },
484-
})
485-
})
481+
mockResolveOAuthAccountId.mockResolvedValue({ ...serviceAccount, providerId })
482+
expect(
483+
await resolveCredentialAccessToken({
484+
requestId: 'binding-test',
485+
credentialId: 'credential-1',
486+
toolId: 'fixture-tool',
487+
authenticate,
488+
})
489+
).toMatchObject({
490+
ok: true,
491+
token: { accessToken: 'service-token', credentialType: 'service_account' },
492+
})
493+
}
494+
)
486495

487496
it.each([
488497
['gmail', 'google-email', true],
489498
['salesforce', 'salesforce-sandbox', true],
490499
['salesforce', 'google', false],
491-
])(
500+
] as const)(
492501
'binds ordinary OAuth %s tokens to the provider (%s)',
493502
async (provider, providerId, allowed) => {
494503
mockGetToolMetadata.mockReturnValue({

‎scripts/check-test-types.baseline.json‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,9 +2012,6 @@
20122012
"TS2348": 1,
20132013
"TS2353": 2
20142014
},
2015-
"apps/sim/lib/oauth/token-resolution.test.ts": {
2016-
"TS2739": 3
2017-
},
20182015
"apps/sim/lib/organizations/instance-org.test.ts": {
20192016
"TS2345": 1
20202017
},

0 commit comments

Comments
 (0)