@strapi/database 4.14.4 → 4.14.6
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 +3 -0
- package/dist/connection.d.ts +3 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/dialects/dialect.d.ts +27 -0
- package/dist/dialects/dialect.d.ts.map +1 -0
- package/dist/dialects/index.d.ts +5 -0
- package/dist/dialects/index.d.ts.map +1 -0
- package/dist/dialects/mysql/constants.d.ts +3 -0
- package/dist/dialects/mysql/constants.d.ts.map +1 -0
- package/dist/dialects/mysql/database-inspector.d.ts +12 -0
- package/dist/dialects/mysql/database-inspector.d.ts.map +1 -0
- package/dist/dialects/mysql/index.d.ts +20 -0
- package/dist/dialects/mysql/index.d.ts.map +1 -0
- package/dist/dialects/mysql/schema-inspector.d.ts +13 -0
- package/dist/dialects/mysql/schema-inspector.d.ts.map +1 -0
- package/dist/dialects/postgresql/index.d.ts +14 -0
- package/dist/dialects/postgresql/index.d.ts.map +1 -0
- package/dist/dialects/postgresql/schema-inspector.d.ts +14 -0
- package/dist/dialects/postgresql/schema-inspector.d.ts.map +1 -0
- package/dist/dialects/sqlite/index.d.ts +19 -0
- package/dist/dialects/sqlite/index.d.ts.map +1 -0
- package/dist/dialects/sqlite/schema-inspector.d.ts +13 -0
- package/dist/dialects/sqlite/schema-inspector.d.ts.map +1 -0
- package/dist/entity-manager/entity-repository.d.ts +4 -0
- package/dist/entity-manager/entity-repository.d.ts.map +1 -0
- package/dist/entity-manager/index.d.ts +5 -0
- package/dist/entity-manager/index.d.ts.map +1 -0
- package/dist/entity-manager/morph-relations.d.ts +13 -0
- package/dist/entity-manager/morph-relations.d.ts.map +1 -0
- package/dist/entity-manager/regular-relations.d.ts +83 -0
- package/dist/entity-manager/regular-relations.d.ts.map +1 -0
- package/dist/entity-manager/relations/cloning/regular-relations.d.ts +17 -0
- package/dist/entity-manager/relations/cloning/regular-relations.d.ts.map +1 -0
- package/dist/entity-manager/relations-orderer.d.ts +73 -0
- package/dist/entity-manager/relations-orderer.d.ts.map +1 -0
- package/dist/entity-manager/types.d.ts +97 -0
- package/dist/entity-manager/types.d.ts.map +1 -0
- package/dist/errors/database.d.ts +5 -0
- package/dist/errors/database.d.ts.map +1 -0
- package/dist/errors/index.d.ts +8 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/invalid-date.d.ts +5 -0
- package/dist/errors/invalid-date.d.ts.map +1 -0
- package/dist/errors/invalid-datetime.d.ts +5 -0
- package/dist/errors/invalid-datetime.d.ts.map +1 -0
- package/dist/errors/invalid-relation.d.ts +5 -0
- package/dist/errors/invalid-relation.d.ts.map +1 -0
- package/dist/errors/invalid-time.d.ts +5 -0
- package/dist/errors/invalid-time.d.ts.map +1 -0
- package/dist/errors/not-null.d.ts +7 -0
- package/dist/errors/not-null.d.ts.map +1 -0
- package/dist/fields/biginteger.d.ts +4 -0
- package/dist/fields/biginteger.d.ts.map +1 -0
- package/dist/fields/boolean.d.ts +6 -0
- package/dist/fields/boolean.d.ts.map +1 -0
- package/dist/fields/date.d.ts +6 -0
- package/dist/fields/date.d.ts.map +1 -0
- package/dist/fields/datetime.d.ts +6 -0
- package/dist/fields/datetime.d.ts.map +1 -0
- package/dist/fields/field.d.ts +7 -0
- package/dist/fields/field.d.ts.map +1 -0
- package/dist/fields/index.d.ts +4 -0
- package/dist/fields/index.d.ts.map +1 -0
- package/dist/fields/json.d.ts +6 -0
- package/dist/fields/json.d.ts.map +1 -0
- package/dist/fields/number.d.ts +6 -0
- package/dist/fields/number.d.ts.map +1 -0
- package/dist/fields/shared/parsers.d.ts +4 -0
- package/dist/fields/shared/parsers.d.ts.map +1 -0
- package/dist/fields/string.d.ts +6 -0
- package/dist/fields/string.d.ts.map +1 -0
- package/dist/fields/time.d.ts +6 -0
- package/dist/fields/time.d.ts.map +1 -0
- package/dist/fields/timestamp.d.ts +6 -0
- package/dist/fields/timestamp.d.ts.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6211 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +6179 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lifecycles/index.d.ts +17 -0
- package/dist/lifecycles/index.d.ts.map +1 -0
- package/dist/lifecycles/subscribers/index.d.ts +5 -0
- package/dist/lifecycles/subscribers/index.d.ts.map +1 -0
- package/dist/lifecycles/subscribers/models-lifecycles.d.ts +6 -0
- package/dist/lifecycles/subscribers/models-lifecycles.d.ts.map +1 -0
- package/dist/lifecycles/subscribers/timestamps.d.ts +3 -0
- package/dist/lifecycles/subscribers/timestamps.d.ts.map +1 -0
- package/dist/lifecycles/types.d.ts +25 -0
- package/dist/lifecycles/types.d.ts.map +1 -0
- package/dist/metadata/index.d.ts +10 -0
- package/dist/metadata/index.d.ts.map +1 -0
- package/dist/metadata/metadata.d.ts +22 -0
- package/dist/metadata/metadata.d.ts.map +1 -0
- package/dist/metadata/relations.d.ts +16 -0
- package/dist/metadata/relations.d.ts.map +1 -0
- package/dist/migrations/index.d.ts +12 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/storage.d.ts +15 -0
- package/dist/migrations/storage.d.ts.map +1 -0
- package/dist/query/helpers/index.d.ts +8 -0
- package/dist/query/helpers/index.d.ts.map +1 -0
- package/dist/query/helpers/join.d.ts +30 -0
- package/dist/query/helpers/join.d.ts.map +1 -0
- package/dist/query/helpers/order-by.d.ts +14 -0
- package/dist/query/helpers/order-by.d.ts.map +1 -0
- package/dist/query/helpers/populate/apply.d.ts +11 -0
- package/dist/query/helpers/populate/apply.d.ts.map +1 -0
- package/dist/query/helpers/populate/index.d.ts +4 -0
- package/dist/query/helpers/populate/index.d.ts.map +1 -0
- package/dist/query/helpers/populate/process.d.ts +24 -0
- package/dist/query/helpers/populate/process.d.ts.map +1 -0
- package/dist/query/helpers/search.d.ts +4 -0
- package/dist/query/helpers/search.d.ts.map +1 -0
- package/dist/query/helpers/streams/index.d.ts +2 -0
- package/dist/query/helpers/streams/index.d.ts.map +1 -0
- package/dist/query/helpers/streams/readable.d.ts +39 -0
- package/dist/query/helpers/streams/readable.d.ts.map +1 -0
- package/dist/query/helpers/transform.d.ts +8 -0
- package/dist/query/helpers/transform.d.ts.map +1 -0
- package/dist/query/helpers/where.d.ts +19 -0
- package/dist/query/helpers/where.d.ts.map +1 -0
- package/dist/query/index.d.ts +3 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/query-builder.d.ts +79 -0
- package/dist/query/query-builder.d.ts.map +1 -0
- package/dist/query/types.d.ts +8 -0
- package/dist/query/types.d.ts.map +1 -0
- package/dist/schema/builder.d.ts +33 -0
- package/dist/schema/builder.d.ts.map +1 -0
- package/dist/schema/diff.d.ts +7 -0
- package/dist/schema/diff.d.ts.map +1 -0
- package/dist/schema/index.d.ts +20 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/schema.d.ts +4 -0
- package/dist/schema/schema.d.ts.map +1 -0
- package/dist/schema/storage.d.ts +10 -0
- package/dist/schema/storage.d.ts.map +1 -0
- package/dist/schema/types.d.ts +103 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/transaction-context.d.ts +22 -0
- package/dist/transaction-context.d.ts.map +1 -0
- package/dist/types/index.d.ts +169 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/content-types.d.ts +13 -0
- package/dist/utils/content-types.d.ts.map +1 -0
- package/dist/utils/knex.d.ts +12 -0
- package/dist/utils/knex.d.ts.map +1 -0
- package/dist/utils/types.d.ts +10 -0
- package/dist/utils/types.d.ts.map +1 -0
- package/dist/validations/index.d.ts +6 -0
- package/dist/validations/index.d.ts.map +1 -0
- package/dist/validations/relations/bidirectional.d.ts +12 -0
- package/dist/validations/relations/bidirectional.d.ts.map +1 -0
- package/dist/validations/relations/index.d.ts +7 -0
- package/dist/validations/relations/index.d.ts.map +1 -0
- package/package.json +20 -8
- package/.eslintignore +0 -2
- package/.eslintrc.js +0 -4
- package/jest.config.js +0 -6
- package/lib/__tests__/index.test.js +0 -93
- package/lib/__tests__/lifecycles.test.js +0 -55
- package/lib/connection.js +0 -64
- package/lib/dialects/dialect.js +0 -63
- package/lib/dialects/index.js +0 -53
- package/lib/dialects/mysql/constants.js +0 -6
- package/lib/dialects/mysql/database-inspector.js +0 -37
- package/lib/dialects/mysql/index.js +0 -92
- package/lib/dialects/mysql/schema-inspector.js +0 -234
- package/lib/dialects/postgresql/index.js +0 -65
- package/lib/dialects/postgresql/schema-inspector.js +0 -283
- package/lib/dialects/sqlite/index.js +0 -87
- package/lib/dialects/sqlite/schema-inspector.js +0 -151
- package/lib/entity-manager/__tests__/relations-orderer.test.js +0 -186
- package/lib/entity-manager/__tests__/sort-connect-array.test.js +0 -79
- package/lib/entity-manager/entity-repository.js +0 -164
- package/lib/entity-manager/index.js +0 -1385
- package/lib/entity-manager/morph-relations.js +0 -63
- package/lib/entity-manager/regular-relations.js +0 -506
- package/lib/entity-manager/relations/cloning/regular-relations.js +0 -76
- package/lib/entity-manager/relations-orderer.js +0 -225
- package/lib/errors/database.js +0 -12
- package/lib/errors/index.js +0 -17
- package/lib/errors/invalid-date.js +0 -14
- package/lib/errors/invalid-datetime.js +0 -14
- package/lib/errors/invalid-relation.js +0 -14
- package/lib/errors/invalid-time.js +0 -14
- package/lib/errors/not-null.js +0 -15
- package/lib/fields/biginteger.js +0 -17
- package/lib/fields/boolean.js +0 -39
- package/lib/fields/date.js +0 -16
- package/lib/fields/datetime.js +0 -19
- package/lib/fields/field.js +0 -17
- package/lib/fields/index.d.ts +0 -9
- package/lib/fields/index.js +0 -50
- package/lib/fields/json.js +0 -21
- package/lib/fields/number.js +0 -23
- package/lib/fields/shared/parsers.js +0 -71
- package/lib/fields/string.js +0 -17
- package/lib/fields/time.js +0 -17
- package/lib/fields/timestamp.js +0 -19
- package/lib/index.d.ts +0 -198
- package/lib/index.js +0 -129
- package/lib/lifecycles/index.d.ts +0 -51
- package/lib/lifecycles/index.js +0 -90
- package/lib/lifecycles/subscribers/index.d.ts +0 -11
- package/lib/lifecycles/subscribers/models-lifecycles.js +0 -19
- package/lib/lifecycles/subscribers/timestamps.js +0 -65
- package/lib/metadata/index.js +0 -244
- package/lib/metadata/relations.js +0 -578
- package/lib/migrations/index.d.ts +0 -9
- package/lib/migrations/index.js +0 -75
- package/lib/migrations/storage.js +0 -44
- package/lib/query/helpers/index.js +0 -11
- package/lib/query/helpers/join.js +0 -96
- package/lib/query/helpers/order-by.js +0 -70
- package/lib/query/helpers/populate/apply.js +0 -664
- package/lib/query/helpers/populate/index.js +0 -9
- package/lib/query/helpers/populate/process.js +0 -102
- package/lib/query/helpers/search.js +0 -84
- package/lib/query/helpers/streams/index.js +0 -5
- package/lib/query/helpers/streams/readable.js +0 -174
- package/lib/query/helpers/transform.js +0 -84
- package/lib/query/helpers/where.js +0 -365
- package/lib/query/index.js +0 -7
- package/lib/query/query-builder.js +0 -514
- package/lib/schema/__tests__/schema-diff.test.js +0 -231
- package/lib/schema/builder.js +0 -386
- package/lib/schema/diff.js +0 -399
- package/lib/schema/index.d.ts +0 -49
- package/lib/schema/index.js +0 -94
- package/lib/schema/schema.js +0 -202
- package/lib/schema/storage.js +0 -76
- package/lib/transaction-context.js +0 -68
- package/lib/types/index.d.ts +0 -6
- package/lib/types/index.js +0 -35
- package/lib/utils/content-types.js +0 -40
- package/lib/utils/knex.js +0 -22
- package/lib/validations/index.js +0 -20
- package/lib/validations/relations/bidirectional.js +0 -89
- package/lib/validations/relations/index.js +0 -14
|
@@ -1,1385 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
castArray,
|
|
5
|
-
compact,
|
|
6
|
-
difference,
|
|
7
|
-
differenceWith,
|
|
8
|
-
flow,
|
|
9
|
-
has,
|
|
10
|
-
isArray,
|
|
11
|
-
isEmpty,
|
|
12
|
-
isEqual,
|
|
13
|
-
isInteger,
|
|
14
|
-
isNil,
|
|
15
|
-
isNull,
|
|
16
|
-
isNumber,
|
|
17
|
-
isPlainObject,
|
|
18
|
-
isString,
|
|
19
|
-
isUndefined,
|
|
20
|
-
map,
|
|
21
|
-
mergeWith,
|
|
22
|
-
omit,
|
|
23
|
-
pick,
|
|
24
|
-
uniqBy,
|
|
25
|
-
uniqWith,
|
|
26
|
-
} = require('lodash/fp');
|
|
27
|
-
|
|
28
|
-
const { mapAsync } = require('@strapi/utils');
|
|
29
|
-
const types = require('../types');
|
|
30
|
-
const { createField } = require('../fields');
|
|
31
|
-
const { createQueryBuilder } = require('../query');
|
|
32
|
-
const { createRepository } = require('./entity-repository');
|
|
33
|
-
const { deleteRelatedMorphOneRelationsAfterMorphToManyUpdate } = require('./morph-relations');
|
|
34
|
-
const {
|
|
35
|
-
isPolymorphic,
|
|
36
|
-
isBidirectional,
|
|
37
|
-
isAnyToOne,
|
|
38
|
-
isOneToAny,
|
|
39
|
-
hasOrderColumn,
|
|
40
|
-
hasInverseOrderColumn,
|
|
41
|
-
} = require('../metadata/relations');
|
|
42
|
-
const {
|
|
43
|
-
deletePreviousOneToAnyRelations,
|
|
44
|
-
deletePreviousAnyToOneRelations,
|
|
45
|
-
deleteRelations,
|
|
46
|
-
cleanOrderColumns,
|
|
47
|
-
} = require('./regular-relations');
|
|
48
|
-
const { relationsOrderer } = require('./relations-orderer');
|
|
49
|
-
const {
|
|
50
|
-
replaceRegularRelations,
|
|
51
|
-
cloneRegularRelations,
|
|
52
|
-
} = require('./relations/cloning/regular-relations');
|
|
53
|
-
const { DatabaseError } = require('../errors');
|
|
54
|
-
|
|
55
|
-
const toId = (value) => value.id || value;
|
|
56
|
-
const toIds = (value) => castArray(value || []).map(toId);
|
|
57
|
-
|
|
58
|
-
const isValidId = (value) => isString(value) || isInteger(value);
|
|
59
|
-
const toIdArray = (data) => {
|
|
60
|
-
const array = castArray(data)
|
|
61
|
-
.filter((datum) => !isNil(datum))
|
|
62
|
-
.map((datum) => {
|
|
63
|
-
// if it is a string or an integer return an obj with id = to datum
|
|
64
|
-
if (isValidId(datum)) {
|
|
65
|
-
return { id: datum, __pivot: {} };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// if it is an object check it has at least a valid id
|
|
69
|
-
if (!has('id', datum) || !isValidId(datum.id)) {
|
|
70
|
-
throw new Error(`Invalid id, expected a string or integer, got ${datum}`);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return datum;
|
|
74
|
-
});
|
|
75
|
-
return uniqWith(isEqual, array);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const toAssocs = (data) => {
|
|
79
|
-
if (isArray(data) || isString(data) || isNumber(data) || isNull(data) || data?.id) {
|
|
80
|
-
return {
|
|
81
|
-
set: isNull(data) ? data : toIdArray(data),
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (data?.set) {
|
|
86
|
-
return {
|
|
87
|
-
set: isNull(data.set) ? data.set : toIdArray(data.set),
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
options: {
|
|
93
|
-
strict: data?.options?.strict,
|
|
94
|
-
},
|
|
95
|
-
connect: toIdArray(data?.connect).map((elm) => ({
|
|
96
|
-
id: elm.id,
|
|
97
|
-
position: elm.position ? elm.position : { end: true },
|
|
98
|
-
})),
|
|
99
|
-
disconnect: toIdArray(data?.disconnect),
|
|
100
|
-
};
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const processData = (metadata, data = {}, { withDefaults = false } = {}) => {
|
|
104
|
-
const { attributes } = metadata;
|
|
105
|
-
|
|
106
|
-
const obj = {};
|
|
107
|
-
|
|
108
|
-
for (const attributeName of Object.keys(attributes)) {
|
|
109
|
-
const attribute = attributes[attributeName];
|
|
110
|
-
|
|
111
|
-
if (types.isScalar(attribute.type)) {
|
|
112
|
-
const field = createField(attribute);
|
|
113
|
-
|
|
114
|
-
if (isUndefined(data[attributeName])) {
|
|
115
|
-
if (!isUndefined(attribute.default) && withDefaults) {
|
|
116
|
-
if (typeof attribute.default === 'function') {
|
|
117
|
-
obj[attributeName] = attribute.default();
|
|
118
|
-
} else {
|
|
119
|
-
obj[attributeName] = attribute.default;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (typeof field.validate === 'function' && data[attributeName] !== null) {
|
|
126
|
-
field.validate(data[attributeName]);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const val = data[attributeName] === null ? null : field.toDB(data[attributeName]);
|
|
130
|
-
|
|
131
|
-
obj[attributeName] = val;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (types.isRelation(attribute.type)) {
|
|
135
|
-
// oneToOne & manyToOne
|
|
136
|
-
if (attribute.joinColumn && attribute.owner) {
|
|
137
|
-
const joinColumnName = attribute.joinColumn.name;
|
|
138
|
-
|
|
139
|
-
// allow setting to null
|
|
140
|
-
const attrValue = !isUndefined(data[attributeName])
|
|
141
|
-
? data[attributeName]
|
|
142
|
-
: data[joinColumnName];
|
|
143
|
-
|
|
144
|
-
if (!isUndefined(attrValue)) {
|
|
145
|
-
obj[joinColumnName] = attrValue;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (attribute.morphColumn && attribute.owner) {
|
|
152
|
-
const { idColumn, typeColumn, typeField = '__type' } = attribute.morphColumn;
|
|
153
|
-
|
|
154
|
-
const value = data[attributeName];
|
|
155
|
-
|
|
156
|
-
if (value === null) {
|
|
157
|
-
Object.assign(obj, {
|
|
158
|
-
[idColumn.name]: null,
|
|
159
|
-
[typeColumn.name]: null,
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (!isUndefined(value)) {
|
|
166
|
-
if (!has('id', value) || !has(typeField, value)) {
|
|
167
|
-
throw new Error(`Expects properties ${typeField} an id to make a morph association`);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
Object.assign(obj, {
|
|
171
|
-
[idColumn.name]: value.id,
|
|
172
|
-
[typeColumn.name]: value[typeField],
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return obj;
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const createEntityManager = (db) => {
|
|
183
|
-
const repoMap = {};
|
|
184
|
-
|
|
185
|
-
return {
|
|
186
|
-
async findOne(uid, params) {
|
|
187
|
-
const states = await db.lifecycles.run('beforeFindOne', uid, { params });
|
|
188
|
-
|
|
189
|
-
const result = await this.createQueryBuilder(uid).init(params).first().execute();
|
|
190
|
-
|
|
191
|
-
await db.lifecycles.run('afterFindOne', uid, { params, result }, states);
|
|
192
|
-
|
|
193
|
-
return result;
|
|
194
|
-
},
|
|
195
|
-
|
|
196
|
-
// should we name it findOne because people are used to it ?
|
|
197
|
-
async findMany(uid, params) {
|
|
198
|
-
const states = await db.lifecycles.run('beforeFindMany', uid, { params });
|
|
199
|
-
|
|
200
|
-
const result = await this.createQueryBuilder(uid).init(params).execute();
|
|
201
|
-
|
|
202
|
-
await db.lifecycles.run('afterFindMany', uid, { params, result }, states);
|
|
203
|
-
|
|
204
|
-
return result;
|
|
205
|
-
},
|
|
206
|
-
|
|
207
|
-
async count(uid, params) {
|
|
208
|
-
const states = await db.lifecycles.run('beforeCount', uid, { params });
|
|
209
|
-
|
|
210
|
-
const res = await this.createQueryBuilder(uid)
|
|
211
|
-
.init(pick(['_q', 'where', 'filters'], params))
|
|
212
|
-
.count()
|
|
213
|
-
.first()
|
|
214
|
-
.execute();
|
|
215
|
-
|
|
216
|
-
const result = Number(res.count);
|
|
217
|
-
|
|
218
|
-
await db.lifecycles.run('afterCount', uid, { params, result }, states);
|
|
219
|
-
|
|
220
|
-
return result;
|
|
221
|
-
},
|
|
222
|
-
|
|
223
|
-
async create(uid, params = {}) {
|
|
224
|
-
const states = await db.lifecycles.run('beforeCreate', uid, { params });
|
|
225
|
-
|
|
226
|
-
const metadata = db.metadata.get(uid);
|
|
227
|
-
const { data } = params;
|
|
228
|
-
|
|
229
|
-
if (!isPlainObject(data)) {
|
|
230
|
-
throw new Error('Create expects a data object');
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const dataToInsert = processData(metadata, data, { withDefaults: true });
|
|
234
|
-
|
|
235
|
-
const res = await this.createQueryBuilder(uid).insert(dataToInsert).execute();
|
|
236
|
-
|
|
237
|
-
const id = res[0].id || res[0];
|
|
238
|
-
|
|
239
|
-
const trx = await strapi.db.transaction();
|
|
240
|
-
try {
|
|
241
|
-
await this.attachRelations(uid, id, data, { transaction: trx.get() });
|
|
242
|
-
|
|
243
|
-
await trx.commit();
|
|
244
|
-
} catch (e) {
|
|
245
|
-
await trx.rollback();
|
|
246
|
-
await this.createQueryBuilder(uid).where({ id }).delete().execute();
|
|
247
|
-
throw e;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// TODO: in case there is no select or populate specified return the inserted data ?
|
|
251
|
-
// TODO: do not trigger the findOne lifecycles ?
|
|
252
|
-
const result = await this.findOne(uid, {
|
|
253
|
-
where: { id },
|
|
254
|
-
select: params.select,
|
|
255
|
-
populate: params.populate,
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
await db.lifecycles.run('afterCreate', uid, { params, result }, states);
|
|
259
|
-
|
|
260
|
-
return result;
|
|
261
|
-
},
|
|
262
|
-
|
|
263
|
-
// TODO: where do we handle relation processing for many queries ?
|
|
264
|
-
async createMany(uid, params = {}) {
|
|
265
|
-
const states = await db.lifecycles.run('beforeCreateMany', uid, { params });
|
|
266
|
-
|
|
267
|
-
const metadata = db.metadata.get(uid);
|
|
268
|
-
const { data } = params;
|
|
269
|
-
|
|
270
|
-
if (!isArray(data)) {
|
|
271
|
-
throw new Error('CreateMany expects data to be an array');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const dataToInsert = data.map((datum) =>
|
|
275
|
-
processData(metadata, datum, { withDefaults: true })
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
if (isEmpty(dataToInsert)) {
|
|
279
|
-
throw new Error('Nothing to insert');
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const createdEntries = await this.createQueryBuilder(uid).insert(dataToInsert).execute();
|
|
283
|
-
|
|
284
|
-
const result = {
|
|
285
|
-
count: data.length,
|
|
286
|
-
ids: createdEntries.map((entry) => (typeof entry === 'object' ? entry?.id : entry)),
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
await db.lifecycles.run('afterCreateMany', uid, { params, result }, states);
|
|
290
|
-
|
|
291
|
-
return result;
|
|
292
|
-
},
|
|
293
|
-
|
|
294
|
-
async update(uid, params = {}) {
|
|
295
|
-
const states = await db.lifecycles.run('beforeUpdate', uid, { params });
|
|
296
|
-
|
|
297
|
-
const metadata = db.metadata.get(uid);
|
|
298
|
-
const { where, data } = params;
|
|
299
|
-
|
|
300
|
-
if (!isPlainObject(data)) {
|
|
301
|
-
throw new Error('Update requires a data object');
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (isEmpty(where)) {
|
|
305
|
-
throw new Error('Update requires a where parameter');
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const entity = await this.createQueryBuilder(uid)
|
|
309
|
-
.select('*')
|
|
310
|
-
.where(where)
|
|
311
|
-
.first()
|
|
312
|
-
.execute({ mapResults: false });
|
|
313
|
-
|
|
314
|
-
if (!entity) {
|
|
315
|
-
return null;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const { id } = entity;
|
|
319
|
-
|
|
320
|
-
const dataToUpdate = processData(metadata, data);
|
|
321
|
-
|
|
322
|
-
if (!isEmpty(dataToUpdate)) {
|
|
323
|
-
await this.createQueryBuilder(uid).where({ id }).update(dataToUpdate).execute();
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const trx = await strapi.db.transaction();
|
|
327
|
-
try {
|
|
328
|
-
await this.updateRelations(uid, id, data, { transaction: trx.get() });
|
|
329
|
-
await trx.commit();
|
|
330
|
-
} catch (e) {
|
|
331
|
-
await trx.rollback();
|
|
332
|
-
await this.createQueryBuilder(uid).where({ id }).update(entity).execute();
|
|
333
|
-
throw e;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// TODO: do not trigger the findOne lifecycles ?
|
|
337
|
-
const result = await this.findOne(uid, {
|
|
338
|
-
where: { id },
|
|
339
|
-
select: params.select,
|
|
340
|
-
populate: params.populate,
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
await db.lifecycles.run('afterUpdate', uid, { params, result }, states);
|
|
344
|
-
|
|
345
|
-
return result;
|
|
346
|
-
},
|
|
347
|
-
|
|
348
|
-
// TODO: where do we handle relation processing for many queries ?
|
|
349
|
-
async updateMany(uid, params = {}) {
|
|
350
|
-
const states = await db.lifecycles.run('beforeUpdateMany', uid, { params });
|
|
351
|
-
|
|
352
|
-
const metadata = db.metadata.get(uid);
|
|
353
|
-
const { where, data } = params;
|
|
354
|
-
|
|
355
|
-
const dataToUpdate = processData(metadata, data);
|
|
356
|
-
|
|
357
|
-
if (isEmpty(dataToUpdate)) {
|
|
358
|
-
throw new Error('Update requires data');
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
const updatedRows = await this.createQueryBuilder(uid)
|
|
362
|
-
.where(where)
|
|
363
|
-
.update(dataToUpdate)
|
|
364
|
-
.execute();
|
|
365
|
-
|
|
366
|
-
const result = { count: updatedRows };
|
|
367
|
-
|
|
368
|
-
await db.lifecycles.run('afterUpdateMany', uid, { params, result }, states);
|
|
369
|
-
|
|
370
|
-
return result;
|
|
371
|
-
},
|
|
372
|
-
|
|
373
|
-
async clone(uid, cloneId, params = {}) {
|
|
374
|
-
const states = await db.lifecycles.run('beforeCreate', uid, { params });
|
|
375
|
-
|
|
376
|
-
const metadata = db.metadata.get(uid);
|
|
377
|
-
const { data } = params;
|
|
378
|
-
|
|
379
|
-
if (!isNil(data) && !isPlainObject(data)) {
|
|
380
|
-
throw new Error('Create expects a data object');
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// TODO: Handle join columns?
|
|
384
|
-
const entity = await this.findOne(uid, { where: { id: cloneId } });
|
|
385
|
-
|
|
386
|
-
const dataToInsert = flow(
|
|
387
|
-
// Omit unwanted properties
|
|
388
|
-
omit(['id', 'created_at', 'updated_at']),
|
|
389
|
-
// Merge with provided data, set attribute to null if data attribute is null
|
|
390
|
-
mergeWith(data || {}, (original, override) => (override === null ? override : original)),
|
|
391
|
-
// Process data with metadata
|
|
392
|
-
(entity) => processData(metadata, entity, { withDefaults: true })
|
|
393
|
-
)(entity);
|
|
394
|
-
|
|
395
|
-
const res = await this.createQueryBuilder(uid).insert(dataToInsert).execute();
|
|
396
|
-
|
|
397
|
-
const id = res[0].id || res[0];
|
|
398
|
-
|
|
399
|
-
const trx = await strapi.db.transaction();
|
|
400
|
-
try {
|
|
401
|
-
const cloneAttrs = Object.entries(metadata.attributes).reduce((acc, [attrName, attr]) => {
|
|
402
|
-
// TODO: handle components in the db layer
|
|
403
|
-
if (attr.type === 'relation' && attr.joinTable && !attr.component) {
|
|
404
|
-
acc.push(attrName);
|
|
405
|
-
}
|
|
406
|
-
return acc;
|
|
407
|
-
}, []);
|
|
408
|
-
|
|
409
|
-
await this.cloneRelations(uid, id, cloneId, data, { cloneAttrs, transaction: trx.get() });
|
|
410
|
-
await trx.commit();
|
|
411
|
-
} catch (e) {
|
|
412
|
-
await trx.rollback();
|
|
413
|
-
await this.createQueryBuilder(uid).where({ id }).delete().execute();
|
|
414
|
-
throw e;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const result = await this.findOne(uid, {
|
|
418
|
-
where: { id },
|
|
419
|
-
select: params.select,
|
|
420
|
-
populate: params.populate,
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
await db.lifecycles.run('afterCreate', uid, { params, result }, states);
|
|
424
|
-
|
|
425
|
-
return result;
|
|
426
|
-
},
|
|
427
|
-
|
|
428
|
-
async delete(uid, params = {}) {
|
|
429
|
-
const states = await db.lifecycles.run('beforeDelete', uid, { params });
|
|
430
|
-
|
|
431
|
-
const { where, select, populate } = params;
|
|
432
|
-
|
|
433
|
-
if (isEmpty(where)) {
|
|
434
|
-
throw new Error('Delete requires a where parameter');
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// TODO: do not trigger the findOne lifecycles ?
|
|
438
|
-
const entity = await this.findOne(uid, {
|
|
439
|
-
select: select && ['id'].concat(select),
|
|
440
|
-
where,
|
|
441
|
-
populate,
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
if (!entity) {
|
|
445
|
-
return null;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
const { id } = entity;
|
|
449
|
-
|
|
450
|
-
await this.createQueryBuilder(uid).where({ id }).delete().execute();
|
|
451
|
-
|
|
452
|
-
const trx = await strapi.db.transaction();
|
|
453
|
-
try {
|
|
454
|
-
await this.deleteRelations(uid, id, { transaction: trx.get() });
|
|
455
|
-
|
|
456
|
-
await trx.commit();
|
|
457
|
-
} catch (e) {
|
|
458
|
-
await trx.rollback();
|
|
459
|
-
throw e;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
await db.lifecycles.run('afterDelete', uid, { params, result: entity }, states);
|
|
463
|
-
|
|
464
|
-
return entity;
|
|
465
|
-
},
|
|
466
|
-
|
|
467
|
-
// TODO: where do we handle relation processing for many queries ?
|
|
468
|
-
async deleteMany(uid, params = {}) {
|
|
469
|
-
const states = await db.lifecycles.run('beforeDeleteMany', uid, { params });
|
|
470
|
-
|
|
471
|
-
const { where } = params;
|
|
472
|
-
|
|
473
|
-
const deletedRows = await this.createQueryBuilder(uid).where(where).delete().execute();
|
|
474
|
-
|
|
475
|
-
const result = { count: deletedRows };
|
|
476
|
-
|
|
477
|
-
await db.lifecycles.run('afterDeleteMany', uid, { params, result }, states);
|
|
478
|
-
|
|
479
|
-
return result;
|
|
480
|
-
},
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* Attach relations to a new entity
|
|
484
|
-
*
|
|
485
|
-
* @param {EntityManager} em - entity manager instance
|
|
486
|
-
* @param {Metadata} metadata - model metadta
|
|
487
|
-
* @param {ID} id - entity ID
|
|
488
|
-
* @param {object} data - data received for creation
|
|
489
|
-
*/
|
|
490
|
-
async attachRelations(uid, id, data, { transaction: trx }) {
|
|
491
|
-
const { attributes } = db.metadata.get(uid);
|
|
492
|
-
|
|
493
|
-
for (const attributeName of Object.keys(attributes)) {
|
|
494
|
-
const attribute = attributes[attributeName];
|
|
495
|
-
|
|
496
|
-
const isValidLink = has(attributeName, data) && !isNil(data[attributeName]);
|
|
497
|
-
|
|
498
|
-
if (attribute.type !== 'relation' || !isValidLink) {
|
|
499
|
-
continue;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
const cleanRelationData = toAssocs(data[attributeName]);
|
|
503
|
-
|
|
504
|
-
if (attribute.relation === 'morphOne' || attribute.relation === 'morphMany') {
|
|
505
|
-
const { target, morphBy } = attribute;
|
|
506
|
-
|
|
507
|
-
const targetAttribute = db.metadata.get(target).attributes[morphBy];
|
|
508
|
-
|
|
509
|
-
if (targetAttribute.relation === 'morphToOne') {
|
|
510
|
-
// set columns
|
|
511
|
-
const { idColumn, typeColumn } = targetAttribute.morphColumn;
|
|
512
|
-
|
|
513
|
-
const relId = toId(cleanRelationData.set[0]);
|
|
514
|
-
|
|
515
|
-
await this.createQueryBuilder(target)
|
|
516
|
-
.update({ [idColumn.name]: id, [typeColumn.name]: uid })
|
|
517
|
-
.where({ id: relId })
|
|
518
|
-
.transacting(trx)
|
|
519
|
-
.execute();
|
|
520
|
-
} else if (targetAttribute.relation === 'morphToMany') {
|
|
521
|
-
const { joinTable } = targetAttribute;
|
|
522
|
-
const { joinColumn, morphColumn } = joinTable;
|
|
523
|
-
|
|
524
|
-
const { idColumn, typeColumn } = morphColumn;
|
|
525
|
-
|
|
526
|
-
if (isEmpty(cleanRelationData.set)) {
|
|
527
|
-
continue;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
const rows = cleanRelationData.set.map((data, idx) => {
|
|
531
|
-
return {
|
|
532
|
-
[joinColumn.name]: data.id,
|
|
533
|
-
[idColumn.name]: id,
|
|
534
|
-
[typeColumn.name]: uid,
|
|
535
|
-
...(joinTable.on || {}),
|
|
536
|
-
...(data.__pivot || {}),
|
|
537
|
-
order: idx + 1,
|
|
538
|
-
field: attributeName,
|
|
539
|
-
};
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
continue;
|
|
546
|
-
} else if (attribute.relation === 'morphToOne') {
|
|
547
|
-
// handled on the entry itself
|
|
548
|
-
continue;
|
|
549
|
-
} else if (attribute.relation === 'morphToMany') {
|
|
550
|
-
const { joinTable } = attribute;
|
|
551
|
-
const { joinColumn, morphColumn } = joinTable;
|
|
552
|
-
|
|
553
|
-
const { idColumn, typeColumn, typeField = '__type' } = morphColumn;
|
|
554
|
-
|
|
555
|
-
if (isEmpty(cleanRelationData.set)) {
|
|
556
|
-
continue;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
const rows = cleanRelationData.set.map((data, idx) => ({
|
|
560
|
-
[joinColumn.name]: id,
|
|
561
|
-
[idColumn.name]: data.id,
|
|
562
|
-
[typeColumn.name]: data[typeField],
|
|
563
|
-
...(joinTable.on || {}),
|
|
564
|
-
...(data.__pivot || {}),
|
|
565
|
-
order: idx + 1,
|
|
566
|
-
}));
|
|
567
|
-
|
|
568
|
-
// delete previous relations
|
|
569
|
-
await deleteRelatedMorphOneRelationsAfterMorphToManyUpdate(rows, {
|
|
570
|
-
uid,
|
|
571
|
-
attributeName,
|
|
572
|
-
joinTable,
|
|
573
|
-
db,
|
|
574
|
-
transaction: trx,
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
578
|
-
|
|
579
|
-
continue;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
if (attribute.joinColumn && attribute.owner) {
|
|
583
|
-
const relIdsToAdd = toIds(cleanRelationData.set);
|
|
584
|
-
if (
|
|
585
|
-
attribute.relation === 'oneToOne' &&
|
|
586
|
-
isBidirectional(attribute) &&
|
|
587
|
-
relIdsToAdd.length
|
|
588
|
-
) {
|
|
589
|
-
await this.createQueryBuilder(uid)
|
|
590
|
-
.where({ [attribute.joinColumn.name]: relIdsToAdd, id: { $ne: id } })
|
|
591
|
-
.update({ [attribute.joinColumn.name]: null })
|
|
592
|
-
.transacting(trx)
|
|
593
|
-
.execute();
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
continue;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
// oneToOne oneToMany on the non owning side
|
|
600
|
-
if (attribute.joinColumn && !attribute.owner) {
|
|
601
|
-
// need to set the column on the target
|
|
602
|
-
const { target } = attribute;
|
|
603
|
-
|
|
604
|
-
// TODO: check it is an id & the entity exists (will throw due to FKs otherwise so not a big pbl in SQL)
|
|
605
|
-
const relIdsToAdd = toIds(cleanRelationData.set);
|
|
606
|
-
|
|
607
|
-
await this.createQueryBuilder(target)
|
|
608
|
-
.where({ [attribute.joinColumn.referencedColumn]: id })
|
|
609
|
-
.update({ [attribute.joinColumn.referencedColumn]: null })
|
|
610
|
-
.transacting(trx)
|
|
611
|
-
.execute();
|
|
612
|
-
|
|
613
|
-
await this.createQueryBuilder(target)
|
|
614
|
-
.update({ [attribute.joinColumn.referencedColumn]: id })
|
|
615
|
-
// NOTE: works if it is an array or a single id
|
|
616
|
-
.where({ id: relIdsToAdd })
|
|
617
|
-
.transacting(trx)
|
|
618
|
-
.execute();
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
if (attribute.joinTable) {
|
|
622
|
-
// need to set the column on the target
|
|
623
|
-
|
|
624
|
-
const { joinTable } = attribute;
|
|
625
|
-
const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } =
|
|
626
|
-
joinTable;
|
|
627
|
-
|
|
628
|
-
const relsToAdd = cleanRelationData.set || cleanRelationData.connect;
|
|
629
|
-
const relIdsToadd = toIds(relsToAdd);
|
|
630
|
-
|
|
631
|
-
if (isBidirectional(attribute) && isOneToAny(attribute)) {
|
|
632
|
-
await deletePreviousOneToAnyRelations({
|
|
633
|
-
id,
|
|
634
|
-
attribute,
|
|
635
|
-
relIdsToadd,
|
|
636
|
-
db,
|
|
637
|
-
transaction: trx,
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
// prepare new relations to insert
|
|
642
|
-
const insert = uniqBy('id', relsToAdd).map((data) => {
|
|
643
|
-
return {
|
|
644
|
-
[joinColumn.name]: id,
|
|
645
|
-
[inverseJoinColumn.name]: data.id,
|
|
646
|
-
...(joinTable.on || {}),
|
|
647
|
-
...(data.__pivot || {}),
|
|
648
|
-
};
|
|
649
|
-
});
|
|
650
|
-
|
|
651
|
-
// add order value
|
|
652
|
-
if (cleanRelationData.set && hasOrderColumn(attribute)) {
|
|
653
|
-
insert.forEach((data, idx) => {
|
|
654
|
-
data[orderColumnName] = idx + 1;
|
|
655
|
-
});
|
|
656
|
-
} else if (cleanRelationData.connect && hasOrderColumn(attribute)) {
|
|
657
|
-
// use position attributes to calculate order
|
|
658
|
-
const orderMap = relationsOrderer(
|
|
659
|
-
[],
|
|
660
|
-
inverseJoinColumn.name,
|
|
661
|
-
joinTable.orderColumnName,
|
|
662
|
-
true // Always make an strict connect when inserting
|
|
663
|
-
)
|
|
664
|
-
.connect(relsToAdd)
|
|
665
|
-
.get()
|
|
666
|
-
// set the order based on the order of the ids
|
|
667
|
-
.reduce((acc, rel, idx) => ({ ...acc, [rel.id]: idx }), {});
|
|
668
|
-
|
|
669
|
-
insert.forEach((row) => {
|
|
670
|
-
row[orderColumnName] = orderMap[row[inverseJoinColumn.name]];
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
// add inv_order value
|
|
675
|
-
if (hasInverseOrderColumn(attribute)) {
|
|
676
|
-
const maxResults = await db
|
|
677
|
-
.getConnection()
|
|
678
|
-
.select(inverseJoinColumn.name)
|
|
679
|
-
.max(inverseOrderColumnName, { as: 'max' })
|
|
680
|
-
.whereIn(inverseJoinColumn.name, relIdsToadd)
|
|
681
|
-
.where(joinTable.on || {})
|
|
682
|
-
.groupBy(inverseJoinColumn.name)
|
|
683
|
-
.from(joinTable.name)
|
|
684
|
-
.transacting(trx);
|
|
685
|
-
|
|
686
|
-
const maxMap = maxResults.reduce(
|
|
687
|
-
(acc, res) => Object.assign(acc, { [res[inverseJoinColumn.name]]: res.max }),
|
|
688
|
-
{}
|
|
689
|
-
);
|
|
690
|
-
|
|
691
|
-
insert.forEach((rel) => {
|
|
692
|
-
rel[inverseOrderColumnName] = (maxMap[rel[inverseJoinColumn.name]] || 0) + 1;
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
if (insert.length === 0) {
|
|
697
|
-
continue;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// insert new relations
|
|
701
|
-
await this.createQueryBuilder(joinTable.name).insert(insert).transacting(trx).execute();
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
},
|
|
705
|
-
|
|
706
|
-
/**
|
|
707
|
-
* Updates relations of an existing entity
|
|
708
|
-
*
|
|
709
|
-
* @param {EntityManager} em - entity manager instance
|
|
710
|
-
* @param {Metadata} metadata - model metadta
|
|
711
|
-
* @param {ID} id - entity ID
|
|
712
|
-
* @param {object} data - data received for creation
|
|
713
|
-
*/
|
|
714
|
-
// TODO: check relation exists (handled by FKs except for polymorphics)
|
|
715
|
-
async updateRelations(uid, id, data, { transaction: trx }) {
|
|
716
|
-
const { attributes } = db.metadata.get(uid);
|
|
717
|
-
|
|
718
|
-
for (const attributeName of Object.keys(attributes)) {
|
|
719
|
-
const attribute = attributes[attributeName];
|
|
720
|
-
|
|
721
|
-
if (attribute.type !== 'relation' || !has(attributeName, data)) {
|
|
722
|
-
continue;
|
|
723
|
-
}
|
|
724
|
-
const cleanRelationData = toAssocs(data[attributeName]);
|
|
725
|
-
|
|
726
|
-
if (attribute.relation === 'morphOne' || attribute.relation === 'morphMany') {
|
|
727
|
-
const { target, morphBy } = attribute;
|
|
728
|
-
|
|
729
|
-
const targetAttribute = db.metadata.get(target).attributes[morphBy];
|
|
730
|
-
|
|
731
|
-
if (targetAttribute.relation === 'morphToOne') {
|
|
732
|
-
// set columns
|
|
733
|
-
const { idColumn, typeColumn } = targetAttribute.morphColumn;
|
|
734
|
-
|
|
735
|
-
// update instead of deleting because the relation is directly on the entity table
|
|
736
|
-
// and not in a join table
|
|
737
|
-
await this.createQueryBuilder(target)
|
|
738
|
-
.update({ [idColumn.name]: null, [typeColumn.name]: null })
|
|
739
|
-
.where({ [idColumn.name]: id, [typeColumn.name]: uid })
|
|
740
|
-
.transacting(trx)
|
|
741
|
-
.execute();
|
|
742
|
-
|
|
743
|
-
if (!isNull(cleanRelationData.set)) {
|
|
744
|
-
const relId = toIds(cleanRelationData.set[0]);
|
|
745
|
-
await this.createQueryBuilder(target)
|
|
746
|
-
.update({ [idColumn.name]: id, [typeColumn.name]: uid })
|
|
747
|
-
.where({ id: relId })
|
|
748
|
-
.transacting(trx)
|
|
749
|
-
.execute();
|
|
750
|
-
}
|
|
751
|
-
} else if (targetAttribute.relation === 'morphToMany') {
|
|
752
|
-
const { joinTable } = targetAttribute;
|
|
753
|
-
const { joinColumn, morphColumn } = joinTable;
|
|
754
|
-
|
|
755
|
-
const { idColumn, typeColumn } = morphColumn;
|
|
756
|
-
|
|
757
|
-
await this.createQueryBuilder(joinTable.name)
|
|
758
|
-
.delete()
|
|
759
|
-
.where({
|
|
760
|
-
[idColumn.name]: id,
|
|
761
|
-
[typeColumn.name]: uid,
|
|
762
|
-
...(joinTable.on || {}),
|
|
763
|
-
field: attributeName,
|
|
764
|
-
})
|
|
765
|
-
.transacting(trx)
|
|
766
|
-
.execute();
|
|
767
|
-
|
|
768
|
-
if (isEmpty(cleanRelationData.set)) {
|
|
769
|
-
continue;
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
const rows = cleanRelationData.set.map((data, idx) => ({
|
|
773
|
-
[joinColumn.name]: data.id,
|
|
774
|
-
[idColumn.name]: id,
|
|
775
|
-
[typeColumn.name]: uid,
|
|
776
|
-
...(joinTable.on || {}),
|
|
777
|
-
...(data.__pivot || {}),
|
|
778
|
-
order: idx + 1,
|
|
779
|
-
field: attributeName,
|
|
780
|
-
}));
|
|
781
|
-
|
|
782
|
-
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
continue;
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
if (attribute.relation === 'morphToOne') {
|
|
789
|
-
// handled on the entry itself
|
|
790
|
-
continue;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
if (attribute.relation === 'morphToMany') {
|
|
794
|
-
const { joinTable } = attribute;
|
|
795
|
-
const { joinColumn, morphColumn } = joinTable;
|
|
796
|
-
|
|
797
|
-
const { idColumn, typeColumn, typeField = '__type' } = morphColumn;
|
|
798
|
-
|
|
799
|
-
await this.createQueryBuilder(joinTable.name)
|
|
800
|
-
.delete()
|
|
801
|
-
.where({
|
|
802
|
-
[joinColumn.name]: id,
|
|
803
|
-
...(joinTable.on || {}),
|
|
804
|
-
})
|
|
805
|
-
.transacting(trx)
|
|
806
|
-
.execute();
|
|
807
|
-
|
|
808
|
-
if (isEmpty(cleanRelationData.set)) {
|
|
809
|
-
continue;
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
const rows = cleanRelationData.set.map((data, idx) => ({
|
|
813
|
-
[joinColumn.name]: id,
|
|
814
|
-
[idColumn.name]: data.id,
|
|
815
|
-
[typeColumn.name]: data[typeField],
|
|
816
|
-
...(joinTable.on || {}),
|
|
817
|
-
...(data.__pivot || {}),
|
|
818
|
-
order: idx + 1,
|
|
819
|
-
}));
|
|
820
|
-
|
|
821
|
-
// delete previous relations
|
|
822
|
-
await deleteRelatedMorphOneRelationsAfterMorphToManyUpdate(rows, {
|
|
823
|
-
uid,
|
|
824
|
-
attributeName,
|
|
825
|
-
joinTable,
|
|
826
|
-
db,
|
|
827
|
-
transaction: trx,
|
|
828
|
-
});
|
|
829
|
-
|
|
830
|
-
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
831
|
-
|
|
832
|
-
continue;
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
if (attribute.joinColumn && attribute.owner) {
|
|
836
|
-
// handled in the row itself
|
|
837
|
-
continue;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
// oneToOne oneToMany on the non owning side.
|
|
841
|
-
// Since it is a join column no need to remove previous relations
|
|
842
|
-
if (attribute.joinColumn && !attribute.owner) {
|
|
843
|
-
// need to set the column on the target
|
|
844
|
-
const { target } = attribute;
|
|
845
|
-
|
|
846
|
-
await this.createQueryBuilder(target)
|
|
847
|
-
.where({ [attribute.joinColumn.referencedColumn]: id })
|
|
848
|
-
.update({ [attribute.joinColumn.referencedColumn]: null })
|
|
849
|
-
.transacting(trx)
|
|
850
|
-
.execute();
|
|
851
|
-
|
|
852
|
-
if (!isNull(cleanRelationData.set)) {
|
|
853
|
-
const relIdsToAdd = toIds(cleanRelationData.set);
|
|
854
|
-
await this.createQueryBuilder(target)
|
|
855
|
-
.where({ id: relIdsToAdd })
|
|
856
|
-
.update({ [attribute.joinColumn.referencedColumn]: id })
|
|
857
|
-
.transacting(trx)
|
|
858
|
-
.execute();
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
if (attribute.joinTable) {
|
|
863
|
-
const { joinTable } = attribute;
|
|
864
|
-
const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } =
|
|
865
|
-
joinTable;
|
|
866
|
-
const select = [joinColumn.name, inverseJoinColumn.name];
|
|
867
|
-
if (hasOrderColumn(attribute)) {
|
|
868
|
-
select.push(orderColumnName);
|
|
869
|
-
}
|
|
870
|
-
if (hasInverseOrderColumn(attribute)) {
|
|
871
|
-
select.push(inverseOrderColumnName);
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
// only delete relations
|
|
875
|
-
if (isNull(cleanRelationData.set)) {
|
|
876
|
-
await deleteRelations({ id, attribute, db, relIdsToDelete: 'all', transaction: trx });
|
|
877
|
-
} else {
|
|
878
|
-
const isPartialUpdate = !has('set', cleanRelationData);
|
|
879
|
-
let relIdsToaddOrMove;
|
|
880
|
-
|
|
881
|
-
if (isPartialUpdate) {
|
|
882
|
-
if (isAnyToOne(attribute)) {
|
|
883
|
-
cleanRelationData.connect = cleanRelationData.connect.slice(-1);
|
|
884
|
-
}
|
|
885
|
-
relIdsToaddOrMove = toIds(cleanRelationData.connect);
|
|
886
|
-
const relIdsToDelete = toIds(
|
|
887
|
-
differenceWith(isEqual, cleanRelationData.disconnect, cleanRelationData.connect)
|
|
888
|
-
);
|
|
889
|
-
|
|
890
|
-
if (!isEmpty(relIdsToDelete)) {
|
|
891
|
-
await deleteRelations({ id, attribute, db, relIdsToDelete, transaction: trx });
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
if (isEmpty(cleanRelationData.connect)) {
|
|
895
|
-
continue;
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// Fetch current relations to handle ordering
|
|
899
|
-
let currentMovingRels;
|
|
900
|
-
if (hasOrderColumn(attribute) || hasInverseOrderColumn(attribute)) {
|
|
901
|
-
currentMovingRels = await this.createQueryBuilder(joinTable.name)
|
|
902
|
-
.select(select)
|
|
903
|
-
.where({
|
|
904
|
-
[joinColumn.name]: id,
|
|
905
|
-
[inverseJoinColumn.name]: { $in: relIdsToaddOrMove },
|
|
906
|
-
})
|
|
907
|
-
.where(joinTable.on || {})
|
|
908
|
-
.transacting(trx)
|
|
909
|
-
.execute();
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
// prepare relations to insert
|
|
913
|
-
const insert = uniqBy('id', cleanRelationData.connect).map((relToAdd) => ({
|
|
914
|
-
[joinColumn.name]: id,
|
|
915
|
-
[inverseJoinColumn.name]: relToAdd.id,
|
|
916
|
-
...(joinTable.on || {}),
|
|
917
|
-
...(relToAdd.__pivot || {}),
|
|
918
|
-
}));
|
|
919
|
-
|
|
920
|
-
if (hasOrderColumn(attribute)) {
|
|
921
|
-
// Get all adjacent relations and the one with the highest order
|
|
922
|
-
const adjacentRelations = await this.createQueryBuilder(joinTable.name)
|
|
923
|
-
.where({
|
|
924
|
-
$or: [
|
|
925
|
-
{
|
|
926
|
-
[joinColumn.name]: id,
|
|
927
|
-
[inverseJoinColumn.name]: {
|
|
928
|
-
$in: compact(
|
|
929
|
-
cleanRelationData.connect.map(
|
|
930
|
-
(r) => r.position?.after || r.position?.before
|
|
931
|
-
)
|
|
932
|
-
),
|
|
933
|
-
},
|
|
934
|
-
},
|
|
935
|
-
{
|
|
936
|
-
[joinColumn.name]: id,
|
|
937
|
-
[orderColumnName]: this.createQueryBuilder(joinTable.name)
|
|
938
|
-
.max(orderColumnName)
|
|
939
|
-
.where({ [joinColumn.name]: id })
|
|
940
|
-
.where(joinTable.on || {})
|
|
941
|
-
.transacting(trx)
|
|
942
|
-
.getKnexQuery(),
|
|
943
|
-
},
|
|
944
|
-
],
|
|
945
|
-
})
|
|
946
|
-
.where(joinTable.on || {})
|
|
947
|
-
.transacting(trx)
|
|
948
|
-
.execute();
|
|
949
|
-
|
|
950
|
-
const orderMap = relationsOrderer(
|
|
951
|
-
adjacentRelations,
|
|
952
|
-
inverseJoinColumn.name,
|
|
953
|
-
joinTable.orderColumnName,
|
|
954
|
-
cleanRelationData.options.strict
|
|
955
|
-
)
|
|
956
|
-
.connect(cleanRelationData.connect)
|
|
957
|
-
.getOrderMap();
|
|
958
|
-
|
|
959
|
-
insert.forEach((row) => {
|
|
960
|
-
row[orderColumnName] = orderMap[row[inverseJoinColumn.name]];
|
|
961
|
-
});
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
// add inv order value
|
|
965
|
-
if (hasInverseOrderColumn(attribute)) {
|
|
966
|
-
const nonExistingRelsIds = difference(
|
|
967
|
-
relIdsToaddOrMove,
|
|
968
|
-
map(inverseJoinColumn.name, currentMovingRels)
|
|
969
|
-
);
|
|
970
|
-
|
|
971
|
-
const maxResults = await db
|
|
972
|
-
.getConnection()
|
|
973
|
-
.select(inverseJoinColumn.name)
|
|
974
|
-
.max(inverseOrderColumnName, { as: 'max' })
|
|
975
|
-
.whereIn(inverseJoinColumn.name, nonExistingRelsIds)
|
|
976
|
-
.where(joinTable.on || {})
|
|
977
|
-
.groupBy(inverseJoinColumn.name)
|
|
978
|
-
.from(joinTable.name)
|
|
979
|
-
.transacting(trx);
|
|
980
|
-
|
|
981
|
-
const maxMap = maxResults.reduce(
|
|
982
|
-
(acc, res) => Object.assign(acc, { [res[inverseJoinColumn.name]]: res.max }),
|
|
983
|
-
{}
|
|
984
|
-
);
|
|
985
|
-
|
|
986
|
-
insert.forEach((row) => {
|
|
987
|
-
row[inverseOrderColumnName] = (maxMap[row[inverseJoinColumn.name]] || 0) + 1;
|
|
988
|
-
});
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
// insert rows
|
|
992
|
-
const query = this.createQueryBuilder(joinTable.name)
|
|
993
|
-
.insert(insert)
|
|
994
|
-
.onConflict(joinTable.pivotColumns)
|
|
995
|
-
.transacting(trx);
|
|
996
|
-
|
|
997
|
-
if (hasOrderColumn(attribute)) {
|
|
998
|
-
query.merge([orderColumnName]);
|
|
999
|
-
} else {
|
|
1000
|
-
query.ignore();
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
await query.execute();
|
|
1004
|
-
|
|
1005
|
-
// remove gap between orders
|
|
1006
|
-
await cleanOrderColumns({ attribute, db, id, transaction: trx });
|
|
1007
|
-
} else {
|
|
1008
|
-
if (isAnyToOne(attribute)) {
|
|
1009
|
-
cleanRelationData.set = cleanRelationData.set.slice(-1);
|
|
1010
|
-
}
|
|
1011
|
-
// overwrite all relations
|
|
1012
|
-
relIdsToaddOrMove = toIds(cleanRelationData.set);
|
|
1013
|
-
await deleteRelations({
|
|
1014
|
-
id,
|
|
1015
|
-
attribute,
|
|
1016
|
-
db,
|
|
1017
|
-
relIdsToDelete: 'all',
|
|
1018
|
-
relIdsToNotDelete: relIdsToaddOrMove,
|
|
1019
|
-
transaction: trx,
|
|
1020
|
-
});
|
|
1021
|
-
|
|
1022
|
-
if (isEmpty(cleanRelationData.set)) {
|
|
1023
|
-
continue;
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
const insert = uniqBy('id', cleanRelationData.set).map((relToAdd) => ({
|
|
1027
|
-
[joinColumn.name]: id,
|
|
1028
|
-
[inverseJoinColumn.name]: relToAdd.id,
|
|
1029
|
-
...(joinTable.on || {}),
|
|
1030
|
-
...(relToAdd.__pivot || {}),
|
|
1031
|
-
}));
|
|
1032
|
-
|
|
1033
|
-
// add order value
|
|
1034
|
-
if (hasOrderColumn(attribute)) {
|
|
1035
|
-
insert.forEach((row, idx) => {
|
|
1036
|
-
row[orderColumnName] = idx + 1;
|
|
1037
|
-
});
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
// add inv order value
|
|
1041
|
-
if (hasInverseOrderColumn(attribute)) {
|
|
1042
|
-
const existingRels = await this.createQueryBuilder(joinTable.name)
|
|
1043
|
-
.select(inverseJoinColumn.name)
|
|
1044
|
-
.where({
|
|
1045
|
-
[joinColumn.name]: id,
|
|
1046
|
-
[inverseJoinColumn.name]: { $in: relIdsToaddOrMove },
|
|
1047
|
-
})
|
|
1048
|
-
.where(joinTable.on || {})
|
|
1049
|
-
.transacting(trx)
|
|
1050
|
-
.execute();
|
|
1051
|
-
|
|
1052
|
-
const nonExistingRelsIds = difference(
|
|
1053
|
-
relIdsToaddOrMove,
|
|
1054
|
-
map(inverseJoinColumn.name, existingRels)
|
|
1055
|
-
);
|
|
1056
|
-
|
|
1057
|
-
const maxResults = await db
|
|
1058
|
-
.getConnection()
|
|
1059
|
-
.select(inverseJoinColumn.name)
|
|
1060
|
-
.max(inverseOrderColumnName, { as: 'max' })
|
|
1061
|
-
.whereIn(inverseJoinColumn.name, nonExistingRelsIds)
|
|
1062
|
-
.where(joinTable.on || {})
|
|
1063
|
-
.groupBy(inverseJoinColumn.name)
|
|
1064
|
-
.from(joinTable.name)
|
|
1065
|
-
.transacting(trx);
|
|
1066
|
-
|
|
1067
|
-
const maxMap = maxResults.reduce(
|
|
1068
|
-
(acc, res) => Object.assign(acc, { [res[inverseJoinColumn.name]]: res.max }),
|
|
1069
|
-
{}
|
|
1070
|
-
);
|
|
1071
|
-
|
|
1072
|
-
insert.forEach((row) => {
|
|
1073
|
-
row[inverseOrderColumnName] = (maxMap[row[inverseJoinColumn.name]] || 0) + 1;
|
|
1074
|
-
});
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
// insert rows
|
|
1078
|
-
const query = this.createQueryBuilder(joinTable.name)
|
|
1079
|
-
.insert(insert)
|
|
1080
|
-
.onConflict(joinTable.pivotColumns)
|
|
1081
|
-
.transacting(trx);
|
|
1082
|
-
|
|
1083
|
-
if (hasOrderColumn(attribute)) {
|
|
1084
|
-
query.merge([orderColumnName]);
|
|
1085
|
-
} else {
|
|
1086
|
-
query.ignore();
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
await query.execute();
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
// Delete the previous relations for oneToAny relations
|
|
1093
|
-
if (isBidirectional(attribute) && isOneToAny(attribute)) {
|
|
1094
|
-
await deletePreviousOneToAnyRelations({
|
|
1095
|
-
id,
|
|
1096
|
-
attribute,
|
|
1097
|
-
relIdsToadd: relIdsToaddOrMove,
|
|
1098
|
-
db,
|
|
1099
|
-
transaction: trx,
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
// Delete the previous relations for anyToOne relations
|
|
1104
|
-
if (isAnyToOne(attribute)) {
|
|
1105
|
-
await deletePreviousAnyToOneRelations({
|
|
1106
|
-
id,
|
|
1107
|
-
attribute,
|
|
1108
|
-
relIdToadd: relIdsToaddOrMove[0],
|
|
1109
|
-
db,
|
|
1110
|
-
transaction: trx,
|
|
1111
|
-
});
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
},
|
|
1117
|
-
|
|
1118
|
-
/**
|
|
1119
|
-
* Delete relational associations of an existing entity
|
|
1120
|
-
* This removes associations but doesn't do cascade deletions for components for example. This will be handled on the entity service layer instead
|
|
1121
|
-
* NOTE: Most of the deletion should be handled by ON DELETE CASCADE for dialects that have FKs
|
|
1122
|
-
*
|
|
1123
|
-
* @param {EntityManager} em - entity manager instance
|
|
1124
|
-
* @param {Metadata} metadata - model metadta
|
|
1125
|
-
* @param {ID} id - entity ID
|
|
1126
|
-
*/
|
|
1127
|
-
async deleteRelations(uid, id, { transaction: trx }) {
|
|
1128
|
-
const { attributes } = db.metadata.get(uid);
|
|
1129
|
-
|
|
1130
|
-
for (const attributeName of Object.keys(attributes)) {
|
|
1131
|
-
const attribute = attributes[attributeName];
|
|
1132
|
-
|
|
1133
|
-
if (attribute.type !== 'relation') {
|
|
1134
|
-
continue;
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
/*
|
|
1138
|
-
if morphOne | morphMany
|
|
1139
|
-
if morphBy is morphToOne
|
|
1140
|
-
set null
|
|
1141
|
-
if morphBy is morphToOne
|
|
1142
|
-
delete links
|
|
1143
|
-
*/
|
|
1144
|
-
if (attribute.relation === 'morphOne' || attribute.relation === 'morphMany') {
|
|
1145
|
-
const { target, morphBy } = attribute;
|
|
1146
|
-
|
|
1147
|
-
const targetAttribute = db.metadata.get(target).attributes[morphBy];
|
|
1148
|
-
|
|
1149
|
-
if (targetAttribute.relation === 'morphToOne') {
|
|
1150
|
-
// set columns
|
|
1151
|
-
const { idColumn, typeColumn } = targetAttribute.morphColumn;
|
|
1152
|
-
|
|
1153
|
-
await this.createQueryBuilder(target)
|
|
1154
|
-
.update({ [idColumn.name]: null, [typeColumn.name]: null })
|
|
1155
|
-
.where({ [idColumn.name]: id, [typeColumn.name]: uid })
|
|
1156
|
-
.transacting(trx)
|
|
1157
|
-
.execute();
|
|
1158
|
-
} else if (targetAttribute.relation === 'morphToMany') {
|
|
1159
|
-
const { joinTable } = targetAttribute;
|
|
1160
|
-
const { morphColumn } = joinTable;
|
|
1161
|
-
|
|
1162
|
-
const { idColumn, typeColumn } = morphColumn;
|
|
1163
|
-
|
|
1164
|
-
await this.createQueryBuilder(joinTable.name)
|
|
1165
|
-
.delete()
|
|
1166
|
-
.where({
|
|
1167
|
-
[idColumn.name]: id,
|
|
1168
|
-
[typeColumn.name]: uid,
|
|
1169
|
-
...(joinTable.on || {}),
|
|
1170
|
-
field: attributeName,
|
|
1171
|
-
})
|
|
1172
|
-
.transacting(trx)
|
|
1173
|
-
.execute();
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
continue;
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
/*
|
|
1180
|
-
if morphToOne
|
|
1181
|
-
nothing to do
|
|
1182
|
-
*/
|
|
1183
|
-
if (attribute.relation === 'morphToOne') {
|
|
1184
|
-
// do nothing
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1187
|
-
/*
|
|
1188
|
-
if morphToMany
|
|
1189
|
-
delete links
|
|
1190
|
-
*/
|
|
1191
|
-
if (attribute.relation === 'morphToMany') {
|
|
1192
|
-
const { joinTable } = attribute;
|
|
1193
|
-
const { joinColumn } = joinTable;
|
|
1194
|
-
|
|
1195
|
-
await this.createQueryBuilder(joinTable.name)
|
|
1196
|
-
.delete()
|
|
1197
|
-
.where({
|
|
1198
|
-
[joinColumn.name]: id,
|
|
1199
|
-
...(joinTable.on || {}),
|
|
1200
|
-
})
|
|
1201
|
-
.transacting(trx)
|
|
1202
|
-
.execute();
|
|
1203
|
-
|
|
1204
|
-
continue;
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
// do not need to delete links when using foreign keys
|
|
1208
|
-
if (db.dialect.usesForeignKeys()) {
|
|
1209
|
-
return;
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
// NOTE: we do not remove existing associations with the target as it should handled by unique FKs instead
|
|
1213
|
-
if (attribute.joinColumn && attribute.owner) {
|
|
1214
|
-
// nothing to do => relation already added on the table
|
|
1215
|
-
continue;
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
// oneToOne oneToMany on the non owning side.
|
|
1219
|
-
if (attribute.joinColumn && !attribute.owner) {
|
|
1220
|
-
// need to set the column on the target
|
|
1221
|
-
const { target } = attribute;
|
|
1222
|
-
|
|
1223
|
-
await this.createQueryBuilder(target)
|
|
1224
|
-
.where({ [attribute.joinColumn.referencedColumn]: id })
|
|
1225
|
-
.update({ [attribute.joinColumn.referencedColumn]: null })
|
|
1226
|
-
.transacting(trx)
|
|
1227
|
-
.execute();
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
if (attribute.joinTable) {
|
|
1231
|
-
await deleteRelations({ id, attribute, db, relIdsToDelete: 'all', transaction: trx });
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
},
|
|
1235
|
-
|
|
1236
|
-
// TODO: Clone polymorphic relations
|
|
1237
|
-
/**
|
|
1238
|
-
*
|
|
1239
|
-
* @param {string} uid - uid of the entity to clone
|
|
1240
|
-
* @param {number} targetId - id of the entity to clone into
|
|
1241
|
-
* @param {number} sourceId - id of the entity to clone from
|
|
1242
|
-
* @param {object} opt
|
|
1243
|
-
* @param {object} opt.cloneAttrs - key value pair of attributes to clone
|
|
1244
|
-
* @param {object} opt.transaction - transaction to use
|
|
1245
|
-
* @example cloneRelations('user', 3, 1, { cloneAttrs: ["comments"]})
|
|
1246
|
-
* @example cloneRelations('post', 5, 2, { cloneAttrs: ["comments", "likes"] })
|
|
1247
|
-
*/
|
|
1248
|
-
async cloneRelations(uid, targetId, sourceId, data, { cloneAttrs = [], transaction }) {
|
|
1249
|
-
const { attributes } = db.metadata.get(uid);
|
|
1250
|
-
|
|
1251
|
-
if (!attributes) {
|
|
1252
|
-
return;
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
await mapAsync(cloneAttrs, async (attrName) => {
|
|
1256
|
-
const attribute = attributes[attrName];
|
|
1257
|
-
|
|
1258
|
-
if (attribute.type !== 'relation') {
|
|
1259
|
-
throw new DatabaseError(
|
|
1260
|
-
`Attribute ${attrName} is not a relation attribute. Cloning relations is only supported for relation attributes.`
|
|
1261
|
-
);
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
if (isPolymorphic(attribute)) {
|
|
1265
|
-
// TODO: add support for cloning polymorphic relations
|
|
1266
|
-
return;
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
if (attribute.joinColumn) {
|
|
1270
|
-
// TODO: add support for cloning oneToMany relations on the owning side
|
|
1271
|
-
return;
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
if (!attribute.joinTable) {
|
|
1275
|
-
return;
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
let omitIds = [];
|
|
1279
|
-
if (has(attrName, data)) {
|
|
1280
|
-
const cleanRelationData = toAssocs(data[attrName]);
|
|
1281
|
-
|
|
1282
|
-
// Don't clone if the relation attr is being set
|
|
1283
|
-
if (cleanRelationData.set) {
|
|
1284
|
-
return;
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
// Disconnected relations don't need to be cloned
|
|
1288
|
-
if (cleanRelationData.disconnect) {
|
|
1289
|
-
omitIds = toIds(cleanRelationData.disconnect);
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
if (isOneToAny(attribute) && isBidirectional(attribute)) {
|
|
1294
|
-
await replaceRegularRelations({ targetId, sourceId, attribute, omitIds, transaction });
|
|
1295
|
-
} else {
|
|
1296
|
-
await cloneRegularRelations({ targetId, sourceId, attribute, transaction });
|
|
1297
|
-
}
|
|
1298
|
-
});
|
|
1299
|
-
|
|
1300
|
-
await this.updateRelations(uid, targetId, data, { transaction });
|
|
1301
|
-
},
|
|
1302
|
-
|
|
1303
|
-
// TODO: add lifecycle events
|
|
1304
|
-
async populate(uid, entity, populate) {
|
|
1305
|
-
const entry = await this.findOne(uid, {
|
|
1306
|
-
select: ['id'],
|
|
1307
|
-
where: { id: entity.id },
|
|
1308
|
-
populate,
|
|
1309
|
-
});
|
|
1310
|
-
|
|
1311
|
-
return { ...entity, ...entry };
|
|
1312
|
-
},
|
|
1313
|
-
|
|
1314
|
-
// TODO: add lifecycle events
|
|
1315
|
-
async load(uid, entity, fields, params) {
|
|
1316
|
-
const { attributes } = db.metadata.get(uid);
|
|
1317
|
-
|
|
1318
|
-
const fieldsArr = castArray(fields);
|
|
1319
|
-
fieldsArr.forEach((field) => {
|
|
1320
|
-
const attribute = attributes[field];
|
|
1321
|
-
|
|
1322
|
-
if (!attribute || attribute.type !== 'relation') {
|
|
1323
|
-
throw new Error(`Invalid load. Expected ${field} to be a relational attribute`);
|
|
1324
|
-
}
|
|
1325
|
-
});
|
|
1326
|
-
|
|
1327
|
-
const entry = await this.findOne(uid, {
|
|
1328
|
-
select: ['id'],
|
|
1329
|
-
where: { id: entity.id },
|
|
1330
|
-
populate: fieldsArr.reduce((acc, field) => {
|
|
1331
|
-
acc[field] = params || true;
|
|
1332
|
-
return acc;
|
|
1333
|
-
}, {}),
|
|
1334
|
-
});
|
|
1335
|
-
|
|
1336
|
-
if (!entry) {
|
|
1337
|
-
return null;
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
if (Array.isArray(fields)) {
|
|
1341
|
-
return pick(fields, entry);
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
return entry[fields];
|
|
1345
|
-
},
|
|
1346
|
-
|
|
1347
|
-
// cascading
|
|
1348
|
-
// aggregations
|
|
1349
|
-
// -> avg
|
|
1350
|
-
// -> min
|
|
1351
|
-
// -> max
|
|
1352
|
-
// -> grouping
|
|
1353
|
-
|
|
1354
|
-
// formulas
|
|
1355
|
-
// custom queries
|
|
1356
|
-
|
|
1357
|
-
// utilities
|
|
1358
|
-
// -> map result
|
|
1359
|
-
// -> map input
|
|
1360
|
-
|
|
1361
|
-
// extra features
|
|
1362
|
-
// -> virtuals
|
|
1363
|
-
// -> private
|
|
1364
|
-
|
|
1365
|
-
createQueryBuilder(uid) {
|
|
1366
|
-
return createQueryBuilder(uid, db);
|
|
1367
|
-
},
|
|
1368
|
-
|
|
1369
|
-
getRepository(uid) {
|
|
1370
|
-
if (!repoMap[uid]) {
|
|
1371
|
-
repoMap[uid] = createRepository(uid, db);
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
return repoMap[uid];
|
|
1375
|
-
},
|
|
1376
|
-
|
|
1377
|
-
clearRepositories() {
|
|
1378
|
-
repoMap.clear();
|
|
1379
|
-
},
|
|
1380
|
-
};
|
|
1381
|
-
};
|
|
1382
|
-
|
|
1383
|
-
module.exports = {
|
|
1384
|
-
createEntityManager,
|
|
1385
|
-
};
|