apostrophe 3.46.0 → 3.47.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/.github/workflows/main.yml +2 -2
- package/CHANGELOG.md +21 -5
- package/modules/@apostrophecms/doc-type/index.js +11 -17
- package/modules/@apostrophecms/doc-type/lib/extendQueries.js +21 -0
- package/modules/@apostrophecms/oembed-field/ui/apos/components/AposInputOembed.vue +15 -7
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +3 -1
- package/package.json +4 -4
- package/test/queryBuilders.js +225 -0
|
@@ -20,8 +20,8 @@ jobs:
|
|
|
20
20
|
runs-on: ubuntu-latest
|
|
21
21
|
strategy:
|
|
22
22
|
matrix:
|
|
23
|
-
node-version: [
|
|
24
|
-
mongodb-version: [4.
|
|
23
|
+
node-version: [16, 18]
|
|
24
|
+
mongodb-version: [4.4, 5.0, 6.0]
|
|
25
25
|
|
|
26
26
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
27
27
|
steps:
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.47.0 (2023-05-05)
|
|
4
|
+
|
|
5
|
+
### Changes
|
|
6
|
+
|
|
7
|
+
* Since Node 14 and MongoDB 4.2 have reached their own end-of-support dates,
|
|
8
|
+
we are **no longer supporting them for A3.** Note that our dependency on
|
|
9
|
+
`jsdom` 22 is incompatible with Node 14. Node 16 and Node 18 are both
|
|
10
|
+
still supported. However, because Node 16 reaches its
|
|
11
|
+
end-of-life date quite soon (September), testing and upgrading directly
|
|
12
|
+
to Node 18 is strongly recommended.
|
|
13
|
+
* Updated `sluggo` to version 1.0.0.
|
|
14
|
+
* Updated `jsdom` to version `22.0.0` to address an installation warning about the `word-wrap` module.
|
|
15
|
+
|
|
16
|
+
### Fixes
|
|
17
|
+
|
|
18
|
+
* Fix `extendQueries` to use super pattern for every function in builders and methods (and override properties that are not functions).
|
|
19
|
+
|
|
3
20
|
## 3.46.0 (2023-05-03)
|
|
4
21
|
|
|
5
22
|
### Fixes
|
|
@@ -58,7 +75,11 @@ shouldn't close the link dialog etc.
|
|
|
58
75
|
|
|
59
76
|
### Fixes
|
|
60
77
|
|
|
78
|
+
* Fix various issues on conditional fields that were occurring when adding new widgets with default values or selecting a falsy value in a field that has a conditional field relying on it.
|
|
79
|
+
Populate new or existing doc instances with default values and add an empty `null` choice to select fields that do not have a default value (required or not) and to the ones configured with dynamic choices.
|
|
61
80
|
* Rich text widgets save more reliably when many actions are taken quickly just before save.
|
|
81
|
+
* Fix an issue in the `oembed` field where the value was kept in memory after cancelling the widget editor, which resulted in saving the value if the widget was nested and the parent widget was saved.
|
|
82
|
+
Also improve the `oembed` field UX by setting the input as `readonly` rather than `disabled` when fetching the video metadata, in order to avoid losing its focus when typing.
|
|
62
83
|
|
|
63
84
|
## 3.44.0 (2023-04-13)
|
|
64
85
|
|
|
@@ -78,11 +99,6 @@ those writing mocha tests of Apostrophe modules.
|
|
|
78
99
|
### Fixes
|
|
79
100
|
* Fix child page slug when title is deleted
|
|
80
101
|
|
|
81
|
-
### Fixes
|
|
82
|
-
|
|
83
|
-
* Fix various issues on conditional fields that were occurring when adding new widgets with default values or selecting a falsy value in a field that has a conditional field relying on it.
|
|
84
|
-
Populate new or existing doc instances with default values and add an empty `null` choice to select fields that do not have a default value (required or not) and to the ones configured with dynamic choices.
|
|
85
|
-
|
|
86
102
|
## 3.43.0 (2023-03-29)
|
|
87
103
|
|
|
88
104
|
### Adds
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const { SemanticAttributes } = require('@opentelemetry/semantic-conventions');
|
|
2
2
|
const _ = require('lodash');
|
|
3
3
|
const util = require('util');
|
|
4
|
+
const extendQueries = require('./lib/extendQueries');
|
|
4
5
|
|
|
5
6
|
module.exports = {
|
|
6
7
|
options: {
|
|
@@ -472,8 +473,9 @@ module.exports = {
|
|
|
472
473
|
}
|
|
473
474
|
}
|
|
474
475
|
if (self.extendQueries[name]) {
|
|
475
|
-
|
|
476
|
-
|
|
476
|
+
const extendedQueries = self.extendQueries[name](self, query);
|
|
477
|
+
extendQueries(query.builders, extendedQueries.builders || {});
|
|
478
|
+
extendQueries(query.methods, extendedQueries.methods || {});
|
|
477
479
|
}
|
|
478
480
|
}
|
|
479
481
|
Object.assign(query, query.methods);
|
|
@@ -2322,8 +2324,14 @@ module.exports = {
|
|
|
2322
2324
|
query.and({ $or });
|
|
2323
2325
|
}
|
|
2324
2326
|
}
|
|
2325
|
-
}
|
|
2327
|
+
},
|
|
2326
2328
|
|
|
2329
|
+
viewContext: {
|
|
2330
|
+
def: null,
|
|
2331
|
+
launder(viewContext) {
|
|
2332
|
+
return [ 'manage', 'relationship' ].includes(viewContext) ? viewContext : null;
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2327
2335
|
},
|
|
2328
2336
|
|
|
2329
2337
|
methods: {
|
|
@@ -3073,17 +3081,3 @@ module.exports = {
|
|
|
3073
3081
|
};
|
|
3074
3082
|
}
|
|
3075
3083
|
};
|
|
3076
|
-
|
|
3077
|
-
function wrap(context, extensions) {
|
|
3078
|
-
for (const [ name, fn ] of extensions) {
|
|
3079
|
-
if ((typeof fn) !== 'function') {
|
|
3080
|
-
// Nested structure is allowed
|
|
3081
|
-
context[name] = context[name] || {};
|
|
3082
|
-
return wrap(context[name], fn);
|
|
3083
|
-
}
|
|
3084
|
-
const superMethod = context[name];
|
|
3085
|
-
context[name] = function(...args) {
|
|
3086
|
-
return fn(superMethod, ...args);
|
|
3087
|
-
};
|
|
3088
|
-
}
|
|
3089
|
-
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module.exports = extendQueries;
|
|
2
|
+
|
|
3
|
+
function extendQueries(queries, extensions) {
|
|
4
|
+
for (const [ name, fn ] of Object.entries(extensions)) {
|
|
5
|
+
if (typeof fn === 'object' && !Array.isArray(fn) && fn !== null) {
|
|
6
|
+
// Nested structure is allowed
|
|
7
|
+
queries[name] = queries[name] || {};
|
|
8
|
+
return extendQueries(queries[name], fn);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (typeof fn !== 'function' || typeof queries[name] !== 'function') {
|
|
12
|
+
queries[name] = fn;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const superMethod = queries[name];
|
|
17
|
+
queries[name] = function(...args) {
|
|
18
|
+
return fn(superMethod, ...args);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
};
|
|
@@ -8,10 +8,14 @@
|
|
|
8
8
|
<div class="apos-input-wrapper">
|
|
9
9
|
<input
|
|
10
10
|
:class="classes"
|
|
11
|
-
v-model="next.url"
|
|
11
|
+
v-model="next.url"
|
|
12
|
+
type="url"
|
|
12
13
|
:placeholder="$t(field.placeholder)"
|
|
13
|
-
:disabled="field.readOnly"
|
|
14
|
-
:
|
|
14
|
+
:disabled="field.readOnly"
|
|
15
|
+
:readonly="tempReadOnly"
|
|
16
|
+
:required="field.required"
|
|
17
|
+
:id="uid"
|
|
18
|
+
:tabindex="tabindex"
|
|
15
19
|
>
|
|
16
20
|
<component
|
|
17
21
|
v-if="icon"
|
|
@@ -43,10 +47,14 @@ export default {
|
|
|
43
47
|
data () {
|
|
44
48
|
return {
|
|
45
49
|
next: (this.value && this.value.data)
|
|
46
|
-
? this.value.data : {},
|
|
50
|
+
? { ...this.value.data } : {},
|
|
47
51
|
oembedResult: {},
|
|
48
52
|
dynamicRatio: '',
|
|
49
|
-
oembedError: null
|
|
53
|
+
oembedError: null,
|
|
54
|
+
|
|
55
|
+
// This variable will set the input as readonly,
|
|
56
|
+
// not disabled, in order to avoid losing focus.
|
|
57
|
+
tempReadOnly: false
|
|
50
58
|
};
|
|
51
59
|
},
|
|
52
60
|
computed: {
|
|
@@ -104,7 +112,7 @@ export default {
|
|
|
104
112
|
this.validateAndEmit();
|
|
105
113
|
},
|
|
106
114
|
async loadOembed () {
|
|
107
|
-
this.
|
|
115
|
+
this.tempReadOnly = true;
|
|
108
116
|
this.oembedResult = {};
|
|
109
117
|
this.oembedError = null;
|
|
110
118
|
this.dynamicRatio = '';
|
|
@@ -132,7 +140,7 @@ export default {
|
|
|
132
140
|
this.next.title = '';
|
|
133
141
|
this.next.thumbnail = '';
|
|
134
142
|
} finally {
|
|
135
|
-
this.
|
|
143
|
+
this.tempReadOnly = false;
|
|
136
144
|
}
|
|
137
145
|
}
|
|
138
146
|
}
|
|
@@ -146,7 +146,9 @@ export default {
|
|
|
146
146
|
totalPages: 1,
|
|
147
147
|
currentPage: 1,
|
|
148
148
|
filterValues: {},
|
|
149
|
-
queryExtras: {
|
|
149
|
+
queryExtras: {
|
|
150
|
+
viewContext: this.relationshipField ? 'relationship' : 'manage'
|
|
151
|
+
},
|
|
150
152
|
holdQueries: false,
|
|
151
153
|
filterChoices: {},
|
|
152
154
|
allPiecesSelection: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apostrophe",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.47.0",
|
|
4
4
|
"description": "The Apostrophe Content Management System.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"url": "git@github.com:apostrophecms/apostrophe.git"
|
|
18
18
|
},
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
20
|
+
"node": ">=16.0.0"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"apostrophe",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"i18next-http-middleware": "^3.1.5",
|
|
81
81
|
"import-fresh": "^3.3.0",
|
|
82
82
|
"is-wsl": "^2.2.0",
|
|
83
|
-
"jsdom": "^
|
|
83
|
+
"jsdom": "^22.0.0",
|
|
84
84
|
"klona": "^2.0.4",
|
|
85
85
|
"launder": "^1.4.0",
|
|
86
86
|
"lodash": "^4.17.20",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"sass": "^1.52.3",
|
|
110
110
|
"sass-loader": "^10.1.1",
|
|
111
111
|
"server-destroy": "^1.0.1",
|
|
112
|
-
"sluggo": "^0.
|
|
112
|
+
"sluggo": "^1.0.0",
|
|
113
113
|
"tinycolor2": "^1.4.2",
|
|
114
114
|
"tough-cookie": "^4.0.0",
|
|
115
115
|
"underscore.string": "^3.3.4",
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
const t = require('../test-lib/test.js');
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
|
|
4
|
+
describe('Query Builders', function() {
|
|
5
|
+
this.timeout(t.timeout);
|
|
6
|
+
|
|
7
|
+
let apos;
|
|
8
|
+
after(function() {
|
|
9
|
+
return t.destroy(apos);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
before(async function () {
|
|
13
|
+
apos = await t.create({
|
|
14
|
+
root: module,
|
|
15
|
+
modules: {
|
|
16
|
+
young: {
|
|
17
|
+
options: {
|
|
18
|
+
alias: 'young'
|
|
19
|
+
},
|
|
20
|
+
extend: '@apostrophecms/piece-type',
|
|
21
|
+
fields: {
|
|
22
|
+
add: {
|
|
23
|
+
age: {
|
|
24
|
+
label: 'Age',
|
|
25
|
+
type: 'integer',
|
|
26
|
+
required: true
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
queries(self, query) {
|
|
31
|
+
return {
|
|
32
|
+
builders: {
|
|
33
|
+
age: {
|
|
34
|
+
launder(str) {
|
|
35
|
+
return [ 'children', 'adult' ].includes(str) ? str : null;
|
|
36
|
+
},
|
|
37
|
+
finalize() {
|
|
38
|
+
const age = query.get('age');
|
|
39
|
+
|
|
40
|
+
if ([ 'children', 'adults' ].includes(age)) {
|
|
41
|
+
const ageCriteria = age === 'children' ? { $lte: 18 } : { $gt: 18 };
|
|
42
|
+
query.and({ age: ageCriteria });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
methods: {
|
|
48
|
+
async sortByAge() {
|
|
49
|
+
await query.finalize();
|
|
50
|
+
|
|
51
|
+
const pipeline = [
|
|
52
|
+
{ $match: query.get('criteria') },
|
|
53
|
+
{ $sort: { age: 1 } }
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const results = await self.apos.doc.db.aggregate(pipeline).toArray();
|
|
57
|
+
|
|
58
|
+
return results;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
person: {
|
|
65
|
+
extend: 'young',
|
|
66
|
+
options: {
|
|
67
|
+
alias: 'person'
|
|
68
|
+
},
|
|
69
|
+
extendQueries(self, query) {
|
|
70
|
+
return {
|
|
71
|
+
builders: {
|
|
72
|
+
age: {
|
|
73
|
+
def: 'adult',
|
|
74
|
+
launder(_super, val) {
|
|
75
|
+
const laundered = _super();
|
|
76
|
+
|
|
77
|
+
if (laundered !== null) {
|
|
78
|
+
return laundered;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return val === 'senior' ? val : null;
|
|
82
|
+
},
|
|
83
|
+
async finalize(_super) {
|
|
84
|
+
await _super();
|
|
85
|
+
|
|
86
|
+
const age = query.get('age');
|
|
87
|
+
|
|
88
|
+
if (age === 'seniors') {
|
|
89
|
+
query.and({ age: { $gt: 60 } });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
methods: {
|
|
95
|
+
async sortByAge(_super) {
|
|
96
|
+
assert(typeof _super === 'function');
|
|
97
|
+
|
|
98
|
+
await query.finalize();
|
|
99
|
+
|
|
100
|
+
const pipeline = [
|
|
101
|
+
{ $match: query.get('criteria') },
|
|
102
|
+
{ $sort: { age: -1 } }
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
const results = await self.apos.doc.db.aggregate(pipeline).toArray();
|
|
106
|
+
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should insert person pieces and verify age query builder is working', async function() {
|
|
118
|
+
const req = apos.task.getReq();
|
|
119
|
+
const persons = getPersons(apos.young);
|
|
120
|
+
const { insertedCount } = await apos.doc.db.insertMany(persons);
|
|
121
|
+
|
|
122
|
+
assert(insertedCount === 6);
|
|
123
|
+
|
|
124
|
+
const children = await apos.young.find(req).age('children').toArray();
|
|
125
|
+
const adults = await apos.young.find(req).age('adults').toArray();
|
|
126
|
+
|
|
127
|
+
assert(children.length === 2);
|
|
128
|
+
children.forEach((child) => {
|
|
129
|
+
assert(child.age <= 18);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
assert(adults.length === 4);
|
|
133
|
+
adults.forEach((adult) => {
|
|
134
|
+
assert(adult.age > 18);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should insert seniors and verify the query builders have been properly extended', async function() {
|
|
139
|
+
const req = apos.task.getReq();
|
|
140
|
+
const persons = getPersons(apos.person, true);
|
|
141
|
+
const { insertedCount } = await apos.doc.db.insertMany(persons);
|
|
142
|
+
|
|
143
|
+
assert(insertedCount === 8);
|
|
144
|
+
|
|
145
|
+
const children = await apos.person.find(req).age('children').toArray();
|
|
146
|
+
const adults = await apos.person.find(req).age('adults').toArray();
|
|
147
|
+
const seniors = await apos.person.find(req).age('seniors').toArray();
|
|
148
|
+
|
|
149
|
+
assert(children.length === 2);
|
|
150
|
+
children.forEach((child) => {
|
|
151
|
+
assert(child.age <= 18);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
assert(adults.length === 6);
|
|
155
|
+
adults.forEach((adult) => {
|
|
156
|
+
assert(adult.age > 18);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
assert(seniors.length === 2);
|
|
160
|
+
seniors.forEach((senior) => {
|
|
161
|
+
assert(senior.age > 60);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should verify that query methods work and can be extende', async function() {
|
|
166
|
+
const req = apos.task.getReq();
|
|
167
|
+
const youngSorted = await apos.young.find(req).age('adults').sortByAge();
|
|
168
|
+
assert(youngSorted[0].age === 25);
|
|
169
|
+
assert(youngSorted[1].age === 32);
|
|
170
|
+
assert(youngSorted[2].age === 50);
|
|
171
|
+
assert(youngSorted[3].age === 58);
|
|
172
|
+
|
|
173
|
+
const personsSorted = await apos.person.find(req).age('adults').sortByAge();
|
|
174
|
+
assert(personsSorted[0].age === 80);
|
|
175
|
+
assert(personsSorted[1].age === 72);
|
|
176
|
+
assert(personsSorted[2].age === 58);
|
|
177
|
+
assert(personsSorted[3].age === 50);
|
|
178
|
+
assert(personsSorted[4].age === 32);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
function getPersons(instance, withSeniors = false) {
|
|
183
|
+
const moduleName = instance.__meta.name;
|
|
184
|
+
return [
|
|
185
|
+
{
|
|
186
|
+
title: 'Jean',
|
|
187
|
+
age: 32
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
title: 'Julie',
|
|
191
|
+
age: 25
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
title: 'Victor',
|
|
195
|
+
age: 14
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
title: 'Marc',
|
|
199
|
+
age: 58
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
title: 'Hector',
|
|
203
|
+
age: 7
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
title: 'Marie',
|
|
207
|
+
age: 50
|
|
208
|
+
},
|
|
209
|
+
...withSeniors ? [
|
|
210
|
+
{
|
|
211
|
+
title: 'Jules',
|
|
212
|
+
age: 72
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
title: 'Renée',
|
|
216
|
+
age: 80
|
|
217
|
+
}
|
|
218
|
+
] : []
|
|
219
|
+
].map((p, i) => ({
|
|
220
|
+
_id: `${moduleName}${i}`,
|
|
221
|
+
...instance.newInstance(),
|
|
222
|
+
slug: `${moduleName}-${p.title.toLowerCase()}`,
|
|
223
|
+
...p
|
|
224
|
+
}));
|
|
225
|
+
}
|