n8n-nodes-lifespace 0.1.3 → 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/README.md +18 -7
- package/dist/nodes/LifeSpace/LifeSpace.node.js +59 -50
- package/dist/nodes/LifeSpace/LifeSpace.node.js.map +1 -1
- package/dist/nodes/LifeSpaceTrigger/LifeSpaceTrigger.node.js +7 -6
- package/dist/nodes/LifeSpaceTrigger/LifeSpaceTrigger.node.js.map +1 -1
- package/dist/nodes/lifespaceDiscovery.d.ts +9 -2
- package/dist/nodes/lifespaceDiscovery.js +90 -27
- package/dist/nodes/lifespaceDiscovery.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/nodes/calendarMutation.d.ts +0 -3
- package/dist/nodes/calendarMutation.js +0 -56
- package/dist/nodes/calendarMutation.js.map +0 -1
package/README.md
CHANGED
|
@@ -70,7 +70,9 @@ Create/Update keep scalar fields in n8n Resource Mapper while using native n8n c
|
|
|
70
70
|
|
|
71
71
|
The node displays the authorized human-readable `spaceName` when present while continuing to submit the stable `spc_*` ID.
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
Record Type identity and REST routing remain distinct. LifeSpace `modelKey` is the stable semantic identity, while the Discovery `route` is a REST transport detail. The n8n adapter persists an adapter-local `recordType` selector containing both values when the workflow is configured. At execution time the selector is decoded locally, so Get/List/Delete do not add a Discovery request merely to translate `modelKey` to a REST route.
|
|
74
|
+
|
|
75
|
+
Calendar-backed models use canonical `capabilityBindings.calendar` field roles instead of Event-specific field names while the node is being configured. Date-only values are normalized to `YYYY-MM-DD`. Create/Update execution does not fetch fresh semantic Discovery solely to produce an adapter-local Calendar conflict error; the canonical mutation goes directly to LifeSpace Core, which remains authoritative for Calendar validation and current authorization.
|
|
74
76
|
|
|
75
77
|
## LifeSpace contract compatibility
|
|
76
78
|
|
|
@@ -93,17 +95,21 @@ The UX depends on these Kernel capabilities:
|
|
|
93
95
|
- `0.30.0`: explicit paginated Change History collection and Model Control Plane ownership split;
|
|
94
96
|
- `0.31.0`: Integration/Eventing wire representation moves to the independent Integration/Eventing `0.1.0` contract while Core remains the Runtime authority.
|
|
95
97
|
|
|
96
|
-
The adapter prefers the `0.27+` progressive flow:
|
|
98
|
+
The adapter prefers the `0.27+` progressive flow while configuring a node:
|
|
97
99
|
|
|
98
100
|
```text
|
|
99
101
|
GET /me/_discovery/inventory
|
|
100
102
|
-> choose current Space / Record Type
|
|
101
103
|
-> GET /spaces/{spaceId}/_discovery/models/{modelKey}
|
|
102
104
|
-> relation target lookup / reference resolution only when needed
|
|
103
|
-
->
|
|
105
|
+
-> persist stable workflow configuration
|
|
104
106
|
```
|
|
105
107
|
|
|
106
|
-
|
|
108
|
+
Design-time callbacks prefer n8n editor-current parameters, so Fields and Actions refresh immediately after Record Type selection. A legacy 0.1.3 `modelRoute` is still readable for design-time compatibility; re-selecting Record Type moves configuration to the composable selector.
|
|
109
|
+
|
|
110
|
+
Execution is deliberately narrower. Get/List/Delete derive the REST route locally from the persisted Record Type selector. Create/Update submit the configured mutation without a fresh Runtime Discovery preflight. Execute Action loads only the selected model's `0.26+` static semantic detail, and reuses that detail within the same node execution for repeated items using the same Space/Record Type. No cached Discovery result is treated as authorization proof; every CRUD or Action request still goes through canonical LifeSpace Core current-state enforcement.
|
|
111
|
+
|
|
112
|
+
The legacy aggregate `GET /me/_discovery` remains an intentional design-time compatibility fallback. Neither cached Discovery nor relation lookup is treated as authorization proof; every mutation still goes through canonical LifeSpace Runtime enforcement.
|
|
107
113
|
|
|
108
114
|
Ordinary Record CRUD/Action routes remain model-contract surfaces derived from published Model Definitions; the n8n adapter does not maintain a second copy of those schemas.
|
|
109
115
|
|
|
@@ -116,10 +122,11 @@ Examples:
|
|
|
116
122
|
```text
|
|
117
123
|
{{$json.spaceId}}
|
|
118
124
|
{{$json.recordId}}
|
|
125
|
+
{{$json.recordType}}
|
|
119
126
|
{{$vars.lifeSpaceRecordType}}
|
|
120
127
|
```
|
|
121
128
|
|
|
122
|
-
Discovery-backed selectors such as **Space**, **
|
|
129
|
+
Discovery-backed selectors such as **Space**, **Filter Field**, **Sort Field** and **Action** support the normal n8n pattern: choose a value from the list, or switch the parameter to an expression and provide the corresponding stable ID/key. **Record Type** uses the adapter-local `recordType` selector so a LifeSpace Trigger can feed a Record node directly without a mapping step or an extra Discovery request.
|
|
123
130
|
|
|
124
131
|
The same applies to ordinary values such as Record ID, Search, Filter Value, Return All, Limit, Sort Direction, Cursor, explicit Version, API Method, API Path and JSON Body.
|
|
125
132
|
|
|
@@ -173,13 +180,15 @@ Older compatible Discovery responses without relation lookup metadata retain the
|
|
|
173
180
|
|
|
174
181
|
The current n8n UI loads bounded relation options when the relevant field is configured. Very large target sets should use expressions with stable IDs until n8n exposes a searchable dynamic relation option surface that can consume LifeSpace's paginated/searchable lookup directly.
|
|
175
182
|
|
|
183
|
+
At execution time Create uses the already-configured Record Type selector and sends the canonical mutation directly. This avoids paying a current-principal inventory round trip merely to rediscover whether adapter-side semantic preflight would improve an error message. Core remains the final authority for schema, Capability, Policy and authorization checks.
|
|
184
|
+
|
|
176
185
|
### Update and Delete
|
|
177
186
|
|
|
178
187
|
LifeSpace uses optimistic concurrency.
|
|
179
188
|
|
|
180
189
|
By default the node reads the current Record version immediately before Update/Delete and sends that version with the mutation. This keeps the ordinary n8n UI free from mandatory internal `version` entry while preserving stale-write protection for the actual mutation race.
|
|
181
190
|
|
|
182
|
-
|
|
191
|
+
Update does not add a Runtime Discovery request before the mutation. When no explicit version is configured, the current-record read is for optimistic concurrency rather than semantic discovery. Core validates Calendar and other Capability semantics against the resulting mutation.
|
|
183
192
|
|
|
184
193
|
If a workflow intentionally needs to bind a known version, add **Concurrency Options → Version**.
|
|
185
194
|
|
|
@@ -204,7 +213,7 @@ Choose an Action from Runtime Discovery.
|
|
|
204
213
|
|
|
205
214
|
**Action Input** contains only semantic/domain inputs. LifeSpace concurrency metadata is not rendered as a business field. For the current `record-version` contract, the node reads the current Record version immediately before Action execution and sends it using the transport declared by Runtime Discovery.
|
|
206
215
|
|
|
207
|
-
Execution-time Action metadata
|
|
216
|
+
Execution-time Action metadata is loaded directly from the selected model's static semantic-detail endpoint rather than first loading the broad current-principal inventory. The static detail is reused for repeated items of the same Space/Record Type during one node execution. It is semantic input only, not cached authority; Core rechecks current Action authority on every invocation.
|
|
208
217
|
|
|
209
218
|
This means actions such as `complete` / `reopen` no longer ask users to type an internal version value.
|
|
210
219
|
|
|
@@ -247,6 +256,8 @@ One Webhook Endpoint can therefore carry events for multiple Record Types throug
|
|
|
247
256
|
|
|
248
257
|
The Trigger verifies `X-LifeSpace-Timestamp` and `X-LifeSpace-Signature` with the LifeSpace HMAC-SHA256 contract before emitting workflow data. `endpoint.test` payloads are always accepted after signature verification.
|
|
249
258
|
|
|
259
|
+
For ordinary record events, the Trigger preserves the LifeSpace `modelKey` exactly as delivered and additionally emits the n8n adapter-local `recordType` selector for the matching configured Record Type. Pass `{{$json.recordType}}` and `{{$json.recordId}}` directly to a downstream LifeSpace Record node. The downstream node derives the REST route locally from that selector.
|
|
260
|
+
|
|
250
261
|
Webhook Endpoint / Event Subscription creation is intentionally not performed by the n8n Service API Token today because LifeSpace treats that configuration as a Space-management operation. The adapter does not bypass that authority boundary.
|
|
251
262
|
|
|
252
263
|
## What the package provides
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LifeSpace = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
-
const calendarMutation_1 = require("../calendarMutation");
|
|
6
5
|
const lifespaceDiscovery_1 = require("../lifespaceDiscovery");
|
|
7
6
|
function parseJsonObject(context, itemIndex, value, fieldName) {
|
|
8
7
|
const parsed = typeof value === 'string' ? JSON.parse(value) : value;
|
|
@@ -272,26 +271,24 @@ async function currentRecord(context, itemIndex, baseUrl, recordPath) {
|
|
|
272
271
|
async function currentRecordVersion(context, itemIndex, baseUrl, recordPath) {
|
|
273
272
|
return recordVersion(context, itemIndex, await currentRecord(context, itemIndex, baseUrl, recordPath));
|
|
274
273
|
}
|
|
275
|
-
async function mutationVersion(context, itemIndex, baseUrl, recordPath
|
|
274
|
+
async function mutationVersion(context, itemIndex, baseUrl, recordPath) {
|
|
276
275
|
const options = context.getNodeParameter('mutationOptions', itemIndex, {});
|
|
277
276
|
const configuredVersion = options.version;
|
|
278
277
|
if (typeof configuredVersion === 'number' && Number.isInteger(configuredVersion) && configuredVersion >= 1) {
|
|
279
278
|
return configuredVersion;
|
|
280
279
|
}
|
|
281
|
-
return
|
|
282
|
-
? recordVersion(context, itemIndex, loadedRecord)
|
|
283
|
-
: currentRecordVersion(context, itemIndex, baseUrl, recordPath);
|
|
280
|
+
return currentRecordVersion(context, itemIndex, baseUrl, recordPath);
|
|
284
281
|
}
|
|
285
|
-
async function executionModel(context, itemIndex, baseUrl, spaceId,
|
|
286
|
-
const discovery = await (0, lifespaceDiscovery_1.loadExecutionRuntimeDiscovery)(context, baseUrl, spaceId,
|
|
287
|
-
const model = (0, lifespaceDiscovery_1.discoveryModel)(discovery, spaceId,
|
|
282
|
+
async function executionModel(context, itemIndex, baseUrl, spaceId, modelKey) {
|
|
283
|
+
const discovery = await (0, lifespaceDiscovery_1.loadExecutionRuntimeDiscovery)(context, baseUrl, spaceId, modelKey);
|
|
284
|
+
const model = (0, lifespaceDiscovery_1.discoveryModel)(discovery, spaceId, modelKey);
|
|
288
285
|
if (!model) {
|
|
289
|
-
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `LifeSpace Record Type ${
|
|
286
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `LifeSpace Record Type ${modelKey} is not available`, { itemIndex });
|
|
290
287
|
}
|
|
291
288
|
return model;
|
|
292
289
|
}
|
|
293
|
-
async function actionBodyWithConcurrency(context, itemIndex, baseUrl, spaceId,
|
|
294
|
-
const model = await executionModel(context, itemIndex, baseUrl, spaceId,
|
|
290
|
+
async function actionBodyWithConcurrency(context, itemIndex, baseUrl, spaceId, modelKey, recordPath, actionKey, semanticInput) {
|
|
291
|
+
const model = await executionModel(context, itemIndex, baseUrl, spaceId, modelKey);
|
|
295
292
|
const action = model.actions.find((entry) => entry.key === actionKey);
|
|
296
293
|
if (!action) {
|
|
297
294
|
throw new n8n_workflow_1.NodeOperationError(context.getNode(), `LifeSpace Action ${actionKey} is not available`, { itemIndex });
|
|
@@ -316,12 +313,20 @@ function actionOption(action) {
|
|
|
316
313
|
};
|
|
317
314
|
}
|
|
318
315
|
async function optionModel(context) {
|
|
319
|
-
const spaceId =
|
|
320
|
-
const
|
|
321
|
-
|
|
316
|
+
const spaceId = (0, lifespaceDiscovery_1.loadOptionParameter)(context, 'spaceId');
|
|
317
|
+
const recordTypeValue = (0, lifespaceDiscovery_1.loadOptionParameter)(context, 'recordType');
|
|
318
|
+
const legacyModelRoute = recordTypeValue ? '' : (0, lifespaceDiscovery_1.loadOptionParameter)(context, 'modelRoute');
|
|
319
|
+
if (!spaceId || (!recordTypeValue && !legacyModelRoute))
|
|
322
320
|
return null;
|
|
321
|
+
const recordType = recordTypeValue ? (0, lifespaceDiscovery_1.decodeRecordTypeSelector)(recordTypeValue) : null;
|
|
322
|
+
if (recordTypeValue && !recordType) {
|
|
323
|
+
throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'LifeSpace Record Type selector is invalid. Choose a Record Type from Discovery or pass a Trigger recordType value.');
|
|
324
|
+
}
|
|
323
325
|
const discovery = await lifespaceDiscovery_1.loadRuntimeDiscovery.call(context);
|
|
324
|
-
const
|
|
326
|
+
const space = (0, lifespaceDiscovery_1.discoverySpace)(discovery, spaceId);
|
|
327
|
+
const model = recordType
|
|
328
|
+
? (0, lifespaceDiscovery_1.discoveryModel)(discovery, spaceId, recordType.modelKey)
|
|
329
|
+
: space === null || space === void 0 ? void 0 : space.models.find((entry) => entry.route === legacyModelRoute);
|
|
325
330
|
return model ? { model, spaceId } : null;
|
|
326
331
|
}
|
|
327
332
|
async function filterFieldOptions(context, types, encodeType = false) {
|
|
@@ -344,7 +349,7 @@ async function relationFieldOptions(context, cardinality, filterableOnly = false
|
|
|
344
349
|
const selected = await optionModel(context);
|
|
345
350
|
if (!selected)
|
|
346
351
|
return [];
|
|
347
|
-
const operation =
|
|
352
|
+
const operation = (0, lifespaceDiscovery_1.loadOptionParameter)(context, 'operation') || 'create';
|
|
348
353
|
const filterable = new Set(selected.model.query.filterable);
|
|
349
354
|
return selected.model.fields
|
|
350
355
|
.filter((field) => { var _a; return ((_a = field.relation) === null || _a === void 0 ? void 0 : _a.lookup.supported) === true; })
|
|
@@ -419,7 +424,7 @@ class LifeSpace {
|
|
|
419
424
|
description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
|
420
425
|
},
|
|
421
426
|
{
|
|
422
|
-
displayName: 'Record Type Name or ID', name: '
|
|
427
|
+
displayName: 'Record Type Name or ID', name: 'recordType', type: 'options',
|
|
423
428
|
typeOptions: { loadOptionsMethod: 'getRecordTypes', loadOptionsDependsOn: ['spaceId', 'operation'] },
|
|
424
429
|
options: [], default: '', required: true, displayOptions: { show: { resource: ['modelRecord'] } },
|
|
425
430
|
description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
|
@@ -432,7 +437,7 @@ class LifeSpace {
|
|
|
432
437
|
displayName: 'Fields', name: 'fields', type: 'resourceMapper',
|
|
433
438
|
default: { mappingMode: 'defineBelow', value: null }, noDataExpression: true, required: true,
|
|
434
439
|
typeOptions: {
|
|
435
|
-
loadOptionsDependsOn: ['spaceId', '
|
|
440
|
+
loadOptionsDependsOn: ['spaceId', 'recordType', 'operation'],
|
|
436
441
|
resourceMapper: {
|
|
437
442
|
resourceMapperMethod: 'getRecordFields', mode: 'add',
|
|
438
443
|
fieldWords: { singular: 'field', plural: 'fields' }, addAllFields: true, supportAutoMap: false,
|
|
@@ -450,7 +455,7 @@ class LifeSpace {
|
|
|
450
455
|
displayName: 'Date', name: 'date', values: [
|
|
451
456
|
{
|
|
452
457
|
displayName: 'Field Name or ID', name: 'field', type: 'options',
|
|
453
|
-
typeOptions: { loadOptionsMethod: 'getWritableDateFields', loadOptionsDependsOn: ['spaceId', '
|
|
458
|
+
typeOptions: { loadOptionsMethod: 'getWritableDateFields', loadOptionsDependsOn: ['spaceId', 'recordType', 'operation'] },
|
|
454
459
|
options: [], default: '', required: true,
|
|
455
460
|
description: 'Choose a LifeSpace date field. The node submits calendar-date YYYY-MM-DD values. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
456
461
|
},
|
|
@@ -466,12 +471,12 @@ class LifeSpace {
|
|
|
466
471
|
{
|
|
467
472
|
displayName: 'Field Name or ID', name: 'field', type: 'options',
|
|
468
473
|
description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
|
469
|
-
typeOptions: { loadOptionsMethod: 'getSingleRelationFields', loadOptionsDependsOn: ['spaceId', '
|
|
474
|
+
typeOptions: { loadOptionsMethod: 'getSingleRelationFields', loadOptionsDependsOn: ['spaceId', 'recordType', 'operation'] },
|
|
470
475
|
options: [], default: '', required: true,
|
|
471
476
|
},
|
|
472
477
|
{
|
|
473
478
|
displayName: 'Target Name or ID', name: 'target', type: 'options',
|
|
474
|
-
typeOptions: { loadOptionsMethod: 'getRelationTargetsForCurrentField', loadOptionsDependsOn: ['spaceId', '
|
|
479
|
+
typeOptions: { loadOptionsMethod: 'getRelationTargetsForCurrentField', loadOptionsDependsOn: ['spaceId', 'recordType', '&field'] },
|
|
475
480
|
options: [], default: '',
|
|
476
481
|
description: 'Choose an authorized relation target, or use an expression with a stable LifeSpace ID. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
477
482
|
},
|
|
@@ -485,12 +490,12 @@ class LifeSpace {
|
|
|
485
490
|
{
|
|
486
491
|
displayName: 'Field Name or ID', name: 'field', type: 'options',
|
|
487
492
|
description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
|
488
|
-
typeOptions: { loadOptionsMethod: 'getMultiRelationFields', loadOptionsDependsOn: ['spaceId', '
|
|
493
|
+
typeOptions: { loadOptionsMethod: 'getMultiRelationFields', loadOptionsDependsOn: ['spaceId', 'recordType', 'operation'] },
|
|
489
494
|
options: [], default: '', required: true,
|
|
490
495
|
},
|
|
491
496
|
{
|
|
492
497
|
displayName: 'Targets Names or IDs', name: 'targets', type: 'multiOptions',
|
|
493
|
-
typeOptions: { loadOptionsMethod: 'getRelationTargetsForCurrentField', loadOptionsDependsOn: ['spaceId', '
|
|
498
|
+
typeOptions: { loadOptionsMethod: 'getRelationTargetsForCurrentField', loadOptionsDependsOn: ['spaceId', 'recordType', '&field'] },
|
|
494
499
|
options: [], default: [],
|
|
495
500
|
description: 'Choose authorized relation targets, or use an expression with stable LifeSpace IDs. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
|
|
496
501
|
},
|
|
@@ -507,30 +512,30 @@ class LifeSpace {
|
|
|
507
512
|
displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
|
|
508
513
|
options: [
|
|
509
514
|
{ displayName: 'Text Filter', name: 'text', values: [
|
|
510
|
-
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getTextFilterableFields', loadOptionsDependsOn: ['spaceId', '
|
|
515
|
+
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getTextFilterableFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true },
|
|
511
516
|
{ displayName: 'Value', name: 'value', type: 'string', default: '', required: true },
|
|
512
517
|
] },
|
|
513
518
|
{ displayName: 'Enum Filter', name: 'enum', values: [
|
|
514
|
-
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getEnumFilterableFields', loadOptionsDependsOn: ['spaceId', '
|
|
515
|
-
{ displayName: 'Value Names or IDs', name: 'values', type: 'multiOptions', description: 'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getEnumValuesForCurrentFilter', loadOptionsDependsOn: ['spaceId', '
|
|
519
|
+
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getEnumFilterableFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true },
|
|
520
|
+
{ displayName: 'Value Names or IDs', name: 'values', type: 'multiOptions', description: 'Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getEnumValuesForCurrentFilter', loadOptionsDependsOn: ['spaceId', 'recordType', '&field'] }, options: [], default: [], required: true },
|
|
516
521
|
] },
|
|
517
522
|
{ displayName: 'Boolean Filter', name: 'boolean', values: [
|
|
518
|
-
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getBooleanFilterableFields', loadOptionsDependsOn: ['spaceId', '
|
|
523
|
+
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getBooleanFilterableFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true },
|
|
519
524
|
{ displayName: 'Value', name: 'value', type: 'boolean', default: true },
|
|
520
525
|
] },
|
|
521
526
|
{ displayName: 'Number Filter', name: 'number', values: [
|
|
522
|
-
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getNumericFilterableFields', loadOptionsDependsOn: ['spaceId', '
|
|
527
|
+
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getNumericFilterableFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true },
|
|
523
528
|
{ displayName: 'Operator', name: 'operator', type: 'options', options: [{ name: 'Equals', value: 'exact' }, { name: 'From / Greater Than or Equal', value: 'from' }, { name: 'To / Less Than or Equal', value: 'to' }], default: 'exact' },
|
|
524
529
|
{ displayName: 'Value', name: 'value', type: 'number', default: 0, required: true },
|
|
525
530
|
] },
|
|
526
531
|
{ displayName: 'Date / Time Filter', name: 'temporal', values: [
|
|
527
|
-
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getTemporalFilterableFields', loadOptionsDependsOn: ['spaceId', '
|
|
532
|
+
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getTemporalFilterableFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true },
|
|
528
533
|
{ displayName: 'Operator', name: 'operator', type: 'options', options: [{ name: 'Equals', value: 'exact' }, { name: 'From / Greater Than or Equal', value: 'from' }, { name: 'To / Less Than or Equal', value: 'to' }], default: 'exact' },
|
|
529
534
|
{ displayName: 'Value', name: 'value', type: 'dateTime', default: '', required: true },
|
|
530
535
|
] },
|
|
531
536
|
{ displayName: 'Person Filter', name: 'person', values: [
|
|
532
|
-
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getPersonFilterableFields', loadOptionsDependsOn: ['spaceId', '
|
|
533
|
-
{ displayName: 'Person Name or ID', name: 'target', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getRelationTargetsForCurrentField', loadOptionsDependsOn: ['spaceId', '
|
|
537
|
+
{ displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getPersonFilterableFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true },
|
|
538
|
+
{ displayName: 'Person Name or ID', name: 'target', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getRelationTargetsForCurrentField', loadOptionsDependsOn: ['spaceId', 'recordType', '&field'] }, options: [], default: '', required: true },
|
|
534
539
|
] },
|
|
535
540
|
{ displayName: 'Raw / Legacy Filter', name: 'filter', values: [
|
|
536
541
|
{ displayName: 'Field Name or ID', name: 'field', type: 'options', typeOptions: { loadOptionsMethod: 'getFilterableFields' }, options: [], default: '', required: true, description: 'Compatibility and unsupported relation escape hatch. Prefer the typed filter variants above. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.' },
|
|
@@ -572,7 +577,7 @@ class LifeSpace {
|
|
|
572
577
|
},
|
|
573
578
|
{
|
|
574
579
|
displayName: 'Action Name or ID', name: 'actionKey', type: 'options',
|
|
575
|
-
typeOptions: { loadOptionsMethod: 'getActions', loadOptionsDependsOn: ['spaceId', '
|
|
580
|
+
typeOptions: { loadOptionsMethod: 'getActions', loadOptionsDependsOn: ['spaceId', 'recordType'] },
|
|
576
581
|
options: [], default: '', required: true,
|
|
577
582
|
displayOptions: { show: { resource: ['modelRecord'], operation: ['executeAction'] } },
|
|
578
583
|
description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
|
|
@@ -581,7 +586,7 @@ class LifeSpace {
|
|
|
581
586
|
displayName: 'Action Input', name: 'actionInput', type: 'resourceMapper',
|
|
582
587
|
default: { mappingMode: 'defineBelow', value: null }, noDataExpression: true,
|
|
583
588
|
typeOptions: {
|
|
584
|
-
loadOptionsDependsOn: ['spaceId', '
|
|
589
|
+
loadOptionsDependsOn: ['spaceId', 'recordType', 'actionKey'],
|
|
585
590
|
resourceMapper: { resourceMapperMethod: 'getActionInputFields', mode: 'add', fieldWords: { singular: 'input', plural: 'inputs' }, addAllFields: true, supportAutoMap: false, noFieldsError: 'This LifeSpace Action has no semantic input fields.' },
|
|
586
591
|
},
|
|
587
592
|
displayOptions: { show: { resource: ['modelRecord'], operation: ['executeAction'] } },
|
|
@@ -622,19 +627,26 @@ class LifeSpace {
|
|
|
622
627
|
});
|
|
623
628
|
},
|
|
624
629
|
async getRecordTypes() {
|
|
625
|
-
const spaceId =
|
|
630
|
+
const spaceId = (0, lifespaceDiscovery_1.loadOptionParameter)(this, 'spaceId');
|
|
626
631
|
if (!spaceId)
|
|
627
632
|
return [];
|
|
628
633
|
const discovery = await lifespaceDiscovery_1.loadRuntimeDiscovery.call(this);
|
|
629
634
|
const space = (0, lifespaceDiscovery_1.discoverySpace)(discovery, spaceId);
|
|
630
635
|
if (!space)
|
|
631
636
|
return [];
|
|
632
|
-
const operation =
|
|
637
|
+
const operation = (0, lifespaceDiscovery_1.loadOptionParameter)(this, 'operation') || 'list';
|
|
633
638
|
return space.models
|
|
634
639
|
.filter((model) => operation === 'executeAction'
|
|
635
640
|
? model.actions.length > 0
|
|
636
641
|
: model.access.includes(requiredAccessForOperation(operation)))
|
|
637
|
-
.map((model) => {
|
|
642
|
+
.map((model) => {
|
|
643
|
+
var _a;
|
|
644
|
+
return ({
|
|
645
|
+
name: `${model.display.plural} (${model.key})`,
|
|
646
|
+
value: (0, lifespaceDiscovery_1.encodeRecordTypeSelector)(model.key, model.route),
|
|
647
|
+
description: (_a = model.description) !== null && _a !== void 0 ? _a : undefined,
|
|
648
|
+
});
|
|
649
|
+
});
|
|
638
650
|
},
|
|
639
651
|
async getActions() {
|
|
640
652
|
const selected = await optionModel(this);
|
|
@@ -658,7 +670,7 @@ class LifeSpace {
|
|
|
658
670
|
const selected = await optionModel(this);
|
|
659
671
|
if (!selected)
|
|
660
672
|
return [];
|
|
661
|
-
const operation =
|
|
673
|
+
const operation = (0, lifespaceDiscovery_1.loadOptionParameter)(this, 'operation') || 'create';
|
|
662
674
|
return selected.model.fields
|
|
663
675
|
.filter((field) => field.type === 'date' && !field.readOnly && (operation !== 'update' || !field.immutable))
|
|
664
676
|
.map((field) => { var _a; return ({ name: ((_a = field.title) === null || _a === void 0 ? void 0 : _a.trim()) || (0, lifespaceDiscovery_1.humanizeKey)(field.key), value: field.key, description: field.description }); });
|
|
@@ -713,7 +725,7 @@ class LifeSpace {
|
|
|
713
725
|
const selected = await optionModel(this);
|
|
714
726
|
if (!selected)
|
|
715
727
|
return { fields: [] };
|
|
716
|
-
const operation =
|
|
728
|
+
const operation = (0, lifespaceDiscovery_1.loadOptionParameter)(this, 'operation') || 'create';
|
|
717
729
|
const writableFields = selected.model.fields
|
|
718
730
|
.filter((field) => !field.readOnly && (operation !== 'update' || !field.immutable))
|
|
719
731
|
.filter((field) => field.type !== 'date')
|
|
@@ -724,7 +736,7 @@ class LifeSpace {
|
|
|
724
736
|
},
|
|
725
737
|
async getActionInputFields() {
|
|
726
738
|
const selected = await optionModel(this);
|
|
727
|
-
const actionKey =
|
|
739
|
+
const actionKey = (0, lifespaceDiscovery_1.loadOptionParameter)(this, 'actionKey');
|
|
728
740
|
const action = selected === null || selected === void 0 ? void 0 : selected.model.actions.find((entry) => entry.key === actionKey);
|
|
729
741
|
return action
|
|
730
742
|
? { fields: action.input.fields.map((field) => mapperField(field, field.required === true)) }
|
|
@@ -734,7 +746,6 @@ class LifeSpace {
|
|
|
734
746
|
};
|
|
735
747
|
}
|
|
736
748
|
async execute() {
|
|
737
|
-
var _a;
|
|
738
749
|
const items = this.getInputData();
|
|
739
750
|
const output = [];
|
|
740
751
|
const credentials = await this.getCredentials('lifeSpaceApi');
|
|
@@ -746,7 +757,12 @@ class LifeSpace {
|
|
|
746
757
|
if (resource === 'modelRecord') {
|
|
747
758
|
const operation = this.getNodeParameter('operation', itemIndex);
|
|
748
759
|
const rawSpaceId = String(this.getNodeParameter('spaceId', itemIndex));
|
|
749
|
-
const
|
|
760
|
+
const recordType = (0, lifespaceDiscovery_1.decodeRecordTypeSelector)(this.getNodeParameter('recordType', itemIndex));
|
|
761
|
+
if (!recordType) {
|
|
762
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'LifeSpace Record Type selector is invalid. Choose a Record Type from Discovery or pass a Trigger recordType value.', { itemIndex });
|
|
763
|
+
}
|
|
764
|
+
const rawModelKey = recordType.modelKey;
|
|
765
|
+
const rawModelRoute = recordType.route;
|
|
750
766
|
const spaceId = encodeURIComponent(rawSpaceId);
|
|
751
767
|
const modelRoute = encodeURIComponent(rawModelRoute);
|
|
752
768
|
const collectionPath = `/spaces/${spaceId}/${modelRoute}`;
|
|
@@ -787,8 +803,6 @@ class LifeSpace {
|
|
|
787
803
|
}
|
|
788
804
|
else if (operation === 'create') {
|
|
789
805
|
const body = mutationMappedValues(this, itemIndex);
|
|
790
|
-
const model = await executionModel(this, itemIndex, baseUrl, rawSpaceId, rawModelRoute);
|
|
791
|
-
(0, calendarMutation_1.validateCalendarMutation)(this, itemIndex, model, body);
|
|
792
806
|
response = await this.helpers.httpRequestWithAuthentication.call(this, 'lifeSpaceApi', { method: 'POST', url: `${baseUrl}${collectionPath}`, body, json: true });
|
|
793
807
|
}
|
|
794
808
|
else {
|
|
@@ -800,17 +814,12 @@ class LifeSpace {
|
|
|
800
814
|
}
|
|
801
815
|
else if (operation === 'update') {
|
|
802
816
|
const body = mutationMappedValues(this, itemIndex);
|
|
803
|
-
const model = await executionModel(this, itemIndex, baseUrl, rawSpaceId, rawModelRoute);
|
|
804
|
-
const loadedRecord = ((_a = model.capabilityBindings) === null || _a === void 0 ? void 0 : _a.calendar)
|
|
805
|
-
? await currentRecord(this, itemIndex, baseUrl, recordPath)
|
|
806
|
-
: undefined;
|
|
807
|
-
(0, calendarMutation_1.validateCalendarMutation)(this, itemIndex, model, body, loadedRecord);
|
|
808
817
|
options = {
|
|
809
818
|
method: 'PATCH',
|
|
810
819
|
url: `${baseUrl}${recordPath}`,
|
|
811
820
|
body: {
|
|
812
821
|
...body,
|
|
813
|
-
version: await mutationVersion(this, itemIndex, baseUrl, recordPath
|
|
822
|
+
version: await mutationVersion(this, itemIndex, baseUrl, recordPath),
|
|
814
823
|
},
|
|
815
824
|
json: true,
|
|
816
825
|
};
|
|
@@ -829,7 +838,7 @@ class LifeSpace {
|
|
|
829
838
|
options = {
|
|
830
839
|
method: 'POST',
|
|
831
840
|
url: `${baseUrl}${recordPath}/actions/${actionKey}`,
|
|
832
|
-
body: await actionBodyWithConcurrency(this, itemIndex, baseUrl, rawSpaceId,
|
|
841
|
+
body: await actionBodyWithConcurrency(this, itemIndex, baseUrl, rawSpaceId, rawModelKey, recordPath, rawActionKey, mappedValue(this, itemIndex, 'actionInput')),
|
|
833
842
|
json: true,
|
|
834
843
|
};
|
|
835
844
|
}
|