n8n-nodes-steyi-ss 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,34 +1,88 @@
1
1
  # n8n-nodes-steyi-ss
2
2
 
3
- An n8n community node for integrating with the Smartsheet API.
3
+ An n8n community node for integrating with the Smartsheet API. This comprehensive node provides full access to Smartsheet's API capabilities including sheets, rows, columns, webhooks, reports, and administrative functions.
4
4
 
5
5
  ## Features
6
6
 
7
- This node provides the following operations:
7
+ This node provides extensive operations across multiple Smartsheet resources:
8
8
 
9
9
  ### Sheet Operations
10
- - **List Sheets** - Get a list of all sheets in your Smartsheet account
11
- - **Get Sheet** - Retrieve detailed information about a specific sheet
12
- - **Create Sheet** - Create a new sheet
10
+ - **List Sheets** - Get a list of all sheets in your Smartsheet account (with pagination support)
11
+ - **Get Sheet** - Retrieve detailed information about a specific sheet with optional include parameters (attachments, discussions, format, etc.)
12
+ - **Create Sheet** - Create a new sheet with custom columns, optionally in a workspace or folder
13
13
  - **Update Sheet** - Update a sheet name
14
14
  - **Delete Sheet** - Delete a sheet
15
15
 
16
16
  ### Row Operations
17
- - **Add Row** - Add a new row to a sheet (with optional discussions, comments, and attachments)
18
- - **Update Row** - Update an existing row in a sheet (with optional discussions, comments, and attachments)
17
+ - **Add Row** - Add a new row to a sheet with:
18
+ - Cell data (supports all column types including multi-contact lists and multi-picklists)
19
+ - Location options (Top, Bottom, or as child of a parent row)
20
+ - Discussions (add to existing or create new)
21
+ - Attachments (file uploads or links, with proof support)
22
+ - **Update Row** - Update an existing row with the same options as Add Row
19
23
  - **Delete Row** - Delete a row from a sheet
24
+ - **Get Row** - Get details of a specific row with include/exclude parameters
25
+ - **Get Row (Mapped)** - Get a row with cells mapped by column title or column ID, including all original response data
26
+
27
+ **Special Row Features:**
28
+ - **Multi-Contact List Support** - Automatically formats comma-separated email lists for `MULTI_CONTACT_LIST` columns
29
+ - **Multi-Picklist Support** - Handles `MULTI_PICKLIST` columns with proper formatting
30
+ - **Attachment Types** - Support for both file uploads (binary data) and link attachments
31
+ - **Proof Attachments** - Special attachment type for row proofs
32
+ - **Discussion Management** - Add comments to existing discussions or create new discussions
33
+ - **Location Control** - Specify row placement (top, bottom, or as child of parent row)
20
34
 
21
35
  ### Column Operations
22
- - **Get Columns** - Get all columns from a sheet
36
+ - **Get Columns** - Get all columns from a sheet with column metadata
37
+ - **Update Column** - Update column properties:
38
+ - Title
39
+ - Index (position)
40
+ - Column type (TEXT_NUMBER, DATE, CHECKBOX, CONTACT_LIST, MULTI_CONTACT_LIST, PICKLIST, MULTI_PICKLIST, DURATION, PREDECESSOR, etc.)
41
+ - Options (for PICKLIST and MULTI_PICKLIST types)
23
42
 
24
43
  ### Webhook Operations
25
44
  - **List Webhooks** - List all webhooks in your account
26
- - **Create Webhook** - Create a new webhook for a sheet
45
+ - **Create Webhook** - Create a new webhook for a sheet:
46
+ - Auto-generates callback URL from `WEBHOOK_URL` environment variable
47
+ - Customizable webhook name
48
+ - Event selection (all events or specific events like `sheet.rowCreated`, `sheet.rowUpdated`, etc.)
27
49
  - **Update Webhook** - Update webhook settings (name, enabled status, events)
28
50
  - **Delete Webhook** - Delete a webhook
29
51
 
52
+ ### Report Operations
53
+ - **List Reports** - Get a list of all reports in your account
54
+ - **Get Report** - Retrieve detailed information about a specific report with include/exclude parameters
55
+
56
+ ### Admin Operations
57
+
58
+ #### User Management
59
+ - **List Users** - Get a list of all users in your account
60
+ - **Get User** - Get details of a specific user (by ID or from dropdown)
61
+ - **Add User** - Add a new user with:
62
+ - Email (required)
63
+ - First name and last name
64
+ - Admin privileges
65
+ - Licensed sheet creator status
66
+ - **Update User** - Update user properties
67
+ - **Delete User** - Delete a user
68
+
69
+ #### Group Management
70
+ - **List Groups** - Get a list of all groups in your account
71
+ - **Get Group** - Get details of a specific group (by ID or from dropdown)
72
+ - **Create Group** - Create a new group with name and description
73
+ - **Update Group** - Update group name and description
74
+ - **Delete Group** - Delete a group
75
+
76
+ #### Group Member Management
77
+ - **List Group Members** - Get all members of a group
78
+ - **Add Group Members** - Add members to a group by email address (supports any email, not just existing contacts)
79
+ - **Remove Group Member** - Remove a member from a group
80
+
30
81
  ### Webhook Trigger
31
- - **Smartsheet Trigger** - A trigger node that starts workflows when Smartsheet events occur
82
+ - **Steyi Smartsheet Trigger** - A trigger node that starts workflows when Smartsheet events occur:
83
+ - Automatically creates and manages webhooks
84
+ - Supports sheet-level events
85
+ - Handles webhook challenge/verification
32
86
 
33
87
  ## Installation
34
88
 
@@ -58,22 +112,12 @@ npm install n8n-nodes-steyi-ss
58
112
  # Then restart n8n
59
113
  ```
60
114
 
61
- For detailed deployment instructions, see [DEPLOYMENT.md](./DEPLOYMENT.md).
62
- 4. Link the node to your n8n installation:
63
- ```bash
64
- npm link
65
- ```
66
- 5. In your n8n installation directory, link the node:
67
- ```bash
68
- npm link n8n-nodes-steyi-ss
69
- ```
70
-
71
115
  ## Configuration
72
116
 
73
117
  ### Credentials
74
118
 
75
119
  1. Go to your n8n credentials settings
76
- 2. Add a new "Smartsheet API" credential
120
+ 2. Add a new "Steyi Smartsheet API" credential
77
121
  3. Enter your Smartsheet API access token
78
122
 
79
123
  To get your API token:
@@ -83,67 +127,271 @@ To get your API token:
83
127
 
84
128
  ## Usage
85
129
 
86
- ### List Sheets
130
+ ### Dynamic Dropdowns
87
131
 
88
- Use this operation to retrieve all sheets in your account. No additional parameters are required.
132
+ The node provides dynamic dropdowns for:
133
+ - **Sheets** - Automatically loads all available sheets
134
+ - **Columns** - Loads columns for the selected sheet
135
+ - **Users** - Lists all users in your account
136
+ - **Groups** - Lists all groups in your account
137
+ - **Group Members** - Lists members of the selected group
138
+ - **Workspaces** - Lists available workspaces for sheet creation
139
+ - **Folders** - Lists folders within a selected workspace
140
+ - **Webhooks** - Lists existing webhooks by name
141
+ - **Discussions** - Lists existing discussions for a row
89
142
 
90
- ### Get Sheet
143
+ All dropdowns support manual ID entry via expressions if needed.
91
144
 
92
- Retrieve detailed information about a specific sheet:
93
- - **Sheet ID**: The ID of the sheet you want to retrieve
94
- - **Include** (optional): Additional fields to include (attachments, discussions, format, etc.)
145
+ ### Sheet Operations
95
146
 
96
- ### Get Columns
147
+ #### List Sheets
148
+ Retrieves all sheets in your account. Supports pagination to fetch all sheets.
97
149
 
98
- Get all columns from a specific sheet:
99
- - **Sheet ID**: The ID of the sheet
150
+ #### Get Sheet
151
+ Retrieve detailed information about a specific sheet:
152
+ - **Sheet ID**: Select from dropdown or enter manually
153
+ - **Include** (optional): Additional fields to include:
154
+ - `attachments` - Sheet attachments
155
+ - `cellLinks` - Cell links
156
+ - `discussions` - Sheet discussions
157
+ - `format` - Formatting information
158
+ - `objectValue` - Object values
159
+ - `rowAttachments` - Row attachments
160
+ - `rowPermalink` - Row permalinks
161
+ - `source` - Source information
162
+
163
+ #### Create Sheet
164
+ Create a new sheet:
165
+ - **Sheet Name**: Name for the new sheet
166
+ - **Workspace** (optional): Select a workspace to create the sheet in
167
+ - **Folder** (optional): Select a folder within the workspace
168
+ - **Columns**: Define columns with:
169
+ - Title
170
+ - Type (TEXT_NUMBER, DATE, CHECKBOX, CONTACT_LIST, MULTI_CONTACT_LIST, PICKLIST, MULTI_PICKLIST, DURATION, PREDECESSOR, etc.)
171
+ - Primary column flag
172
+ - Options (for PICKLIST and MULTI_PICKLIST types)
173
+
174
+ #### Update Sheet
175
+ Update a sheet name:
176
+ - **Sheet ID**: Select from dropdown or enter manually
177
+ - **Name**: New name for the sheet
178
+
179
+ #### Delete Sheet
180
+ Delete a sheet:
181
+ - **Sheet ID**: Select from dropdown or enter manually
100
182
 
101
- ### Add Row
183
+ ### Row Operations
102
184
 
185
+ #### Add Row
103
186
  Add a new row to a sheet:
104
- - **Sheet ID**: The ID of the sheet
187
+ - **Sheet ID**: Select from dropdown or enter manually
105
188
  - **Cells**: Array of cells with Column ID and Value
106
- - **To Top** (optional): Whether to add the row to the top of the sheet
107
-
108
- ### Update Row
109
-
110
- Update an existing row:
111
- - **Sheet ID**: The ID of the sheet
189
+ - **Special Options** (optional): Enable additional features:
190
+ - **Location**: Control row placement
191
+ - `Top` - Add to top of sheet
192
+ - `Bottom` - Add to bottom of sheet
193
+ - `Use Parent Row` - Add as child of parent row (requires Parent Row ID)
194
+ - **Discussions**: Add discussions to the row
195
+ - Add to existing discussion or create new
196
+ - Comment text
197
+ - **Attachments**: Add attachments to the row
198
+ - File upload (binary data) or link
199
+ - Attachment category (Regular Attachment or Proof)
200
+ - Custom filename
201
+
202
+ **Multi-Contact List Formatting:**
203
+ When adding or updating rows with `MULTI_CONTACT_LIST` columns, you can provide:
204
+ - Comma-separated email string: `"email1@example.com, email2@example.com"`
205
+ - JSON array: `["email1@example.com", "email2@example.com"]`
206
+
207
+ The node automatically formats these for the Smartsheet API.
208
+
209
+ **Multi-Picklist Formatting:**
210
+ For `MULTI_PICKLIST` columns, provide:
211
+ - Comma-separated options: `"Option 1, Option 2"`
212
+ - JSON array: `["Option 1", "Option 2"]`
213
+
214
+ #### Update Row
215
+ Update an existing row with the same options as Add Row:
216
+ - **Sheet ID**: Select from dropdown or enter manually
112
217
  - **Row ID**: The ID of the row to update
113
- - **Cells**: Array of cells with Column ID and Value to update
114
-
115
- ### Delete Row
218
+ - **Cells**: Updated cell values
219
+ - **Special Options**: Same as Add Row
116
220
 
221
+ #### Delete Row
117
222
  Delete a row from a sheet:
118
- - **Sheet ID**: The ID of the sheet
223
+ - **Sheet ID**: Select from dropdown or enter manually
119
224
  - **Row ID**: The ID of the row to delete
120
225
 
226
+ #### Get Row
227
+ Get details of a specific row:
228
+ - **Sheet ID**: Select from dropdown or enter manually
229
+ - **Row ID**: The ID of the row
230
+ - **Include** (optional): Fields to include (same options as Get Sheet)
231
+ - **Exclude** (optional): Fields to exclude
232
+
233
+ #### Get Row (Mapped)
234
+ Get a row with cells mapped by column:
235
+ - **Sheet ID**: Select from dropdown or enter manually
236
+ - **Row ID**: The ID of the row
237
+ - **Mapping Type**:
238
+ - `By Column Title` - Map cells by column title (default)
239
+ - `By Column ID` - Map cells by column ID (still includes column title)
240
+ - **Include** (optional): Fields to include
241
+ - **Exclude** (optional): Fields to exclude
242
+
243
+ Returns a `mapped` object with cell values keyed by column title/ID, plus all original response data.
244
+
245
+ ### Column Operations
246
+
247
+ #### Get Columns
248
+ Get all columns from a sheet:
249
+ - **Sheet ID**: Select from dropdown or enter manually
250
+
251
+ Returns column metadata including ID, title, type, index, and options.
252
+
253
+ #### Update Column
254
+ Update column properties:
255
+ - **Sheet ID**: Select from dropdown or enter manually
256
+ - **Column ID**: Select from dropdown or enter manually
257
+ - **Title** (optional): New column title
258
+ - **Index** (optional): New column position
259
+ - **Column Type**: Column type (TEXT_NUMBER, DATE, CHECKBOX, CONTACT_LIST, MULTI_CONTACT_LIST, PICKLIST, MULTI_PICKLIST, DURATION, PREDECESSOR, etc.)
260
+ - **Options** (for PICKLIST/MULTI_PICKLIST): Array of option values
261
+
121
262
  ### Webhook Operations
122
263
 
123
264
  #### List Webhooks
124
265
  List all webhooks in your account. No additional parameters required.
125
266
 
126
267
  #### Create Webhook
127
- Create a new webhook for a sheet:
128
- - **Sheet ID**: The ID of the sheet to monitor
129
- - **Callback URL**: The publicly accessible URL that Smartsheet will call (see note below)
130
- - **Webhook Name** (optional): Name for the webhook
131
- - **Events**: Events to subscribe to (default: all events)
268
+ Create a new webhook:
269
+ - **Sheet ID**: Select from dropdown or enter manually
270
+ - **Callback URL** (optional): Auto-generates from `WEBHOOK_URL` environment variable if not provided
271
+ - **Webhook Name** (optional): Custom name for the webhook
272
+ - **Events**: Events to subscribe to:
273
+ - `*.*` - All events (default)
274
+ - `sheet.rowCreated` - Row created
275
+ - `sheet.rowUpdated` - Row updated
276
+ - `sheet.rowDeleted` - Row deleted
277
+ - `sheet.created` - Sheet created
278
+ - `sheet.updated` - Sheet updated
279
+ - `sheet.deleted` - Sheet deleted
132
280
 
133
281
  #### Update Webhook
134
282
  Update an existing webhook:
135
- - **Webhook ID**: The ID of the webhook to update
136
- - **Update Fields**: Fields to update (name, enabled status, events)
283
+ - **Webhook ID**: Select from dropdown or enter manually
284
+ - **Update Fields**:
285
+ - Name
286
+ - Enabled status
287
+ - Events
137
288
 
138
289
  #### Delete Webhook
139
290
  Delete a webhook:
140
- - **Webhook ID**: The ID of the webhook to delete
291
+ - **Webhook ID**: Select from dropdown or enter manually
292
+
293
+ ### Report Operations
294
+
295
+ #### List Reports
296
+ Get a list of all reports in your account. Supports pagination.
297
+
298
+ #### Get Report
299
+ Retrieve detailed information about a specific report:
300
+ - **Report ID**: Select from dropdown or enter manually
301
+ - **Include** (optional): Fields to include (same options as Get Sheet)
302
+ - **Exclude** (optional): Fields to exclude
303
+
304
+ ### Admin Operations
305
+
306
+ #### User Management
307
+
308
+ **List Users**
309
+ Get a list of all users in your account.
310
+
311
+ **Get User**
312
+ Get details of a specific user:
313
+ - **User Input Method**: Select from dropdown or enter ID manually
314
+ - **User ID**: User ID (if using manual entry)
315
+
316
+ **Add User**
317
+ Add a new user:
318
+ - **Email**: User email address (required)
319
+ - **First Name** (optional): User's first name
320
+ - **Last Name** (optional): User's last name
321
+ - **Admin**: Grant admin privileges
322
+ - **Licensed Sheet Creator**: Grant licensed sheet creator status
323
+
324
+ **Update User**
325
+ Update user properties:
326
+ - **User Input Method**: Select from dropdown or enter ID manually
327
+ - **User ID**: User ID (if using manual entry)
328
+ - **First Name** (optional): Updated first name
329
+ - **Last Name** (optional): Updated last name
330
+ - **Admin**: Update admin status
331
+ - **Licensed Sheet Creator**: Update licensed sheet creator status
332
+
333
+ **Delete User**
334
+ Delete a user:
335
+ - **User Input Method**: Select from dropdown or enter ID manually
336
+ - **User ID**: User ID (if using manual entry)
337
+
338
+ #### Group Management
339
+
340
+ **List Groups**
341
+ Get a list of all groups in your account.
342
+
343
+ **Get Group**
344
+ Get details of a specific group:
345
+ - **Group Input Method**: Select from dropdown or enter ID manually
346
+ - **Group ID**: Group ID (if using manual entry)
347
+
348
+ **Create Group**
349
+ Create a new group:
350
+ - **Group Name**: Name for the group
351
+ - **Description** (optional): Group description
352
+
353
+ **Update Group**
354
+ Update group properties:
355
+ - **Group Input Method**: Select from dropdown or enter ID manually
356
+ - **Group ID**: Group ID (if using manual entry)
357
+ - **Group Name** (optional): Updated group name
358
+ - **Description** (optional): Updated description
359
+
360
+ **Delete Group**
361
+ Delete a group:
362
+ - **Group Input Method**: Select from dropdown or enter ID manually
363
+ - **Group ID**: Group ID (if using manual entry)
364
+
365
+ #### Group Member Management
366
+
367
+ **List Group Members**
368
+ Get all members of a group:
369
+ - **Group Input Method**: Select from dropdown or enter ID manually
370
+ - **Group ID**: Group ID (if using manual entry)
371
+
372
+ **Add Group Members**
373
+ Add members to a group:
374
+ - **Group Input Method**: Select from dropdown or enter ID manually
375
+ - **Group ID**: Group ID (if using manual entry)
376
+ - **Members**: Array of email addresses (supports any email, not just existing contacts)
377
+
378
+ **Remove Group Member**
379
+ Remove a member from a group:
380
+ - **Group Input Method**: Select from dropdown or enter ID manually
381
+ - **Group ID**: Group ID (if using manual entry)
382
+ - **Member User ID**: User ID of the member to remove
141
383
 
142
384
  ### Webhook Trigger
143
385
 
144
386
  The Smartsheet Trigger node allows workflows to start automatically when Smartsheet events occur:
145
387
  - **Type**: Type of webhook (currently supports Sheet)
146
- - **Sheet ID**: The ID of the sheet to monitor
388
+ - **Sheet ID**: Select from dropdown or enter manually
389
+
390
+ The trigger automatically:
391
+ - Creates a webhook for the specified sheet
392
+ - Handles webhook verification challenges
393
+ - Manages webhook lifecycle (creation, updates, deletion)
394
+ - Subscribes to all sheet events (`*.*`)
147
395
 
148
396
  **Important Note about Webhooks and Localhost:**
149
397
 
@@ -161,6 +409,37 @@ Smartsheet webhooks require a publicly accessible URL. If you're running n8n on
161
409
 
162
410
  For the trigger node, n8n automatically generates the webhook URL, but it must be publicly accessible for Smartsheet to reach it.
163
411
 
412
+ ## Advanced Features
413
+
414
+ ### Multi-Contact List Support
415
+ The node automatically handles `MULTI_CONTACT_LIST` columns by:
416
+ - Parsing comma-separated email strings
417
+ - Formatting as proper Smartsheet API `objectValue` structure
418
+ - Supporting `strict: false` mode for flexible updates
419
+
420
+ ### Multi-Picklist Support
421
+ The node handles `MULTI_PICKLIST` columns by:
422
+ - Parsing comma-separated option strings
423
+ - Formatting as proper Smartsheet API `objectValue` structure
424
+ - Validating against column options
425
+
426
+ ### Attachment Handling
427
+ - **File Uploads**: Supports binary data from n8n's binary data system
428
+ - **Link Attachments**: Supports URL-based attachments
429
+ - **Proof Attachments**: Special endpoint for row proofs
430
+ - **Custom Filenames**: Override default binary filename with custom name
431
+
432
+ ### Discussion Management
433
+ - **Add to Existing**: Add comments to existing discussions
434
+ - **Create New**: Create new discussions with initial comment
435
+ - **Automatic Loading**: Fetches existing discussions for easy selection
436
+
437
+ ### Location Control
438
+ When adding or updating rows, control placement:
439
+ - **Top**: Add to top of sheet
440
+ - **Bottom**: Add to bottom of sheet
441
+ - **Parent Row**: Add as child of specified parent row
442
+
164
443
  ## API Documentation
165
444
 
166
445
  For more information about the Smartsheet API, visit:
@@ -189,4 +468,3 @@ npm run format
189
468
  ## License
190
469
 
191
470
  MIT
192
-
@@ -163,14 +163,6 @@ async function executeRowOperation(operation, itemIndex) {
163
163
  catch (error) {
164
164
  // If we can't get columns, continue without column type info
165
165
  }
166
- // Helper function to check if a string looks like comma-separated emails
167
- const looksLikeEmailList = (str) => {
168
- if (typeof str !== 'string')
169
- return false;
170
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
171
- const parts = str.split(',').map(p => p.trim());
172
- return parts.length > 1 && parts.every(part => emailRegex.test(part));
173
- };
174
166
  const cells = (cellsData.cell || []).map((cell) => {
175
167
  let columnId;
176
168
  if (cell.columnInputMethod === 'id') {
@@ -193,129 +185,33 @@ async function executeRowOperation(operation, itemIndex) {
193
185
  }
194
186
  }
195
187
  // Handle MULTI_CONTACT_LIST and MULTI_PICKLIST columns with objectValue
196
- // Also handle if value looks like comma-separated emails (fallback for when column type isn't detected)
197
- if (columnType === 'MULTI_CONTACT_LIST' || columnType === 'MULTI_PICKLIST' ||
198
- (columnType === undefined && typeof cellValue === 'string' && looksLikeEmailList(cellValue))) {
199
- // Try to parse the value as JSON if it's a string
200
- let parsedValue;
188
+ // User must provide a JSON block that will be used directly
189
+ if (columnType === 'MULTI_CONTACT_LIST' || columnType === 'MULTI_PICKLIST') {
190
+ // Parse the value as JSON
191
+ let objectValue;
201
192
  try {
202
193
  if (typeof cellValue === 'string') {
203
- parsedValue = JSON.parse(cellValue);
194
+ objectValue = JSON.parse(cellValue);
204
195
  }
205
196
  else {
206
- parsedValue = cellValue;
197
+ objectValue = cellValue;
207
198
  }
208
199
  }
209
200
  catch (error) {
210
- // If parsing fails, treat as regular value (comma-separated string)
211
- parsedValue = cellValue;
212
- }
213
- // If it's already in the correct MULTI_CONTACT format, use it directly
214
- if (parsedValue && typeof parsedValue === 'object' && parsedValue.objectType === 'MULTI_CONTACT' && parsedValue.values) {
215
- return {
216
- columnId: colIdNum,
217
- objectValue: parsedValue,
218
- strict: false,
219
- };
201
+ throw new Error(`Invalid JSON for ${columnType} column (Column ID: ${colIdNum}). ` +
202
+ `Please provide a valid JSON object. Error: ${error instanceof Error ? error.message : String(error)}`);
220
203
  }
221
- // If it's an array of contacts (old format), wrap it in MULTI_CONTACT structure
222
- if (parsedValue && Array.isArray(parsedValue) && parsedValue.length > 0) {
223
- return {
224
- columnId: colIdNum,
225
- objectValue: {
226
- objectType: 'MULTI_CONTACT',
227
- values: parsedValue.map((contact) => {
228
- if (typeof contact === 'string') {
229
- return { email: contact.trim() };
230
- }
231
- if (contact.objectType === 'CONTACT') {
232
- // Remove objectType from contact objects
233
- const contactObj = { email: contact.email || contact };
234
- if (contact.name && contact.name !== contact.email) {
235
- contactObj.name = contact.name;
236
- }
237
- return contactObj;
238
- }
239
- return contact;
240
- }),
241
- },
242
- strict: false,
243
- };
244
- }
245
- // Otherwise, format it properly based on column type
246
- // If columnType is undefined but value looks like emails, treat as MULTI_CONTACT_LIST
247
- if (columnType === 'MULTI_CONTACT_LIST' ||
248
- (columnType === undefined && typeof cellValue === 'string' && looksLikeEmailList(cellValue))) {
249
- // Format for MULTI_CONTACT_LIST
250
- let contacts = [];
251
- if (Array.isArray(parsedValue)) {
252
- contacts = parsedValue;
253
- }
254
- else if (typeof parsedValue === 'string') {
255
- // Try to parse as JSON array first
256
- try {
257
- contacts = JSON.parse(parsedValue);
258
- }
259
- catch {
260
- // If JSON parsing fails, check if it's comma-separated
261
- if (parsedValue.includes(',')) {
262
- // Split by comma and trim each email
263
- contacts = parsedValue.split(',').map((email) => email.trim());
264
- }
265
- else {
266
- // If it's a single email, wrap it
267
- contacts = [parsedValue.trim()];
268
- }
269
- }
270
- }
271
- // For MULTI_CONTACT_LIST, objectValue should be an object with objectType and values array
272
- return {
273
- columnId: colIdNum,
274
- objectValue: {
275
- objectType: 'MULTI_CONTACT',
276
- values: contacts.map((contact) => {
277
- if (typeof contact === 'string') {
278
- // For comma-separated emails, return only email field
279
- return {
280
- email: contact.trim(),
281
- };
282
- }
283
- // For object format, include email and optionally name
284
- const contactObj = {
285
- email: contact.email || contact,
286
- };
287
- // Only include name if it's different from email
288
- if (contact.name && contact.name !== contact.email) {
289
- contactObj.name = contact.name;
290
- }
291
- return contactObj;
292
- }),
293
- },
294
- strict: false,
295
- };
296
- }
297
- else if (columnType === 'MULTI_PICKLIST') {
298
- // Format for MULTI_PICKLIST
299
- let options = [];
300
- if (Array.isArray(parsedValue)) {
301
- options = parsedValue;
302
- }
303
- else if (typeof parsedValue === 'string') {
304
- try {
305
- options = JSON.parse(parsedValue);
306
- }
307
- catch {
308
- options = parsedValue.split(',').map((s) => s.trim());
309
- }
310
- }
311
- return {
312
- columnId: colIdNum,
313
- objectValue: {
314
- objectType: 'MULTI_PICKLIST',
315
- values: options,
316
- },
317
- };
204
+ // Validate that it's an object
205
+ if (typeof objectValue !== 'object' || objectValue === null) {
206
+ throw new Error(`Invalid objectValue for ${columnType} column (Column ID: ${colIdNum}). ` +
207
+ `Expected a JSON object, got ${typeof objectValue}.`);
318
208
  }
209
+ // Use the parsed JSON directly as objectValue
210
+ return {
211
+ columnId: colIdNum,
212
+ objectValue: objectValue,
213
+ strict: false,
214
+ };
319
215
  }
320
216
  // For regular columns, use value (use cleaned cellValue)
321
217
  return {
@@ -575,14 +471,6 @@ async function executeRowOperation(operation, itemIndex) {
575
471
  catch (error) {
576
472
  // If we can't get columns, continue without column type info
577
473
  }
578
- // Helper function to check if a string looks like comma-separated emails
579
- const looksLikeEmailList = (str) => {
580
- if (typeof str !== 'string')
581
- return false;
582
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
583
- const parts = str.split(',').map(p => p.trim());
584
- return parts.length > 1 && parts.every(part => emailRegex.test(part));
585
- };
586
474
  const cells = (cellsData.cell || []).map((cell) => {
587
475
  let columnId;
588
476
  if (cell.columnInputMethod === 'id') {
@@ -605,129 +493,33 @@ async function executeRowOperation(operation, itemIndex) {
605
493
  }
606
494
  }
607
495
  // Handle MULTI_CONTACT_LIST and MULTI_PICKLIST columns with objectValue
608
- // Also handle if value looks like comma-separated emails (fallback for when column type isn't detected)
609
- if (columnType === 'MULTI_CONTACT_LIST' || columnType === 'MULTI_PICKLIST' ||
610
- (columnType === undefined && typeof cellValue === 'string' && looksLikeEmailList(cellValue))) {
611
- // Try to parse the value as JSON if it's a string
612
- let parsedValue;
496
+ // User must provide a JSON block that will be used directly
497
+ if (columnType === 'MULTI_CONTACT_LIST' || columnType === 'MULTI_PICKLIST') {
498
+ // Parse the value as JSON
499
+ let objectValue;
613
500
  try {
614
501
  if (typeof cellValue === 'string') {
615
- parsedValue = JSON.parse(cellValue);
502
+ objectValue = JSON.parse(cellValue);
616
503
  }
617
504
  else {
618
- parsedValue = cellValue;
505
+ objectValue = cellValue;
619
506
  }
620
507
  }
621
508
  catch (error) {
622
- // If parsing fails, treat as regular value (comma-separated string)
623
- parsedValue = cellValue;
624
- }
625
- // If it's already in the correct MULTI_CONTACT format, use it directly
626
- if (parsedValue && typeof parsedValue === 'object' && parsedValue.objectType === 'MULTI_CONTACT' && parsedValue.values) {
627
- return {
628
- columnId: colIdNum,
629
- objectValue: parsedValue,
630
- strict: false,
631
- };
509
+ throw new Error(`Invalid JSON for ${columnType} column (Column ID: ${colIdNum}). ` +
510
+ `Please provide a valid JSON object. Error: ${error instanceof Error ? error.message : String(error)}`);
632
511
  }
633
- // If it's an array of contacts (old format), wrap it in MULTI_CONTACT structure
634
- if (parsedValue && Array.isArray(parsedValue) && parsedValue.length > 0) {
635
- return {
636
- columnId: colIdNum,
637
- objectValue: {
638
- objectType: 'MULTI_CONTACT',
639
- values: parsedValue.map((contact) => {
640
- if (typeof contact === 'string') {
641
- return { email: contact.trim() };
642
- }
643
- if (contact.objectType === 'CONTACT') {
644
- // Remove objectType from contact objects
645
- const contactObj = { email: contact.email || contact };
646
- if (contact.name && contact.name !== contact.email) {
647
- contactObj.name = contact.name;
648
- }
649
- return contactObj;
650
- }
651
- return contact;
652
- }),
653
- },
654
- strict: false,
655
- };
656
- }
657
- // Otherwise, format it properly based on column type
658
- // If columnType is undefined but value looks like emails, treat as MULTI_CONTACT_LIST
659
- if (columnType === 'MULTI_CONTACT_LIST' ||
660
- (columnType === undefined && typeof cellValue === 'string' && looksLikeEmailList(cellValue))) {
661
- // Format for MULTI_CONTACT_LIST
662
- let contacts = [];
663
- if (Array.isArray(parsedValue)) {
664
- contacts = parsedValue;
665
- }
666
- else if (typeof parsedValue === 'string') {
667
- // Try to parse as JSON array first
668
- try {
669
- contacts = JSON.parse(parsedValue);
670
- }
671
- catch {
672
- // If JSON parsing fails, check if it's comma-separated
673
- if (parsedValue.includes(',')) {
674
- // Split by comma and trim each email
675
- contacts = parsedValue.split(',').map((email) => email.trim());
676
- }
677
- else {
678
- // If it's a single email, wrap it
679
- contacts = [parsedValue.trim()];
680
- }
681
- }
682
- }
683
- // For MULTI_CONTACT_LIST, objectValue should be an object with objectType and values array
684
- return {
685
- columnId: colIdNum,
686
- objectValue: {
687
- objectType: 'MULTI_CONTACT',
688
- values: contacts.map((contact) => {
689
- if (typeof contact === 'string') {
690
- // For comma-separated emails, return only email field
691
- return {
692
- email: contact.trim(),
693
- };
694
- }
695
- // For object format, include email and optionally name
696
- const contactObj = {
697
- email: contact.email || contact,
698
- };
699
- // Only include name if it's different from email
700
- if (contact.name && contact.name !== contact.email) {
701
- contactObj.name = contact.name;
702
- }
703
- return contactObj;
704
- }),
705
- },
706
- strict: false,
707
- };
708
- }
709
- else if (columnType === 'MULTI_PICKLIST') {
710
- // Format for MULTI_PICKLIST
711
- let options = [];
712
- if (Array.isArray(parsedValue)) {
713
- options = parsedValue;
714
- }
715
- else if (typeof parsedValue === 'string') {
716
- try {
717
- options = JSON.parse(parsedValue);
718
- }
719
- catch {
720
- options = parsedValue.split(',').map((s) => s.trim());
721
- }
722
- }
723
- return {
724
- columnId: colIdNum,
725
- objectValue: {
726
- objectType: 'MULTI_PICKLIST',
727
- values: options,
728
- },
729
- };
512
+ // Validate that it's an object
513
+ if (typeof objectValue !== 'object' || objectValue === null) {
514
+ throw new Error(`Invalid objectValue for ${columnType} column (Column ID: ${colIdNum}). ` +
515
+ `Expected a JSON object, got ${typeof objectValue}.`);
730
516
  }
517
+ // Use the parsed JSON directly as objectValue
518
+ return {
519
+ columnId: colIdNum,
520
+ objectValue: objectValue,
521
+ strict: false,
522
+ };
731
523
  }
732
524
  // For regular columns, use value (use cleaned cellValue)
733
525
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-steyi-ss",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "n8n node for Smartsheet API integration",
5
5
  "keywords": [
6
6
  "n8n-node-package",
@@ -34,9 +34,9 @@
34
34
  "dist/nodes/SteyiSmartsheet/SteyiSmartsheet.node.js",
35
35
  "dist/nodes/SteyiSmartsheet/SteyiSmartsheetTrigger.node.js"
36
36
  ],
37
- "credentials": [
38
- "dist/credentials/SteyiSmartsheetCreds.credentials.js"
39
- ]
37
+ "credentials": [
38
+ "dist/credentials/SteyiSmartsheetCreds.credentials.js"
39
+ ]
40
40
  },
41
41
  "devDependencies": {
42
42
  "@typescript-eslint/parser": "^5.45.0",
@@ -53,4 +53,3 @@
53
53
  "n8n-workflow": "*"
54
54
  }
55
55
  }
56
-