@learningsuite/n8n-nodes-learningsuite 1.2.5 → 1.3.4
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 +41 -12
- package/dist/nodes/LearningSuite/LearningSuite.node.d.ts +7 -1
- package/dist/nodes/LearningSuite/LearningSuite.node.js +2 -4
- package/dist/nodes/LearningSuite/LearningSuiteTrigger.node.d.ts +7 -1
- package/dist/nodes/LearningSuite/LearningSuiteTrigger.node.js +15 -1
- package/dist/nodes/LearningSuite/descriptions/ai.properties.js +86 -0
- package/dist/nodes/LearningSuite/descriptions/customFields.properties.js +138 -17
- package/dist/nodes/LearningSuite/descriptions/member.properties.js +1 -1
- package/dist/nodes/LearningSuite/descriptions/module.properties.js +1 -1
- package/dist/nodes/LearningSuite/descriptions/trigger.instant.properties.js +35 -3
- package/dist/nodes/LearningSuite/descriptions/webhook.properties.js +37 -2
- package/dist/nodes/LearningSuite/descriptions/webhook.sampleData.properties.js +26 -0
- package/dist/nodes/LearningSuite/exec.types.d.ts +2 -2
- package/dist/nodes/LearningSuite/execute/ai.handlers.d.ts +3 -0
- package/dist/nodes/LearningSuite/execute/ai.handlers.js +44 -0
- package/dist/nodes/LearningSuite/execute/customFields.handlers.d.ts +1 -0
- package/dist/nodes/LearningSuite/execute/customFields.handlers.js +273 -22
- package/dist/nodes/LearningSuite/execute/user.handlers.js +1 -1
- package/dist/nodes/LearningSuite/execute/webhook.handlers.js +17 -1
- package/dist/nodes/LearningSuite/methods/loadOptions/ai.loadOptions.d.ts +3 -0
- package/dist/nodes/LearningSuite/methods/loadOptions/ai.loadOptions.js +11 -0
- package/dist/nodes/LearningSuite/methods/loadOptions/common.d.ts +1 -0
- package/dist/nodes/LearningSuite/methods/loadOptions/common.js +5 -0
- package/dist/nodes/LearningSuite/methods/loadOptions/customFields.loadOptions.d.ts +8 -4
- package/dist/nodes/LearningSuite/methods/loadOptions/customFields.loadOptions.js +76 -3
- package/dist/nodes/LearningSuite/methods/loadOptions/teamMember.loadOptions.js +1 -1
- package/dist/nodes/LearningSuite/shared/customFields.helpers.js +1 -4
- package/dist/nodes/LearningSuite/shared/customFields.shared.d.ts +1 -0
- package/dist/nodes/LearningSuite/shared/customFields.upload.d.ts +1 -1
- package/dist/nodes/LearningSuite/shared/customFields.upload.js +11 -10
- package/dist/nodes/LearningSuite/shared/request.d.ts +3 -0
- package/dist/nodes/LearningSuite/shared/request.js +18 -0
- package/package.json +1 -1
|
@@ -80,6 +80,7 @@ exports.webhookProperties = [
|
|
|
80
80
|
displayOptions: { show: { resource: ['webhook'], operation: ['createSubscription', 'updateSubscription'] } },
|
|
81
81
|
description: 'Type of event to subscribe to',
|
|
82
82
|
options: [
|
|
83
|
+
{ name: 'Agent Action Executed', value: 'agentAction.executed' },
|
|
83
84
|
{ name: 'Community Post Commented', value: 'communityPost.commented' },
|
|
84
85
|
{ name: 'Community Post Created', value: 'communityPost.created' },
|
|
85
86
|
{ name: 'Community Post Moderated', value: 'communityPost.moderated' },
|
|
@@ -109,6 +110,7 @@ exports.webhookProperties = [
|
|
|
109
110
|
required: true,
|
|
110
111
|
description: 'Type of sample data to retrieve',
|
|
111
112
|
options: [
|
|
113
|
+
{ name: 'Agent Action Executed Events', value: 'agent-action-executed-events' },
|
|
112
114
|
{ name: 'Community Post Commented', value: 'community-post-commented-events' },
|
|
113
115
|
{ name: 'Community Post Created Events', value: 'community-post-created-events' },
|
|
114
116
|
{ name: 'Community Post Moderated Events', value: 'community-post-moderated-events' },
|
|
@@ -126,7 +128,40 @@ exports.webhookProperties = [
|
|
|
126
128
|
{ name: 'User Activation Status Changed Events', value: 'user-activation-status-changed-events' },
|
|
127
129
|
],
|
|
128
130
|
},
|
|
129
|
-
// ======
|
|
131
|
+
// ====== Options per event (identical to instantProperties) ======
|
|
132
|
+
// Agent Action Executed
|
|
133
|
+
{
|
|
134
|
+
displayName: 'Agent Action Executed Options',
|
|
135
|
+
name: 'additionalAgentActionExecuted',
|
|
136
|
+
type: 'collection',
|
|
137
|
+
default: {},
|
|
138
|
+
placeholder: 'Add option',
|
|
139
|
+
displayOptions: {
|
|
140
|
+
show: {
|
|
141
|
+
resource: ['webhook'],
|
|
142
|
+
operation: ['createSubscription', 'updateSubscription'],
|
|
143
|
+
eventType: ['agentAction.executed'],
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
options: [
|
|
147
|
+
{
|
|
148
|
+
displayName: 'Tool Key Name or ID',
|
|
149
|
+
name: 'toolKey',
|
|
150
|
+
type: 'options',
|
|
151
|
+
default: '',
|
|
152
|
+
description: 'Optional: Only trigger for a specific agent action. Leave empty to trigger for all actions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
153
|
+
typeOptions: { loadOptionsMethod: 'ai_getAgentActions' },
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
displayName: 'Agent Name or ID',
|
|
157
|
+
name: 'agentId',
|
|
158
|
+
type: 'options',
|
|
159
|
+
default: '',
|
|
160
|
+
description: 'Optional: Only trigger for actions executed by a specific AI agent. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
161
|
+
typeOptions: { loadOptionsMethod: 'ai_getAiAgents' },
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
},
|
|
130
165
|
// Login Options
|
|
131
166
|
{
|
|
132
167
|
displayName: 'Login Options',
|
|
@@ -532,7 +567,7 @@ exports.webhookProperties = [
|
|
|
532
567
|
},
|
|
533
568
|
],
|
|
534
569
|
},
|
|
535
|
-
// Lesson Completed (
|
|
570
|
+
// Lesson Completed (cascading)
|
|
536
571
|
{
|
|
537
572
|
displayName: 'Lesson Completed Options',
|
|
538
573
|
name: 'additionalLessonCompleted',
|
|
@@ -3,6 +3,32 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.webhookSampleDataProperties = void 0;
|
|
5
5
|
exports.webhookSampleDataProperties = [
|
|
6
|
+
{
|
|
7
|
+
displayName: 'Agent Action Executed Options',
|
|
8
|
+
name: 'additionalAgentActionExecutedSample',
|
|
9
|
+
type: 'collection',
|
|
10
|
+
default: {},
|
|
11
|
+
placeholder: 'Add option',
|
|
12
|
+
displayOptions: { show: { sampleDataType: ['agent-action-executed-events'] } },
|
|
13
|
+
options: [
|
|
14
|
+
{
|
|
15
|
+
displayName: 'Tool Key Name or ID',
|
|
16
|
+
name: 'toolKey',
|
|
17
|
+
type: 'options',
|
|
18
|
+
default: '',
|
|
19
|
+
description: 'Optional tool key to filter agent action executions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
20
|
+
typeOptions: { loadOptionsMethod: 'ai_getAgentActions' },
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
displayName: 'Agent Name or ID',
|
|
24
|
+
name: 'agentId',
|
|
25
|
+
type: 'options',
|
|
26
|
+
default: '',
|
|
27
|
+
description: 'Optional agent ID to filter agent action executions. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
28
|
+
typeOptions: { loadOptionsMethod: 'ai_getAiAgents' },
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
6
32
|
{
|
|
7
33
|
displayName: 'Progress Changed Options',
|
|
8
34
|
name: 'additionalProgressChangedSample',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IDataObject, IExecuteFunctions } from 'n8n-workflow';
|
|
2
|
-
/**
|
|
2
|
+
/** Unified function type for all execute handlers. */
|
|
3
3
|
export type ExecuteHandler = (ctx: IExecuteFunctions, itemIndex: number) => Promise<IDataObject | IDataObject[]>;
|
|
4
|
-
/** Registry
|
|
4
|
+
/** Registry type: resource -> operation -> handler */
|
|
5
5
|
export type HandlersRegistry = Record<string, Record<string, ExecuteHandler>>;
|
|
@@ -1,7 +1,48 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.aiHandlers = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
4
5
|
const shared_1 = require("../shared");
|
|
6
|
+
const getAgentActions = async (ctx) => {
|
|
7
|
+
return await shared_1.lsRequest.call(ctx, 'GET', '/agent-actions');
|
|
8
|
+
};
|
|
9
|
+
const getAiAgents = async (ctx) => {
|
|
10
|
+
return await shared_1.lsRequest.call(ctx, 'GET', '/ai-agents');
|
|
11
|
+
};
|
|
12
|
+
function parseJsonObjectParam(ctx, i, name) {
|
|
13
|
+
const raw = ctx.getNodeParameter(name, i, '');
|
|
14
|
+
if (raw === undefined || raw === null || raw === '')
|
|
15
|
+
return undefined;
|
|
16
|
+
if (typeof raw === 'object')
|
|
17
|
+
return raw;
|
|
18
|
+
let parsed;
|
|
19
|
+
try {
|
|
20
|
+
parsed = JSON.parse(raw);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Invalid JSON in "${name}": ${String(err)}`);
|
|
24
|
+
}
|
|
25
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
26
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `"${name}" must be a JSON object`);
|
|
27
|
+
}
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
30
|
+
const agentChat = async (ctx, i) => {
|
|
31
|
+
const agentId = ctx.getNodeParameter('agentId', i);
|
|
32
|
+
if (!agentId)
|
|
33
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'AI Agent is required');
|
|
34
|
+
const body = {
|
|
35
|
+
userId: ctx.getNodeParameter('userId', i),
|
|
36
|
+
chatId: ctx.getNodeParameter('chatId', i, undefined),
|
|
37
|
+
message: ctx.getNodeParameter('message', i, undefined),
|
|
38
|
+
includeChatHistory: ctx.getNodeParameter('includeChatHistory', i, false),
|
|
39
|
+
endChat: ctx.getNodeParameter('endChat', i, false),
|
|
40
|
+
metadata: parseJsonObjectParam(ctx, i, 'metadata'),
|
|
41
|
+
profileContext: parseJsonObjectParam(ctx, i, 'profileContext'),
|
|
42
|
+
};
|
|
43
|
+
const cleanBody = Object.fromEntries(Object.entries(body).filter(([, v]) => v !== undefined && v !== ''));
|
|
44
|
+
return await shared_1.lsRequest.call(ctx, 'POST', `/ai-agents/${encodeURIComponent(agentId)}/chat`, { body: cleanBody });
|
|
45
|
+
};
|
|
5
46
|
const ragChat = async (ctx, i) => {
|
|
6
47
|
const body = {
|
|
7
48
|
question: ctx.getNodeParameter('question', i),
|
|
@@ -19,5 +60,8 @@ const ragChat = async (ctx, i) => {
|
|
|
19
60
|
return await shared_1.lsRequest.call(ctx, 'POST', '/ai/rag-chat', { body: cleanBody });
|
|
20
61
|
};
|
|
21
62
|
exports.aiHandlers = {
|
|
63
|
+
agentChat,
|
|
64
|
+
getAgentActions,
|
|
65
|
+
getAiAgents,
|
|
22
66
|
ragChat,
|
|
23
67
|
};
|
|
@@ -9,6 +9,7 @@ export declare const customFieldsHandlers: {
|
|
|
9
9
|
getFieldValues: ExecuteHandler;
|
|
10
10
|
setFieldValue: ExecuteHandler;
|
|
11
11
|
setMultipleFieldValues: ExecuteHandler;
|
|
12
|
+
createFileUploadTarget: ExecuteHandler;
|
|
12
13
|
getProfiles: ExecuteHandler;
|
|
13
14
|
getProfilesExpanded: ExecuteHandler;
|
|
14
15
|
getProfileByCard: ExecuteHandler;
|
|
@@ -95,6 +95,178 @@ function normalizeCustomFieldValue(response) {
|
|
|
95
95
|
return { value: response[0] };
|
|
96
96
|
return { value: response };
|
|
97
97
|
}
|
|
98
|
+
function normalizeValuesArray(value) {
|
|
99
|
+
if (value === undefined || value === null) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
return Array.isArray(value) ? value : [value];
|
|
103
|
+
}
|
|
104
|
+
function normalizeStoreFileValuesResponse(response) {
|
|
105
|
+
if (!Array.isArray(response)) {
|
|
106
|
+
return normalizeValuesArray(response);
|
|
107
|
+
}
|
|
108
|
+
if (response.length === 0) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
if (response.length === 1 && Array.isArray(response[0])) {
|
|
112
|
+
return response[0];
|
|
113
|
+
}
|
|
114
|
+
return response;
|
|
115
|
+
}
|
|
116
|
+
function normalizeFileValuesResponse(response) {
|
|
117
|
+
return normalizeStoreFileValuesResponse(response);
|
|
118
|
+
}
|
|
119
|
+
function normalizeProfileIndex(value) {
|
|
120
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
124
|
+
const parsed = Number(value);
|
|
125
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
function getMediaFieldLimit(type, typeDefinition) {
|
|
130
|
+
switch (type) {
|
|
131
|
+
case 'files':
|
|
132
|
+
return typeDefinition.maxFiles;
|
|
133
|
+
case 'images':
|
|
134
|
+
return typeDefinition.maxImages;
|
|
135
|
+
case 'videos':
|
|
136
|
+
return typeDefinition.maxVideos;
|
|
137
|
+
case 'audios':
|
|
138
|
+
return typeDefinition.maxAudios;
|
|
139
|
+
default:
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function buildProfileSelector(ctx, i) {
|
|
144
|
+
const profileId = ctx.getNodeParameter('profileId', i, '');
|
|
145
|
+
const profileIndex = normalizeProfileIndex(ctx.getNodeParameter('profileIndex', i, ''));
|
|
146
|
+
const profileName = ctx.getNodeParameter('profileName', i, '');
|
|
147
|
+
const selector = {};
|
|
148
|
+
if (profileId) {
|
|
149
|
+
selector.profileId = profileId;
|
|
150
|
+
}
|
|
151
|
+
else if (profileIndex !== undefined) {
|
|
152
|
+
selector.profileIndex = profileIndex;
|
|
153
|
+
}
|
|
154
|
+
else if (profileName) {
|
|
155
|
+
selector.profileName = profileName;
|
|
156
|
+
}
|
|
157
|
+
return selector;
|
|
158
|
+
}
|
|
159
|
+
function hasProfileSelector(selector) {
|
|
160
|
+
return selector.profileId !== undefined || selector.profileIndex !== undefined || selector.profileName !== undefined;
|
|
161
|
+
}
|
|
162
|
+
function getEffectiveProfileSelector(fieldContext, selector) {
|
|
163
|
+
return fieldContext.multipleProfilesAllowed ? selector : {};
|
|
164
|
+
}
|
|
165
|
+
async function cardAllowsMultipleProfiles(ctx, cardId) {
|
|
166
|
+
const cards = await shared_1.lsRequest.call(ctx, 'GET', '/custom-fields/cards');
|
|
167
|
+
if (!Array.isArray(cards)) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
const card = cards.find((entry) => {
|
|
171
|
+
if (typeof entry !== 'object' || entry === null)
|
|
172
|
+
return false;
|
|
173
|
+
return entry.id === cardId;
|
|
174
|
+
});
|
|
175
|
+
if (!card || typeof card !== 'object') {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
return card.multipleProfilesAllowed === true;
|
|
179
|
+
}
|
|
180
|
+
function getFileValueMode(ctx, i) {
|
|
181
|
+
return ctx.getNodeParameter('fileValueMode', i, 'add');
|
|
182
|
+
}
|
|
183
|
+
function countBinaryPropertyNames(binaryPropertyNames) {
|
|
184
|
+
return binaryPropertyNames
|
|
185
|
+
.split(',')
|
|
186
|
+
.map((name) => name.trim())
|
|
187
|
+
.filter((name) => name.length > 0).length;
|
|
188
|
+
}
|
|
189
|
+
function findFileFieldContext(ctx, cards, customFieldKey) {
|
|
190
|
+
var _a, _b, _c;
|
|
191
|
+
for (const card of cards) {
|
|
192
|
+
if (!(card === null || card === void 0 ? void 0 : card.id) || !Array.isArray(card.definitions)) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const definition = card.definitions.find((def) => (def === null || def === void 0 ? void 0 : def.key) === customFieldKey);
|
|
196
|
+
if (!definition) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const type = String((_b = (_a = definition.typeDefinition) === null || _a === void 0 ? void 0 : _a.type) !== null && _b !== void 0 ? _b : '');
|
|
200
|
+
if (!FILE_FIELD_TYPES.has(type)) {
|
|
201
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Custom field "${customFieldKey}" is not a file, image, video, or audio field.`);
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
cardId: card.id,
|
|
205
|
+
type,
|
|
206
|
+
maxItems: getMediaFieldLimit(type, (_c = definition.typeDefinition) !== null && _c !== void 0 ? _c : {}),
|
|
207
|
+
multipleProfilesAllowed: card.multipleProfilesAllowed === true,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Unknown custom field "${customFieldKey}".`);
|
|
211
|
+
}
|
|
212
|
+
async function getPublicUrlFileFieldContext(ctx, customFieldKey) {
|
|
213
|
+
const cardsResponse = await shared_1.lsRequest.call(ctx, 'GET', '/custom-fields/cards/expanded');
|
|
214
|
+
if (!Array.isArray(cardsResponse)) {
|
|
215
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Failed to load custom field definitions.');
|
|
216
|
+
}
|
|
217
|
+
return findFileFieldContext(ctx, cardsResponse, customFieldKey);
|
|
218
|
+
}
|
|
219
|
+
async function readExistingFileValues(ctx, userId, customFieldKey, fieldContext, profileSelector, forceProfileContext = false) {
|
|
220
|
+
const effectiveProfileSelector = getEffectiveProfileSelector(fieldContext, profileSelector);
|
|
221
|
+
const useProfileContext = forceProfileContext || fieldContext.multipleProfilesAllowed || hasProfileSelector(effectiveProfileSelector);
|
|
222
|
+
if (useProfileContext) {
|
|
223
|
+
const existingProfileValues = await shared_1.lsRequest.call(ctx, 'GET', `/custom-fields/store/${userId}/profiles/by-card/${fieldContext.cardId}`, { qs: effectiveProfileSelector });
|
|
224
|
+
return {
|
|
225
|
+
existingValues: normalizeFileValuesResponse(existingProfileValues === null || existingProfileValues === void 0 ? void 0 : existingProfileValues[customFieldKey]),
|
|
226
|
+
useProfileContext,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const existingFieldValues = await shared_1.lsRequest.call(ctx, 'GET', `/custom-fields/store/${userId}/fields/${customFieldKey}`);
|
|
230
|
+
return {
|
|
231
|
+
existingValues: normalizeStoreFileValuesResponse(existingFieldValues),
|
|
232
|
+
useProfileContext,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function assertFileValueCountWithinLimit(ctx, customFieldKey, valueCount, maxItems) {
|
|
236
|
+
if (maxItems !== undefined && valueCount > maxItems) {
|
|
237
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Cannot set custom field "${customFieldKey}". Maximum of ${maxItems} file values would be exceeded.`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function buildNextFileValues(ctx, customFieldKey, existingValues, uploadedValues, maxItems, mode) {
|
|
241
|
+
assertFileValueCountWithinLimit(ctx, customFieldKey, uploadedValues.length, maxItems);
|
|
242
|
+
if (mode === 'replace') {
|
|
243
|
+
return uploadedValues;
|
|
244
|
+
}
|
|
245
|
+
const appendedValues = [...existingValues, ...uploadedValues];
|
|
246
|
+
if (maxItems === undefined || appendedValues.length <= maxItems) {
|
|
247
|
+
return appendedValues;
|
|
248
|
+
}
|
|
249
|
+
if (mode === 'replaceIfFull') {
|
|
250
|
+
return uploadedValues;
|
|
251
|
+
}
|
|
252
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Cannot add file to custom field "${customFieldKey}". Maximum of ${maxItems} would be exceeded.`);
|
|
253
|
+
}
|
|
254
|
+
async function writeFileValues(ctx, userId, customFieldKey, fieldContext, profileSelector, useProfileContext, nextValues) {
|
|
255
|
+
const effectiveProfileSelector = getEffectiveProfileSelector(fieldContext, profileSelector);
|
|
256
|
+
if (useProfileContext) {
|
|
257
|
+
const updateBody = {
|
|
258
|
+
fieldKey: customFieldKey,
|
|
259
|
+
fieldValue: nextValues,
|
|
260
|
+
...effectiveProfileSelector,
|
|
261
|
+
};
|
|
262
|
+
return await shared_1.lsRequest.call(ctx, 'PUT', `/custom-fields/store/${userId}/profiles/by-card/${fieldContext.cardId}`, {
|
|
263
|
+
body: updateBody,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
return await shared_1.lsRequest.call(ctx, 'PUT', `/custom-fields/store/${userId}/fields/${customFieldKey}`, {
|
|
267
|
+
body: { fieldValue: nextValues },
|
|
268
|
+
});
|
|
269
|
+
}
|
|
98
270
|
function readTypedFieldValue(ctx, i, fieldType) {
|
|
99
271
|
switch (fieldType) {
|
|
100
272
|
case 'string':
|
|
@@ -154,9 +326,9 @@ const getStore = async (ctx, i) => {
|
|
|
154
326
|
};
|
|
155
327
|
const getStoreValues = async (ctx, i) => {
|
|
156
328
|
const userId = ctx.getNodeParameter('userId', i);
|
|
157
|
-
const profileIndex = ctx.getNodeParameter('profileIndex', i,
|
|
329
|
+
const profileIndex = normalizeProfileIndex(ctx.getNodeParameter('profileIndex', i, ''));
|
|
158
330
|
const qs = {};
|
|
159
|
-
if (profileIndex !==
|
|
331
|
+
if (profileIndex !== undefined) {
|
|
160
332
|
qs.profileIndex = profileIndex;
|
|
161
333
|
}
|
|
162
334
|
return await shared_1.lsRequest.call(ctx, 'GET', `/custom-fields/store/${userId}/values`, { qs });
|
|
@@ -172,10 +344,24 @@ const setFieldValue = async (ctx, i) => {
|
|
|
172
344
|
const fieldKey = ctx.getNodeParameter('fieldKey', i);
|
|
173
345
|
const fieldType = ctx.getNodeParameter('fieldType', i, '');
|
|
174
346
|
let fieldValue;
|
|
347
|
+
let fileFieldContext;
|
|
348
|
+
let fileProfileSelector;
|
|
349
|
+
let useFileProfileContext = false;
|
|
175
350
|
if (FILE_FIELD_TYPES.has(fieldType)) {
|
|
176
351
|
const binaryPropertyNames = readTypedFieldValue(ctx, i, fieldType);
|
|
177
352
|
const fileNameOverride = ctx.getNodeParameter('fieldValueFileName', i, '').trim() || undefined;
|
|
178
|
-
|
|
353
|
+
const fileValueMode = getFileValueMode(ctx, i);
|
|
354
|
+
fileFieldContext = await getPublicUrlFileFieldContext(ctx, fieldKey);
|
|
355
|
+
fileProfileSelector = buildProfileSelector(ctx, i);
|
|
356
|
+
const { existingValues, useProfileContext } = await readExistingFileValues(ctx, userId, fieldKey, fileFieldContext, fileProfileSelector);
|
|
357
|
+
useFileProfileContext = useProfileContext;
|
|
358
|
+
const uploadCount = countBinaryPropertyNames(binaryPropertyNames);
|
|
359
|
+
if (fileValueMode !== 'replaceIfFull') {
|
|
360
|
+
const expectedValues = fileValueMode === 'replace' ? uploadCount : existingValues.length + uploadCount;
|
|
361
|
+
assertFileValueCountWithinLimit(ctx, fieldKey, expectedValues, fileFieldContext.maxItems);
|
|
362
|
+
}
|
|
363
|
+
const uploadedValues = await (0, shared_1.uploadFilesFromBinaryProperties)(ctx, i, userId, fieldKey, binaryPropertyNames, fieldType, fileNameOverride);
|
|
364
|
+
fieldValue = buildNextFileValues(ctx, fieldKey, existingValues, uploadedValues, fileFieldContext.maxItems, fileValueMode);
|
|
179
365
|
}
|
|
180
366
|
else {
|
|
181
367
|
fieldValue = readTypedFieldValue(ctx, i, fieldType);
|
|
@@ -184,8 +370,7 @@ const setFieldValue = async (ctx, i) => {
|
|
|
184
370
|
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `No value provided for custom field "${fieldKey}".`);
|
|
185
371
|
}
|
|
186
372
|
const profileId = ctx.getNodeParameter('profileId', i, '');
|
|
187
|
-
const
|
|
188
|
-
const validProfileIndex = profileIndex !== null && Number.isFinite(profileIndex) ? profileIndex : undefined;
|
|
373
|
+
const validProfileIndex = normalizeProfileIndex(ctx.getNodeParameter('profileIndex', i, ''));
|
|
189
374
|
const body = { fieldValue };
|
|
190
375
|
if (profileId) {
|
|
191
376
|
body.profileId = profileId;
|
|
@@ -193,6 +378,10 @@ const setFieldValue = async (ctx, i) => {
|
|
|
193
378
|
else if (validProfileIndex !== undefined) {
|
|
194
379
|
body.profileIndex = validProfileIndex;
|
|
195
380
|
}
|
|
381
|
+
if (fileFieldContext && fileProfileSelector) {
|
|
382
|
+
const response = await writeFileValues(ctx, userId, fieldKey, fileFieldContext, fileProfileSelector, useFileProfileContext, fieldValue);
|
|
383
|
+
return normalizeCustomFieldValue(response);
|
|
384
|
+
}
|
|
196
385
|
const response = await shared_1.lsRequest.call(ctx, 'PUT', `/custom-fields/store/${userId}/fields/${fieldKey}`, { body });
|
|
197
386
|
return normalizeCustomFieldValue(response);
|
|
198
387
|
};
|
|
@@ -213,8 +402,8 @@ function buildFieldTypeMap(cards) {
|
|
|
213
402
|
const setMultipleFieldValues = async (ctx, i) => {
|
|
214
403
|
const userId = ctx.getNodeParameter('userId', i);
|
|
215
404
|
const profileId = ctx.getNodeParameter('profileId', i, '');
|
|
216
|
-
const
|
|
217
|
-
const
|
|
405
|
+
const validProfileIndex = normalizeProfileIndex(ctx.getNodeParameter('profileIndex', i, ''));
|
|
406
|
+
const fileValueMode = getFileValueMode(ctx, i);
|
|
218
407
|
const mapperData = ctx.getNodeParameter('fieldValues', i);
|
|
219
408
|
const fieldMappings = (mapperData === null || mapperData === void 0 ? void 0 : mapperData.value) || {};
|
|
220
409
|
const cardsResponse = await shared_1.lsRequest.call(ctx, 'GET', '/custom-fields/cards/expanded');
|
|
@@ -222,6 +411,7 @@ const setMultipleFieldValues = async (ctx, i) => {
|
|
|
222
411
|
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Failed to load custom field definitions.');
|
|
223
412
|
}
|
|
224
413
|
const cards = cardsResponse;
|
|
414
|
+
const expandedCards = cardsResponse;
|
|
225
415
|
const fieldTypeMap = buildFieldTypeMap(cards);
|
|
226
416
|
const entries = Object.entries(fieldMappings).filter(([, value]) => value !== undefined);
|
|
227
417
|
const payload = [];
|
|
@@ -233,7 +423,22 @@ const setMultipleFieldValues = async (ctx, i) => {
|
|
|
233
423
|
let fieldValue;
|
|
234
424
|
if (FILE_FIELD_TYPES.has(lsType)) {
|
|
235
425
|
const binaryPropertyNames = typeof value === 'string' ? value : 'data';
|
|
236
|
-
|
|
426
|
+
const fieldContext = findFileFieldContext(ctx, expandedCards, fieldKey);
|
|
427
|
+
const profileSelector = {};
|
|
428
|
+
if (profileId) {
|
|
429
|
+
profileSelector.profileId = profileId;
|
|
430
|
+
}
|
|
431
|
+
else if (validProfileIndex !== undefined) {
|
|
432
|
+
profileSelector.profileIndex = validProfileIndex;
|
|
433
|
+
}
|
|
434
|
+
const { existingValues } = await readExistingFileValues(ctx, userId, fieldKey, fieldContext, profileSelector);
|
|
435
|
+
const uploadCount = countBinaryPropertyNames(binaryPropertyNames);
|
|
436
|
+
if (fileValueMode !== 'replaceIfFull') {
|
|
437
|
+
const expectedValues = fileValueMode === 'replace' ? uploadCount : existingValues.length + uploadCount;
|
|
438
|
+
assertFileValueCountWithinLimit(ctx, fieldKey, expectedValues, fieldContext.maxItems);
|
|
439
|
+
}
|
|
440
|
+
const uploadedValues = await (0, shared_1.uploadFilesFromBinaryProperties)(ctx, i, userId, fieldKey, binaryPropertyNames, lsType);
|
|
441
|
+
fieldValue = buildNextFileValues(ctx, fieldKey, existingValues, uploadedValues, fieldContext.maxItems, fileValueMode);
|
|
237
442
|
}
|
|
238
443
|
else {
|
|
239
444
|
fieldValue = normalizeSingleFieldValueOrFail(ctx, value, fieldKey, lsType);
|
|
@@ -254,6 +459,37 @@ const setMultipleFieldValues = async (ctx, i) => {
|
|
|
254
459
|
body: payload,
|
|
255
460
|
});
|
|
256
461
|
};
|
|
462
|
+
const createFileUploadTarget = async (ctx, i) => {
|
|
463
|
+
const userId = ctx.getNodeParameter('userId', i);
|
|
464
|
+
const customFieldKey = (ctx.getNodeParameter('customFieldKey', i, '') || ctx.getNodeParameter('fieldKey', i, '')).trim();
|
|
465
|
+
const fileName = ctx.getNodeParameter('customFieldFileName', i, '').trim();
|
|
466
|
+
const publicDownloadUrl = ctx.getNodeParameter('publicDownloadUrl', i, '').trim();
|
|
467
|
+
const profileSelector = buildProfileSelector(ctx, i);
|
|
468
|
+
const fileValueMode = getFileValueMode(ctx, i);
|
|
469
|
+
if (!customFieldKey) {
|
|
470
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'No custom field key provided.');
|
|
471
|
+
}
|
|
472
|
+
if (!publicDownloadUrl) {
|
|
473
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'No public download URL provided.');
|
|
474
|
+
}
|
|
475
|
+
const fieldContext = await getPublicUrlFileFieldContext(ctx, customFieldKey);
|
|
476
|
+
const { existingValues, useProfileContext } = await readExistingFileValues(ctx, userId, customFieldKey, fieldContext, profileSelector);
|
|
477
|
+
const body = { customFieldKey };
|
|
478
|
+
if (fileName) {
|
|
479
|
+
body.fileName = fileName;
|
|
480
|
+
}
|
|
481
|
+
body.publicDownloadUrl = publicDownloadUrl;
|
|
482
|
+
const createFileResponse = (await shared_1.lsRequest.call(ctx, 'POST', `/custom-fields/store/${userId}/files`, {
|
|
483
|
+
body,
|
|
484
|
+
}));
|
|
485
|
+
const customFieldValue = createFileResponse.customFieldValue;
|
|
486
|
+
if (customFieldValue === undefined || customFieldValue === null) {
|
|
487
|
+
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'LearningSuite did not return a custom field value for the uploaded file.');
|
|
488
|
+
}
|
|
489
|
+
const uploadedValues = normalizeValuesArray(customFieldValue);
|
|
490
|
+
const nextValues = buildNextFileValues(ctx, customFieldKey, existingValues, uploadedValues, fieldContext.maxItems, fileValueMode);
|
|
491
|
+
return await writeFileValues(ctx, userId, customFieldKey, fieldContext, profileSelector, useProfileContext, nextValues);
|
|
492
|
+
};
|
|
257
493
|
const getProfiles = async (ctx, i) => {
|
|
258
494
|
const userId = ctx.getNodeParameter('userId', i);
|
|
259
495
|
const customFieldCardId = ctx.getNodeParameter('customFieldCardId', i, undefined);
|
|
@@ -274,13 +510,13 @@ const getProfileByCard = async (ctx, i) => {
|
|
|
274
510
|
const userId = ctx.getNodeParameter('userId', i);
|
|
275
511
|
const cardId = ctx.getNodeParameter('customFieldCardId', i);
|
|
276
512
|
const profileId = ctx.getNodeParameter('profileId', i, '');
|
|
277
|
-
const profileIndex = ctx.getNodeParameter('profileIndex', i,
|
|
513
|
+
const profileIndex = normalizeProfileIndex(ctx.getNodeParameter('profileIndex', i, ''));
|
|
278
514
|
const profileName = ctx.getNodeParameter('profileName', i, '');
|
|
279
515
|
const qs = {};
|
|
280
516
|
if (profileId) {
|
|
281
517
|
qs.profileId = profileId;
|
|
282
518
|
}
|
|
283
|
-
else if (profileIndex !==
|
|
519
|
+
else if (profileIndex !== undefined) {
|
|
284
520
|
qs.profileIndex = profileIndex;
|
|
285
521
|
}
|
|
286
522
|
else if (profileName) {
|
|
@@ -294,10 +530,22 @@ const updateProfileField = async (ctx, i) => {
|
|
|
294
530
|
const fieldKey = ctx.getNodeParameter('fieldKey', i);
|
|
295
531
|
const fieldType = ctx.getNodeParameter('fieldType', i, '');
|
|
296
532
|
let fieldValue;
|
|
533
|
+
let fileFieldContext;
|
|
534
|
+
let fileProfileSelector;
|
|
297
535
|
if (FILE_FIELD_TYPES.has(fieldType)) {
|
|
298
536
|
const binaryPropertyNames = readTypedFieldValue(ctx, i, fieldType);
|
|
299
537
|
const fileNameOverride = ctx.getNodeParameter('fieldValueFileName', i, '').trim() || undefined;
|
|
300
|
-
|
|
538
|
+
const fileValueMode = getFileValueMode(ctx, i);
|
|
539
|
+
fileFieldContext = await getPublicUrlFileFieldContext(ctx, fieldKey);
|
|
540
|
+
fileProfileSelector = getEffectiveProfileSelector(fileFieldContext, buildProfileSelector(ctx, i));
|
|
541
|
+
const { existingValues } = await readExistingFileValues(ctx, userId, fieldKey, fileFieldContext, fileProfileSelector, true);
|
|
542
|
+
const uploadCount = countBinaryPropertyNames(binaryPropertyNames);
|
|
543
|
+
if (fileValueMode !== 'replaceIfFull') {
|
|
544
|
+
const expectedValues = fileValueMode === 'replace' ? uploadCount : existingValues.length + uploadCount;
|
|
545
|
+
assertFileValueCountWithinLimit(ctx, fieldKey, expectedValues, fileFieldContext.maxItems);
|
|
546
|
+
}
|
|
547
|
+
const uploadedValues = await (0, shared_1.uploadFilesFromBinaryProperties)(ctx, i, userId, fieldKey, binaryPropertyNames, fieldType, fileNameOverride);
|
|
548
|
+
fieldValue = buildNextFileValues(ctx, fieldKey, existingValues, uploadedValues, fileFieldContext.maxItems, fileValueMode);
|
|
301
549
|
}
|
|
302
550
|
else {
|
|
303
551
|
fieldValue = readTypedFieldValue(ctx, i, fieldType);
|
|
@@ -306,19 +554,21 @@ const updateProfileField = async (ctx, i) => {
|
|
|
306
554
|
throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `No value provided for custom field "${fieldKey}".`);
|
|
307
555
|
}
|
|
308
556
|
const body = { fieldKey, fieldValue };
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
body.profileId = profileId;
|
|
557
|
+
let profileSelector = buildProfileSelector(ctx, i);
|
|
558
|
+
if (fileFieldContext) {
|
|
559
|
+
profileSelector = getEffectiveProfileSelector(fileFieldContext, profileSelector);
|
|
313
560
|
}
|
|
314
|
-
else if (
|
|
315
|
-
|
|
561
|
+
else if (!(await cardAllowsMultipleProfiles(ctx, cardId))) {
|
|
562
|
+
profileSelector = {};
|
|
316
563
|
}
|
|
317
|
-
if (
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
564
|
+
if (profileSelector.profileId !== undefined) {
|
|
565
|
+
body.profileId = profileSelector.profileId;
|
|
566
|
+
}
|
|
567
|
+
else if (profileSelector.profileIndex !== undefined) {
|
|
568
|
+
body.profileIndex = profileSelector.profileIndex;
|
|
569
|
+
}
|
|
570
|
+
else if (profileSelector.profileName !== undefined) {
|
|
571
|
+
body.profileName = profileSelector.profileName;
|
|
322
572
|
}
|
|
323
573
|
const response = await shared_1.lsRequest.call(ctx, 'PUT', `/custom-fields/store/${userId}/profiles/by-card/${cardId}`, {
|
|
324
574
|
body,
|
|
@@ -335,6 +585,7 @@ exports.customFieldsHandlers = {
|
|
|
335
585
|
getFieldValues,
|
|
336
586
|
setFieldValue,
|
|
337
587
|
setMultipleFieldValues,
|
|
588
|
+
createFileUploadTarget,
|
|
338
589
|
getProfiles,
|
|
339
590
|
getProfilesExpanded,
|
|
340
591
|
getProfileByCard,
|
|
@@ -34,6 +34,17 @@ function buildDesiredFilter(ctx, i, eventType) {
|
|
|
34
34
|
var _a;
|
|
35
35
|
const filter = {};
|
|
36
36
|
switch (eventType) {
|
|
37
|
+
// ---------------- Agent Action
|
|
38
|
+
case 'agentAction.executed': {
|
|
39
|
+
const col = getCol(ctx, i, 'additionalAgentActionExecuted');
|
|
40
|
+
if (col === null || col === void 0 ? void 0 : col.toolKey) {
|
|
41
|
+
filter.toolKey = String(col.toolKey);
|
|
42
|
+
}
|
|
43
|
+
if (col === null || col === void 0 ? void 0 : col.agentId) {
|
|
44
|
+
filter.agentId = String(col.agentId);
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
37
48
|
// ---------------- Community
|
|
38
49
|
case 'communityPost.commented': {
|
|
39
50
|
const col = getCol(ctx, i, 'additionalCommunityPostCommented');
|
|
@@ -161,7 +172,7 @@ function buildDesiredFilter(ctx, i, eventType) {
|
|
|
161
172
|
filter.groupId = String(col.groupId);
|
|
162
173
|
break;
|
|
163
174
|
}
|
|
164
|
-
// ---------------- Lesson Completed (
|
|
175
|
+
// ---------------- Lesson Completed (cascading)
|
|
165
176
|
case 'lesson.completed': {
|
|
166
177
|
const col = getCol(ctx, i, 'additionalLessonCompleted');
|
|
167
178
|
if (col.courseId)
|
|
@@ -223,6 +234,11 @@ const getSampleData = async (ctx, i) => {
|
|
|
223
234
|
const sampleDataType = ctx.getNodeParameter('sampleDataType', i);
|
|
224
235
|
let qs = {};
|
|
225
236
|
switch (sampleDataType) {
|
|
237
|
+
case 'agent-action-executed-events': {
|
|
238
|
+
const col = ctx.getNodeParameter('additionalAgentActionExecutedSample', i, {});
|
|
239
|
+
qs = col;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
226
242
|
case 'progress-changed-events': {
|
|
227
243
|
const col = ctx.getNodeParameter('additionalProgressChangedSample', i, {});
|
|
228
244
|
qs = col;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ILoadOptionsFunctions, INodePropertyOptions } from 'n8n-workflow';
|
|
2
|
+
export declare function ai_getAgentActions(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
3
|
+
export declare function ai_getAiAgents(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ai_getAgentActions = ai_getAgentActions;
|
|
4
|
+
exports.ai_getAiAgents = ai_getAiAgents;
|
|
5
|
+
const common_1 = require("./common");
|
|
6
|
+
async function ai_getAgentActions() {
|
|
7
|
+
return common_1.fetchOptions.call(this, '/agent-actions', undefined, ['name'], ['toolKey']);
|
|
8
|
+
}
|
|
9
|
+
async function ai_getAiAgents() {
|
|
10
|
+
return common_1.fetchOptions.call(this, '/ai-agents', undefined, ['name'], ['id']);
|
|
11
|
+
}
|
|
@@ -4,4 +4,5 @@ type LoadOptionRow = IDataObject;
|
|
|
4
4
|
export declare function toOptions(rows: LoadOptionRow[], labelKeys?: string[], valueKeys?: string[]): INodePropertyOptions[];
|
|
5
5
|
export declare function ensureArray<T>(res: T | T[]): T[];
|
|
6
6
|
export declare function fetchOptions(this: ILoadOptionsFunctions, endpoint: string, qs?: IDataObject, labelKeys?: string[], valueKeys?: string[]): Promise<INodePropertyOptions[]>;
|
|
7
|
+
export declare function fetchOptionsAll(this: ILoadOptionsFunctions, endpoint: string, qs?: IDataObject, labelKeys?: string[], valueKeys?: string[]): Promise<INodePropertyOptions[]>;
|
|
7
8
|
export {};
|