Skip to content

Commit 2a8aedf

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(eloqua): address integration review
1 parent 7b02ba5 commit 2a8aedf

13 files changed

Lines changed: 176 additions & 18 deletions

File tree

apps/sim/blocks/blocks/eloqua.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -194,17 +194,47 @@ export const EloquaBlock: BlockConfig<EloquaResponse> = {
194194
create_account: ['Create an Eloqua account'],
195195
update_account: [{ text: 'Update Eloqua account', field: 'accountId', core: true }],
196196
list_campaigns: ['List Eloqua campaigns', { text: ', matching', field: 'search' }],
197-
get_campaign: ['Read an Eloqua campaign'],
198-
activate_campaign: ['Activate an Eloqua campaign'],
199-
deactivate_campaign: ['Deactivate an Eloqua campaign'],
197+
get_campaign: [
198+
{
199+
text: 'Read Eloqua campaign',
200+
field: ['campaignSelector', 'campaignIdInput'],
201+
core: true,
202+
},
203+
],
204+
activate_campaign: [
205+
{
206+
text: 'Activate Eloqua campaign',
207+
field: ['campaignSelector', 'campaignIdInput'],
208+
core: true,
209+
},
210+
],
211+
deactivate_campaign: [
212+
{
213+
text: 'Deactivate Eloqua campaign',
214+
field: ['campaignSelector', 'campaignIdInput'],
215+
core: true,
216+
},
217+
],
200218
list_contact_lists: ['List Eloqua contact lists', { text: ', matching', field: 'search' }],
201-
get_contact_list: ['Read an Eloqua contact list'],
219+
get_contact_list: [
220+
{
221+
text: 'Read Eloqua contact list',
222+
field: ['contactListSelector', 'contactListIdInput'],
223+
core: true,
224+
},
225+
],
202226
list_segments: ['List Eloqua segments', { text: ', matching', field: 'search' }],
203-
get_segment: ['Read an Eloqua segment'],
227+
get_segment: [
228+
{ text: 'Read Eloqua segment', field: ['segmentSelector', 'segmentIdInput'], core: true },
229+
],
204230
list_emails: ['List Eloqua emails', { text: ', matching', field: 'search' }],
205-
get_email: ['Read an Eloqua email'],
231+
get_email: [
232+
{ text: 'Read Eloqua email', field: ['emailSelector', 'emailIdInput'], core: true },
233+
],
206234
list_forms: ['List Eloqua forms', { text: ', matching', field: 'search' }],
207-
get_form: ['Read an Eloqua form'],
235+
get_form: [
236+
{ text: 'Read Eloqua form', field: ['formSelector', 'formIdInput'], core: true },
237+
],
208238
list_contact_fields: ['List Eloqua Bulk contact fields'],
209239
create_contact_import: ['Create an Eloqua contact import definition'],
210240
upload_contact_import_data: [

apps/sim/tools/eloqua/create_contact_export.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { createEloquaBulkDefinitionTool } from '@/tools/eloqua/factories'
33
export const eloquaCreateContactExportTool = createEloquaBulkDefinitionTool({
44
id: 'eloqua_create_contact_export',
55
name: 'Create Oracle Eloqua Contact Export',
6-
description: 'Create a Bulk API contact export definition with dynamic field aliases.',
6+
description: 'Create a Bulk API contact export definition with up to 250 field aliases.',
77
path: '/api/bulk/2.0/contacts/exports',
88
definitionKind: 'contactExport',
99
})

apps/sim/tools/eloqua/eloqua.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { EloquaBlock } from '@/blocks/blocks/eloqua'
1010
import * as eloquaToolExports from '@/tools/eloqua'
1111
import {
1212
eloquaActivateCampaignTool,
13+
eloquaCreateContactExportTool,
1314
eloquaCreateContactImportTool,
1415
eloquaGetBulkSyncDataTool,
1516
eloquaGetBulkSyncTool,
@@ -525,6 +526,87 @@ describe('Oracle Eloqua tool contracts', () => {
525526
expect(eloquaStartBulkSyncTool.request.retry).toBeUndefined()
526527
})
527528

529+
it('enforces the documented Bulk definition field limits', () => {
530+
const fields = (count: number) =>
531+
Object.fromEntries(
532+
Array.from({ length: count }, (_, index) => [
533+
`Field${index}`,
534+
`{{Contact.Field(C_Field${index})}}`,
535+
])
536+
)
537+
538+
expect(() =>
539+
eloquaCreateContactImportTool.request.body?.({
540+
...AUTH,
541+
definition: { fields: fields(100) },
542+
})
543+
).not.toThrow()
544+
expect(() =>
545+
eloquaCreateContactImportTool.request.body?.({ ...AUTH, definition: { fields: {} } })
546+
).toThrow('1 to 100 field aliases')
547+
expect(() =>
548+
eloquaCreateContactImportTool.request.body?.({
549+
...AUTH,
550+
definition: { fields: fields(101) },
551+
})
552+
).toThrow('1 to 100 field aliases')
553+
554+
expect(() =>
555+
eloquaCreateContactExportTool.request.body?.({
556+
...AUTH,
557+
definition: { fields: fields(250) },
558+
})
559+
).not.toThrow()
560+
expect(() =>
561+
eloquaCreateContactExportTool.request.body?.({
562+
...AUTH,
563+
definition: { fields: fields(251) },
564+
})
565+
).toThrow('1 to 250 field aliases')
566+
expect(() =>
567+
eloquaCreateContactImportTool.request.body?.({
568+
...AUTH,
569+
definition: { fields: [] as unknown as Record<string, string> },
570+
})
571+
).toThrow('definition.fields must be a JSON object')
572+
expect(() =>
573+
eloquaCreateContactImportTool.request.body?.({
574+
...AUTH,
575+
definition: { fields: { Email: 42 as unknown as string } },
576+
})
577+
).toThrow('must map non-empty aliases to string statements')
578+
})
579+
580+
it.each([
581+
['eloqua_list_contacts', 'viewId'],
582+
['eloqua_get_contact', 'viewId'],
583+
['eloqua_list_accounts', 'viewId'],
584+
['eloqua_list_accounts', 'ownedByUserId'],
585+
['eloqua_get_account', 'viewId'],
586+
['eloqua_list_campaigns', 'externalSystemId'],
587+
['eloqua_get_campaign', 'externalSystemId'],
588+
['eloqua_activate_campaign', 'runAsUserId'],
589+
] as const)('%s validates %s as a positive safe integer', (id, field) => {
590+
for (const invalid of [0, 1.5, Number.MAX_SAFE_INTEGER + 1]) {
591+
expect(() => requestUrl(tools[id], { ...toolParams(id), [field]: invalid })).toThrow(
592+
'must be a positive integer'
593+
)
594+
}
595+
})
596+
597+
it('keeps selector-backed asset targets core in basic and advanced canvas modes', () => {
598+
const sentences = EloquaBlock.canvasPresentation?.sentences.byOperation
599+
expect(sentences).toMatchObject({
600+
get_campaign: [{ field: ['campaignSelector', 'campaignIdInput'], core: true }],
601+
activate_campaign: [{ field: ['campaignSelector', 'campaignIdInput'], core: true }],
602+
deactivate_campaign: [{ field: ['campaignSelector', 'campaignIdInput'], core: true }],
603+
get_contact_list: [{ field: ['contactListSelector', 'contactListIdInput'], core: true }],
604+
get_segment: [{ field: ['segmentSelector', 'segmentIdInput'], core: true }],
605+
get_email: [{ field: ['emailSelector', 'emailIdInput'], core: true }],
606+
get_form: [{ field: ['formSelector', 'formIdInput'], core: true }],
607+
})
608+
})
609+
528610
it('handles a 204 import upload and enforces the 10 MiB inline ceiling', async () => {
529611
await expect(
530612
transform(eloquaUploadContactImportDataTool, new Response(null, { status: 204 }), {

apps/sim/tools/eloqua/factories.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ import {
3838
eloquaCampaignSchedule,
3939
eloquaHeaders,
4040
eloquaJsonObject,
41+
eloquaPositiveInteger,
4142
eloquaResourceId,
4243
eloquaResourceUri,
4344
requireEloquaObject,
4445
validateApplicationPagination,
4546
validateBulkPagination,
47+
validateEloquaBulkDefinition,
4648
validateEloquaSync,
4749
validateInlineImportData,
4850
} from '@/tools/eloqua/utils'
@@ -433,7 +435,9 @@ export function createEloquaCampaignActionTool(
433435
...(isActivate && params.scheduledFor !== undefined
434436
? { scheduledFor: eloquaCampaignSchedule(params.scheduledFor) }
435437
: {}),
436-
runAsUserId: isActivate ? params.runAsUserId : undefined,
438+
runAsUserId: isActivate
439+
? eloquaPositiveInteger(params.runAsUserId, 'Eloqua run-as user ID')
440+
: undefined,
437441
activateNow: isActivate ? params.activateNow : undefined,
438442
}),
439443
method: 'POST',
@@ -500,7 +504,7 @@ export function createEloquaBulkDefinitionTool<TKind extends EloquaBulkDefinitio
500504
url: (params) => buildEloquaUrl(params, options.path),
501505
method: 'POST',
502506
headers: eloquaHeaders,
503-
body: (params) => requireEloquaObject(params.definition, 'definition'),
507+
body: (params) => validateEloquaBulkDefinition(params.definition, options.definitionKind),
504508
stripAuthOnRedirect: true,
505509
},
506510
transformResponse: async (response) => {

apps/sim/tools/eloqua/get_account.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createEloquaApplicationItemTool } from '@/tools/eloqua/factories'
2+
import { eloquaPositiveInteger } from '@/tools/eloqua/utils'
23

34
export const eloquaGetAccountTool = createEloquaApplicationItemTool({
45
id: 'eloqua_get_account',
@@ -14,5 +15,7 @@ export const eloquaGetAccountTool = createEloquaApplicationItemTool({
1415
description: 'Account view ID used to project the response',
1516
},
1617
},
17-
query: (params) => ({ viewId: params.viewId }),
18+
query: (params) => ({
19+
viewId: eloquaPositiveInteger(params.viewId, 'Eloqua account view ID'),
20+
}),
1821
})

apps/sim/tools/eloqua/get_campaign.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createEloquaApplicationItemTool } from '@/tools/eloqua/factories'
2+
import { eloquaPositiveInteger } from '@/tools/eloqua/utils'
23

34
export const eloquaGetCampaignTool = createEloquaApplicationItemTool({
45
id: 'eloqua_get_campaign',
@@ -21,7 +22,7 @@ export const eloquaGetCampaignTool = createEloquaApplicationItemTool({
2122
},
2223
},
2324
query: (params) => ({
24-
externalSystemId: params.externalSystemId,
25+
externalSystemId: eloquaPositiveInteger(params.externalSystemId, 'Eloqua external system ID'),
2526
includeCrmIdsMapping: params.includeCrmIdsMapping,
2627
}),
2728
})

apps/sim/tools/eloqua/get_contact.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createEloquaApplicationItemTool } from '@/tools/eloqua/factories'
2+
import { eloquaPositiveInteger } from '@/tools/eloqua/utils'
23

34
export const eloquaGetContactTool = createEloquaApplicationItemTool({
45
id: 'eloqua_get_contact',
@@ -14,5 +15,7 @@ export const eloquaGetContactTool = createEloquaApplicationItemTool({
1415
description: 'Contact view ID used to project the response',
1516
},
1617
},
17-
query: (params) => ({ viewId: params.viewId }),
18+
query: (params) => ({
19+
viewId: eloquaPositiveInteger(params.viewId, 'Eloqua contact view ID'),
20+
}),
1821
})

apps/sim/tools/eloqua/list_accounts.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createEloquaApplicationListTool } from '@/tools/eloqua/factories'
2+
import { eloquaPositiveInteger } from '@/tools/eloqua/utils'
23

34
export const eloquaListAccountsTool = createEloquaApplicationListTool({
45
id: 'eloqua_list_accounts',
@@ -21,5 +22,8 @@ export const eloquaListAccountsTool = createEloquaApplicationListTool({
2122
description: 'Return accounts owned by this Eloqua user ID',
2223
},
2324
},
24-
query: (params) => ({ viewId: params.viewId, ownedByUserId: params.ownedByUserId }),
25+
query: (params) => ({
26+
viewId: eloquaPositiveInteger(params.viewId, 'Eloqua account view ID'),
27+
ownedByUserId: eloquaPositiveInteger(params.ownedByUserId, 'Eloqua account owner user ID'),
28+
}),
2529
})

apps/sim/tools/eloqua/list_campaigns.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createEloquaApplicationListTool } from '@/tools/eloqua/factories'
2+
import { eloquaPositiveInteger } from '@/tools/eloqua/utils'
23

34
export const eloquaListCampaignsTool = createEloquaApplicationListTool({
45
id: 'eloqua_list_campaigns',
@@ -21,7 +22,7 @@ export const eloquaListCampaignsTool = createEloquaApplicationListTool({
2122
},
2223
},
2324
query: (params) => ({
24-
externalSystemId: params.externalSystemId,
25+
externalSystemId: eloquaPositiveInteger(params.externalSystemId, 'Eloqua external system ID'),
2526
includeCrmIdsMapping: params.includeCrmIdsMapping,
2627
}),
2728
})

apps/sim/tools/eloqua/list_contacts.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createEloquaApplicationListTool } from '@/tools/eloqua/factories'
2+
import { eloquaPositiveInteger } from '@/tools/eloqua/utils'
23

34
export const eloquaListContactsTool = createEloquaApplicationListTool({
45
id: 'eloqua_list_contacts',
@@ -15,5 +16,7 @@ export const eloquaListContactsTool = createEloquaApplicationListTool({
1516
description: 'Contact view ID used to filter and project the results',
1617
},
1718
},
18-
query: (params) => ({ viewId: params.viewId }),
19+
query: (params) => ({
20+
viewId: eloquaPositiveInteger(params.viewId, 'Eloqua contact view ID'),
21+
}),
1922
})

0 commit comments

Comments
 (0)