@strapi/database 5.12.1 → 5.12.2

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