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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
-
|
|
212
|
-
-
|
|
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
|
-
-
|
|
417
|
-
-
|
|
418
|
-
-
|
|
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
|
-
-
|
|
423
|
-
-
|
|
424
|
-
-
|
|
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
|
-
|
|
1772
|
-
|
|
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.
|
|
1776
|
-
|
|
1777
|
-
|
|
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
|
-
|
|
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'
|
|
200
|
+
if (columnType === 'MULTI_CONTACT_LIST') {
|
|
190
201
|
// Parse the value as JSON
|
|
191
|
-
let
|
|
202
|
+
let parsedValue;
|
|
192
203
|
try {
|
|
193
204
|
if (typeof cellValue === 'string') {
|
|
194
|
-
|
|
205
|
+
parsedValue = JSON.parse(cellValue);
|
|
195
206
|
}
|
|
196
207
|
else {
|
|
197
|
-
|
|
208
|
+
parsedValue = cellValue;
|
|
198
209
|
}
|
|
199
210
|
}
|
|
200
211
|
catch (error) {
|
|
201
|
-
throw new Error(`Invalid JSON for
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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'
|
|
667
|
+
if (columnType === 'MULTI_CONTACT_LIST') {
|
|
498
668
|
// Parse the value as JSON
|
|
499
|
-
let
|
|
669
|
+
let parsedValue;
|
|
500
670
|
try {
|
|
501
671
|
if (typeof cellValue === 'string') {
|
|
502
|
-
|
|
672
|
+
parsedValue = JSON.parse(cellValue);
|
|
503
673
|
}
|
|
504
674
|
else {
|
|
505
|
-
|
|
675
|
+
parsedValue = cellValue;
|
|
506
676
|
}
|
|
507
677
|
}
|
|
508
678
|
catch (error) {
|
|
509
|
-
throw new Error(`Invalid JSON for
|
|
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
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
-
|
|
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
|
});
|