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