n8n-nodes-steyi-ss 1.0.2 → 1.0.3

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
@@ -199,17 +199,65 @@ Add a new row to a sheet:
199
199
  - Attachment category (Regular Attachment or Proof)
200
200
  - Custom filename
201
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"]`
202
+ ## Column Value Formatting
203
+
204
+ When adding or updating rows, different column types require specific value formats:
205
+
206
+ ### TEXT_NUMBER, DATE, DATETIME
207
+ - **Format**: Plain text or number
208
+ - **Example**: `"John Doe"`, `123`, `"2024-01-15"`
209
+
210
+ ### CHECKBOX
211
+ - **Format**: String values that are auto-converted to boolean
212
+ - **Accepted values**: `"true"`, `"false"`, `"1"`, `"0"`, `"yes"`, `"no"` (case-insensitive)
213
+ - **Example**: `"true"` → `true`, `"false"` → `false`
214
+ - The node automatically converts these strings to boolean values
215
+
216
+ ### PICKLIST
217
+ - **Format**: Option text (must match exactly, case-sensitive)
218
+ - **Example**: `"High Priority"` (must match an option defined in the column)
219
+
220
+ ### MULTI_CONTACT_LIST
221
+ - **Format**: JSON object with `values` array containing email objects
222
+ - **Example**:
223
+ ```json
224
+ {
225
+ "values": [
226
+ { "email": "john.smith@example.com" },
227
+ { "email": "jane.doe@example.com" }
228
+ ]
229
+ }
230
+ ```
231
+ - The node automatically transforms this to the correct Smartsheet API format:
232
+ ```json
233
+ {
234
+ "objectType": "MULTI_CONTACT",
235
+ "values": [
236
+ { "objectType": "CONTACT", "email": "john.smith@example.com" },
237
+ { "objectType": "CONTACT", "email": "jane.doe@example.com" }
238
+ ]
239
+ }
240
+ ```
241
+
242
+ ### MULTI_PICKLIST
243
+ - **Format**: JSON object with `values` array containing option strings
244
+ - **Example**:
245
+ ```json
246
+ {
247
+ "values": [
248
+ "Option 1",
249
+ "Option 2"
250
+ ]
251
+ }
252
+ ```
253
+ - **Important**: Option values must match exactly (case-sensitive) the options defined in the MULTI_PICKLIST column
254
+ - The node automatically transforms this to the correct Smartsheet API format:
255
+ ```json
256
+ {
257
+ "objectType": "MULTI_PICKLIST",
258
+ "values": ["Option 1", "Option 2"]
259
+ }
260
+ ```
213
261
 
214
262
  #### Update Row
215
263
  Update an existing row with the same options as Add Row:
@@ -413,15 +461,23 @@ For the trigger node, n8n automatically generates the webhook URL, but it must b
413
461
 
414
462
  ### Multi-Contact List Support
415
463
  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
464
+ - Accepting JSON format: `{ "values": [{ "email": "..." }, ...] }`
465
+ - Automatically adding `objectType: "MULTI_CONTACT"` to the objectValue
466
+ - Wrapping each contact with `objectType: "CONTACT"` in the values array
467
+ - Validating email format and structure
419
468
 
420
469
  ### Multi-Picklist Support
421
470
  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
471
+ - Accepting JSON format: `{ "values": ["Option 1", "Option 2"] }`
472
+ - Automatically adding `objectType: "MULTI_PICKLIST"` to the objectValue
473
+ - Keeping values as strings (not wrapped in objects)
474
+ - Validating that option values match column options (case-sensitive)
475
+
476
+ ### Checkbox Support
477
+ The node automatically handles `CHECKBOX` columns by:
478
+ - Converting string values to boolean: `"true"`, `"false"`, `"1"`, `"0"`, `"yes"`, `"no"`
479
+ - Case-insensitive conversion
480
+ - Empty values default to `false`
425
481
 
426
482
  ### Attachment Handling
427
483
  - **File Uploads**: Supports binary data from n8n's binary data system
@@ -1,4 +1,27 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
26
  exports.SteyiSmartsheet = void 0;
4
27
  const Sheets_1 = require("./executors/Sheets");
@@ -571,7 +594,7 @@ class SteyiSmartsheet {
571
594
  },
572
595
  },
573
596
  default: '',
574
- description: 'Select a column',
597
+ description: 'Select a column. The column type is shown in parentheses next to each column name (e.g., "Column Name (MULTI_CONTACT_LIST)")',
575
598
  },
576
599
  {
577
600
  displayName: 'Column ID',
@@ -590,7 +613,7 @@ class SteyiSmartsheet {
590
613
  name: 'value',
591
614
  type: 'string',
592
615
  default: '',
593
- description: 'The value to set',
616
+ description: 'The value to set. Column type is shown in parentheses in the dropdown above. For format examples for each column type, see the <a href="https://www.npmjs.com/package/n8n-nodes-steyi-ss" target="_blank">npm package README</a>.',
594
617
  },
595
618
  ],
596
619
  },
@@ -1768,14 +1791,40 @@ class SteyiSmartsheet {
1768
1791
  if (sheetId === undefined || sheetId === null || sheetId === '') {
1769
1792
  return [];
1770
1793
  }
1771
- const columns = await SteyiSmartsheetApi_1.listColumns.call(this, parseInt(sheetId.toString()));
1772
- if (!columns || !columns.data) {
1794
+ // Get columns with level=2 to ensure we get the correct column types
1795
+ let columns = [];
1796
+ try {
1797
+ const columnsResponse = await SteyiSmartsheetApi_1.listColumns.call(this, parseInt(sheetId.toString()));
1798
+ if (columnsResponse && columnsResponse.data && Array.isArray(columnsResponse.data)) {
1799
+ columns = columnsResponse.data;
1800
+ }
1801
+ }
1802
+ catch (error) {
1803
+ // If columns endpoint fails, try getting the sheet with columns included and level=2
1804
+ try {
1805
+ const { smartsheetApiRequest } = await Promise.resolve().then(() => __importStar(require('./SteyiGenericFunction')));
1806
+ const sheetResponse = await smartsheetApiRequest.call(this, 'GET', `/sheets/${sheetId}?include=columns&level=2`, {}, 0);
1807
+ if (sheetResponse && sheetResponse.columns && Array.isArray(sheetResponse.columns)) {
1808
+ columns = sheetResponse.columns;
1809
+ }
1810
+ }
1811
+ catch (sheetError) {
1812
+ // If both fail, return empty array
1813
+ return [];
1814
+ }
1815
+ }
1816
+ if (columns.length === 0) {
1773
1817
  return [];
1774
1818
  }
1775
- return columns.data.map((column) => ({
1776
- name: column.title,
1777
- value: column.id.toString(),
1778
- }));
1819
+ return columns.map((column) => {
1820
+ // The type field should be present in the API response with level=2
1821
+ const columnType = column.type || 'UNKNOWN';
1822
+ return {
1823
+ name: `${column.title} (${columnType})`,
1824
+ value: column.id.toString(),
1825
+ description: `Type: ${columnType}`,
1826
+ };
1827
+ });
1779
1828
  },
1780
1829
  async getDiscussions() {
1781
1830
  const sheetId = this.getNodeParameter('sheetId', 0);
@@ -24,7 +24,7 @@ async function listSheets(includeAll = true) {
24
24
  }
25
25
  exports.listSheets = listSheets;
26
26
  async function listColumns(sheetId) {
27
- return await SteyiGenericFunction_1.smartsheetApiRequest.call(this, 'GET', `/sheets/${sheetId}/columns`, {}, 0);
27
+ return await SteyiGenericFunction_1.smartsheetApiRequest.call(this, 'GET', `/sheets/${sheetId}/columns?level=2`, {}, 0);
28
28
  }
29
29
  exports.listColumns = listColumns;
30
30
  async function listWebhooks() {
@@ -163,7 +163,14 @@ 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
- const cells = (cellsData.cell || []).map((cell) => {
166
+ // Filter out any cells with empty columnId or invalid data
167
+ const validCells = (cellsData.cell || []).filter((cell) => {
168
+ const columnId = cell.columnInputMethod === 'id'
169
+ ? cell.columnIdManual
170
+ : cell.columnId;
171
+ return columnId && columnId !== '' && cell.value !== undefined;
172
+ });
173
+ const cells = validCells.map((cell) => {
167
174
  let columnId;
168
175
  if (cell.columnInputMethod === 'id') {
169
176
  columnId = cell.columnIdManual || '';
@@ -171,7 +178,11 @@ async function executeRowOperation(operation, itemIndex) {
171
178
  else {
172
179
  columnId = cell.columnId || '';
173
180
  }
181
+ // Parse columnId - keep as number for API, but ensure it's valid
174
182
  const colIdNum = parseInt(columnId, 10);
183
+ if (isNaN(colIdNum)) {
184
+ throw new Error(`Invalid column ID: ${columnId}`);
185
+ }
175
186
  const column = columnsMap.get(colIdNum);
176
187
  const columnType = column?.type;
177
188
  // Clean up the value - remove surrounding quotes if present
@@ -186,36 +197,184 @@ async function executeRowOperation(operation, itemIndex) {
186
197
  }
187
198
  // Handle MULTI_CONTACT_LIST and MULTI_PICKLIST columns with objectValue
188
199
  // User must provide a JSON block that will be used directly
189
- if (columnType === 'MULTI_CONTACT_LIST' || columnType === 'MULTI_PICKLIST') {
200
+ if (columnType === 'MULTI_CONTACT_LIST') {
190
201
  // Parse the value as JSON
191
- let objectValue;
202
+ let parsedValue;
192
203
  try {
193
204
  if (typeof cellValue === 'string') {
194
- objectValue = JSON.parse(cellValue);
205
+ parsedValue = JSON.parse(cellValue);
195
206
  }
196
207
  else {
197
- objectValue = cellValue;
208
+ parsedValue = cellValue;
198
209
  }
199
210
  }
200
211
  catch (error) {
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)}`);
212
+ throw new Error(`Invalid JSON for MULTI_CONTACT_LIST column (Column ID: ${colIdNum}). ` +
213
+ `Please provide a valid JSON object with a "values" array. Error: ${error instanceof Error ? error.message : String(error)}`);
214
+ }
215
+ // Validate that it's an object with values array
216
+ if (typeof parsedValue !== 'object' || parsedValue === null) {
217
+ throw new Error(`Invalid objectValue for MULTI_CONTACT_LIST column (Column ID: ${colIdNum}). ` +
218
+ `Expected a JSON object with "values" array, got ${typeof parsedValue}.`);
219
+ }
220
+ // Validate that it has a values array
221
+ if (!Array.isArray(parsedValue.values)) {
222
+ throw new Error(`Invalid objectValue for MULTI_CONTACT_LIST column (Column ID: ${colIdNum}). ` +
223
+ `Expected a JSON object with "values" array property. Example: { "values": [{ "email": "user@example.com" }] }`);
224
+ }
225
+ // Validate that each value in the array has an email property
226
+ // Transform values to include objectType: "CONTACT" for each contact
227
+ const contactValues = parsedValue.values.map((value, i) => {
228
+ if (typeof value !== 'object' || value === null || !value.email) {
229
+ throw new Error(`Invalid objectValue for MULTI_CONTACT_LIST column (Column ID: ${colIdNum}). ` +
230
+ `Each item in the "values" array must be an object with an "email" property. ` +
231
+ `Example: { "values": [{ "email": "user@example.com" }] }`);
232
+ }
233
+ // Ensure each contact has objectType: "CONTACT"
234
+ return {
235
+ objectType: 'CONTACT',
236
+ email: value.email,
237
+ };
238
+ });
239
+ // For MULTI_CONTACT_LIST, objectValue should have structure:
240
+ // { "objectType": "MULTI_CONTACT", "values": [{ "objectType": "CONTACT", "email": "..." }, ...] }
241
+ // User provides the values array with emails, we add the objectType fields
242
+ // Don't include "strict" for MULTI_CONTACT_LIST as it causes parsing errors
243
+ // Ensure columnId is a number (not string) for the API
244
+ return {
245
+ columnId: Number(colIdNum),
246
+ objectValue: {
247
+ objectType: 'MULTI_CONTACT',
248
+ values: contactValues,
249
+ },
250
+ };
251
+ }
252
+ else if (columnType === 'MULTI_PICKLIST') {
253
+ // Parse the value as JSON
254
+ // Expected format: { "values": ["Option 1", "Option 2", ...] }
255
+ let parsedValue;
256
+ try {
257
+ if (typeof cellValue === 'string') {
258
+ parsedValue = JSON.parse(cellValue);
259
+ }
260
+ else {
261
+ parsedValue = cellValue;
262
+ }
263
+ }
264
+ catch (error) {
265
+ throw new Error(`Invalid JSON for MULTI_PICKLIST column (Column ID: ${colIdNum}). ` +
266
+ `Please provide a valid JSON object with structure: { "values": ["Option 1", "Option 2"] }. ` +
267
+ `Error: ${error instanceof Error ? error.message : String(error)}`);
268
+ }
269
+ // Validate that it's an object with values array
270
+ if (typeof parsedValue !== 'object' || parsedValue === null) {
271
+ throw new Error(`Invalid objectValue for MULTI_PICKLIST column (Column ID: ${colIdNum}). ` +
272
+ `Expected a JSON object with "values" array, got ${typeof parsedValue}.`);
273
+ }
274
+ // Validate that it has a values array
275
+ if (!Array.isArray(parsedValue.values)) {
276
+ throw new Error(`Invalid objectValue for MULTI_PICKLIST column (Column ID: ${colIdNum}). ` +
277
+ `Expected a JSON object with "values" array property. Example: { "values": ["Option 1", "Option 2"] }`);
278
+ }
279
+ // For MULTI_PICKLIST, values should be an array of strings, not objects
280
+ // Transform values to ensure they're all strings
281
+ // Note: Picklist values must match exactly (case-sensitive) the options defined in the column
282
+ const picklistValues = parsedValue.values.map((value) => {
283
+ // Value should be a string (the option text)
284
+ if (typeof value === 'string') {
285
+ return value;
286
+ }
287
+ else if (typeof value === 'object' && value !== null && value.value) {
288
+ // If it's an object with a value property, extract the string value
289
+ return String(value.value);
290
+ }
291
+ else {
292
+ throw new Error(`Invalid value in MULTI_PICKLIST values array (Column ID: ${colIdNum}). ` +
293
+ `Each value must be a string (option text). ` +
294
+ `Note: Option values must match exactly (case-sensitive) the options defined in the MULTI_PICKLIST column.`);
295
+ }
296
+ });
297
+ // For MULTI_PICKLIST, objectValue should have structure:
298
+ // { "objectType": "MULTI_PICKLIST", "values": ["Option 1", "Option 2", ...] }
299
+ // User provides the values array with option strings, we use them directly (not wrapped in objects)
300
+ // Don't include "strict" for MULTI_PICKLIST as it may cause parsing errors
301
+ // Ensure columnId is a number (not string) for the API
302
+ return {
303
+ columnId: Number(colIdNum),
304
+ objectValue: {
305
+ objectType: 'MULTI_PICKLIST',
306
+ values: picklistValues,
307
+ },
308
+ };
309
+ }
310
+ // For CHECKBOX columns, convert string "true"/"false" to boolean
311
+ if (columnType === 'CHECKBOX') {
312
+ let boolValue;
313
+ if (typeof cellValue === 'boolean') {
314
+ boolValue = cellValue;
315
+ }
316
+ else if (typeof cellValue === 'string') {
317
+ const lowerValue = cellValue.toLowerCase().trim();
318
+ if (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes') {
319
+ boolValue = true;
320
+ }
321
+ else if (lowerValue === 'false' || lowerValue === '0' || lowerValue === 'no' || lowerValue === '') {
322
+ boolValue = false;
323
+ }
324
+ else {
325
+ // If it's not a recognized boolean string, try to parse it
326
+ throw new Error(`Invalid value for CHECKBOX column (Column ID: ${colIdNum}). ` +
327
+ `Expected boolean or string "true"/"false", got: ${cellValue}`);
328
+ }
329
+ }
330
+ else if (cellValue === null || cellValue === undefined || cellValue === '') {
331
+ boolValue = false;
332
+ }
333
+ else {
334
+ // Try to convert to boolean
335
+ boolValue = Boolean(cellValue);
336
+ }
337
+ return {
338
+ columnId: Number(colIdNum),
339
+ value: boolValue,
340
+ };
341
+ }
342
+ // For CHECKBOX columns, convert string "true"/"false" to boolean
343
+ if (columnType === 'CHECKBOX') {
344
+ let boolValue;
345
+ if (typeof cellValue === 'boolean') {
346
+ boolValue = cellValue;
347
+ }
348
+ else if (typeof cellValue === 'string') {
349
+ const lowerValue = cellValue.toLowerCase().trim();
350
+ if (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes') {
351
+ boolValue = true;
352
+ }
353
+ else if (lowerValue === 'false' || lowerValue === '0' || lowerValue === 'no' || lowerValue === '') {
354
+ boolValue = false;
355
+ }
356
+ else {
357
+ // If it's not a recognized boolean string, try to parse it
358
+ throw new Error(`Invalid value for CHECKBOX column (Column ID: ${colIdNum}). ` +
359
+ `Expected boolean or string "true"/"false", got: ${cellValue}`);
360
+ }
361
+ }
362
+ else if (cellValue === null || cellValue === undefined || cellValue === '') {
363
+ boolValue = false;
203
364
  }
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}.`);
365
+ else {
366
+ // Try to convert to boolean
367
+ boolValue = Boolean(cellValue);
208
368
  }
209
- // Use the parsed JSON directly as objectValue
210
369
  return {
211
- columnId: colIdNum,
212
- objectValue: objectValue,
213
- strict: false,
370
+ columnId: Number(colIdNum),
371
+ value: boolValue,
214
372
  };
215
373
  }
216
374
  // For regular columns, use value (use cleaned cellValue)
375
+ // Ensure columnId is a number (not string) for the API
217
376
  return {
218
- columnId: colIdNum,
377
+ columnId: Number(colIdNum),
219
378
  value: cellValue,
220
379
  };
221
380
  });
@@ -471,7 +630,14 @@ async function executeRowOperation(operation, itemIndex) {
471
630
  catch (error) {
472
631
  // If we can't get columns, continue without column type info
473
632
  }
474
- const cells = (cellsData.cell || []).map((cell) => {
633
+ // Filter out any cells with empty columnId or invalid data
634
+ const validCells = (cellsData.cell || []).filter((cell) => {
635
+ const columnId = cell.columnInputMethod === 'id'
636
+ ? cell.columnIdManual
637
+ : cell.columnId;
638
+ return columnId && columnId !== '' && cell.value !== undefined;
639
+ });
640
+ const cells = validCells.map((cell) => {
475
641
  let columnId;
476
642
  if (cell.columnInputMethod === 'id') {
477
643
  columnId = cell.columnIdManual || '';
@@ -479,7 +645,11 @@ async function executeRowOperation(operation, itemIndex) {
479
645
  else {
480
646
  columnId = cell.columnId || '';
481
647
  }
648
+ // Parse columnId - keep as number for API, but ensure it's valid
482
649
  const colIdNum = parseInt(columnId, 10);
650
+ if (isNaN(colIdNum)) {
651
+ throw new Error(`Invalid column ID: ${columnId}`);
652
+ }
483
653
  const column = columnsMap.get(colIdNum);
484
654
  const columnType = column?.type;
485
655
  // Clean up the value - remove surrounding quotes if present
@@ -494,36 +664,184 @@ async function executeRowOperation(operation, itemIndex) {
494
664
  }
495
665
  // Handle MULTI_CONTACT_LIST and MULTI_PICKLIST columns with objectValue
496
666
  // User must provide a JSON block that will be used directly
497
- if (columnType === 'MULTI_CONTACT_LIST' || columnType === 'MULTI_PICKLIST') {
667
+ if (columnType === 'MULTI_CONTACT_LIST') {
498
668
  // Parse the value as JSON
499
- let objectValue;
669
+ let parsedValue;
500
670
  try {
501
671
  if (typeof cellValue === 'string') {
502
- objectValue = JSON.parse(cellValue);
672
+ parsedValue = JSON.parse(cellValue);
503
673
  }
504
674
  else {
505
- objectValue = cellValue;
675
+ parsedValue = cellValue;
506
676
  }
507
677
  }
508
678
  catch (error) {
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)}`);
679
+ throw new Error(`Invalid JSON for MULTI_CONTACT_LIST column (Column ID: ${colIdNum}). ` +
680
+ `Please provide a valid JSON object with a "values" array. Error: ${error instanceof Error ? error.message : String(error)}`);
681
+ }
682
+ // Validate that it's an object with values array
683
+ if (typeof parsedValue !== 'object' || parsedValue === null) {
684
+ throw new Error(`Invalid objectValue for MULTI_CONTACT_LIST column (Column ID: ${colIdNum}). ` +
685
+ `Expected a JSON object with "values" array, got ${typeof parsedValue}.`);
686
+ }
687
+ // Validate that it has a values array
688
+ if (!Array.isArray(parsedValue.values)) {
689
+ throw new Error(`Invalid objectValue for MULTI_CONTACT_LIST column (Column ID: ${colIdNum}). ` +
690
+ `Expected a JSON object with "values" array property. Example: { "values": [{ "email": "user@example.com" }] }`);
691
+ }
692
+ // Validate that each value in the array has an email property
693
+ // Transform values to include objectType: "CONTACT" for each contact
694
+ const contactValues = parsedValue.values.map((value, i) => {
695
+ if (typeof value !== 'object' || value === null || !value.email) {
696
+ throw new Error(`Invalid objectValue for MULTI_CONTACT_LIST column (Column ID: ${colIdNum}). ` +
697
+ `Each item in the "values" array must be an object with an "email" property. ` +
698
+ `Example: { "values": [{ "email": "user@example.com" }] }`);
699
+ }
700
+ // Ensure each contact has objectType: "CONTACT"
701
+ return {
702
+ objectType: 'CONTACT',
703
+ email: value.email,
704
+ };
705
+ });
706
+ // For MULTI_CONTACT_LIST, objectValue should have structure:
707
+ // { "objectType": "MULTI_CONTACT", "values": [{ "objectType": "CONTACT", "email": "..." }, ...] }
708
+ // User provides the values array with emails, we add the objectType fields
709
+ // Don't include "strict" for MULTI_CONTACT_LIST as it causes parsing errors
710
+ // Ensure columnId is a number (not string) for the API
711
+ return {
712
+ columnId: Number(colIdNum),
713
+ objectValue: {
714
+ objectType: 'MULTI_CONTACT',
715
+ values: contactValues,
716
+ },
717
+ };
718
+ }
719
+ else if (columnType === 'MULTI_PICKLIST') {
720
+ // Parse the value as JSON
721
+ // Expected format: { "values": ["Option 1", "Option 2", ...] }
722
+ let parsedValue;
723
+ try {
724
+ if (typeof cellValue === 'string') {
725
+ parsedValue = JSON.parse(cellValue);
726
+ }
727
+ else {
728
+ parsedValue = cellValue;
729
+ }
730
+ }
731
+ catch (error) {
732
+ throw new Error(`Invalid JSON for MULTI_PICKLIST column (Column ID: ${colIdNum}). ` +
733
+ `Please provide a valid JSON object with structure: { "values": ["Option 1", "Option 2"] }. ` +
734
+ `Error: ${error instanceof Error ? error.message : String(error)}`);
735
+ }
736
+ // Validate that it's an object with values array
737
+ if (typeof parsedValue !== 'object' || parsedValue === null) {
738
+ throw new Error(`Invalid objectValue for MULTI_PICKLIST column (Column ID: ${colIdNum}). ` +
739
+ `Expected a JSON object with "values" array, got ${typeof parsedValue}.`);
740
+ }
741
+ // Validate that it has a values array
742
+ if (!Array.isArray(parsedValue.values)) {
743
+ throw new Error(`Invalid objectValue for MULTI_PICKLIST column (Column ID: ${colIdNum}). ` +
744
+ `Expected a JSON object with "values" array property. Example: { "values": ["Option 1", "Option 2"] }`);
745
+ }
746
+ // For MULTI_PICKLIST, values should be an array of strings, not objects
747
+ // Transform values to ensure they're all strings
748
+ // Note: Picklist values must match exactly (case-sensitive) the options defined in the column
749
+ const picklistValues = parsedValue.values.map((value) => {
750
+ // Value should be a string (the option text)
751
+ if (typeof value === 'string') {
752
+ return value;
753
+ }
754
+ else if (typeof value === 'object' && value !== null && value.value) {
755
+ // If it's an object with a value property, extract the string value
756
+ return String(value.value);
757
+ }
758
+ else {
759
+ throw new Error(`Invalid value in MULTI_PICKLIST values array (Column ID: ${colIdNum}). ` +
760
+ `Each value must be a string (option text). ` +
761
+ `Note: Option values must match exactly (case-sensitive) the options defined in the MULTI_PICKLIST column.`);
762
+ }
763
+ });
764
+ // For MULTI_PICKLIST, objectValue should have structure:
765
+ // { "objectType": "MULTI_PICKLIST", "values": ["Option 1", "Option 2", ...] }
766
+ // User provides the values array with option strings, we use them directly (not wrapped in objects)
767
+ // Don't include "strict" for MULTI_PICKLIST as it may cause parsing errors
768
+ // Ensure columnId is a number (not string) for the API
769
+ return {
770
+ columnId: Number(colIdNum),
771
+ objectValue: {
772
+ objectType: 'MULTI_PICKLIST',
773
+ values: picklistValues,
774
+ },
775
+ };
776
+ }
777
+ // For CHECKBOX columns, convert string "true"/"false" to boolean
778
+ if (columnType === 'CHECKBOX') {
779
+ let boolValue;
780
+ if (typeof cellValue === 'boolean') {
781
+ boolValue = cellValue;
782
+ }
783
+ else if (typeof cellValue === 'string') {
784
+ const lowerValue = cellValue.toLowerCase().trim();
785
+ if (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes') {
786
+ boolValue = true;
787
+ }
788
+ else if (lowerValue === 'false' || lowerValue === '0' || lowerValue === 'no' || lowerValue === '') {
789
+ boolValue = false;
790
+ }
791
+ else {
792
+ // If it's not a recognized boolean string, try to parse it
793
+ throw new Error(`Invalid value for CHECKBOX column (Column ID: ${colIdNum}). ` +
794
+ `Expected boolean or string "true"/"false", got: ${cellValue}`);
795
+ }
796
+ }
797
+ else if (cellValue === null || cellValue === undefined || cellValue === '') {
798
+ boolValue = false;
799
+ }
800
+ else {
801
+ // Try to convert to boolean
802
+ boolValue = Boolean(cellValue);
803
+ }
804
+ return {
805
+ columnId: Number(colIdNum),
806
+ value: boolValue,
807
+ };
808
+ }
809
+ // For CHECKBOX columns, convert string "true"/"false" to boolean
810
+ if (columnType === 'CHECKBOX') {
811
+ let boolValue;
812
+ if (typeof cellValue === 'boolean') {
813
+ boolValue = cellValue;
814
+ }
815
+ else if (typeof cellValue === 'string') {
816
+ const lowerValue = cellValue.toLowerCase().trim();
817
+ if (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes') {
818
+ boolValue = true;
819
+ }
820
+ else if (lowerValue === 'false' || lowerValue === '0' || lowerValue === 'no' || lowerValue === '') {
821
+ boolValue = false;
822
+ }
823
+ else {
824
+ // If it's not a recognized boolean string, try to parse it
825
+ throw new Error(`Invalid value for CHECKBOX column (Column ID: ${colIdNum}). ` +
826
+ `Expected boolean or string "true"/"false", got: ${cellValue}`);
827
+ }
828
+ }
829
+ else if (cellValue === null || cellValue === undefined || cellValue === '') {
830
+ boolValue = false;
511
831
  }
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}.`);
832
+ else {
833
+ // Try to convert to boolean
834
+ boolValue = Boolean(cellValue);
516
835
  }
517
- // Use the parsed JSON directly as objectValue
518
836
  return {
519
- columnId: colIdNum,
520
- objectValue: objectValue,
521
- strict: false,
837
+ columnId: Number(colIdNum),
838
+ value: boolValue,
522
839
  };
523
840
  }
524
841
  // For regular columns, use value (use cleaned cellValue)
842
+ // Ensure columnId is a number (not string) for the API
525
843
  return {
526
- columnId: colIdNum,
844
+ columnId: Number(colIdNum),
527
845
  value: cellValue,
528
846
  };
529
847
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-steyi-ss",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "n8n node for Smartsheet API integration",
5
5
  "keywords": [
6
6
  "n8n-node-package",