n8n-nodes-steyi-ss 1.0.1 → 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,15 +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
|
-
//
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
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) => {
|
|
175
174
|
let columnId;
|
|
176
175
|
if (cell.columnInputMethod === 'id') {
|
|
177
176
|
columnId = cell.columnIdManual || '';
|
|
@@ -179,7 +178,11 @@ async function executeRowOperation(operation, itemIndex) {
|
|
|
179
178
|
else {
|
|
180
179
|
columnId = cell.columnId || '';
|
|
181
180
|
}
|
|
181
|
+
// Parse columnId - keep as number for API, but ensure it's valid
|
|
182
182
|
const colIdNum = parseInt(columnId, 10);
|
|
183
|
+
if (isNaN(colIdNum)) {
|
|
184
|
+
throw new Error(`Invalid column ID: ${columnId}`);
|
|
185
|
+
}
|
|
183
186
|
const column = columnsMap.get(colIdNum);
|
|
184
187
|
const columnType = column?.type;
|
|
185
188
|
// Clean up the value - remove surrounding quotes if present
|
|
@@ -193,10 +196,9 @@ async function executeRowOperation(operation, itemIndex) {
|
|
|
193
196
|
}
|
|
194
197
|
}
|
|
195
198
|
// Handle MULTI_CONTACT_LIST and MULTI_PICKLIST columns with objectValue
|
|
196
|
-
//
|
|
197
|
-
if (columnType === 'MULTI_CONTACT_LIST'
|
|
198
|
-
|
|
199
|
-
// Try to parse the value as JSON if it's a string
|
|
199
|
+
// User must provide a JSON block that will be used directly
|
|
200
|
+
if (columnType === 'MULTI_CONTACT_LIST') {
|
|
201
|
+
// Parse the value as JSON
|
|
200
202
|
let parsedValue;
|
|
201
203
|
try {
|
|
202
204
|
if (typeof cellValue === 'string') {
|
|
@@ -207,119 +209,172 @@ async function executeRowOperation(operation, itemIndex) {
|
|
|
207
209
|
}
|
|
208
210
|
}
|
|
209
211
|
catch (error) {
|
|
210
|
-
|
|
211
|
-
|
|
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)}`);
|
|
212
214
|
}
|
|
213
|
-
//
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
objectValue: parsedValue,
|
|
218
|
-
strict: false,
|
|
219
|
-
};
|
|
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}.`);
|
|
220
219
|
}
|
|
221
|
-
//
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
};
|
|
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" }] }`);
|
|
244
224
|
}
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
}
|
|
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" }] }`);
|
|
270
232
|
}
|
|
271
|
-
//
|
|
233
|
+
// Ensure each contact has objectType: "CONTACT"
|
|
272
234
|
return {
|
|
273
|
-
|
|
274
|
-
|
|
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,
|
|
235
|
+
objectType: 'CONTACT',
|
|
236
|
+
email: value.email,
|
|
295
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
|
+
}
|
|
296
263
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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;
|
|
302
286
|
}
|
|
303
|
-
else if (typeof
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
catch {
|
|
308
|
-
options = parsedValue.split(',').map((s) => s.trim());
|
|
309
|
-
}
|
|
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);
|
|
310
290
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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;
|
|
318
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;
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
// Try to convert to boolean
|
|
367
|
+
boolValue = Boolean(cellValue);
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
columnId: Number(colIdNum),
|
|
371
|
+
value: boolValue,
|
|
372
|
+
};
|
|
319
373
|
}
|
|
320
374
|
// For regular columns, use value (use cleaned cellValue)
|
|
375
|
+
// Ensure columnId is a number (not string) for the API
|
|
321
376
|
return {
|
|
322
|
-
columnId: colIdNum,
|
|
377
|
+
columnId: Number(colIdNum),
|
|
323
378
|
value: cellValue,
|
|
324
379
|
};
|
|
325
380
|
});
|
|
@@ -575,15 +630,14 @@ async function executeRowOperation(operation, itemIndex) {
|
|
|
575
630
|
catch (error) {
|
|
576
631
|
// If we can't get columns, continue without column type info
|
|
577
632
|
}
|
|
578
|
-
//
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
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) => {
|
|
587
641
|
let columnId;
|
|
588
642
|
if (cell.columnInputMethod === 'id') {
|
|
589
643
|
columnId = cell.columnIdManual || '';
|
|
@@ -591,7 +645,11 @@ async function executeRowOperation(operation, itemIndex) {
|
|
|
591
645
|
else {
|
|
592
646
|
columnId = cell.columnId || '';
|
|
593
647
|
}
|
|
648
|
+
// Parse columnId - keep as number for API, but ensure it's valid
|
|
594
649
|
const colIdNum = parseInt(columnId, 10);
|
|
650
|
+
if (isNaN(colIdNum)) {
|
|
651
|
+
throw new Error(`Invalid column ID: ${columnId}`);
|
|
652
|
+
}
|
|
595
653
|
const column = columnsMap.get(colIdNum);
|
|
596
654
|
const columnType = column?.type;
|
|
597
655
|
// Clean up the value - remove surrounding quotes if present
|
|
@@ -605,10 +663,9 @@ async function executeRowOperation(operation, itemIndex) {
|
|
|
605
663
|
}
|
|
606
664
|
}
|
|
607
665
|
// Handle MULTI_CONTACT_LIST and MULTI_PICKLIST columns with objectValue
|
|
608
|
-
//
|
|
609
|
-
if (columnType === 'MULTI_CONTACT_LIST'
|
|
610
|
-
|
|
611
|
-
// Try to parse the value as JSON if it's a string
|
|
666
|
+
// User must provide a JSON block that will be used directly
|
|
667
|
+
if (columnType === 'MULTI_CONTACT_LIST') {
|
|
668
|
+
// Parse the value as JSON
|
|
612
669
|
let parsedValue;
|
|
613
670
|
try {
|
|
614
671
|
if (typeof cellValue === 'string') {
|
|
@@ -619,119 +676,172 @@ async function executeRowOperation(operation, itemIndex) {
|
|
|
619
676
|
}
|
|
620
677
|
}
|
|
621
678
|
catch (error) {
|
|
622
|
-
|
|
623
|
-
|
|
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)}`);
|
|
624
681
|
}
|
|
625
|
-
//
|
|
626
|
-
if (
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
objectValue: parsedValue,
|
|
630
|
-
strict: false,
|
|
631
|
-
};
|
|
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}.`);
|
|
632
686
|
}
|
|
633
|
-
//
|
|
634
|
-
if (
|
|
635
|
-
|
|
636
|
-
|
|
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
|
-
};
|
|
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" }] }`);
|
|
656
691
|
}
|
|
657
|
-
//
|
|
658
|
-
//
|
|
659
|
-
|
|
660
|
-
(
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
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
|
-
}
|
|
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" }] }`);
|
|
682
699
|
}
|
|
683
|
-
//
|
|
700
|
+
// Ensure each contact has objectType: "CONTACT"
|
|
684
701
|
return {
|
|
685
|
-
|
|
686
|
-
|
|
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,
|
|
702
|
+
objectType: 'CONTACT',
|
|
703
|
+
email: value.email,
|
|
707
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
|
+
}
|
|
708
730
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
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;
|
|
714
753
|
}
|
|
715
|
-
else if (typeof
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
}
|
|
719
|
-
catch {
|
|
720
|
-
options = parsedValue.split(',').map((s) => s.trim());
|
|
721
|
-
}
|
|
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);
|
|
722
757
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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;
|
|
730
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;
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
// Try to convert to boolean
|
|
834
|
+
boolValue = Boolean(cellValue);
|
|
835
|
+
}
|
|
836
|
+
return {
|
|
837
|
+
columnId: Number(colIdNum),
|
|
838
|
+
value: boolValue,
|
|
839
|
+
};
|
|
731
840
|
}
|
|
732
841
|
// For regular columns, use value (use cleaned cellValue)
|
|
842
|
+
// Ensure columnId is a number (not string) for the API
|
|
733
843
|
return {
|
|
734
|
-
columnId: colIdNum,
|
|
844
|
+
columnId: Number(colIdNum),
|
|
735
845
|
value: cellValue,
|
|
736
846
|
};
|
|
737
847
|
});
|