Skip to content

Commit bead59f

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(netsuite): harden integration contracts
1 parent c7bbb56 commit bead59f

29 files changed

Lines changed: 471 additions & 371 deletions

apps/docs/content/docs/en/integrations/netsuite.mdx

Lines changed: 23 additions & 46 deletions
Large diffs are not rendered by default.

apps/sim/blocks/blocks/netsuite.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,45 @@ describe('Oracle NetSuite block', () => {
3131
}
3232
})
3333

34+
it('covers every operation on canvas and preserves required action targets', () => {
35+
const byOperation = NetSuiteBlock.canvasPresentation?.sentences?.byOperation
36+
if (!byOperation) throw new Error('NetSuite block must define operation canvas sentences')
37+
expect(Object.keys(byOperation)).toEqual([...NETSUITE_TOOL_IDS])
38+
39+
const coreFields = (operation: keyof typeof byOperation) => {
40+
const sentence = byOperation[operation]
41+
if (!Array.isArray(sentence)) return []
42+
return sentence.flatMap((part) =>
43+
typeof part === 'object' && part.core && part.field ? [part.field] : []
44+
)
45+
}
46+
47+
expect(coreFields('netsuite_get_subresource')).toEqual([
48+
'subresourcePath',
49+
'recordType',
50+
'recordId',
51+
])
52+
expect(coreFields('netsuite_get_select_options')).toEqual(['fields', 'recordType'])
53+
expect(coreFields('netsuite_attach_record')).toEqual([
54+
'relatedType',
55+
'relatedId',
56+
'recordType',
57+
'recordId',
58+
])
59+
expect(coreFields('netsuite_detach_record')).toEqual([
60+
'relatedType',
61+
'relatedId',
62+
'recordType',
63+
'recordId',
64+
])
65+
expect(coreFields('netsuite_execute_action')).toEqual(['action', 'recordType', 'recordId'])
66+
expect(coreFields('netsuite_transform_record')).toEqual([
67+
'recordType',
68+
'recordId',
69+
'targetRecordType',
70+
])
71+
})
72+
3473
it('requires the four inline credential fields and masks the private key', () => {
3574
for (const field of ['accountId', 'clientId', 'certificateId', 'privateKey']) {
3675
expect(getSubBlock(field).required, field).toBe(true)
@@ -221,6 +260,27 @@ describe('Oracle NetSuite block', () => {
221260
]
222261
expect(Object.keys(NetSuiteBlock.inputs)).toEqual(expectedInputs)
223262
expect(Object.keys(NetSuiteBlock.outputs)).toEqual(['status', 'data', 'location', 'jobId'])
263+
expect(NetSuiteBlock.outputs.location.condition).toEqual({
264+
field: 'operation',
265+
value: [
266+
'netsuite_create_record',
267+
'netsuite_batch_get_records',
268+
'netsuite_batch_create_records',
269+
'netsuite_batch_update_records',
270+
'netsuite_batch_upsert_records',
271+
'netsuite_batch_delete_records',
272+
],
273+
})
274+
expect(NetSuiteBlock.outputs.jobId.condition).toEqual({
275+
field: 'operation',
276+
value: [
277+
'netsuite_batch_get_records',
278+
'netsuite_batch_create_records',
279+
'netsuite_batch_update_records',
280+
'netsuite_batch_upsert_records',
281+
'netsuite_batch_delete_records',
282+
],
283+
})
224284
for (const input of expectedInputs) {
225285
if (input !== 'operation' && input !== 'taskId')
226286
expect(getSubBlock(input), input).toBeDefined()

apps/sim/blocks/blocks/netsuite.ts

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ const BATCH_OPERATIONS = [
111111
'netsuite_batch_delete_records',
112112
]
113113

114+
const LOCATION_OPERATIONS = ['netsuite_create_record', ...BATCH_OPERATIONS]
115+
114116
const BATCH_WRITE_OPERATIONS = [
115117
'netsuite_batch_create_records',
116118
'netsuite_batch_update_records',
@@ -193,8 +195,8 @@ export const NetSuiteBlock: BlockConfig<NetSuiteResponse> = {
193195
],
194196
netsuite_get_subresource: [
195197
{ text: 'Read subresource', field: 'subresourcePath', core: true },
196-
{ text: 'from', field: 'recordType' },
197-
{ text: 'record', field: 'recordId' },
198+
{ text: 'from', field: 'recordType', core: true },
199+
{ text: 'record', field: 'recordId', core: true },
198200
],
199201
netsuite_get_record_form: [
200202
{ text: 'Get the form for', field: 'recordType', core: true },
@@ -206,7 +208,7 @@ export const NetSuiteBlock: BlockConfig<NetSuiteResponse> = {
206208
],
207209
netsuite_get_select_options: [
208210
{ text: 'Get select options for', field: 'fields', core: true },
209-
{ text: 'on', field: 'recordType' },
211+
{ text: 'on', field: 'recordType', core: true },
210212
{ text: 'record', field: 'recordId' },
211213
{ text: ', matching', field: 'q' },
212214
{ text: ', using', field: 'body' },
@@ -216,27 +218,27 @@ export const NetSuiteBlock: BlockConfig<NetSuiteResponse> = {
216218
netsuite_attach_record: [
217219
{ text: 'Attach', field: 'relatedType', core: true },
218220
{ text: 'ID', field: 'relatedId', core: true },
219-
{ text: 'to', field: 'recordType' },
220-
{ text: 'record', field: 'recordId' },
221+
{ text: 'to', field: 'recordType', core: true },
222+
{ text: 'record', field: 'recordId', core: true },
221223
{ text: ', with role ID', field: 'roleId' },
222224
{ text: ', or role external ID', field: 'roleExternalId' },
223225
],
224226
netsuite_detach_record: [
225227
{ text: 'Detach', field: 'relatedType', core: true },
226228
{ text: 'ID', field: 'relatedId', core: true },
227-
{ text: 'from', field: 'recordType' },
228-
{ text: 'record', field: 'recordId' },
229+
{ text: 'from', field: 'recordType', core: true },
230+
{ text: 'record', field: 'recordId', core: true },
229231
],
230232
netsuite_execute_action: [
231233
{ text: 'Run action', field: 'action', core: true },
232-
{ text: 'on', field: 'recordType' },
233-
{ text: 'record', field: 'recordId' },
234+
{ text: 'on', field: 'recordType', core: true },
235+
{ text: 'record', field: 'recordId', core: true },
234236
{ text: ', with', field: 'body' },
235237
],
236238
netsuite_transform_record: [
237239
{ text: 'Transform', field: 'recordType', core: true },
238240
{ text: 'record', field: 'recordId', core: true },
239-
{ text: 'into', field: 'targetRecordType' },
241+
{ text: 'into', field: 'targetRecordType', core: true },
240242
{ text: ', with', field: 'body' },
241243
],
242244
netsuite_batch_get_records: [
@@ -296,8 +298,8 @@ export const NetSuiteBlock: BlockConfig<NetSuiteResponse> = {
296298
{ text: 'Get result from async job', field: 'jobId', core: true },
297299
{ text: 'for task', field: 'resultTaskId', core: true },
298300
],
299-
netsuite_get_server_time: ['Get NetSuite server time'],
300-
netsuite_get_governance_limits: ['Get NetSuite governance limits'],
301+
netsuite_get_server_time: ['Get server time'],
302+
netsuite_get_governance_limits: ['Get governance limits'],
301303
},
302304
},
303305
},
@@ -828,8 +830,16 @@ Return ONLY the comma-separated sublist IDs - no explanations, no extra text.`,
828830
description:
829831
'NetSuite payload: account-specific record fields, collection items and paging fields, metadata, or async task data',
830832
},
831-
location: { type: 'string', description: 'Created resource or async job location' },
832-
jobId: { type: 'string', description: 'Async job ID parsed from the Location header' },
833+
location: {
834+
type: 'string',
835+
description: 'Created resource or async job location',
836+
condition: { field: 'operation', value: LOCATION_OPERATIONS },
837+
},
838+
jobId: {
839+
type: 'string',
840+
description: 'Async job ID parsed from the Location header',
841+
condition: { field: 'operation', value: BATCH_OPERATIONS },
842+
},
833843
},
834844
}
835845

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

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

apps/sim/tools/netsuite/attach_record.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,5 @@ export const netsuiteAttachRecordTool: ToolConfig<NetSuiteAttachParams, NetSuite
114114
description: 'NetSuite response body; record fields are account-specific and dynamic',
115115
nullable: true,
116116
},
117-
location: {
118-
type: 'string',
119-
description: 'Created resource or asynchronous job location returned by NetSuite',
120-
optional: true,
121-
},
122-
jobId: {
123-
type: 'string',
124-
description: 'Asynchronous job ID parsed from the Location header',
125-
optional: true,
126-
},
127117
},
128118
}

apps/sim/tools/netsuite/create_record.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,5 @@ export const netsuiteCreateRecordTool: ToolConfig<NetSuiteCreateRecordParams, Ne
6969
description: 'Created resource or asynchronous job location returned by NetSuite',
7070
optional: true,
7171
},
72-
jobId: {
73-
type: 'string',
74-
description: 'Asynchronous job ID parsed from the Location header',
75-
optional: true,
76-
},
7772
},
7873
}

apps/sim/tools/netsuite/delete_record.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,5 @@ export const netsuiteDeleteRecordTool: ToolConfig<NetSuiteDeleteRecordParams, Ne
6666
description: 'NetSuite response body; record fields are account-specific and dynamic',
6767
nullable: true,
6868
},
69-
location: {
70-
type: 'string',
71-
description: 'Created resource or asynchronous job location returned by NetSuite',
72-
optional: true,
73-
},
74-
jobId: {
75-
type: 'string',
76-
description: 'Asynchronous job ID parsed from the Location header',
77-
optional: true,
78-
},
7969
},
8070
}

apps/sim/tools/netsuite/detach_record.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,5 @@ export const netsuiteDetachRecordTool: ToolConfig<NetSuiteRelationshipParams, Ne
8585
description: 'NetSuite response body; record fields are account-specific and dynamic',
8686
nullable: true,
8787
},
88-
location: {
89-
type: 'string',
90-
description: 'Created resource or asynchronous job location returned by NetSuite',
91-
optional: true,
92-
},
93-
jobId: {
94-
type: 'string',
95-
description: 'Asynchronous job ID parsed from the Location header',
96-
optional: true,
97-
},
9888
},
9989
}

apps/sim/tools/netsuite/execute_action.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -94,21 +94,11 @@ export const netsuiteExecuteActionTool: ToolConfig<NetSuiteExecuteActionParams,
9494
},
9595
},
9696
result: {
97-
type: 'json',
98-
description: 'Action-specific result returned by NetSuite',
97+
type: 'boolean',
98+
description: 'Whether NetSuite completed the record action',
9999
optional: true,
100100
},
101101
},
102102
},
103-
location: {
104-
type: 'string',
105-
description: 'Created resource or asynchronous job location returned by NetSuite',
106-
optional: true,
107-
},
108-
jobId: {
109-
type: 'string',
110-
description: 'Asynchronous job ID parsed from the Location header',
111-
optional: true,
112-
},
113103
},
114104
}

apps/sim/tools/netsuite/execute_dataset.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,5 @@ export const netsuiteExecuteDatasetTool: ToolConfig<
111111
},
112112
},
113113
},
114-
location: {
115-
type: 'string',
116-
description: 'Created resource or asynchronous job location returned by NetSuite',
117-
optional: true,
118-
},
119-
jobId: {
120-
type: 'string',
121-
description: 'Asynchronous job ID parsed from the Location header',
122-
optional: true,
123-
},
124114
},
125115
}

0 commit comments

Comments
 (0)