@hustleops/n8n-nodes-hustleops 0.1.5
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/LICENSE +21 -0
- package/README.md +293 -0
- package/dist/credentials/HustleOpsApi.credentials.d.ts +12 -0
- package/dist/credentials/HustleOpsApi.credentials.js +53 -0
- package/dist/nodes/HustleOps/GenericFunctions.d.ts +38 -0
- package/dist/nodes/HustleOps/GenericFunctions.js +366 -0
- package/dist/nodes/HustleOps/HustleOps.node.d.ts +29 -0
- package/dist/nodes/HustleOps/HustleOps.node.js +1594 -0
- package/dist/nodes/HustleOps/HustleOps.node.json +18 -0
- package/dist/nodes/HustleOps/commentDefinitions.d.ts +21 -0
- package/dist/nodes/HustleOps/commentDefinitions.js +254 -0
- package/dist/nodes/HustleOps/constants.d.ts +1 -0
- package/dist/nodes/HustleOps/constants.js +4 -0
- package/dist/nodes/HustleOps/customFieldDefinitions.d.ts +36 -0
- package/dist/nodes/HustleOps/customFieldDefinitions.js +447 -0
- package/dist/nodes/HustleOps/hustleops.dark.svg +7 -0
- package/dist/nodes/HustleOps/hustleops.svg +7 -0
- package/dist/nodes/HustleOps/payloadInputMode.d.ts +18 -0
- package/dist/nodes/HustleOps/payloadInputMode.js +56 -0
- package/dist/nodes/HustleOps/resourceDefinitions.d.ts +48 -0
- package/dist/nodes/HustleOps/resourceDefinitions.js +629 -0
- package/dist/nodes/HustleOps/structuredCoreFields.d.ts +7 -0
- package/dist/nodes/HustleOps/structuredCoreFields.js +29 -0
- package/dist/nodes/HustleOps/tagDefinitions.d.ts +15 -0
- package/dist/nodes/HustleOps/tagDefinitions.js +215 -0
- package/dist/package.json +69 -0
- package/package.json +69 -0
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CUSTOM_FIELD_ENTITY_TYPE_OPTIONS = exports.CUSTOM_FIELD_OPERATION_OPTIONS = exports.CUSTOM_FIELD_RESOURCE_OPTION = void 0;
|
|
4
|
+
exports.sanitizeCustomFieldGroupBody = sanitizeCustomFieldGroupBody;
|
|
5
|
+
exports.sanitizeCustomFieldDefinitionBody = sanitizeCustomFieldDefinitionBody;
|
|
6
|
+
exports.sanitizeCustomFieldDefinitionBulkUpdate = sanitizeCustomFieldDefinitionBulkUpdate;
|
|
7
|
+
exports.sanitizeCustomFieldDefinitionBulkDelete = sanitizeCustomFieldDefinitionBulkDelete;
|
|
8
|
+
exports.parseCustomFieldEntityIds = parseCustomFieldEntityIds;
|
|
9
|
+
exports.parseCustomFieldValues = parseCustomFieldValues;
|
|
10
|
+
exports.customFieldEntityScope = customFieldEntityScope;
|
|
11
|
+
exports.customFieldBatchBody = customFieldBatchBody;
|
|
12
|
+
exports.extractAttachedCustomFieldValues = extractAttachedCustomFieldValues;
|
|
13
|
+
exports.mergeCustomFieldValues = mergeCustomFieldValues;
|
|
14
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
15
|
+
const GenericFunctions_1 = require("./GenericFunctions");
|
|
16
|
+
const resourceDefinitions_1 = require("./resourceDefinitions");
|
|
17
|
+
const CUSTOM_FIELD_ENTITY_TYPES = Object.values(resourceDefinitions_1.CORE_RESOURCE_DEFINITIONS).map((definition) => definition.entityType);
|
|
18
|
+
const CUSTOM_FIELD_ENTITY_TYPE_SET = new Set(CUSTOM_FIELD_ENTITY_TYPES);
|
|
19
|
+
const CUSTOM_FIELD_TYPES = new Set([
|
|
20
|
+
'TEXT',
|
|
21
|
+
'NUMBER',
|
|
22
|
+
'BOOLEAN',
|
|
23
|
+
'SELECT',
|
|
24
|
+
'MULTI_SELECT',
|
|
25
|
+
'DATE',
|
|
26
|
+
'URL',
|
|
27
|
+
]);
|
|
28
|
+
const GROUP_FIELDS = new Set(['name', 'description', 'sortOrder']);
|
|
29
|
+
const CREATE_DEFINITION_FIELDS = new Set([
|
|
30
|
+
'name',
|
|
31
|
+
'description',
|
|
32
|
+
'fieldType',
|
|
33
|
+
'isRequired',
|
|
34
|
+
'defaultValue',
|
|
35
|
+
'options',
|
|
36
|
+
'entityTypes',
|
|
37
|
+
'groupId',
|
|
38
|
+
'sortOrder',
|
|
39
|
+
]);
|
|
40
|
+
const UPDATE_DEFINITION_FIELDS = new Set([
|
|
41
|
+
'name',
|
|
42
|
+
'description',
|
|
43
|
+
'isRequired',
|
|
44
|
+
'defaultValue',
|
|
45
|
+
'options',
|
|
46
|
+
'entityTypes',
|
|
47
|
+
'groupId',
|
|
48
|
+
'sortOrder',
|
|
49
|
+
]);
|
|
50
|
+
const BULK_UPDATE_DEFINITION_FIELDS = new Set(['ids', 'isRequired', 'groupId']);
|
|
51
|
+
const BULK_DELETE_DEFINITION_FIELDS = new Set(['ids', 'force']);
|
|
52
|
+
const MAX_BULK_IDS = 100;
|
|
53
|
+
const CUSTOM_FIELD_DEFINITION_NODE = {
|
|
54
|
+
id: 'hustleops-custom-field-definitions',
|
|
55
|
+
name: 'HustleOps',
|
|
56
|
+
type: 'hustleOps',
|
|
57
|
+
typeVersion: 1,
|
|
58
|
+
position: [0, 0],
|
|
59
|
+
parameters: {},
|
|
60
|
+
};
|
|
61
|
+
exports.CUSTOM_FIELD_RESOURCE_OPTION = {
|
|
62
|
+
name: 'Custom Field',
|
|
63
|
+
value: 'customField',
|
|
64
|
+
description: 'Custom field groups, definitions, and entity values',
|
|
65
|
+
};
|
|
66
|
+
exports.CUSTOM_FIELD_OPERATION_OPTIONS = [
|
|
67
|
+
{
|
|
68
|
+
name: 'List Groups',
|
|
69
|
+
value: 'listGroups',
|
|
70
|
+
description: 'List custom field groups',
|
|
71
|
+
action: 'List custom field groups',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'Create Group',
|
|
75
|
+
value: 'createGroup',
|
|
76
|
+
description: 'Create a custom field group. Requires Admin.',
|
|
77
|
+
action: 'Create a custom field group',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'Update Group',
|
|
81
|
+
value: 'updateGroup',
|
|
82
|
+
description: 'Update a custom field group. Requires Admin.',
|
|
83
|
+
action: 'Update a custom field group',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'Delete Group',
|
|
87
|
+
value: 'deleteGroup',
|
|
88
|
+
description: 'Delete a custom field group. Requires Admin.',
|
|
89
|
+
action: 'Delete a custom field group',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'List Definitions',
|
|
93
|
+
value: 'listDefinitions',
|
|
94
|
+
description: 'List custom field definitions',
|
|
95
|
+
action: 'List custom field definitions',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'Search Definitions',
|
|
99
|
+
value: 'searchDefinitions',
|
|
100
|
+
description: 'Search custom field definitions. Requires Admin.',
|
|
101
|
+
action: 'Search custom field definitions',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'Create Definition',
|
|
105
|
+
value: 'createDefinition',
|
|
106
|
+
description: 'Create a custom field definition. Requires Admin.',
|
|
107
|
+
action: 'Create a custom field definition',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'Update Definition',
|
|
111
|
+
value: 'updateDefinition',
|
|
112
|
+
description: 'Update a custom field definition except immutable fieldType. Requires Admin.',
|
|
113
|
+
action: 'Update a custom field definition',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'Bulk Update Definitions',
|
|
117
|
+
value: 'bulkUpdateDefinitions',
|
|
118
|
+
description: 'Bulk update custom field definition required state or group. Requires Admin.',
|
|
119
|
+
action: 'Bulk update custom field definitions',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'Delete Definition',
|
|
123
|
+
value: 'deleteDefinition',
|
|
124
|
+
description: 'Delete one custom field definition. Requires Admin.',
|
|
125
|
+
action: 'Delete a custom field definition',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'Bulk Delete Definitions',
|
|
129
|
+
value: 'bulkDeleteDefinitions',
|
|
130
|
+
description: 'Delete multiple custom field definitions. Requires Admin.',
|
|
131
|
+
action: 'Bulk delete custom field definitions',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'Get Values',
|
|
135
|
+
value: 'getValues',
|
|
136
|
+
description: 'Get custom field values for one entity',
|
|
137
|
+
action: 'Get custom field values',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: 'Get Available',
|
|
141
|
+
value: 'getAvailable',
|
|
142
|
+
description: 'Get applicable custom field definitions plus values for one entity',
|
|
143
|
+
action: 'Get available custom fields',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'Batch Get Values',
|
|
147
|
+
value: 'batchGetValues',
|
|
148
|
+
description: 'Get custom field values for up to 100 entity IDs',
|
|
149
|
+
action: 'Batch get custom field values',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'Replace Values',
|
|
153
|
+
value: 'replaceValues',
|
|
154
|
+
description: 'Replace the attached custom field value set for one entity',
|
|
155
|
+
action: 'Replace custom field values',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'Update Selected Values Safely',
|
|
159
|
+
value: 'updateSelectedValuesSafely',
|
|
160
|
+
description: 'Read current values, merge selected field changes, and send the complete attached set',
|
|
161
|
+
action: 'Safely update selected custom field values',
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
exports.CUSTOM_FIELD_ENTITY_TYPE_OPTIONS = CUSTOM_FIELD_ENTITY_TYPES.map((entityType) => ({
|
|
165
|
+
name: entityType.charAt(0) + entityType.slice(1).toLowerCase(),
|
|
166
|
+
value: entityType,
|
|
167
|
+
description: `Custom fields for ${entityType.toLowerCase()} entities`,
|
|
168
|
+
}));
|
|
169
|
+
function nodeError(context, message, itemIndex) {
|
|
170
|
+
var _a, _b;
|
|
171
|
+
return new n8n_workflow_1.NodeOperationError((_b = (_a = context.getNode) === null || _a === void 0 ? void 0 : _a.call(context)) !== null && _b !== void 0 ? _b : CUSTOM_FIELD_DEFINITION_NODE, message, {
|
|
172
|
+
itemIndex,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function assertAllowedKeys(context, label, body, allowedFields, itemIndex) {
|
|
176
|
+
for (const key of Object.keys(body)) {
|
|
177
|
+
if (!allowedFields.has(key)) {
|
|
178
|
+
throw nodeError(context, `Unsupported Custom Field ${label} field: ${key}`, itemIndex);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function requireString(context, value, label, itemIndex) {
|
|
183
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
184
|
+
throw nodeError(context, `${label} is required.`, itemIndex);
|
|
185
|
+
}
|
|
186
|
+
return value;
|
|
187
|
+
}
|
|
188
|
+
function optionalString(context, value, label, itemIndex) {
|
|
189
|
+
if (value === undefined || value === null || value === '') {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
if (typeof value !== 'string') {
|
|
193
|
+
throw nodeError(context, `${label} must be a string.`, itemIndex);
|
|
194
|
+
}
|
|
195
|
+
return value;
|
|
196
|
+
}
|
|
197
|
+
function optionalNumber(context, value, label, itemIndex) {
|
|
198
|
+
if (value === undefined || value === null || value === '') {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
202
|
+
throw nodeError(context, `${label} must be a number.`, itemIndex);
|
|
203
|
+
}
|
|
204
|
+
return value;
|
|
205
|
+
}
|
|
206
|
+
function optionalBoolean(context, value, label, itemIndex) {
|
|
207
|
+
if (value === undefined) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
if (typeof value !== 'boolean') {
|
|
211
|
+
throw nodeError(context, `${label} must be a boolean.`, itemIndex);
|
|
212
|
+
}
|
|
213
|
+
return value;
|
|
214
|
+
}
|
|
215
|
+
function requireUuid(context, value, label, itemIndex) {
|
|
216
|
+
try {
|
|
217
|
+
return (0, GenericFunctions_1.assertUuid)(value, label);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
throw nodeError(context, `${label} must be a valid UUID.`, itemIndex);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function optionalUuid(context, value, label, itemIndex) {
|
|
224
|
+
if (value === undefined || value === '') {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
if (value === null) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
return requireUuid(context, value, label, itemIndex);
|
|
231
|
+
}
|
|
232
|
+
function requireEntityType(context, value, itemIndex) {
|
|
233
|
+
if (typeof value !== 'string' || !CUSTOM_FIELD_ENTITY_TYPE_SET.has(value)) {
|
|
234
|
+
throw nodeError(context, `Custom field entity type must be one of: ${CUSTOM_FIELD_ENTITY_TYPES.join(', ')}.`, itemIndex);
|
|
235
|
+
}
|
|
236
|
+
return value;
|
|
237
|
+
}
|
|
238
|
+
function entityTypes(context, value, required, itemIndex) {
|
|
239
|
+
if (value === undefined || value === null) {
|
|
240
|
+
if (required) {
|
|
241
|
+
throw nodeError(context, 'Custom field definition entityTypes is required.', itemIndex);
|
|
242
|
+
}
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
246
|
+
throw nodeError(context, 'Custom field definition entityTypes must contain at least one entity type.', itemIndex);
|
|
247
|
+
}
|
|
248
|
+
return value.map((entityType) => requireEntityType(context, entityType, itemIndex));
|
|
249
|
+
}
|
|
250
|
+
function definitionOptions(context, value, fieldType, required, itemIndex) {
|
|
251
|
+
if (value === undefined || value === null) {
|
|
252
|
+
if (required && (fieldType === 'SELECT' || fieldType === 'MULTI_SELECT')) {
|
|
253
|
+
throw nodeError(context, 'Custom field definition options are required for SELECT and MULTI_SELECT.', itemIndex);
|
|
254
|
+
}
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
if (!Array.isArray(value) || value.some((option) => typeof option !== 'string')) {
|
|
258
|
+
throw nodeError(context, 'Custom field definition options must be an array of strings.', itemIndex);
|
|
259
|
+
}
|
|
260
|
+
return value;
|
|
261
|
+
}
|
|
262
|
+
function definitionIds(context, value, label, itemIndex) {
|
|
263
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
264
|
+
throw nodeError(context, `${label} must contain at least one ID.`, itemIndex);
|
|
265
|
+
}
|
|
266
|
+
if (value.length > MAX_BULK_IDS) {
|
|
267
|
+
throw nodeError(context, `${label} cannot contain more than ${MAX_BULK_IDS} IDs.`, itemIndex);
|
|
268
|
+
}
|
|
269
|
+
return value.map((id) => requireUuid(context, id, 'Custom field definition ID', itemIndex));
|
|
270
|
+
}
|
|
271
|
+
function requireFieldType(context, value, itemIndex) {
|
|
272
|
+
if (typeof value !== 'string' || !CUSTOM_FIELD_TYPES.has(value)) {
|
|
273
|
+
throw nodeError(context, 'Custom field definition fieldType is required.', itemIndex);
|
|
274
|
+
}
|
|
275
|
+
return value;
|
|
276
|
+
}
|
|
277
|
+
function sanitizeCustomFieldGroupBody(context, body, required, itemIndex) {
|
|
278
|
+
assertAllowedKeys(context, 'group', body, GROUP_FIELDS, itemIndex);
|
|
279
|
+
const sanitized = (0, GenericFunctions_1.compactObject)({
|
|
280
|
+
name: required
|
|
281
|
+
? requireString(context, body.name, 'Custom field group name', itemIndex)
|
|
282
|
+
: optionalString(context, body.name, 'Custom field group name', itemIndex),
|
|
283
|
+
description: optionalString(context, body.description, 'Custom field group description', itemIndex),
|
|
284
|
+
sortOrder: optionalNumber(context, body.sortOrder, 'Custom field group sortOrder', itemIndex),
|
|
285
|
+
});
|
|
286
|
+
if (!required && Object.keys(sanitized).length === 0) {
|
|
287
|
+
throw nodeError(context, 'Custom field group update body must include at least one field.', itemIndex);
|
|
288
|
+
}
|
|
289
|
+
return sanitized;
|
|
290
|
+
}
|
|
291
|
+
function sanitizeCustomFieldDefinitionBody(context, body, operation, itemIndex) {
|
|
292
|
+
if (operation === 'update' && Object.prototype.hasOwnProperty.call(body, 'fieldType')) {
|
|
293
|
+
throw nodeError(context, 'Custom field definition fieldType is immutable.', itemIndex);
|
|
294
|
+
}
|
|
295
|
+
const allowedFields = operation === 'create' ? CREATE_DEFINITION_FIELDS : UPDATE_DEFINITION_FIELDS;
|
|
296
|
+
assertAllowedKeys(context, 'definition', body, allowedFields, itemIndex);
|
|
297
|
+
const fieldType = operation === 'create' ? requireFieldType(context, body.fieldType, itemIndex) : undefined;
|
|
298
|
+
const sanitized = (0, GenericFunctions_1.compactObject)({
|
|
299
|
+
name: operation === 'create'
|
|
300
|
+
? requireString(context, body.name, 'Custom field definition name', itemIndex)
|
|
301
|
+
: optionalString(context, body.name, 'Custom field definition name', itemIndex),
|
|
302
|
+
description: optionalString(context, body.description, 'Custom field definition description', itemIndex),
|
|
303
|
+
fieldType,
|
|
304
|
+
isRequired: optionalBoolean(context, body.isRequired, 'Custom field definition isRequired', itemIndex),
|
|
305
|
+
defaultValue: optionalString(context, body.defaultValue, 'Custom field definition defaultValue', itemIndex),
|
|
306
|
+
options: definitionOptions(context, body.options, fieldType, operation === 'create', itemIndex),
|
|
307
|
+
entityTypes: entityTypes(context, body.entityTypes, operation === 'create', itemIndex),
|
|
308
|
+
groupId: optionalUuid(context, body.groupId, 'Custom field group ID', itemIndex),
|
|
309
|
+
sortOrder: optionalNumber(context, body.sortOrder, 'Custom field definition sortOrder', itemIndex),
|
|
310
|
+
});
|
|
311
|
+
if (operation === 'update' && Object.keys(sanitized).length === 0) {
|
|
312
|
+
throw nodeError(context, 'Custom field definition update body must include at least one field.', itemIndex);
|
|
313
|
+
}
|
|
314
|
+
return sanitized;
|
|
315
|
+
}
|
|
316
|
+
function sanitizeCustomFieldDefinitionBulkUpdate(context, body, itemIndex) {
|
|
317
|
+
assertAllowedKeys(context, 'definition bulk update', body, BULK_UPDATE_DEFINITION_FIELDS, itemIndex);
|
|
318
|
+
return (0, GenericFunctions_1.compactObject)({
|
|
319
|
+
ids: definitionIds(context, body.ids, 'Custom field definition bulk update IDs', itemIndex),
|
|
320
|
+
isRequired: optionalBoolean(context, body.isRequired, 'Custom field definition isRequired', itemIndex),
|
|
321
|
+
groupId: optionalUuid(context, body.groupId, 'Custom field group ID', itemIndex),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function sanitizeCustomFieldDefinitionBulkDelete(context, body, itemIndex) {
|
|
325
|
+
assertAllowedKeys(context, 'definition bulk delete', body, BULK_DELETE_DEFINITION_FIELDS, itemIndex);
|
|
326
|
+
return (0, GenericFunctions_1.compactObject)({
|
|
327
|
+
ids: definitionIds(context, body.ids, 'Custom field definition bulk delete IDs', itemIndex),
|
|
328
|
+
force: optionalBoolean(context, body.force, 'Custom field definition bulk delete force', itemIndex),
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
function parseCustomFieldEntityIds(context, value, itemIndex) {
|
|
332
|
+
const ids = (0, GenericFunctions_1.parseJsonArray)(context, value, 'Entity IDs', itemIndex);
|
|
333
|
+
if (ids.length === 0) {
|
|
334
|
+
throw nodeError(context, 'Custom field batch requires at least one entity ID.', itemIndex);
|
|
335
|
+
}
|
|
336
|
+
if (ids.length > MAX_BULK_IDS) {
|
|
337
|
+
throw nodeError(context, `Custom field batch cannot contain more than ${MAX_BULK_IDS} IDs.`, itemIndex);
|
|
338
|
+
}
|
|
339
|
+
return ids.map((id) => requireUuid(context, id, 'Custom field entity ID', itemIndex));
|
|
340
|
+
}
|
|
341
|
+
function validateFieldValueByType(context, fieldType, value, itemIndex) {
|
|
342
|
+
if (fieldType === 'BOOLEAN' && value !== 'true' && value !== 'false') {
|
|
343
|
+
throw nodeError(context, 'BOOLEAN custom field values must be "true" or "false".', itemIndex);
|
|
344
|
+
}
|
|
345
|
+
if (fieldType === 'MULTI_SELECT') {
|
|
346
|
+
try {
|
|
347
|
+
const parsed = JSON.parse(value);
|
|
348
|
+
if (!Array.isArray(parsed) || parsed.some((entry) => typeof entry !== 'string')) {
|
|
349
|
+
throw new Error('not a string array');
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
throw nodeError(context, 'MULTI_SELECT custom field values must be a JSON array string.', itemIndex);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (fieldType === 'NUMBER' && (value.trim() === '' || Number.isNaN(Number(value)))) {
|
|
357
|
+
throw nodeError(context, 'NUMBER custom field values must parse as a number.', itemIndex);
|
|
358
|
+
}
|
|
359
|
+
if (fieldType === 'DATE' && Number.isNaN(Date.parse(value))) {
|
|
360
|
+
throw nodeError(context, 'DATE custom field values must parse as an ISO date.', itemIndex);
|
|
361
|
+
}
|
|
362
|
+
if (fieldType === 'URL') {
|
|
363
|
+
try {
|
|
364
|
+
new URL(value);
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
throw nodeError(context, 'URL custom field values must be valid URLs.', itemIndex);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function serializeCustomFieldValue(context, fieldType, value, itemIndex) {
|
|
372
|
+
if (value === null) {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
if (Array.isArray(value)) {
|
|
376
|
+
if (fieldType && fieldType !== 'MULTI_SELECT') {
|
|
377
|
+
throw nodeError(context, 'Only MULTI_SELECT custom fields accept array values.', itemIndex);
|
|
378
|
+
}
|
|
379
|
+
if (value.some((entry) => typeof entry !== 'string')) {
|
|
380
|
+
throw nodeError(context, 'MULTI_SELECT custom field arrays must contain only strings.', itemIndex);
|
|
381
|
+
}
|
|
382
|
+
return JSON.stringify(value);
|
|
383
|
+
}
|
|
384
|
+
if (fieldType === 'NUMBER' && typeof value === 'number' && Number.isFinite(value)) {
|
|
385
|
+
return String(value);
|
|
386
|
+
}
|
|
387
|
+
if (typeof value !== 'string') {
|
|
388
|
+
throw nodeError(context, 'Custom field values must be strings or null.', itemIndex);
|
|
389
|
+
}
|
|
390
|
+
validateFieldValueByType(context, fieldType, value, itemIndex);
|
|
391
|
+
return value;
|
|
392
|
+
}
|
|
393
|
+
function parseCustomFieldValues(context, value, itemIndex) {
|
|
394
|
+
return (0, GenericFunctions_1.parseJsonArray)(context, value, 'Custom Field Values', itemIndex).map((entry) => {
|
|
395
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
396
|
+
throw nodeError(context, 'Each custom field value must be an object.', itemIndex);
|
|
397
|
+
}
|
|
398
|
+
const valueEntry = entry;
|
|
399
|
+
const fieldType = valueEntry.fieldType === undefined
|
|
400
|
+
? undefined
|
|
401
|
+
: requireFieldType(context, valueEntry.fieldType, itemIndex);
|
|
402
|
+
return {
|
|
403
|
+
fieldId: requireUuid(context, valueEntry.fieldId, 'Custom field definition ID', itemIndex),
|
|
404
|
+
value: serializeCustomFieldValue(context, fieldType, valueEntry.value, itemIndex),
|
|
405
|
+
};
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
function customFieldEntityScope(context, input, itemIndex) {
|
|
409
|
+
return {
|
|
410
|
+
entityType: requireEntityType(context, input.entityType, itemIndex),
|
|
411
|
+
entityId: requireUuid(context, input.entityId, 'Custom field entity ID', itemIndex),
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function customFieldBatchBody(context, input, itemIndex) {
|
|
415
|
+
return {
|
|
416
|
+
entityType: requireEntityType(context, input.entityType, itemIndex),
|
|
417
|
+
entityIds: parseCustomFieldEntityIds(context, input.entityIds, itemIndex),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function extractAttachedCustomFieldValues(value) {
|
|
421
|
+
const container = value;
|
|
422
|
+
const rows = Array.isArray(value)
|
|
423
|
+
? value
|
|
424
|
+
: Array.isArray(container === null || container === void 0 ? void 0 : container.values)
|
|
425
|
+
? container.values
|
|
426
|
+
: Array.isArray(container === null || container === void 0 ? void 0 : container.data)
|
|
427
|
+
? container.data
|
|
428
|
+
: [];
|
|
429
|
+
return rows
|
|
430
|
+
.filter((row) => !!row && typeof row === 'object' && !Array.isArray(row))
|
|
431
|
+
.filter((row) => typeof row.fieldId === 'string' && Object.prototype.hasOwnProperty.call(row, 'value'))
|
|
432
|
+
.map((row) => ({
|
|
433
|
+
fieldId: row.fieldId,
|
|
434
|
+
value: row.value === null ? null : String(row.value),
|
|
435
|
+
}));
|
|
436
|
+
}
|
|
437
|
+
function mergeCustomFieldValues(existing, updates) {
|
|
438
|
+
const updatesByFieldId = new Map(updates.map((value) => [value.fieldId, value]));
|
|
439
|
+
const merged = existing.map((value) => { var _a; return (_a = updatesByFieldId.get(value.fieldId)) !== null && _a !== void 0 ? _a : value; });
|
|
440
|
+
const existingIds = new Set(existing.map((value) => value.fieldId));
|
|
441
|
+
for (const update of updates) {
|
|
442
|
+
if (!existingIds.has(update.fieldId)) {
|
|
443
|
+
merged.push(update);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return merged;
|
|
447
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<path d="M16 3C16 3 2 6 2 6C2 6 1.5 17 7 22.5C10 25.5 16 29 16 29C16 29 22 25.5 25 22.5C30.5 17 30 6 30 6C30 6 16 3 16 3Z" fill="#FEED01" stroke="#E5D600" stroke-width="0.5"/>
|
|
3
|
+
<path d="M16 4.5C16 4.5 4 7 4 7C4 7 3.5 16.5 8.5 21.5C11 24 16 27 16 27C16 27 21 24 23.5 21.5C28.5 16.5 28 7 28 7C28 7 16 4.5 16 4.5Z" fill="#FEED01"/>
|
|
4
|
+
<path d="M16 4.5C16 4.5 4 7 4 7C4 7 3.5 13 6 17.5C6 17.5 5 9 16 6.5C16 6.5 16 4.5 16 4.5Z" fill="#FFF84D" opacity="0.4"/>
|
|
5
|
+
<path d="M18.5 7L11 17H15L13 25L22 14.5H17.5L18.5 7Z" fill="#1a1a1a"/>
|
|
6
|
+
<path d="M17.8 8.5L12.5 16H15.5L14 23L20.5 15.5H17L17.8 8.5Z" fill="#333" opacity="0.3"/>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<path d="M16 3C16 3 2 6 2 6C2 6 1.5 17 7 22.5C10 25.5 16 29 16 29C16 29 22 25.5 25 22.5C30.5 17 30 6 30 6C30 6 16 3 16 3Z" fill="#FEED01" stroke="#E5D600" stroke-width="0.5"/>
|
|
3
|
+
<path d="M16 4.5C16 4.5 4 7 4 7C4 7 3.5 16.5 8.5 21.5C11 24 16 27 16 27C16 27 21 24 23.5 21.5C28.5 16.5 28 7 28 7C28 7 16 4.5 16 4.5Z" fill="#FEED01"/>
|
|
4
|
+
<path d="M16 4.5C16 4.5 4 7 4 7C4 7 3.5 13 6 17.5C6 17.5 5 9 16 6.5C16 6.5 16 4.5 16 4.5Z" fill="#FFF84D" opacity="0.4"/>
|
|
5
|
+
<path d="M18.5 7L11 17H15L13 25L22 14.5H17.5L18.5 7Z" fill="#1a1a1a"/>
|
|
6
|
+
<path d="M17.8 8.5L12.5 16H15.5L14 23L20.5 15.5H17L17.8 8.5Z" fill="#333" opacity="0.3"/>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare const PAYLOAD_INPUT_MODE_PARAMETER = "payloadInputMode";
|
|
3
|
+
export declare const PAYLOAD_MODE_INDIVIDUAL_FIELDS = "individualFields";
|
|
4
|
+
export declare const PAYLOAD_MODE_JSON_OBJECT = "jsonObject";
|
|
5
|
+
export type PayloadInputMode = typeof PAYLOAD_MODE_INDIVIDUAL_FIELDS | typeof PAYLOAD_MODE_JSON_OBJECT;
|
|
6
|
+
export declare const PAYLOAD_INPUT_MODE_OPTIONS: readonly [{
|
|
7
|
+
readonly name: "Individual Fields";
|
|
8
|
+
readonly value: "individualFields";
|
|
9
|
+
readonly description: "Build the request body from visible node fields";
|
|
10
|
+
}, {
|
|
11
|
+
readonly name: "JSON Object";
|
|
12
|
+
readonly value: "jsonObject";
|
|
13
|
+
readonly description: "Submit the JSON object as the complete request body";
|
|
14
|
+
}];
|
|
15
|
+
export declare const RETIRED_PAYLOAD_PARAMETERS: readonly [string, "body", string, string, string, string, string, string, "entityIds"];
|
|
16
|
+
export declare function payloadJsonObjectParameterName(resource: string, operation: string): string;
|
|
17
|
+
export declare function payloadModeDisplayOptions(resources: readonly string[], operations: readonly string[]): INodeProperties['displayOptions'];
|
|
18
|
+
export declare function modeDisplayOptions(resources: readonly string[], operations: readonly string[], mode: PayloadInputMode): INodeProperties['displayOptions'];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RETIRED_PAYLOAD_PARAMETERS = exports.PAYLOAD_INPUT_MODE_OPTIONS = exports.PAYLOAD_MODE_JSON_OBJECT = exports.PAYLOAD_MODE_INDIVIDUAL_FIELDS = exports.PAYLOAD_INPUT_MODE_PARAMETER = void 0;
|
|
4
|
+
exports.payloadJsonObjectParameterName = payloadJsonObjectParameterName;
|
|
5
|
+
exports.payloadModeDisplayOptions = payloadModeDisplayOptions;
|
|
6
|
+
exports.modeDisplayOptions = modeDisplayOptions;
|
|
7
|
+
exports.PAYLOAD_INPUT_MODE_PARAMETER = 'payloadInputMode';
|
|
8
|
+
exports.PAYLOAD_MODE_INDIVIDUAL_FIELDS = 'individualFields';
|
|
9
|
+
exports.PAYLOAD_MODE_JSON_OBJECT = 'jsonObject';
|
|
10
|
+
exports.PAYLOAD_INPUT_MODE_OPTIONS = [
|
|
11
|
+
{
|
|
12
|
+
name: 'Individual Fields',
|
|
13
|
+
value: exports.PAYLOAD_MODE_INDIVIDUAL_FIELDS,
|
|
14
|
+
description: 'Build the request body from visible node fields',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'JSON Object',
|
|
18
|
+
value: exports.PAYLOAD_MODE_JSON_OBJECT,
|
|
19
|
+
description: 'Submit the JSON object as the complete request body',
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
const retiredParameter = (...parts) => parts.join('');
|
|
23
|
+
exports.RETIRED_PAYLOAD_PARAMETERS = [
|
|
24
|
+
retiredParameter('additional', 'Json'),
|
|
25
|
+
'body',
|
|
26
|
+
retiredParameter('search', 'Body'),
|
|
27
|
+
retiredParameter('tag', 'Body'),
|
|
28
|
+
retiredParameter('tag', 'Values'),
|
|
29
|
+
retiredParameter('comment', 'Body'),
|
|
30
|
+
retiredParameter('customField', 'Body'),
|
|
31
|
+
retiredParameter('customField', 'Values'),
|
|
32
|
+
'entityIds',
|
|
33
|
+
];
|
|
34
|
+
function toPascalCase(value) {
|
|
35
|
+
return value.replace(/(^|[-_\s]+)([a-z0-9])/g, (_match, _separator, character) => character.toUpperCase());
|
|
36
|
+
}
|
|
37
|
+
function payloadJsonObjectParameterName(resource, operation) {
|
|
38
|
+
return `payload${toPascalCase(resource)}${toPascalCase(operation)}JsonObject`;
|
|
39
|
+
}
|
|
40
|
+
function payloadModeDisplayOptions(resources, operations) {
|
|
41
|
+
return {
|
|
42
|
+
show: {
|
|
43
|
+
resource: [...resources],
|
|
44
|
+
operation: [...operations],
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function modeDisplayOptions(resources, operations, mode) {
|
|
49
|
+
return {
|
|
50
|
+
show: {
|
|
51
|
+
resource: [...resources],
|
|
52
|
+
operation: [...operations],
|
|
53
|
+
[exports.PAYLOAD_INPUT_MODE_PARAMETER]: [mode],
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { IDataObject } from 'n8n-workflow';
|
|
2
|
+
export type CoreResource = 'alert' | 'incident' | 'observable' | 'knowledge';
|
|
3
|
+
export type DtoOperation = 'create' | 'update';
|
|
4
|
+
export type FieldSpec = {
|
|
5
|
+
name: string;
|
|
6
|
+
type: 'string' | 'number' | 'boolean' | 'uuid' | 'uuid[]' | 'enum' | 'date-time' | 'url' | 'tags';
|
|
7
|
+
requiredForCreate?: boolean;
|
|
8
|
+
allowedValues?: readonly string[];
|
|
9
|
+
picklistDomain?: string;
|
|
10
|
+
picklistValueTransform?: 'uppercase';
|
|
11
|
+
maxLength?: number;
|
|
12
|
+
nonEmpty?: boolean;
|
|
13
|
+
pattern?: RegExp;
|
|
14
|
+
patternDescription?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
};
|
|
17
|
+
export type CoreResourceDefinition = {
|
|
18
|
+
resource: CoreResource;
|
|
19
|
+
displayName: string;
|
|
20
|
+
description: string;
|
|
21
|
+
entityType: 'ALERT' | 'INCIDENT' | 'OBSERVABLE' | 'KNOWLEDGE';
|
|
22
|
+
path: string;
|
|
23
|
+
defaultSortBy: string;
|
|
24
|
+
defaultSortOrder: 'asc' | 'desc';
|
|
25
|
+
searchFields: readonly string[];
|
|
26
|
+
sortFields: readonly string[];
|
|
27
|
+
createFields: readonly string[];
|
|
28
|
+
updateFields: readonly string[];
|
|
29
|
+
requiredCreateFields: readonly string[];
|
|
30
|
+
fieldSpecs: Readonly<Record<string, FieldSpec>>;
|
|
31
|
+
};
|
|
32
|
+
export type SearchRequestDefinition = {
|
|
33
|
+
displayName: string;
|
|
34
|
+
defaultSortBy: string;
|
|
35
|
+
defaultSortOrder: 'asc' | 'desc';
|
|
36
|
+
searchFields: readonly string[];
|
|
37
|
+
sortFields: readonly string[];
|
|
38
|
+
};
|
|
39
|
+
export declare const CORE_RESOURCE_DEFINITIONS: Record<CoreResource, CoreResourceDefinition>;
|
|
40
|
+
export declare const CORE_RESOURCE_OPTIONS: {
|
|
41
|
+
name: string;
|
|
42
|
+
value: CoreResource;
|
|
43
|
+
description: string;
|
|
44
|
+
}[];
|
|
45
|
+
export declare function getCoreResourceDefinition(resource: string): CoreResourceDefinition;
|
|
46
|
+
export declare function sanitizeDtoBody(definition: CoreResourceDefinition, operation: DtoOperation, body: IDataObject): IDataObject;
|
|
47
|
+
export declare function buildGenericSearchRequest(definition: SearchRequestDefinition, input: IDataObject): IDataObject;
|
|
48
|
+
export declare function buildSearchRequest(definition: CoreResourceDefinition, input: IDataObject): IDataObject;
|