@strapi/database 4.4.0-beta.2 → 4.4.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
File without changes
|
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const {
|
|
4
|
+
isUndefined,
|
|
5
|
+
castArray,
|
|
6
|
+
isNil,
|
|
7
|
+
has,
|
|
8
|
+
isString,
|
|
9
|
+
isInteger,
|
|
10
|
+
pick,
|
|
11
|
+
isPlainObject,
|
|
12
|
+
isEmpty,
|
|
13
|
+
isArray,
|
|
14
|
+
isNull,
|
|
15
|
+
} = require('lodash/fp');
|
|
16
|
+
const types = require('../types');
|
|
17
|
+
const { createField } = require('../fields');
|
|
18
|
+
const { createQueryBuilder } = require('../query');
|
|
7
19
|
const { createRepository } = require('./entity-repository');
|
|
8
|
-
const { isBidirectional, isOneToAny } = require('
|
|
20
|
+
const { isBidirectional, isOneToAny } = require('../metadata/relations');
|
|
21
|
+
const { deleteRelatedMorphOneRelationsAfterMorphToManyUpdate } = require('./morph-relations');
|
|
9
22
|
|
|
10
23
|
const toId = (value) => value.id || value;
|
|
11
|
-
const toIds = (value) =>
|
|
24
|
+
const toIds = (value) => castArray(value || []).map(toId);
|
|
12
25
|
|
|
13
|
-
const isValidId = (value) =>
|
|
26
|
+
const isValidId = (value) => isString(value) || isInteger(value);
|
|
14
27
|
const toAssocs = (data) => {
|
|
15
|
-
return
|
|
16
|
-
.filter((datum) => !
|
|
28
|
+
return castArray(data)
|
|
29
|
+
.filter((datum) => !isNil(datum))
|
|
17
30
|
.map((datum) => {
|
|
18
31
|
// if it is a string or an integer return an obj with id = to datum
|
|
19
32
|
if (isValidId(datum)) {
|
|
@@ -21,7 +34,7 @@ const toAssocs = (data) => {
|
|
|
21
34
|
}
|
|
22
35
|
|
|
23
36
|
// if it is an object check it has at least a valid id
|
|
24
|
-
if (!
|
|
37
|
+
if (!has('id', datum) || !isValidId(datum.id)) {
|
|
25
38
|
throw new Error(`Invalid id, expected a string or integer, got ${datum}`);
|
|
26
39
|
}
|
|
27
40
|
|
|
@@ -40,8 +53,8 @@ const processData = (metadata, data = {}, { withDefaults = false } = {}) => {
|
|
|
40
53
|
if (types.isScalar(attribute.type)) {
|
|
41
54
|
const field = createField(attribute);
|
|
42
55
|
|
|
43
|
-
if (
|
|
44
|
-
if (!
|
|
56
|
+
if (isUndefined(data[attributeName])) {
|
|
57
|
+
if (!isUndefined(attribute.default) && withDefaults) {
|
|
45
58
|
if (typeof attribute.default === 'function') {
|
|
46
59
|
obj[attributeName] = attribute.default();
|
|
47
60
|
} else {
|
|
@@ -66,11 +79,11 @@ const processData = (metadata, data = {}, { withDefaults = false } = {}) => {
|
|
|
66
79
|
const joinColumnName = attribute.joinColumn.name;
|
|
67
80
|
|
|
68
81
|
// allow setting to null
|
|
69
|
-
const attrValue = !
|
|
82
|
+
const attrValue = !isUndefined(data[attributeName])
|
|
70
83
|
? data[attributeName]
|
|
71
84
|
: data[joinColumnName];
|
|
72
85
|
|
|
73
|
-
if (!
|
|
86
|
+
if (!isUndefined(attrValue)) {
|
|
74
87
|
obj[joinColumnName] = attrValue;
|
|
75
88
|
}
|
|
76
89
|
|
|
@@ -91,8 +104,8 @@ const processData = (metadata, data = {}, { withDefaults = false } = {}) => {
|
|
|
91
104
|
continue;
|
|
92
105
|
}
|
|
93
106
|
|
|
94
|
-
if (!
|
|
95
|
-
if (!
|
|
107
|
+
if (!isUndefined(value)) {
|
|
108
|
+
if (!has('id', value) || !has(typeField, value)) {
|
|
96
109
|
throw new Error(`Expects properties ${typeField} an id to make a morph association`);
|
|
97
110
|
}
|
|
98
111
|
|
|
@@ -137,7 +150,7 @@ const createEntityManager = (db) => {
|
|
|
137
150
|
const states = await db.lifecycles.run('beforeCount', uid, { params });
|
|
138
151
|
|
|
139
152
|
const res = await this.createQueryBuilder(uid)
|
|
140
|
-
.init(
|
|
153
|
+
.init(pick(['_q', 'where', 'filters'], params))
|
|
141
154
|
.count()
|
|
142
155
|
.first()
|
|
143
156
|
.execute();
|
|
@@ -155,7 +168,7 @@ const createEntityManager = (db) => {
|
|
|
155
168
|
const metadata = db.metadata.get(uid);
|
|
156
169
|
const { data } = params;
|
|
157
170
|
|
|
158
|
-
if (!
|
|
171
|
+
if (!isPlainObject(data)) {
|
|
159
172
|
throw new Error('Create expects a data object');
|
|
160
173
|
}
|
|
161
174
|
|
|
@@ -187,7 +200,7 @@ const createEntityManager = (db) => {
|
|
|
187
200
|
const metadata = db.metadata.get(uid);
|
|
188
201
|
const { data } = params;
|
|
189
202
|
|
|
190
|
-
if (!
|
|
203
|
+
if (!isArray(data)) {
|
|
191
204
|
throw new Error('CreateMany expects data to be an array');
|
|
192
205
|
}
|
|
193
206
|
|
|
@@ -195,7 +208,7 @@ const createEntityManager = (db) => {
|
|
|
195
208
|
processData(metadata, datum, { withDefaults: true })
|
|
196
209
|
);
|
|
197
210
|
|
|
198
|
-
if (
|
|
211
|
+
if (isEmpty(dataToInsert)) {
|
|
199
212
|
throw new Error('Nothing to insert');
|
|
200
213
|
}
|
|
201
214
|
|
|
@@ -214,11 +227,11 @@ const createEntityManager = (db) => {
|
|
|
214
227
|
const metadata = db.metadata.get(uid);
|
|
215
228
|
const { where, data } = params;
|
|
216
229
|
|
|
217
|
-
if (!
|
|
230
|
+
if (!isPlainObject(data)) {
|
|
218
231
|
throw new Error('Update requires a data object');
|
|
219
232
|
}
|
|
220
233
|
|
|
221
|
-
if (
|
|
234
|
+
if (isEmpty(where)) {
|
|
222
235
|
throw new Error('Update requires a where parameter');
|
|
223
236
|
}
|
|
224
237
|
|
|
@@ -232,7 +245,7 @@ const createEntityManager = (db) => {
|
|
|
232
245
|
|
|
233
246
|
const dataToUpdate = processData(metadata, data);
|
|
234
247
|
|
|
235
|
-
if (!
|
|
248
|
+
if (!isEmpty(dataToUpdate)) {
|
|
236
249
|
await this.createQueryBuilder(uid).where({ id }).update(dataToUpdate).execute();
|
|
237
250
|
}
|
|
238
251
|
|
|
@@ -259,7 +272,7 @@ const createEntityManager = (db) => {
|
|
|
259
272
|
|
|
260
273
|
const dataToUpdate = processData(metadata, data);
|
|
261
274
|
|
|
262
|
-
if (
|
|
275
|
+
if (isEmpty(dataToUpdate)) {
|
|
263
276
|
throw new Error('Update requires data');
|
|
264
277
|
}
|
|
265
278
|
|
|
@@ -280,7 +293,7 @@ const createEntityManager = (db) => {
|
|
|
280
293
|
|
|
281
294
|
const { where, select, populate } = params;
|
|
282
295
|
|
|
283
|
-
if (
|
|
296
|
+
if (isEmpty(where)) {
|
|
284
297
|
throw new Error('Delete requires a where parameter');
|
|
285
298
|
}
|
|
286
299
|
|
|
@@ -336,7 +349,7 @@ const createEntityManager = (db) => {
|
|
|
336
349
|
for (const attributeName of Object.keys(attributes)) {
|
|
337
350
|
const attribute = attributes[attributeName];
|
|
338
351
|
|
|
339
|
-
const isValidLink =
|
|
352
|
+
const isValidLink = has(attributeName, data) && !isNil(data[attributeName]);
|
|
340
353
|
|
|
341
354
|
if (attribute.type !== 'relation' || !isValidLink) {
|
|
342
355
|
continue;
|
|
@@ -373,7 +386,7 @@ const createEntityManager = (db) => {
|
|
|
373
386
|
};
|
|
374
387
|
});
|
|
375
388
|
|
|
376
|
-
if (
|
|
389
|
+
if (isEmpty(rows)) {
|
|
377
390
|
continue;
|
|
378
391
|
}
|
|
379
392
|
|
|
@@ -398,10 +411,18 @@ const createEntityManager = (db) => {
|
|
|
398
411
|
...(data.__pivot || {}),
|
|
399
412
|
}));
|
|
400
413
|
|
|
401
|
-
if (
|
|
414
|
+
if (isEmpty(rows)) {
|
|
402
415
|
continue;
|
|
403
416
|
}
|
|
404
417
|
|
|
418
|
+
// delete previous relations
|
|
419
|
+
await deleteRelatedMorphOneRelationsAfterMorphToManyUpdate(rows, {
|
|
420
|
+
uid,
|
|
421
|
+
attributeName,
|
|
422
|
+
joinTable,
|
|
423
|
+
db,
|
|
424
|
+
});
|
|
425
|
+
|
|
405
426
|
await this.createQueryBuilder(joinTable.name).insert(rows).execute();
|
|
406
427
|
|
|
407
428
|
continue;
|
|
@@ -450,7 +471,7 @@ const createEntityManager = (db) => {
|
|
|
450
471
|
if (isOneToAny(attribute) && isBidirectional(attribute)) {
|
|
451
472
|
await this.createQueryBuilder(joinTable.name)
|
|
452
473
|
.delete()
|
|
453
|
-
.where({ [inverseJoinColumn.name]:
|
|
474
|
+
.where({ [inverseJoinColumn.name]: castArray(data[attributeName]) })
|
|
454
475
|
.where(joinTable.on || {})
|
|
455
476
|
.execute();
|
|
456
477
|
}
|
|
@@ -490,7 +511,7 @@ const createEntityManager = (db) => {
|
|
|
490
511
|
for (const attributeName of Object.keys(attributes)) {
|
|
491
512
|
const attribute = attributes[attributeName];
|
|
492
513
|
|
|
493
|
-
if (attribute.type !== 'relation' || !
|
|
514
|
+
if (attribute.type !== 'relation' || !has(attributeName, data)) {
|
|
494
515
|
continue;
|
|
495
516
|
}
|
|
496
517
|
|
|
@@ -503,12 +524,14 @@ const createEntityManager = (db) => {
|
|
|
503
524
|
// set columns
|
|
504
525
|
const { idColumn, typeColumn } = targetAttribute.morphColumn;
|
|
505
526
|
|
|
527
|
+
// update instead of deleting because the relation is directly on the entity table
|
|
528
|
+
// and not in a join table
|
|
506
529
|
await this.createQueryBuilder(target)
|
|
507
530
|
.update({ [idColumn.name]: null, [typeColumn.name]: null })
|
|
508
531
|
.where({ [idColumn.name]: id, [typeColumn.name]: uid })
|
|
509
532
|
.execute();
|
|
510
533
|
|
|
511
|
-
if (!
|
|
534
|
+
if (!isNull(data[attributeName])) {
|
|
512
535
|
await this.createQueryBuilder(target)
|
|
513
536
|
.update({ [idColumn.name]: id, [typeColumn.name]: uid })
|
|
514
537
|
.where({ id: toId(data[attributeName]) })
|
|
@@ -540,7 +563,7 @@ const createEntityManager = (db) => {
|
|
|
540
563
|
field: attributeName,
|
|
541
564
|
}));
|
|
542
565
|
|
|
543
|
-
if (
|
|
566
|
+
if (isEmpty(rows)) {
|
|
544
567
|
continue;
|
|
545
568
|
}
|
|
546
569
|
|
|
@@ -577,10 +600,18 @@ const createEntityManager = (db) => {
|
|
|
577
600
|
...(data.__pivot || {}),
|
|
578
601
|
}));
|
|
579
602
|
|
|
580
|
-
if (
|
|
603
|
+
if (isEmpty(rows)) {
|
|
581
604
|
continue;
|
|
582
605
|
}
|
|
583
606
|
|
|
607
|
+
// delete previous relations
|
|
608
|
+
await deleteRelatedMorphOneRelationsAfterMorphToManyUpdate(rows, {
|
|
609
|
+
uid,
|
|
610
|
+
attributeName,
|
|
611
|
+
joinTable,
|
|
612
|
+
db,
|
|
613
|
+
});
|
|
614
|
+
|
|
584
615
|
await this.createQueryBuilder(joinTable.name).insert(rows).execute();
|
|
585
616
|
|
|
586
617
|
continue;
|
|
@@ -602,7 +633,7 @@ const createEntityManager = (db) => {
|
|
|
602
633
|
.update({ [attribute.joinColumn.referencedColumn]: null })
|
|
603
634
|
.execute();
|
|
604
635
|
|
|
605
|
-
if (!
|
|
636
|
+
if (!isNull(data[attributeName])) {
|
|
606
637
|
await this.createQueryBuilder(target)
|
|
607
638
|
// NOTE: works if it is an array or a single id
|
|
608
639
|
.where({ id: data[attributeName] })
|
|
@@ -633,7 +664,7 @@ const createEntityManager = (db) => {
|
|
|
633
664
|
.execute();
|
|
634
665
|
}
|
|
635
666
|
|
|
636
|
-
if (!
|
|
667
|
+
if (!isNull(data[attributeName])) {
|
|
637
668
|
const insert = toAssocs(data[attributeName]).map((data) => {
|
|
638
669
|
return {
|
|
639
670
|
[joinColumn.name]: id,
|
|
@@ -791,7 +822,7 @@ const createEntityManager = (db) => {
|
|
|
791
822
|
async load(uid, entity, fields, params) {
|
|
792
823
|
const { attributes } = db.metadata.get(uid);
|
|
793
824
|
|
|
794
|
-
const fieldsArr =
|
|
825
|
+
const fieldsArr = castArray(fields);
|
|
795
826
|
fieldsArr.forEach((field) => {
|
|
796
827
|
const attribute = attributes[field];
|
|
797
828
|
|
|
@@ -814,7 +845,7 @@ const createEntityManager = (db) => {
|
|
|
814
845
|
}
|
|
815
846
|
|
|
816
847
|
if (Array.isArray(fields)) {
|
|
817
|
-
return
|
|
848
|
+
return pick(fields, entry);
|
|
818
849
|
}
|
|
819
850
|
|
|
820
851
|
return entry[fields];
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { groupBy, pipe, mapValues, map, isEmpty } = require('lodash/fp');
|
|
4
|
+
const { createQueryBuilder } = require('../query');
|
|
5
|
+
|
|
6
|
+
const getMorphToManyRowsLinkedToMorphOne = (rows, { uid, attributeName, typeColumn, db }) =>
|
|
7
|
+
rows.filter((row) => {
|
|
8
|
+
const relatedType = row[typeColumn.name];
|
|
9
|
+
const field = row.field;
|
|
10
|
+
|
|
11
|
+
const targetAttribute = db.metadata.get(relatedType).attributes[field];
|
|
12
|
+
|
|
13
|
+
// ensure targeted field is the right one + check if it is a morphOne
|
|
14
|
+
return (
|
|
15
|
+
targetAttribute?.target === uid &&
|
|
16
|
+
targetAttribute?.morphBy === attributeName &&
|
|
17
|
+
targetAttribute?.relation === 'morphOne'
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const deleteRelatedMorphOneRelationsAfterMorphToManyUpdate = async (
|
|
22
|
+
rows,
|
|
23
|
+
{ uid, attributeName, joinTable, db }
|
|
24
|
+
) => {
|
|
25
|
+
const { morphColumn } = joinTable;
|
|
26
|
+
const { idColumn, typeColumn } = morphColumn;
|
|
27
|
+
|
|
28
|
+
const morphOneRows = getMorphToManyRowsLinkedToMorphOne(rows, {
|
|
29
|
+
uid,
|
|
30
|
+
attributeName,
|
|
31
|
+
typeColumn,
|
|
32
|
+
db,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const groupByType = groupBy(typeColumn.name);
|
|
36
|
+
const groupByField = groupBy('field');
|
|
37
|
+
|
|
38
|
+
const typeAndFieldIdsGrouped = pipe(groupByType, mapValues(groupByField))(morphOneRows);
|
|
39
|
+
|
|
40
|
+
const orWhere = [];
|
|
41
|
+
|
|
42
|
+
for (const [type, v] of Object.entries(typeAndFieldIdsGrouped)) {
|
|
43
|
+
for (const [field, arr] of Object.entries(v)) {
|
|
44
|
+
orWhere.push({
|
|
45
|
+
[typeColumn.name]: type,
|
|
46
|
+
field,
|
|
47
|
+
[idColumn.name]: { $in: map(idColumn.name, arr) },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!isEmpty(orWhere)) {
|
|
53
|
+
await createQueryBuilder(joinTable.name, db).delete().where({ $or: orWhere }).execute();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
module.exports = {
|
|
58
|
+
deleteRelatedMorphOneRelationsAfterMorphToManyUpdate,
|
|
59
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/database",
|
|
3
|
-
"version": "4.4.0-beta.
|
|
3
|
+
"version": "4.4.0-beta.4",
|
|
4
4
|
"description": "Strapi's database layer",
|
|
5
5
|
"homepage": "https://strapi.io",
|
|
6
6
|
"bugs": {
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"node": ">=14.19.1 <=18.x.x",
|
|
43
43
|
"npm": ">=6.0.0"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "8ad31453be3eda4e01eae027995e7e584892e688"
|
|
46
46
|
}
|