apostrophe 3.40.2-alpha → 3.41.0
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/CHANGELOG.md +6 -1
- package/modules/@apostrophecms/doc/index.js +1 -1
- package/modules/@apostrophecms/i18n/i18n/en.json +1 -0
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +120 -4
- package/modules/@apostrophecms/schema/index.js +83 -12
- package/package.json +1 -1
- package/test/schemas.js +184 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 3.41.0 (2023-03-06)
|
|
4
|
+
|
|
5
|
+
### Adds
|
|
6
|
+
|
|
7
|
+
* Handle external conditions to display fields according to the result of a module method, or multiple methods from different modules.
|
|
8
|
+
This can be useful for displaying fields according to the result of an external API or any business logic run on the server.
|
|
4
9
|
|
|
5
10
|
### Fixes
|
|
6
11
|
|
|
@@ -1014,7 +1014,7 @@ module.exports = {
|
|
|
1014
1014
|
// }
|
|
1015
1015
|
// All properties are required.
|
|
1016
1016
|
// The only supported `context` for now is `update`.
|
|
1017
|
-
// `action` is the operation
|
|
1017
|
+
// `action` is the operation identifier and should be globally unique.
|
|
1018
1018
|
// Overriding existing custom actions is possible (the last wins).
|
|
1019
1019
|
// `modal` is the name of the modal component to be opened.
|
|
1020
1020
|
// `label` is the menu label to be shown when expanding the context menu.
|
|
@@ -149,6 +149,7 @@
|
|
|
149
149
|
"errorCount": "{{ count }} error remaining",
|
|
150
150
|
"errorCount_plural": "{{ count }} errors remaining",
|
|
151
151
|
"errorCreatingNewContent": "Error while creating new, empty content.",
|
|
152
|
+
"errorEvaluatingExternalCondition": "An error occurred while evaluating the external condition for the field \"{{ name }}\"",
|
|
152
153
|
"errorFetchingTitleFieldChoicesByMethod": "An error occurred while fetching the choices for the titleField {{ name }}",
|
|
153
154
|
"errorWhileRestoring": "An error occurred while restoring the previously published version.",
|
|
154
155
|
"errorWhileUnpublishing": "An error occurred while unpublishing the document.",
|
|
@@ -24,7 +24,8 @@ export default {
|
|
|
24
24
|
},
|
|
25
25
|
serverErrors: null,
|
|
26
26
|
restoreOnly: false,
|
|
27
|
-
changed: []
|
|
27
|
+
changed: [],
|
|
28
|
+
externalConditionsResults: {}
|
|
28
29
|
};
|
|
29
30
|
},
|
|
30
31
|
|
|
@@ -43,7 +44,91 @@ export default {
|
|
|
43
44
|
}
|
|
44
45
|
},
|
|
45
46
|
|
|
47
|
+
watch: {
|
|
48
|
+
docType: {
|
|
49
|
+
// Evaluate external conditions found in current page-type's schema
|
|
50
|
+
async handler() {
|
|
51
|
+
if (this.moduleName === '@apostrophecms/page') {
|
|
52
|
+
await this.evaluateExternalConditions();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
async created() {
|
|
59
|
+
await this.evaluateExternalConditions();
|
|
60
|
+
},
|
|
61
|
+
|
|
46
62
|
methods: {
|
|
63
|
+
// Evaluate the external conditions found in each field
|
|
64
|
+
// via API calls -made in parallel for performance-
|
|
65
|
+
// and store their result for reusability.
|
|
66
|
+
async evaluateExternalConditions() {
|
|
67
|
+
const self = this;
|
|
68
|
+
for (const field of this.schema) {
|
|
69
|
+
if (field.if) {
|
|
70
|
+
const externalConditionKeys = Object
|
|
71
|
+
.entries(field.if)
|
|
72
|
+
.flatMap(getExternalConditionKeys)
|
|
73
|
+
.filter(Boolean);
|
|
74
|
+
|
|
75
|
+
const uniqExternalConditionKeys = [ ...new Set(externalConditionKeys) ];
|
|
76
|
+
|
|
77
|
+
let results = [];
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const docOrContextDocId = this.docId || this.docFields.data._docId;
|
|
81
|
+
const promises = uniqExternalConditionKeys
|
|
82
|
+
.map(key => this.externalConditionsResults[key] !== undefined
|
|
83
|
+
? null
|
|
84
|
+
: this.evaluateExternalCondition(key, field._id, docOrContextDocId)
|
|
85
|
+
)
|
|
86
|
+
.filter(Boolean);
|
|
87
|
+
|
|
88
|
+
results = await Promise.all(promises);
|
|
89
|
+
|
|
90
|
+
this.externalConditionsResults = {
|
|
91
|
+
...this.externalConditionsResults,
|
|
92
|
+
...Object.fromEntries(results)
|
|
93
|
+
};
|
|
94
|
+
} catch (error) {
|
|
95
|
+
await apos.notify(this.$t('apostrophe:errorEvaluatingExternalCondition', { name: field.name }), {
|
|
96
|
+
type: 'danger',
|
|
97
|
+
icon: 'alert-circle-icon',
|
|
98
|
+
dismiss: true,
|
|
99
|
+
localize: false
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getExternalConditionKeys([ key, val ]) {
|
|
106
|
+
if (key === '$or') {
|
|
107
|
+
return val.flatMap(nested => Object.entries(nested).map(getExternalConditionKeys));
|
|
108
|
+
}
|
|
109
|
+
if (self.isExternalCondition(key)) {
|
|
110
|
+
return key;
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
async evaluateExternalCondition(conditionKey, fieldId, docId) {
|
|
117
|
+
const response = await apos.http.get(
|
|
118
|
+
`${apos.schema.action}/evaluate-external-condition`,
|
|
119
|
+
{
|
|
120
|
+
qs: {
|
|
121
|
+
fieldId,
|
|
122
|
+
docId,
|
|
123
|
+
conditionKey
|
|
124
|
+
},
|
|
125
|
+
busy: true
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
return [ conditionKey, response ];
|
|
130
|
+
},
|
|
131
|
+
|
|
47
132
|
// followedByCategory may be falsy (all fields), "other" or "utility". The returned
|
|
48
133
|
// object contains properties named for each field in that category that
|
|
49
134
|
// follows other fields. For instance if followedBy is "utility" then in our
|
|
@@ -93,8 +178,22 @@ export default {
|
|
|
93
178
|
// in that category, although they may be conditional upon fields in either
|
|
94
179
|
// category.
|
|
95
180
|
|
|
96
|
-
|
|
181
|
+
// Checking if key ends with a closing parenthesis here to throw later if any argument is passed.
|
|
182
|
+
isExternalCondition(conditionKey) {
|
|
183
|
+
if (!conditionKey.endsWith(')')) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const [ methodDefinition ] = conditionKey.split('(');
|
|
188
|
+
|
|
189
|
+
if (!conditionKey.endsWith('()')) {
|
|
190
|
+
console.warn(`Warning in \`if\` definition: "${methodDefinition}()" should not be passed any argument.`);
|
|
191
|
+
}
|
|
97
192
|
|
|
193
|
+
return true;
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
conditionalFields(followedByCategory) {
|
|
98
197
|
const self = this;
|
|
99
198
|
const conditionalFields = {};
|
|
100
199
|
|
|
@@ -128,8 +227,26 @@ export default {
|
|
|
128
227
|
let result = true;
|
|
129
228
|
for (const [ key, val ] of Object.entries(clause)) {
|
|
130
229
|
if (key === '$or') {
|
|
131
|
-
|
|
230
|
+
if (!val.some(clause => evaluate(clause))) {
|
|
231
|
+
result = false;
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// No need to go further here, the key is an "$or" condition...
|
|
236
|
+
continue;
|
|
132
237
|
}
|
|
238
|
+
|
|
239
|
+
if (self.isExternalCondition(key)) {
|
|
240
|
+
if (self.externalConditionsResults[key] !== val) {
|
|
241
|
+
result = false;
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Stop there, this is an external condition thus
|
|
246
|
+
// does not need to be checked against doc fields.
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
|
|
133
250
|
if (conditionalFields[key] === false) {
|
|
134
251
|
result = false;
|
|
135
252
|
break;
|
|
@@ -145,7 +262,6 @@ export default {
|
|
|
145
262
|
}
|
|
146
263
|
return result;
|
|
147
264
|
}
|
|
148
|
-
|
|
149
265
|
},
|
|
150
266
|
|
|
151
267
|
// Overridden by components that split the fields into several AposSchemas
|
|
@@ -487,18 +487,21 @@ module.exports = {
|
|
|
487
487
|
throw new Error('convert invoked without a req, do you have one in your context?');
|
|
488
488
|
}
|
|
489
489
|
|
|
490
|
-
|
|
490
|
+
const errors = [];
|
|
491
491
|
|
|
492
492
|
for (const field of schema) {
|
|
493
493
|
if (field.readOnly) {
|
|
494
494
|
continue;
|
|
495
495
|
}
|
|
496
|
+
|
|
496
497
|
// Fields that are contextual are left alone, not blanked out, if
|
|
497
498
|
// they do not appear at all in the data object.
|
|
498
499
|
if (field.contextual && !_.has(data, field.name)) {
|
|
499
500
|
continue;
|
|
500
501
|
}
|
|
501
|
-
|
|
502
|
+
|
|
503
|
+
const { convert } = self.fieldTypes[field.type];
|
|
504
|
+
|
|
502
505
|
if (convert) {
|
|
503
506
|
try {
|
|
504
507
|
await convert(req, field, data, destination);
|
|
@@ -520,15 +523,21 @@ module.exports = {
|
|
|
520
523
|
}
|
|
521
524
|
}
|
|
522
525
|
|
|
523
|
-
|
|
524
|
-
|
|
526
|
+
const errorsList = [];
|
|
527
|
+
|
|
528
|
+
for (const error of errors) {
|
|
529
|
+
const isVisible = await self.isVisible(req, schema, destination, error.path);
|
|
530
|
+
|
|
531
|
+
if ((error.name === 'required' || error.name === 'mandatory') && !isVisible) {
|
|
525
532
|
// It is not reasonable to enforce required for
|
|
526
533
|
// fields hidden via conditional fields
|
|
527
|
-
|
|
534
|
+
continue;
|
|
528
535
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
536
|
+
|
|
537
|
+
errorsList.push(error);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (errorsList.length) {
|
|
532
541
|
throw errors;
|
|
533
542
|
}
|
|
534
543
|
},
|
|
@@ -536,13 +545,13 @@ module.exports = {
|
|
|
536
545
|
// Determine whether the given field is visible
|
|
537
546
|
// based on `if` conditions of all fields
|
|
538
547
|
|
|
539
|
-
isVisible(schema, object, name) {
|
|
548
|
+
async isVisible(req, schema, object, name) {
|
|
540
549
|
const conditionalFields = {};
|
|
541
550
|
while (true) {
|
|
542
551
|
let change = false;
|
|
543
552
|
for (const field of schema) {
|
|
544
553
|
if (field.if) {
|
|
545
|
-
const result = evaluate(field.if);
|
|
554
|
+
const result = await evaluate(field.if, field.name, field.moduleName);
|
|
546
555
|
const previous = conditionalFields[field.name];
|
|
547
556
|
if (previous !== result) {
|
|
548
557
|
change = true;
|
|
@@ -559,12 +568,38 @@ module.exports = {
|
|
|
559
568
|
} else {
|
|
560
569
|
return true;
|
|
561
570
|
}
|
|
562
|
-
function evaluate(clause) {
|
|
571
|
+
async function evaluate(clause, fieldName, fieldModuleName) {
|
|
563
572
|
let result = true;
|
|
564
573
|
for (const [ key, val ] of Object.entries(clause)) {
|
|
565
574
|
if (key === '$or') {
|
|
566
|
-
|
|
575
|
+
const results = await Promise.all(val.map(clause => evaluate(clause, fieldName, fieldModuleName)));
|
|
576
|
+
|
|
577
|
+
if (!results.some(({ value }) => value)) {
|
|
578
|
+
result = false;
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// No need to go further here, the key is an "$or" condition...
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Handle external conditions:
|
|
587
|
+
// - `if: { 'methodName()': true }`
|
|
588
|
+
// - `if: { 'moduleName:methodName()': 'expected value' }`
|
|
589
|
+
// Checking if key ends with a closing parenthesis here to throw later if any argument is passed.
|
|
590
|
+
if (key.endsWith(')')) {
|
|
591
|
+
const externalConditionResult = await self.evaluateExternalCondition(req, key, fieldName, fieldModuleName, object._id);
|
|
592
|
+
|
|
593
|
+
if (externalConditionResult !== val) {
|
|
594
|
+
result = false;
|
|
595
|
+
break;
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
// Stop there, this is an external condition thus
|
|
599
|
+
// does not need to be checked against doc fields.
|
|
600
|
+
continue;
|
|
567
601
|
}
|
|
602
|
+
|
|
568
603
|
if (conditionalFields[key] === false) {
|
|
569
604
|
result = false;
|
|
570
605
|
break;
|
|
@@ -578,6 +613,28 @@ module.exports = {
|
|
|
578
613
|
}
|
|
579
614
|
},
|
|
580
615
|
|
|
616
|
+
async evaluateExternalCondition(req, conditionKey, fieldName, fieldModuleName, docId = null) {
|
|
617
|
+
const [ methodDefinition ] = conditionKey.split('(');
|
|
618
|
+
|
|
619
|
+
if (!conditionKey.endsWith('()')) {
|
|
620
|
+
self.apos.util.warn(`Warning in the \`if\` definition of the "${fieldName}" field: "${methodDefinition}()" should not be passed any argument.`);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const [ methodName, moduleName = fieldModuleName ] = methodDefinition
|
|
624
|
+
.split(':')
|
|
625
|
+
.reverse();
|
|
626
|
+
|
|
627
|
+
const module = self.apos.modules[moduleName];
|
|
628
|
+
|
|
629
|
+
if (!module) {
|
|
630
|
+
throw new Error(`Error in the \`if\` definition of the "${fieldName}" field: "${moduleName}" module not found.`);
|
|
631
|
+
} else if (!module[methodName]) {
|
|
632
|
+
throw new Error(`Error in the \`if\` definition of the "${fieldName}" field: "${methodName}" method not found in "${moduleName}" module.`);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return module[methodName](req, { docId });
|
|
636
|
+
},
|
|
637
|
+
|
|
581
638
|
// Driver invoked by the "relationship" methods of the standard
|
|
582
639
|
// relationship field types.
|
|
583
640
|
//
|
|
@@ -1493,6 +1550,20 @@ module.exports = {
|
|
|
1493
1550
|
} else {
|
|
1494
1551
|
throw self.apos.error('invalid', `The method ${field.choices} from the module ${field.moduleName} did not return an array`);
|
|
1495
1552
|
}
|
|
1553
|
+
},
|
|
1554
|
+
async evaluateExternalCondition(req) {
|
|
1555
|
+
const fieldId = self.apos.launder.string(req.query.fieldId);
|
|
1556
|
+
const docId = self.apos.launder.string(req.query.docId, null);
|
|
1557
|
+
const conditionKey = self.apos.launder.string(req.query.conditionKey);
|
|
1558
|
+
|
|
1559
|
+
const field = self.getFieldById(fieldId);
|
|
1560
|
+
|
|
1561
|
+
try {
|
|
1562
|
+
const result = await self.evaluateExternalCondition(req, conditionKey, field.name, field.moduleName, docId);
|
|
1563
|
+
return result;
|
|
1564
|
+
} catch (error) {
|
|
1565
|
+
throw self.apos.error('invalid', error.message);
|
|
1566
|
+
}
|
|
1496
1567
|
}
|
|
1497
1568
|
}
|
|
1498
1569
|
};
|
package/package.json
CHANGED
package/test/schemas.js
CHANGED
|
@@ -235,6 +235,8 @@ const hasAreaWithoutWidgets = {
|
|
|
235
235
|
]
|
|
236
236
|
};
|
|
237
237
|
|
|
238
|
+
const warnMessages = [];
|
|
239
|
+
|
|
238
240
|
describe('Schemas', function() {
|
|
239
241
|
|
|
240
242
|
this.timeout(t.timeout);
|
|
@@ -249,7 +251,31 @@ describe('Schemas', function() {
|
|
|
249
251
|
|
|
250
252
|
it('should be a property of the apos object', async function() {
|
|
251
253
|
apos = await t.create({
|
|
252
|
-
root: module
|
|
254
|
+
root: module,
|
|
255
|
+
modules: {
|
|
256
|
+
'@apostrophecms/util': {
|
|
257
|
+
extendMethods() {
|
|
258
|
+
return {
|
|
259
|
+
warn(_super, ...args) {
|
|
260
|
+
warnMessages.push(...args);
|
|
261
|
+
return _super(...args);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
'external-condition': {
|
|
267
|
+
methods() {
|
|
268
|
+
return {
|
|
269
|
+
async externalCondition() {
|
|
270
|
+
return 'yes';
|
|
271
|
+
},
|
|
272
|
+
async externalCondition2(req, { docId }) {
|
|
273
|
+
return `yes - ${req.someReqAttr} - ${docId}`;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
253
279
|
});
|
|
254
280
|
assert(apos.schema);
|
|
255
281
|
apos.argv._ = [];
|
|
@@ -1805,6 +1831,163 @@ describe('Schemas', function() {
|
|
|
1805
1831
|
}, 'age', 'required');
|
|
1806
1832
|
});
|
|
1807
1833
|
|
|
1834
|
+
it('should ignore required property when external condition does not match', async function() {
|
|
1835
|
+
const req = apos.task.getReq();
|
|
1836
|
+
const schema = apos.schema.compose({
|
|
1837
|
+
addFields: [
|
|
1838
|
+
{
|
|
1839
|
+
name: 'age',
|
|
1840
|
+
type: 'integer',
|
|
1841
|
+
required: true,
|
|
1842
|
+
if: {
|
|
1843
|
+
'external-condition:externalCondition()': 'no'
|
|
1844
|
+
}
|
|
1845
|
+
},
|
|
1846
|
+
{
|
|
1847
|
+
name: 'shoeSize',
|
|
1848
|
+
type: 'integer',
|
|
1849
|
+
required: false
|
|
1850
|
+
}
|
|
1851
|
+
]
|
|
1852
|
+
});
|
|
1853
|
+
const output = {};
|
|
1854
|
+
await apos.schema.convert(req, schema, {
|
|
1855
|
+
shoeSize: 20
|
|
1856
|
+
}, output);
|
|
1857
|
+
assert(output.shoeSize === 20);
|
|
1858
|
+
});
|
|
1859
|
+
|
|
1860
|
+
it('should enforce required property when external condition matches', async function() {
|
|
1861
|
+
const schema = apos.schema.compose({
|
|
1862
|
+
addFields: [
|
|
1863
|
+
{
|
|
1864
|
+
name: 'age',
|
|
1865
|
+
type: 'integer',
|
|
1866
|
+
required: true,
|
|
1867
|
+
if: {
|
|
1868
|
+
'external-condition:externalCondition()': 'yes'
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
]
|
|
1872
|
+
});
|
|
1873
|
+
|
|
1874
|
+
await testSchemaError(schema, {}, 'age', 'required');
|
|
1875
|
+
});
|
|
1876
|
+
|
|
1877
|
+
it('should use the field module name by default when the external condition key does not contain it', async function() {
|
|
1878
|
+
const req = apos.task.getReq();
|
|
1879
|
+
const conditionKey = 'externalCondition()';
|
|
1880
|
+
const fieldName = 'someField';
|
|
1881
|
+
const fieldModuleName = 'external-condition';
|
|
1882
|
+
const docId = 'some-doc-id';
|
|
1883
|
+
|
|
1884
|
+
const result = await apos.schema.evaluateExternalCondition(req, conditionKey, fieldName, fieldModuleName, docId);
|
|
1885
|
+
|
|
1886
|
+
assert(result === 'yes');
|
|
1887
|
+
});
|
|
1888
|
+
|
|
1889
|
+
it('should pass req and the doc ID to the external condition method', async function() {
|
|
1890
|
+
const someReqAttr = 'some-attribute-on-req';
|
|
1891
|
+
const req = apos.task.getReq({
|
|
1892
|
+
someReqAttr
|
|
1893
|
+
});
|
|
1894
|
+
const conditionKey = 'external-condition:externalCondition2()';
|
|
1895
|
+
const fieldName = 'someField';
|
|
1896
|
+
const fieldModuleName = 'external-condition';
|
|
1897
|
+
const docId = 'some-doc-id';
|
|
1898
|
+
|
|
1899
|
+
const result = await apos.schema.evaluateExternalCondition(req, conditionKey, fieldName, fieldModuleName, docId);
|
|
1900
|
+
|
|
1901
|
+
assert(result === `yes - ${someReqAttr} - ${docId}`);
|
|
1902
|
+
});
|
|
1903
|
+
|
|
1904
|
+
it('should warn when an argument is passed in the external condition key', async function() {
|
|
1905
|
+
const req = apos.task.getReq();
|
|
1906
|
+
const conditionKey = 'external-condition:externalCondition(letsNotArgue)';
|
|
1907
|
+
const fieldName = 'someField';
|
|
1908
|
+
const fieldModuleName = 'external-condition';
|
|
1909
|
+
const docId = 'some-doc-id';
|
|
1910
|
+
|
|
1911
|
+
const result = await apos.schema.evaluateExternalCondition(req, conditionKey, fieldName, fieldModuleName, docId);
|
|
1912
|
+
|
|
1913
|
+
assert(warnMessages.includes('Warning in the `if` definition of the "someField" field: "external-condition:externalCondition()" should not be passed any argument.'));
|
|
1914
|
+
assert(result === 'yes');
|
|
1915
|
+
});
|
|
1916
|
+
|
|
1917
|
+
it('should throw when the module defined in the external condition key is not found', async function() {
|
|
1918
|
+
const req = apos.task.getReq();
|
|
1919
|
+
const conditionKey = 'unknown-module:externalCondition()';
|
|
1920
|
+
const fieldName = 'someField';
|
|
1921
|
+
const fieldModuleName = 'unknown-module';
|
|
1922
|
+
const docId = 'some-doc-id';
|
|
1923
|
+
|
|
1924
|
+
try {
|
|
1925
|
+
await apos.schema.evaluateExternalCondition(req, conditionKey, fieldName, fieldModuleName, docId);
|
|
1926
|
+
} catch (error) {
|
|
1927
|
+
assert(error.message === 'Error in the `if` definition of the "someField" field: "unknown-module" module not found.');
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1930
|
+
throw new Error('should have thrown');
|
|
1931
|
+
});
|
|
1932
|
+
|
|
1933
|
+
it('should throw when the method defined in the external condition key is not found', async function() {
|
|
1934
|
+
const req = apos.task.getReq();
|
|
1935
|
+
const conditionKey = 'external-condition:unknownMethod()';
|
|
1936
|
+
const fieldName = 'someField';
|
|
1937
|
+
const fieldModuleName = 'external-condition';
|
|
1938
|
+
const docId = 'some-doc-id';
|
|
1939
|
+
|
|
1940
|
+
try {
|
|
1941
|
+
await apos.schema.evaluateExternalCondition(req, conditionKey, fieldName, fieldModuleName, docId);
|
|
1942
|
+
} catch (error) {
|
|
1943
|
+
assert(error.message === 'Error in the `if` definition of the "someField" field: "unknownMethod" method not found in "external-condition" module.');
|
|
1944
|
+
return;
|
|
1945
|
+
}
|
|
1946
|
+
throw new Error('should have thrown');
|
|
1947
|
+
});
|
|
1948
|
+
|
|
1949
|
+
it('should call the evaluate-external-condition API successfully', async function() {
|
|
1950
|
+
apos.schema.fieldsById['some-field-id'] = {
|
|
1951
|
+
name: 'someField',
|
|
1952
|
+
moduleName: 'external-condition'
|
|
1953
|
+
};
|
|
1954
|
+
|
|
1955
|
+
const res = await apos.http.get('/api/v1/@apostrophecms/schema/evaluate-external-condition?fieldId=some-field-id&docId=some-doc-id&conditionKey=externalCondition()', {});
|
|
1956
|
+
assert(res === 'yes');
|
|
1957
|
+
});
|
|
1958
|
+
|
|
1959
|
+
it('should receive a clean error response when the evaluate-external-condition API call fails (module not found)', async function() {
|
|
1960
|
+
apos.schema.fieldsById['some-field-id'] = {
|
|
1961
|
+
name: 'someField',
|
|
1962
|
+
moduleName: 'unknown-module'
|
|
1963
|
+
};
|
|
1964
|
+
|
|
1965
|
+
try {
|
|
1966
|
+
await apos.http.get('/api/v1/@apostrophecms/schema/evaluate-external-condition?fieldId=some-field-id&docId=some-doc-id&conditionKey=externalCondition()', {});
|
|
1967
|
+
} catch (error) {
|
|
1968
|
+
assert(error.status = 400);
|
|
1969
|
+
assert(error.body.message === 'Error in the `if` definition of the "someField" field: "unknown-module" module not found.');
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
throw new Error('should have thrown');
|
|
1973
|
+
});
|
|
1974
|
+
|
|
1975
|
+
it('should receive a clean error response when the evaluate-external-condition API call fails (external method not found)', async function() {
|
|
1976
|
+
apos.schema.fieldsById['some-field-id'] = {
|
|
1977
|
+
name: 'someField',
|
|
1978
|
+
moduleName: 'external-condition'
|
|
1979
|
+
};
|
|
1980
|
+
|
|
1981
|
+
try {
|
|
1982
|
+
await apos.http.get('/api/v1/@apostrophecms/schema/evaluate-external-condition?fieldId=some-field-id&docId=some-doc-id&conditionKey=unknownMethod()', {});
|
|
1983
|
+
} catch (error) {
|
|
1984
|
+
assert(error.status = 400);
|
|
1985
|
+
assert(error.body.message === 'Error in the `if` definition of the "someField" field: "unknownMethod" method not found in "external-condition" module.');
|
|
1986
|
+
return;
|
|
1987
|
+
}
|
|
1988
|
+
throw new Error('should have thrown');
|
|
1989
|
+
});
|
|
1990
|
+
|
|
1808
1991
|
it('should save date and time with the right format', async function () {
|
|
1809
1992
|
const req = apos.task.getReq();
|
|
1810
1993
|
const schema = apos.schema.compose({
|