apostrophe 3.61.1 → 3.63.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 +49 -0
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBar.vue +1 -1
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +6 -4
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +9 -1
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +8 -0
- package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +6 -3
- package/modules/@apostrophecms/doc/index.js +256 -7
- package/modules/@apostrophecms/doc/ui/apos/mixins/AposFieldMetaUtilsMixin.js +93 -0
- package/modules/@apostrophecms/doc-type/index.js +78 -12
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +9 -1
- package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +24 -6
- package/modules/@apostrophecms/i18n/i18n/en.json +1 -0
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +8 -7
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerDisplay.vue +1 -5
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +5 -2
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerSelections.vue +1 -5
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +4 -2
- package/modules/@apostrophecms/login/index.js +25 -19
- package/modules/@apostrophecms/login/ui/apos/components/AposLoginForm.vue +11 -1
- package/modules/@apostrophecms/login/ui/apos/logic/AposLoginForm.js +46 -2
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalShareDraft.vue +8 -3
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +3 -0
- package/modules/@apostrophecms/page/index.js +118 -27
- package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +3 -1
- package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +7 -0
- package/modules/@apostrophecms/page-type/index.js +81 -4
- package/modules/@apostrophecms/permission/index.js +60 -31
- package/modules/@apostrophecms/piece-type/index.js +19 -41
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +9 -1
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposUtilityOperations.vue +16 -1
- package/modules/@apostrophecms/rich-text-widget/index.js +141 -1
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +8 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapImage.vue +7 -7
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapLink.vue +38 -79
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Link.js +11 -0
- package/modules/@apostrophecms/schema/index.js +9 -0
- package/modules/@apostrophecms/schema/lib/addFieldTypes.js +22 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +4 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +7 -8
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +2 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +76 -30
- package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +2 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +1 -1
- package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +7 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArea.js +13 -1
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +5 -1
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +21 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +12 -8
- package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +35 -0
- package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +6 -0
- package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +41 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +1 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposCheckbox.vue +1 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposTree.vue +7 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposTreeRows.vue +8 -0
- package/modules/@apostrophecms/ui/ui/apos/mixins/AposPublishMixin.js +10 -4
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_inputs.scss +2 -2
- package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +7 -0
- package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +6 -0
- package/package.json +2 -2
- package/test/areas.js +1 -1
- package/test/assets.js +2 -2
- package/test/attachments.js +2 -2
- package/test/base-module.js +2 -1
- package/test/base-url-env-var.js +2 -2
- package/test/change-doc-ids.js +33 -31
- package/test/command-menu.js +2 -2
- package/test/content-i18n.js +47 -46
- package/test/db.js +2 -2
- package/test/docs.js +301 -126
- package/test/draft-published.js +2 -2
- package/test/email.js +2 -1
- package/test/express.js +3 -2
- package/test/external-front.js +4 -4
- package/test/field-meta.js +363 -0
- package/test/global.js +2 -1
- package/test/http.js +4 -2
- package/test/images.js +87 -88
- package/test/job.js +34 -34
- package/test/locks.js +2 -2
- package/test/login-requirements.js +3 -2
- package/test/middleware-and-route-order.js +2 -2
- package/test/page-type.js +2 -1
- package/test/pages-autocomplete.js +192 -0
- package/test/pages-public-api.js +2 -2
- package/test/pages-rest.js +4 -4
- package/test/pages.js +389 -57
- package/test/parked-pages.js +47 -47
- package/test/permissions.js +76 -0
- package/test/pieces-page-type.js +2 -1
- package/test/pieces-public-api.js +38 -38
- package/test/pieces.js +4 -4
- package/test/published-pages.js +16 -16
- package/test/rich-text-widget.js +164 -0
- package/test/schema-queryBuilders.js +180 -0
- package/test/schemaBuilders.js +4 -4
- package/test/schemas.js +220 -221
- package/test/search.js +2 -2
- package/test/soft-redirects.js +2 -1
- package/test/templates.js +2 -2
- package/test/users.js +2 -1
package/test/docs.js
CHANGED
|
@@ -2,19 +2,18 @@ const t = require('../test-lib/test.js');
|
|
|
2
2
|
const assert = require('assert');
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
|
|
5
|
-
let apos;
|
|
6
|
-
|
|
7
5
|
describe('Docs', function() {
|
|
6
|
+
let apos;
|
|
8
7
|
|
|
9
8
|
this.timeout(t.timeout);
|
|
10
9
|
|
|
11
|
-
after(function() {
|
|
10
|
+
after(async function() {
|
|
11
|
+
await apos.doc.db.deleteMany({});
|
|
12
|
+
await apos.lock.db.deleteMany({});
|
|
12
13
|
return t.destroy(apos);
|
|
13
14
|
});
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
it('should be a property of the apos object', async function() {
|
|
16
|
+
before(async function() {
|
|
18
17
|
apos = await t.create({
|
|
19
18
|
root: module,
|
|
20
19
|
|
|
@@ -32,6 +31,22 @@ describe('Docs', function() {
|
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
},
|
|
34
|
+
unlocalized: {
|
|
35
|
+
extend: '@apostrophecms/piece-type',
|
|
36
|
+
options: {
|
|
37
|
+
localized: false
|
|
38
|
+
},
|
|
39
|
+
fields: {
|
|
40
|
+
add: {
|
|
41
|
+
_friends: {
|
|
42
|
+
type: 'relationship',
|
|
43
|
+
max: 1,
|
|
44
|
+
withType: 'test-people',
|
|
45
|
+
label: 'Friends'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
35
50
|
'@apostrophecms/page': {
|
|
36
51
|
options: {
|
|
37
52
|
park: [],
|
|
@@ -48,19 +63,17 @@ describe('Docs', function() {
|
|
|
48
63
|
}
|
|
49
64
|
}
|
|
50
65
|
});
|
|
66
|
+
});
|
|
51
67
|
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
afterEach(async function () {
|
|
69
|
+
await apos.doc.db.deleteMany({ type: 'test-people' });
|
|
70
|
+
await apos.lock.db.deleteMany({});
|
|
54
71
|
});
|
|
55
72
|
|
|
56
73
|
it('should have a db property', function() {
|
|
57
74
|
assert(apos.doc.db);
|
|
58
75
|
});
|
|
59
76
|
|
|
60
|
-
/// ///
|
|
61
|
-
// SETUP
|
|
62
|
-
/// ///
|
|
63
|
-
|
|
64
77
|
it('should make sure all of the expected indexes are configured', async function() {
|
|
65
78
|
const expectedIndexes = [
|
|
66
79
|
'type',
|
|
@@ -84,64 +97,8 @@ describe('Docs', function() {
|
|
|
84
97
|
assert(info.highSearchText_text_lowSearchText_text_title_text_searchBoost_text[0][1] === 'text');
|
|
85
98
|
});
|
|
86
99
|
|
|
87
|
-
it('should make sure there is no test data hanging around from last time', async function() {
|
|
88
|
-
// Attempt to purge the entire aposDocs collection
|
|
89
|
-
await apos.doc.db.deleteMany({});
|
|
90
|
-
|
|
91
|
-
// Make sure it went away
|
|
92
|
-
const docs = await apos.doc.db.find({ slug: 'larry' }).toArray();
|
|
93
|
-
|
|
94
|
-
assert(docs.length === 0);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should be able to use db to insert documents', async function() {
|
|
98
|
-
const testItems = [
|
|
99
|
-
{
|
|
100
|
-
_id: 'lori:en:published',
|
|
101
|
-
aposDocId: 'lori',
|
|
102
|
-
aposLocale: 'en:published',
|
|
103
|
-
slug: 'lori',
|
|
104
|
-
visibility: 'public',
|
|
105
|
-
type: 'test-people',
|
|
106
|
-
firstName: 'Lori',
|
|
107
|
-
lastName: 'Pizzaroni',
|
|
108
|
-
age: 32,
|
|
109
|
-
alive: true
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
_id: 'larry:en:published',
|
|
113
|
-
aposDocId: 'larry',
|
|
114
|
-
aposLocale: 'en:published',
|
|
115
|
-
slug: 'larry',
|
|
116
|
-
visibility: 'public',
|
|
117
|
-
type: 'test-people',
|
|
118
|
-
firstName: 'Larry',
|
|
119
|
-
lastName: 'Cherber',
|
|
120
|
-
age: 28,
|
|
121
|
-
alive: true
|
|
122
|
-
},
|
|
123
|
-
{
|
|
124
|
-
_id: 'carl:en:published',
|
|
125
|
-
aposDocId: 'carl',
|
|
126
|
-
aposLocale: 'en:published',
|
|
127
|
-
slug: 'carl',
|
|
128
|
-
visibility: 'public',
|
|
129
|
-
type: 'test-people',
|
|
130
|
-
firstName: 'Carl',
|
|
131
|
-
lastName: 'Sagan',
|
|
132
|
-
age: 62,
|
|
133
|
-
alive: false,
|
|
134
|
-
friendsIds: [ 'larry' ]
|
|
135
|
-
}
|
|
136
|
-
];
|
|
137
|
-
|
|
138
|
-
const response = await apos.doc.db.insertMany(testItems);
|
|
139
|
-
|
|
140
|
-
assert(response.result.ok === 1);
|
|
141
|
-
assert(response.insertedCount === 3);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
100
|
it('should be able to fetch schema relationships', async function() {
|
|
101
|
+
await insertPeople(apos);
|
|
145
102
|
const manager = apos.doc.getManager('test-people');
|
|
146
103
|
const req = apos.task.getAnonReq();
|
|
147
104
|
|
|
@@ -299,6 +256,7 @@ describe('Docs', function() {
|
|
|
299
256
|
});
|
|
300
257
|
|
|
301
258
|
it('should be able to find all test documents and output them as an array', async function () {
|
|
259
|
+
await insertPeople(apos);
|
|
302
260
|
const cursor = apos.doc.find(apos.task.getAnonReq(), { type: 'test-people' });
|
|
303
261
|
|
|
304
262
|
const docs = await cursor.toArray();
|
|
@@ -314,6 +272,7 @@ describe('Docs', function() {
|
|
|
314
272
|
/// ///
|
|
315
273
|
|
|
316
274
|
it('should be able to specify which fields to get by passing a projection object', async function() {
|
|
275
|
+
await insertPeople(apos);
|
|
317
276
|
const cursor = apos.doc.find(apos.task.getAnonReq(), { type: 'test-people' }, {
|
|
318
277
|
project: {
|
|
319
278
|
age: 1
|
|
@@ -332,6 +291,7 @@ describe('Docs', function() {
|
|
|
332
291
|
/// ///
|
|
333
292
|
|
|
334
293
|
it('should be able to sort', async function () {
|
|
294
|
+
await insertPeople(apos);
|
|
335
295
|
const cursor = apos.doc.find(apos.task.getAnonReq(), { type: 'test-people' }).sort({ age: 1 });
|
|
336
296
|
const docs = await cursor.toArray();
|
|
337
297
|
|
|
@@ -339,6 +299,7 @@ describe('Docs', function() {
|
|
|
339
299
|
});
|
|
340
300
|
|
|
341
301
|
it('should be able to sort by multiple keys', async function () {
|
|
302
|
+
await insertPeople(apos);
|
|
342
303
|
const cursor = apos.doc.find(apos.task.getAnonReq(), { type: 'test-people' }).sort({
|
|
343
304
|
firstName: 1,
|
|
344
305
|
age: 1
|
|
@@ -353,19 +314,8 @@ describe('Docs', function() {
|
|
|
353
314
|
// INSERTING
|
|
354
315
|
/// ///
|
|
355
316
|
|
|
356
|
-
it('should
|
|
357
|
-
const
|
|
358
|
-
slug: 'one',
|
|
359
|
-
visibility: 'public',
|
|
360
|
-
type: 'test-people',
|
|
361
|
-
firstName: 'Lori',
|
|
362
|
-
lastName: 'Ferber',
|
|
363
|
-
age: 15,
|
|
364
|
-
alive: true
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
const response = await apos.doc.insert(apos.task.getReq(), object);
|
|
368
|
-
|
|
317
|
+
it('should be able to insert a new object into the docs collection in the database', async function() {
|
|
318
|
+
const response = await insertOne(apos);
|
|
369
319
|
assert(response);
|
|
370
320
|
assert(response._id);
|
|
371
321
|
assert(response._id.endsWith(':en:published'));
|
|
@@ -377,12 +327,9 @@ describe('Docs', function() {
|
|
|
377
327
|
});
|
|
378
328
|
assert(draft);
|
|
379
329
|
// Unique index allows for duplicates across locales
|
|
380
|
-
assert(
|
|
330
|
+
assert(draft.slug === 'one');
|
|
381
331
|
// Content properties coming through
|
|
382
332
|
assert(draft.firstName === response.firstName);
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
it('should be able to insert a new object into the docs collection in the database', async function() {
|
|
386
333
|
const cursor = apos.doc.find(apos.task.getReq(), {
|
|
387
334
|
type: 'test-people',
|
|
388
335
|
slug: 'one'
|
|
@@ -393,6 +340,7 @@ describe('Docs', function() {
|
|
|
393
340
|
});
|
|
394
341
|
|
|
395
342
|
it('should append the slug property with a numeral if inserting an object whose slug already exists in the database', async function() {
|
|
343
|
+
await insertOne(apos);
|
|
396
344
|
const object = {
|
|
397
345
|
slug: 'one',
|
|
398
346
|
visibility: 'public',
|
|
@@ -410,21 +358,8 @@ describe('Docs', function() {
|
|
|
410
358
|
});
|
|
411
359
|
|
|
412
360
|
it('should add the aposDocId to the related documents\' relatedReverseIds field and update their `cacheInvalidatedAt` field', async function() {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
aposLocale: 'en:published',
|
|
416
|
-
slug: 'paul',
|
|
417
|
-
visibility: 'public',
|
|
418
|
-
type: 'test-people',
|
|
419
|
-
firstName: 'Paul',
|
|
420
|
-
lastName: 'McCartney',
|
|
421
|
-
age: 24,
|
|
422
|
-
alive: false,
|
|
423
|
-
friendsIds: [ 'carl', 'larry' ],
|
|
424
|
-
_friends: [ { _id: 'carl:en:published' }, { _id: 'larry:en:published' } ]
|
|
425
|
-
};
|
|
426
|
-
|
|
427
|
-
const response = await apos.doc.insert(apos.task.getReq(), object);
|
|
361
|
+
await insertPeople(apos);
|
|
362
|
+
const response = await insertOneWithRelated(apos);
|
|
428
363
|
|
|
429
364
|
const carlDoc = await apos.doc.db.findOne({
|
|
430
365
|
slug: 'carl',
|
|
@@ -443,6 +378,103 @@ describe('Docs', function() {
|
|
|
443
378
|
assert(larryDoc.relatedReverseIds.length === 1);
|
|
444
379
|
assert(larryDoc.relatedReverseIds[0] === 'paul');
|
|
445
380
|
assert(larryDoc.cacheInvalidatedAt.getTime() === response.updatedAt.getTime());
|
|
381
|
+
|
|
382
|
+
apos.doc.db.deleteMany({ slug: { $in: [ 'paul', 'carl', 'larry' ] } });
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should remove the related reverse IDs when you delete a draft document', async function () {
|
|
386
|
+
const req = apos.task.getReq();
|
|
387
|
+
await insertPeople(apos);
|
|
388
|
+
const personWithRelatedPublished = await insertOneWithRelated(apos);
|
|
389
|
+
const personWithRelatedDraft = await apos.doc.find(apos.task.getReq({ mode: 'draft' }), { slug: 'paul' }).toObject();
|
|
390
|
+
|
|
391
|
+
await apos.doc.delete(req, personWithRelatedPublished);
|
|
392
|
+
|
|
393
|
+
const larryDoc = await apos.doc.db.findOne({
|
|
394
|
+
slug: 'larry',
|
|
395
|
+
aposLocale: 'en:published'
|
|
396
|
+
});
|
|
397
|
+
const carlDoc = await apos.doc.db.findOne({
|
|
398
|
+
slug: 'carl',
|
|
399
|
+
aposLocale: 'en:published'
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
await apos.doc.delete(req, personWithRelatedDraft);
|
|
403
|
+
|
|
404
|
+
const larryDocUpdated = await apos.doc.db.findOne({
|
|
405
|
+
slug: 'larry',
|
|
406
|
+
aposLocale: 'en:published'
|
|
407
|
+
});
|
|
408
|
+
const carlDocUpdated = await apos.doc.db.findOne({
|
|
409
|
+
slug: 'carl',
|
|
410
|
+
aposLocale: 'en:published'
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
const actual = {
|
|
414
|
+
larryHasRelatedBeforeDelete: larryDoc.relatedReverseIds.includes('paul'),
|
|
415
|
+
carlHasRelatedBeforeDelete: carlDoc.relatedReverseIds.includes('paul'),
|
|
416
|
+
larryHasRelatedAfterDelete: larryDocUpdated.relatedReverseIds.includes('paul'),
|
|
417
|
+
carlHasRelatedAfterDelete: carlDocUpdated.relatedReverseIds.includes('paul')
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
const expected = {
|
|
421
|
+
larryHasRelatedBeforeDelete: true,
|
|
422
|
+
carlHasRelatedBeforeDelete: true,
|
|
423
|
+
larryHasRelatedAfterDelete: false,
|
|
424
|
+
carlHasRelatedAfterDelete: false
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
assert.deepEqual(actual, expected);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('should remove the related reverse IDs when you delete an unlocalized document', async function () {
|
|
431
|
+
const req = apos.task.getReq();
|
|
432
|
+
await insertPeople(apos);
|
|
433
|
+
const unlocalizedDoc = await apos.doc.insert(req, {
|
|
434
|
+
aposDocId: 'paul',
|
|
435
|
+
aposLocale: 'en:published',
|
|
436
|
+
slug: 'paul',
|
|
437
|
+
visibility: 'public',
|
|
438
|
+
type: 'unlocalized',
|
|
439
|
+
friendsIds: [ 'carl', 'larry' ],
|
|
440
|
+
_friends: [ { _id: 'carl:en:published' }, { _id: 'larry:en:published' } ]
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
const larryDoc = await apos.doc.db.findOne({
|
|
444
|
+
slug: 'larry',
|
|
445
|
+
aposLocale: 'en:published'
|
|
446
|
+
});
|
|
447
|
+
const carlDoc = await apos.doc.db.findOne({
|
|
448
|
+
aposDocId: 'carl',
|
|
449
|
+
aposLocale: 'en:published'
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
await apos.doc.delete(req, unlocalizedDoc);
|
|
453
|
+
|
|
454
|
+
const larryDocUpdated = await apos.doc.db.findOne({
|
|
455
|
+
slug: 'larry',
|
|
456
|
+
aposLocale: 'en:published'
|
|
457
|
+
});
|
|
458
|
+
const carlDocUpdated = await apos.doc.db.findOne({
|
|
459
|
+
slug: 'carl',
|
|
460
|
+
aposLocale: 'en:published'
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
const actual = {
|
|
464
|
+
larryHasRelatedBeforeDelete: larryDoc.relatedReverseIds.includes('paul'),
|
|
465
|
+
carlHasRelatedBeforeDelete: carlDoc.relatedReverseIds.includes('paul'),
|
|
466
|
+
larryHasRelatedAfterDelete: larryDocUpdated.relatedReverseIds.includes('paul'),
|
|
467
|
+
carlHasRelatedAfterDelete: carlDocUpdated.relatedReverseIds.includes('paul')
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
const expected = {
|
|
471
|
+
larryHasRelatedBeforeDelete: true,
|
|
472
|
+
carlHasRelatedBeforeDelete: true,
|
|
473
|
+
larryHasRelatedAfterDelete: false,
|
|
474
|
+
carlHasRelatedAfterDelete: false
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
assert.deepEqual(actual, expected);
|
|
446
478
|
});
|
|
447
479
|
|
|
448
480
|
it('should not allow you to call the insert method if you are not an admin', async function() {
|
|
@@ -469,6 +501,7 @@ describe('Docs', function() {
|
|
|
469
501
|
/// ///
|
|
470
502
|
|
|
471
503
|
it('should have an "update" method on docs that updates an existing database object', async function() {
|
|
504
|
+
await insertOne(apos);
|
|
472
505
|
const req = apos.task.getReq();
|
|
473
506
|
const docs = await apos.doc.find(req, { slug: 'one' }).toArray();
|
|
474
507
|
|
|
@@ -488,41 +521,55 @@ describe('Docs', function() {
|
|
|
488
521
|
});
|
|
489
522
|
|
|
490
523
|
it('should append an updated slug with a numeral if the updated slug already exists', async function() {
|
|
524
|
+
await insertPeople(apos);
|
|
525
|
+
await insertOne(apos);
|
|
491
526
|
const req = apos.task.getReq();
|
|
492
527
|
const cursor = apos.doc.find(req, {
|
|
493
528
|
type: 'test-people',
|
|
494
529
|
slug: 'one'
|
|
495
530
|
});
|
|
496
531
|
const doc = await cursor.toObject();
|
|
497
|
-
|
|
498
532
|
assert(doc);
|
|
499
533
|
|
|
500
534
|
doc.slug = 'peter';
|
|
501
|
-
|
|
502
535
|
const updated = await apos.doc.update(req, doc);
|
|
503
536
|
assert(updated);
|
|
504
537
|
// Has the updated slug been appended?
|
|
505
538
|
assert(updated.slug.match(/^peter\d+$/));
|
|
506
539
|
});
|
|
540
|
+
|
|
507
541
|
it('should be able to fetch all unique firstNames with toDistinct', async function() {
|
|
542
|
+
await insertPeople(apos);
|
|
508
543
|
const firstNames = await apos.doc.find(apos.task.getReq(), {
|
|
509
544
|
type: 'test-people'
|
|
510
545
|
}).toDistinct('firstName');
|
|
511
546
|
|
|
512
547
|
assert(Array.isArray(firstNames));
|
|
513
|
-
assert(firstNames.length ===
|
|
548
|
+
assert(firstNames.length === 4);
|
|
514
549
|
assert(_.includes(firstNames, 'Larry'));
|
|
515
550
|
});
|
|
516
551
|
|
|
517
552
|
it('should be able to fetch all unique firstNames and their counts with toDistinct and distinctCounts', async function() {
|
|
553
|
+
await insertPeople(apos);
|
|
518
554
|
const req = apos.task.getReq();
|
|
555
|
+
await apos.doc.db.insertOne({
|
|
556
|
+
_id: 'random:en:published',
|
|
557
|
+
slug: 'random',
|
|
558
|
+
aposDocId: 'lori2',
|
|
559
|
+
aposLocale: 'en:published',
|
|
560
|
+
type: 'test-people',
|
|
561
|
+
visibility: 'loginRequired',
|
|
562
|
+
firstName: 'Lori',
|
|
563
|
+
lastName: 'Lora',
|
|
564
|
+
age: 70
|
|
565
|
+
});
|
|
519
566
|
const cursor = apos.doc.find(req, {
|
|
520
567
|
type: 'test-people'
|
|
521
568
|
}).distinctCounts(true);
|
|
522
569
|
const firstNames = await cursor.toDistinct('firstName');
|
|
523
570
|
|
|
524
571
|
assert(Array.isArray(firstNames));
|
|
525
|
-
assert(firstNames.length ===
|
|
572
|
+
assert(firstNames.length === 4);
|
|
526
573
|
assert(_.includes(firstNames, 'Larry'));
|
|
527
574
|
|
|
528
575
|
const counts = await cursor.get('distinctCounts');
|
|
@@ -532,6 +579,8 @@ describe('Docs', function() {
|
|
|
532
579
|
});
|
|
533
580
|
|
|
534
581
|
it('should remove the aposDocId from the related documents\' relatedReverseIds field and update their `cacheInvalidatedAt` field', async function() {
|
|
582
|
+
await insertPeople(apos);
|
|
583
|
+
await insertOneWithRelated(apos);
|
|
535
584
|
const paulDoc = await apos.doc.db.findOne({
|
|
536
585
|
slug: 'paul',
|
|
537
586
|
aposLocale: 'en:published'
|
|
@@ -565,6 +614,7 @@ describe('Docs', function() {
|
|
|
565
614
|
});
|
|
566
615
|
|
|
567
616
|
it('should update the related reverse documents\' `cacheInvalidatedAt` field', async function() {
|
|
617
|
+
await insertPeople(apos);
|
|
568
618
|
const object = {
|
|
569
619
|
aposDocId: 'john',
|
|
570
620
|
aposLocale: 'en:published',
|
|
@@ -656,21 +706,26 @@ describe('Docs', function() {
|
|
|
656
706
|
/// ///
|
|
657
707
|
|
|
658
708
|
it('should archive docs by updating them', async function() {
|
|
709
|
+
await insertPeople(apos);
|
|
659
710
|
const req = apos.task.getReq();
|
|
660
711
|
const doc = await apos.doc.find(req, {
|
|
661
712
|
type: 'test-people',
|
|
662
713
|
slug: 'carl'
|
|
663
714
|
}).toObject();
|
|
664
|
-
const archived = await apos
|
|
665
|
-
...doc,
|
|
666
|
-
archived: true
|
|
667
|
-
});
|
|
715
|
+
const archived = await archiveDoc(apos, doc);
|
|
668
716
|
|
|
669
717
|
assert(archived.archived === true);
|
|
670
718
|
});
|
|
671
719
|
|
|
672
720
|
it('should not be able to find the archived object', async function() {
|
|
673
721
|
const req = apos.task.getReq();
|
|
722
|
+
await insertPeople(apos);
|
|
723
|
+
const carlDoc = await apos.doc.find(req, {
|
|
724
|
+
type: 'test-people',
|
|
725
|
+
slug: 'carl'
|
|
726
|
+
}).toObject();
|
|
727
|
+
await archiveDoc(apos, carlDoc);
|
|
728
|
+
|
|
674
729
|
const doc = await apos.doc.find(req, {
|
|
675
730
|
slug: 'carl'
|
|
676
731
|
}).toObject();
|
|
@@ -690,11 +745,17 @@ describe('Docs', function() {
|
|
|
690
745
|
});
|
|
691
746
|
|
|
692
747
|
it('should be able to find the archived object when using the "archived" method on find()', async function() {
|
|
748
|
+
const req = apos.task.getReq();
|
|
749
|
+
await insertPeople(apos);
|
|
750
|
+
const carlDoc = await apos.doc.find(req, {
|
|
751
|
+
type: 'test-people',
|
|
752
|
+
slug: 'carl'
|
|
753
|
+
}).toObject();
|
|
754
|
+
await archiveDoc(apos, carlDoc);
|
|
693
755
|
// Look for the archived doc with the `deduplicate-` + its `_id` + its `name` properties.
|
|
694
|
-
const doc = await apos.doc.find(
|
|
756
|
+
const doc = await apos.doc.find(req, {
|
|
695
757
|
slug: 'deduplicate-carl-carl'
|
|
696
758
|
}).archived(true).toObject();
|
|
697
|
-
|
|
698
759
|
assert(doc);
|
|
699
760
|
assert(doc.archived);
|
|
700
761
|
});
|
|
@@ -705,6 +766,12 @@ describe('Docs', function() {
|
|
|
705
766
|
|
|
706
767
|
it('should rescue a doc by updating the "archived" property from an object', async function() {
|
|
707
768
|
const req = apos.task.getReq();
|
|
769
|
+
await insertPeople(apos);
|
|
770
|
+
const carlDoc = await apos.doc.find(req, {
|
|
771
|
+
type: 'test-people',
|
|
772
|
+
slug: 'carl'
|
|
773
|
+
}).toObject();
|
|
774
|
+
await archiveDoc(apos, carlDoc);
|
|
708
775
|
|
|
709
776
|
const doc = await apos.doc.find(req, {
|
|
710
777
|
slug: 'deduplicate-carl-carl'
|
|
@@ -724,6 +791,12 @@ describe('Docs', function() {
|
|
|
724
791
|
|
|
725
792
|
it('should not allow you to call the restore method if you are not an admin', async function() {
|
|
726
793
|
try {
|
|
794
|
+
await insertPeople(apos);
|
|
795
|
+
const carlDoc = await apos.doc.find(apos.task.getReq(), {
|
|
796
|
+
type: 'test-people',
|
|
797
|
+
slug: 'carl'
|
|
798
|
+
}).toObject();
|
|
799
|
+
await archiveDoc(apos, carlDoc);
|
|
727
800
|
await apos.doc.restore(apos.task.getAnonReq(), {
|
|
728
801
|
slug: 'carl'
|
|
729
802
|
});
|
|
@@ -769,21 +842,19 @@ describe('Docs', function() {
|
|
|
769
842
|
assert(docs[2]._id === 'i27:en:published');
|
|
770
843
|
assert(docs[3]._id === 'i9:en:published');
|
|
771
844
|
assert(!docs[4]);
|
|
772
|
-
});
|
|
773
845
|
|
|
774
|
-
|
|
775
|
-
// Relies on test data of previous test
|
|
776
|
-
const docs = await apos.doc.find(apos.task.getAnonReq(), {})
|
|
846
|
+
const filteredDocs = await apos.doc.find(apos.task.getAnonReq(), {})
|
|
777
847
|
._ids([ 'i7:en:published', 'i3:en:published', 'i27:en:published', 'i9:en:published' ]).skip(2).limit(2).toArray();
|
|
778
848
|
|
|
779
|
-
assert(
|
|
780
|
-
assert(
|
|
781
|
-
assert(!
|
|
849
|
+
assert(filteredDocs[0]._id === 'i27:en:published');
|
|
850
|
+
assert(filteredDocs[1]._id === 'i9:en:published');
|
|
851
|
+
assert(!filteredDocs[2]);
|
|
782
852
|
});
|
|
783
853
|
|
|
784
854
|
it('should be able to lock a document', async function() {
|
|
785
855
|
const req = apos.task.getReq();
|
|
786
|
-
|
|
856
|
+
await insertPeople(apos);
|
|
857
|
+
const doc = await apos.doc.db.findOne({ _id: 'carl:en:published' });
|
|
787
858
|
try {
|
|
788
859
|
await apos.doc.lock(req, doc, 'abc');
|
|
789
860
|
} catch (e) {
|
|
@@ -793,10 +864,13 @@ describe('Docs', function() {
|
|
|
793
864
|
|
|
794
865
|
it('should not be able to lock a document with a different tabId', async function() {
|
|
795
866
|
const req = apos.task.getReq();
|
|
796
|
-
|
|
867
|
+
await insertPeople(apos);
|
|
868
|
+
const doc = await apos.doc.db.findOne({ _id: 'carl:en:published' });
|
|
797
869
|
|
|
798
870
|
try {
|
|
799
|
-
await apos.doc.lock(req, doc, '
|
|
871
|
+
await apos.doc.lock(req, doc, 'abc');
|
|
872
|
+
const locked = await apos.doc.db.findOne({ _id: 'carl:en:published' });
|
|
873
|
+
await apos.doc.lock(req, locked, 'def');
|
|
800
874
|
} catch (e) {
|
|
801
875
|
assert(e);
|
|
802
876
|
assert(e.name === 'locked');
|
|
@@ -805,10 +879,17 @@ describe('Docs', function() {
|
|
|
805
879
|
|
|
806
880
|
it('should be able to refresh the lock with the same tabId', async function() {
|
|
807
881
|
const req = apos.task.getReq();
|
|
808
|
-
|
|
882
|
+
await insertPeople(apos);
|
|
883
|
+
const doc = await apos.doc.db.findOne({ _id: 'carl:en:published' });
|
|
884
|
+
const wait = (time) => new Promise((resolve) => setTimeout(() => {
|
|
885
|
+
resolve();
|
|
886
|
+
}, time));
|
|
809
887
|
|
|
810
888
|
try {
|
|
811
889
|
await apos.doc.lock(req, doc, 'abc');
|
|
890
|
+
const locked = await apos.doc.db.findOne({ _id: 'carl:en:published' });
|
|
891
|
+
await wait(500);
|
|
892
|
+
await apos.doc.lock(req, locked, 'abc');
|
|
812
893
|
} catch (e) {
|
|
813
894
|
assert(!e);
|
|
814
895
|
}
|
|
@@ -816,9 +897,11 @@ describe('Docs', function() {
|
|
|
816
897
|
|
|
817
898
|
it('should be able to unlock a document', async function() {
|
|
818
899
|
const req = apos.task.getReq();
|
|
819
|
-
|
|
900
|
+
await insertPeople(apos);
|
|
901
|
+
const doc = await apos.doc.db.findOne({ _id: 'carl:en:published' });
|
|
820
902
|
|
|
821
903
|
try {
|
|
904
|
+
await apos.doc.lock(req, doc, 'abc');
|
|
822
905
|
await apos.doc.unlock(req, doc, 'abc');
|
|
823
906
|
} catch (e) {
|
|
824
907
|
assert(false);
|
|
@@ -827,9 +910,12 @@ describe('Docs', function() {
|
|
|
827
910
|
|
|
828
911
|
it('should be able to re-lock an unlocked document', async function() {
|
|
829
912
|
const req = apos.task.getReq();
|
|
830
|
-
|
|
913
|
+
await insertPeople(apos);
|
|
914
|
+
const doc = await apos.doc.db.findOne({ _id: 'carl:en:published' });
|
|
831
915
|
|
|
832
916
|
try {
|
|
917
|
+
await apos.doc.lock(req, doc, 'abc');
|
|
918
|
+
await apos.doc.unlock(req, doc, 'abc');
|
|
833
919
|
await apos.doc.lock(req, doc, 'def');
|
|
834
920
|
} catch (e) {
|
|
835
921
|
assert(false);
|
|
@@ -838,9 +924,11 @@ describe('Docs', function() {
|
|
|
838
924
|
|
|
839
925
|
it('should be able to lock a locked document with force: true', async function() {
|
|
840
926
|
const req = apos.task.getReq();
|
|
841
|
-
|
|
927
|
+
await insertPeople(apos);
|
|
928
|
+
const doc = await apos.doc.db.findOne({ _id: 'carl:en:published' });
|
|
842
929
|
|
|
843
930
|
try {
|
|
931
|
+
await apos.doc.lock(req, doc, 'def');
|
|
844
932
|
await apos.doc.lock(req, doc, 'abc', { force: true });
|
|
845
933
|
} catch (e) {
|
|
846
934
|
assert(false);
|
|
@@ -959,5 +1047,92 @@ describe('Docs', function() {
|
|
|
959
1047
|
assert(response.cacheInvalidatedAt.getTime() === response.updatedAt.getTime());
|
|
960
1048
|
assert(draft.cacheInvalidatedAt.getTime() === draft.updatedAt.getTime());
|
|
961
1049
|
});
|
|
962
|
-
|
|
963
1050
|
});
|
|
1051
|
+
|
|
1052
|
+
async function insertPeople(apos) {
|
|
1053
|
+
return apos.doc.db.insertMany([
|
|
1054
|
+
{
|
|
1055
|
+
_id: 'lori:en:published',
|
|
1056
|
+
aposDocId: 'lori',
|
|
1057
|
+
aposLocale: 'en:published',
|
|
1058
|
+
slug: 'lori',
|
|
1059
|
+
visibility: 'public',
|
|
1060
|
+
type: 'test-people',
|
|
1061
|
+
firstName: 'Lori',
|
|
1062
|
+
lastName: 'Pizzaroni',
|
|
1063
|
+
age: 32,
|
|
1064
|
+
alive: true
|
|
1065
|
+
},
|
|
1066
|
+
{
|
|
1067
|
+
_id: 'larry:en:published',
|
|
1068
|
+
aposDocId: 'larry',
|
|
1069
|
+
aposLocale: 'en:published',
|
|
1070
|
+
slug: 'larry',
|
|
1071
|
+
visibility: 'public',
|
|
1072
|
+
type: 'test-people',
|
|
1073
|
+
firstName: 'Larry',
|
|
1074
|
+
lastName: 'Cherber',
|
|
1075
|
+
age: 28,
|
|
1076
|
+
alive: true
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
_id: 'carl:en:published',
|
|
1080
|
+
aposDocId: 'carl',
|
|
1081
|
+
aposLocale: 'en:published',
|
|
1082
|
+
slug: 'carl',
|
|
1083
|
+
visibility: 'public',
|
|
1084
|
+
type: 'test-people',
|
|
1085
|
+
firstName: 'Carl',
|
|
1086
|
+
lastName: 'Sagan',
|
|
1087
|
+
age: 62,
|
|
1088
|
+
alive: false,
|
|
1089
|
+
friendsIds: [ 'larry' ]
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
_id: 'peter:en:published',
|
|
1093
|
+
aposDocId: 'peter',
|
|
1094
|
+
aposLocale: 'en:published',
|
|
1095
|
+
type: 'test-people',
|
|
1096
|
+
visibility: 'loginRequired',
|
|
1097
|
+
firstName: 'Peter',
|
|
1098
|
+
lastName: 'Pan',
|
|
1099
|
+
age: 70,
|
|
1100
|
+
slug: 'peter'
|
|
1101
|
+
}
|
|
1102
|
+
]);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
async function insertOne(apos) {
|
|
1106
|
+
return apos.doc.insert(apos.task.getReq(), {
|
|
1107
|
+
slug: 'one',
|
|
1108
|
+
visibility: 'public',
|
|
1109
|
+
type: 'test-people',
|
|
1110
|
+
firstName: 'Lori',
|
|
1111
|
+
lastName: 'Ferber',
|
|
1112
|
+
age: 15,
|
|
1113
|
+
alive: true
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
async function insertOneWithRelated(apos) {
|
|
1118
|
+
return apos.doc.insert(apos.task.getReq(), {
|
|
1119
|
+
aposDocId: 'paul',
|
|
1120
|
+
aposLocale: 'en:published',
|
|
1121
|
+
slug: 'paul',
|
|
1122
|
+
visibility: 'public',
|
|
1123
|
+
type: 'test-people',
|
|
1124
|
+
firstName: 'Paul',
|
|
1125
|
+
lastName: 'McCartney',
|
|
1126
|
+
age: 24,
|
|
1127
|
+
alive: false,
|
|
1128
|
+
friendsIds: [ 'carl', 'larry' ],
|
|
1129
|
+
_friends: [ { _id: 'carl:en:published' }, { _id: 'larry:en:published' } ]
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
async function archiveDoc(apos, doc) {
|
|
1134
|
+
return apos.doc.update(apos.task.getReq(), {
|
|
1135
|
+
...doc,
|
|
1136
|
+
archived: true
|
|
1137
|
+
});
|
|
1138
|
+
}
|