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