@reldens/storage 0.87.0 → 0.89.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/.claude/driver-methods-complete-analysis.md +1978 -0
  2. package/.claude/test-architecture.md +973 -0
  3. package/CLAUDE.md +141 -7
  4. package/README.md +14 -2
  5. package/bin/reldens-storage.js +1 -1
  6. package/lib/base-driver.js +8 -0
  7. package/lib/entities-generator.js +1 -3
  8. package/lib/entity-templates/mikro-orm-model.template +2 -1
  9. package/lib/generators/models-generation.js +126 -5
  10. package/lib/mikro-orm/mikro-orm-data-server.js +24 -10
  11. package/lib/mikro-orm/mikro-orm-driver.js +184 -116
  12. package/lib/objection-js/objection-js-driver.js +111 -19
  13. package/lib/prisma/prisma-client-loader.js +4 -0
  14. package/lib/prisma/prisma-data-server.js +3 -2
  15. package/lib/prisma/prisma-driver.js +12 -10
  16. package/lib/prisma/prisma-filter-processor.js +193 -146
  17. package/lib/prisma/prisma-metadata-loader.js +0 -4
  18. package/lib/prisma/prisma-relation-resolver.js +292 -267
  19. package/lib/prisma/prisma-type-caster.js +280 -260
  20. package/package.json +15 -6
  21. package/tests/.env.test.example +7 -0
  22. package/tests/fixtures/categories-fixtures.js +164 -0
  23. package/tests/fixtures/expected-entities-mikro-orm/entities/test-categories-entity.js +70 -0
  24. package/tests/fixtures/expected-entities-mikro-orm/entities/test-products-entity.js +95 -0
  25. package/tests/fixtures/expected-entities-mikro-orm/entities/test-reviews-entity.js +84 -0
  26. package/tests/fixtures/expected-entities-mikro-orm/entities-config.js +17 -0
  27. package/tests/fixtures/expected-entities-mikro-orm/entities-translations.js +13 -0
  28. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/registered-models-mikro-orm.js +23 -0
  29. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-categories-model.js +57 -0
  30. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-products-model.js +79 -0
  31. package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-reviews-model.js +70 -0
  32. package/tests/fixtures/expected-entities-objection-js/entities/test-categories-entity.js +70 -0
  33. package/tests/fixtures/expected-entities-objection-js/entities/test-products-entity.js +95 -0
  34. package/tests/fixtures/expected-entities-objection-js/entities/test-reviews-entity.js +84 -0
  35. package/tests/fixtures/expected-entities-objection-js/entities-config.js +17 -0
  36. package/tests/fixtures/expected-entities-objection-js/entities-translations.js +13 -0
  37. package/tests/fixtures/expected-entities-objection-js/models/objection-js/registered-models-objection-js.js +23 -0
  38. package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-categories-model.js +33 -0
  39. package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-products-model.js +42 -0
  40. package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-reviews-model.js +33 -0
  41. package/tests/fixtures/expected-entities-prisma/entities/test-categories-entity.js +70 -0
  42. package/tests/fixtures/expected-entities-prisma/entities/test-products-entity.js +95 -0
  43. package/tests/fixtures/expected-entities-prisma/entities/test-reviews-entity.js +84 -0
  44. package/tests/fixtures/expected-entities-prisma/entities-config.js +17 -0
  45. package/tests/fixtures/expected-entities-prisma/entities-translations.js +13 -0
  46. package/tests/fixtures/expected-entities-prisma/models/prisma/registered-models-prisma.js +23 -0
  47. package/tests/fixtures/expected-entities-prisma/models/prisma/test-categories-model.js +43 -0
  48. package/tests/fixtures/expected-entities-prisma/models/prisma/test-products-model.js +50 -0
  49. package/tests/fixtures/expected-entities-prisma/models/prisma/test-reviews-model.js +46 -0
  50. package/tests/fixtures/products-fixtures.js +119 -0
  51. package/tests/fixtures/reviews-fixtures.js +42 -0
  52. package/tests/fixtures/sql/test-schema.sql +59 -0
  53. package/tests/integration/test-drivers.js +257 -0
  54. package/tests/integration/test-nested-filters.js +408 -0
  55. package/tests/integration/test-relations.js +349 -0
  56. package/tests/run-tests.js +213 -0
  57. package/tests/unit/test-drivers.js +89 -0
  58. package/tests/unit/test-entities-generator.js +533 -0
  59. package/tests/unit/test-entity-manager.js +128 -0
  60. package/tests/unit/test-type-mapper.js +232 -0
  61. package/tests/utils/custom-reporter.js +73 -0
  62. package/tests/utils/driver-registry.js +144 -0
  63. package/tests/utils/prisma-subprocess-worker.js +150 -0
  64. package/tests/utils/test-helpers.js +726 -0
  65. package/tests/utils/test-runner.js +63 -0
@@ -1,267 +1,292 @@
1
- /**
2
- *
3
- * Reldens - PrismaRelationResolver
4
- *
5
- */
6
-
7
- const { sc } = require('@reldens/utils');
8
-
9
- class PrismaRelationResolver
10
- {
11
-
12
- constructor(props)
13
- {
14
- this.relationMetadata = sc.get(props, 'relationMetadata', {});
15
- this.metadataLoader = sc.get(props, 'metadataLoader', null);
16
- this.allRelationMappings = sc.get(props, 'allRelationMappings', {});
17
- this.foreignKeyMappings = sc.get(props, 'foreignKeyMappings', {});
18
- this.foreignKeyTargetFields = sc.get(props, 'foreignKeyTargetFields', {});
19
- this.typeCaster = sc.get(props, 'typeCaster', null);
20
- this.currentModelMappings = {};
21
- this.reverseModelMappings = {};
22
- }
23
-
24
- updateMetadata(metadata)
25
- {
26
- this.relationMetadata = sc.get(metadata, 'relationMetadata', this.relationMetadata);
27
- this.foreignKeyMappings = sc.get(metadata, 'foreignKeyMappings', this.foreignKeyMappings);
28
- this.foreignKeyTargetFields = sc.get(metadata, 'foreignKeyTargetFields', this.foreignKeyTargetFields);
29
- }
30
-
31
- setCurrentModelMappings(mappings)
32
- {
33
- this.currentModelMappings = mappings || {};
34
- this.reverseModelMappings = this.buildReverseMappings(this.currentModelMappings);
35
- }
36
-
37
- buildReverseMappings(mappings)
38
- {
39
- let reverse = {};
40
- for(let reldensName of Object.keys(mappings)){
41
- let prismaName = mappings[reldensName];
42
- reverse[prismaName] = reldensName;
43
- }
44
- return reverse;
45
- }
46
-
47
- getAllRelations()
48
- {
49
- return Object.keys(this.relationMetadata || {});
50
- }
51
-
52
- mapToPrismaName(reldensName, modelName)
53
- {
54
- if(!modelName){
55
- if(sc.hasOwn(this.relationMetadata, reldensName)){
56
- return reldensName;
57
- }
58
- if(sc.hasOwn(this.currentModelMappings, reldensName)){
59
- return this.currentModelMappings[reldensName];
60
- }
61
- return null;
62
- }
63
- let modelMappings = sc.get(this.allRelationMappings, modelName.toLowerCase(), {});
64
- if(sc.hasOwn(modelMappings, reldensName)){
65
- return modelMappings[reldensName];
66
- }
67
- return null;
68
- }
69
-
70
- mapToReldensName(prismaName)
71
- {
72
- if(sc.hasOwn(this.reverseModelMappings, prismaName)){
73
- return this.reverseModelMappings[prismaName];
74
- }
75
- return prismaName;
76
- }
77
-
78
- getRelatedModelName(prismaName, fromModelName)
79
- {
80
- if(!fromModelName){
81
- let meta = sc.get(this.relationMetadata, prismaName, null);
82
- if(meta){
83
- return meta.model;
84
- }
85
- return null;
86
- }
87
- if(!this.metadataLoader){
88
- return null;
89
- }
90
- let dmmf = this.metadataLoader.getDmmf();
91
- if(!dmmf){
92
- return null;
93
- }
94
- let modelInfo = this.findModelInDmmf(dmmf, fromModelName);
95
- if(!modelInfo){
96
- return null;
97
- }
98
- for(let field of modelInfo.fields){
99
- if(field.name === prismaName && 'object' === field.kind){
100
- return field.type;
101
- }
102
- }
103
- return null;
104
- }
105
-
106
- findModelInDmmf(dmmf, modelName)
107
- {
108
- let lowerName = modelName.toLowerCase();
109
- if(sc.hasOwn(dmmf.models, lowerName)){
110
- return dmmf.models[lowerName];
111
- }
112
- if(sc.isArray(dmmf.models)){
113
- for(let model of dmmf.models){
114
- if(model.name.toLowerCase() === lowerName){
115
- return model;
116
- }
117
- }
118
- }
119
- return null;
120
- }
121
-
122
- convertForeignKeysToRelations(params, isCreate = false)
123
- {
124
- let data = {};
125
- let relations = {};
126
- for(let key of Object.keys(params)){
127
- if(!sc.hasOwn(this.foreignKeyMappings, key)){
128
- data[key] = params[key];
129
- continue;
130
- }
131
- let relationName = this.foreignKeyMappings[key];
132
- let value = params[key];
133
- let targetField = sc.get(this.foreignKeyTargetFields, key, 'id');
134
- if(!this.typeCaster){
135
- relations[relationName] = {connect: {[targetField]: value}};
136
- continue;
137
- }
138
- if(this.typeCaster.isNullOrEmpty(value)){
139
- if(isCreate){
140
- continue;
141
- }
142
- relations[relationName] = {disconnect: true};
143
- continue;
144
- }
145
- let fieldType = sc.get(this.typeCaster.fieldTypes, key, null);
146
- let connectValue = fieldType
147
- ? this.typeCaster.castValue(value, fieldType, key)
148
- : this.typeCaster.castToIdType(value);
149
- relations[relationName] = {
150
- connect: {[targetField]: connectValue}
151
- };
152
- }
153
- return {...data, ...relations};
154
- }
155
-
156
- buildIncludeObjectWithMapping(relations)
157
- {
158
- let include = {};
159
- let mapping = {};
160
- for(let relation of relations){
161
- this.processRelationPath(relation, include, mapping);
162
- }
163
- return {include, mapping};
164
- }
165
-
166
- processRelationPath(relationPath, include, mapping)
167
- {
168
- let parts = relationPath.split('.');
169
- let currentInclude = include;
170
- let currentModelName = null;
171
- for(let i = 0; i < parts.length; i++){
172
- let reldensName = parts[i];
173
- let prismaName = this.mapToPrismaName(reldensName, currentModelName);
174
- if(!prismaName){
175
- return;
176
- }
177
- if(reldensName !== prismaName){
178
- mapping[prismaName] = reldensName;
179
- }
180
- let isLastPart = (i === parts.length - 1);
181
- if(isLastPart){
182
- currentInclude[prismaName] = true;
183
- return;
184
- }
185
- if(!sc.hasOwn(currentInclude, prismaName) || true === currentInclude[prismaName]){
186
- currentInclude[prismaName] = {include: {}};
187
- }
188
- currentModelName = this.getRelatedModelName(prismaName, currentModelName);
189
- if(!currentModelName){
190
- return;
191
- }
192
- currentInclude = currentInclude[prismaName].include;
193
- }
194
- }
195
-
196
- transformRelationResults(result, includeConfig, relationMapping)
197
- {
198
- if(!result || !includeConfig){
199
- return result;
200
- }
201
- if(sc.isArray(result)){
202
- return result.map(item => this.transformSingleResult(item, includeConfig, relationMapping));
203
- }
204
- return this.transformSingleResult(result, includeConfig, relationMapping);
205
- }
206
-
207
- transformSingleResult(item, includeConfig, relationMapping)
208
- {
209
- if(!item || !sc.isObject(item)){
210
- return item;
211
- }
212
- let transformed = {...item};
213
- for(let prismaName of Object.keys(includeConfig)){
214
- if(!sc.hasOwn(transformed, prismaName)){
215
- continue;
216
- }
217
- let relationValue = transformed[prismaName];
218
- let nestedConfig = includeConfig[prismaName];
219
- if(sc.isObject(nestedConfig) && sc.hasOwn(nestedConfig, 'include')){
220
- relationValue = this.transformRelationResults(relationValue, nestedConfig.include, relationMapping);
221
- }
222
- let reldensName = sc.get(relationMapping, prismaName, null);
223
- if(!reldensName){
224
- reldensName = this.mapToReldensName(prismaName);
225
- }
226
- transformed[reldensName] = relationValue;
227
- if(reldensName !== prismaName){
228
- delete transformed[prismaName];
229
- }
230
- }
231
- return transformed;
232
- }
233
-
234
- buildCreateDataWithRelations(params, relations)
235
- {
236
- let createData = {};
237
- for(let relation of relations){
238
- let prismaName = this.mapToPrismaName(relation, null);
239
- if(!prismaName){
240
- continue;
241
- }
242
- if(!sc.hasOwn(params, relation)){
243
- continue;
244
- }
245
- let relationData = params[relation];
246
- if(!this.typeCaster){
247
- createData[prismaName] = sc.isArray(relationData)
248
- ? {connect: relationData.map(item => ({id: item.id}))}
249
- : {connect: {id: relationData.id}};
250
- continue;
251
- }
252
- if(sc.isArray(relationData)){
253
- createData[prismaName] = {
254
- connect: relationData.map(item => ({id: this.typeCaster.castToIdType(item.id)}))
255
- };
256
- continue;
257
- }
258
- createData[prismaName] = {
259
- connect: {id: this.typeCaster.castToIdType(relationData.id)}
260
- };
261
- }
262
- return createData;
263
- }
264
-
265
- }
266
-
267
- module.exports.PrismaRelationResolver = PrismaRelationResolver;
1
+ /**
2
+ *
3
+ * Reldens - PrismaRelationResolver
4
+ *
5
+ */
6
+
7
+ const { sc } = require('@reldens/utils');
8
+
9
+ class PrismaRelationResolver
10
+ {
11
+
12
+ constructor(props)
13
+ {
14
+ this.relationMetadata = sc.get(props, 'relationMetadata', {});
15
+ this.metadataLoader = sc.get(props, 'metadataLoader', null);
16
+ this.allRelationMappings = sc.get(props, 'allRelationMappings', {});
17
+ this.foreignKeyMappings = sc.get(props, 'foreignKeyMappings', {});
18
+ this.foreignKeyTargetFields = sc.get(props, 'foreignKeyTargetFields', {});
19
+ this.typeCaster = sc.get(props, 'typeCaster', null);
20
+ this.currentModelMappings = {};
21
+ this.reverseModelMappings = {};
22
+ }
23
+
24
+ updateMetadata(metadata)
25
+ {
26
+ this.relationMetadata = sc.get(metadata, 'relationMetadata', this.relationMetadata);
27
+ this.foreignKeyMappings = sc.get(metadata, 'foreignKeyMappings', this.foreignKeyMappings);
28
+ this.foreignKeyTargetFields = sc.get(metadata, 'foreignKeyTargetFields', this.foreignKeyTargetFields);
29
+ }
30
+
31
+ setCurrentModelMappings(mappings)
32
+ {
33
+ this.currentModelMappings = mappings || {};
34
+ this.reverseModelMappings = this.buildReverseMappings(this.currentModelMappings);
35
+ }
36
+
37
+ buildReverseMappings(mappings)
38
+ {
39
+ let reverse = {};
40
+ for(let reldensName of Object.keys(mappings)){
41
+ let prismaName = mappings[reldensName];
42
+ reverse[prismaName] = reldensName;
43
+ }
44
+ return reverse;
45
+ }
46
+
47
+ getAllRelations()
48
+ {
49
+ return Object.keys(this.relationMetadata || {});
50
+ }
51
+
52
+ mapToPrismaName(reldensName, modelName)
53
+ {
54
+ if(!modelName){
55
+ if(sc.hasOwn(this.currentModelMappings, reldensName)){
56
+ return this.currentModelMappings[reldensName];
57
+ }
58
+ if(sc.hasOwn(this.relationMetadata, reldensName)){
59
+ return reldensName;
60
+ }
61
+ return null;
62
+ }
63
+ let modelMappings = sc.get(this.allRelationMappings, modelName.toLowerCase(), {});
64
+ if(sc.hasOwn(modelMappings, reldensName)){
65
+ return modelMappings[reldensName];
66
+ }
67
+ return null;
68
+ }
69
+
70
+ mapToReldensName(prismaName)
71
+ {
72
+ if(sc.hasOwn(this.reverseModelMappings, prismaName)){
73
+ return this.reverseModelMappings[prismaName];
74
+ }
75
+ return prismaName;
76
+ }
77
+
78
+ getRelatedModelName(prismaName, fromModelName)
79
+ {
80
+ if(!fromModelName){
81
+ let meta = sc.get(this.relationMetadata, prismaName, null);
82
+ if(meta){
83
+ return meta.model;
84
+ }
85
+ return null;
86
+ }
87
+ if(!this.metadataLoader){
88
+ return null;
89
+ }
90
+ let dmmf = this.metadataLoader.getDmmf();
91
+ if(!dmmf){
92
+ return null;
93
+ }
94
+ let modelInfo = this.findModelInDmmf(dmmf, fromModelName);
95
+ if(!modelInfo){
96
+ return null;
97
+ }
98
+ for(let field of modelInfo.fields){
99
+ if(field.name === prismaName && 'object' === field.kind){
100
+ return field.type;
101
+ }
102
+ }
103
+ return null;
104
+ }
105
+
106
+ findModelInDmmf(dmmf, modelName)
107
+ {
108
+ let lowerName = modelName.toLowerCase();
109
+ if(sc.hasOwn(dmmf.models, lowerName)){
110
+ return dmmf.models[lowerName];
111
+ }
112
+ if(sc.isArray(dmmf.models)){
113
+ for(let model of dmmf.models){
114
+ if(model.name.toLowerCase() === lowerName){
115
+ return model;
116
+ }
117
+ }
118
+ }
119
+ return null;
120
+ }
121
+
122
+ convertForeignKeysToRelations(params, isCreate = false)
123
+ {
124
+ let data = {};
125
+ let relations = {};
126
+ for(let key of Object.keys(params)){
127
+ if(!sc.hasOwn(this.foreignKeyMappings, key)){
128
+ data[key] = params[key];
129
+ continue;
130
+ }
131
+ let relationName = this.foreignKeyMappings[key];
132
+ let value = params[key];
133
+ let targetField = sc.get(this.foreignKeyTargetFields, key, 'id');
134
+ if(!this.typeCaster){
135
+ relations[relationName] = {connect: {[targetField]: value}};
136
+ continue;
137
+ }
138
+ if(this.typeCaster.isNullOrEmpty(value)){
139
+ if(isCreate){
140
+ continue;
141
+ }
142
+ relations[relationName] = {disconnect: true};
143
+ continue;
144
+ }
145
+ let fieldType = sc.get(this.typeCaster.fieldTypes, key, null);
146
+ let connectValue = fieldType
147
+ ? this.typeCaster.castValue(value, fieldType, key)
148
+ : this.typeCaster.castToIdType(value);
149
+ relations[relationName] = {
150
+ connect: {[targetField]: connectValue}
151
+ };
152
+ }
153
+ return {...data, ...relations};
154
+ }
155
+
156
+ buildIncludeObjectWithMapping(relations)
157
+ {
158
+ let include = {};
159
+ let mapping = {};
160
+ for(let relation of relations){
161
+ this.processRelationPath(relation, include, mapping);
162
+ }
163
+ return {include, mapping};
164
+ }
165
+
166
+ processRelationPath(relationPath, include, mapping)
167
+ {
168
+ let parts = relationPath.split('.');
169
+ let currentInclude = include;
170
+ let currentModelName = null;
171
+ for(let i = 0; i < parts.length; i++){
172
+ let reldensName = parts[i];
173
+ let prismaName = this.mapToPrismaName(reldensName, currentModelName);
174
+ if(!prismaName){
175
+ return;
176
+ }
177
+ if(reldensName !== prismaName){
178
+ mapping[prismaName] = reldensName;
179
+ }
180
+ let isLastPart = (i === parts.length - 1);
181
+ if(isLastPart){
182
+ currentInclude[prismaName] = true;
183
+ return;
184
+ }
185
+ if(!sc.hasOwn(currentInclude, prismaName) || true === currentInclude[prismaName]){
186
+ currentInclude[prismaName] = {include: {}};
187
+ }
188
+ currentModelName = this.getRelatedModelName(prismaName, currentModelName);
189
+ if(!currentModelName){
190
+ return;
191
+ }
192
+ currentInclude = currentInclude[prismaName].include;
193
+ }
194
+ }
195
+
196
+ transformRelationResults(result, includeConfig, relationMapping)
197
+ {
198
+ if(!result || !includeConfig){
199
+ return result;
200
+ }
201
+ if(sc.isArray(result)){
202
+ return result.map(item => this.transformSingleResult(item, includeConfig, relationMapping));
203
+ }
204
+ return this.transformSingleResult(result, includeConfig, relationMapping);
205
+ }
206
+
207
+ transformSingleResult(item, includeConfig, relationMapping)
208
+ {
209
+ if(!item || !sc.isObject(item)){
210
+ return item;
211
+ }
212
+ let transformed = {...item};
213
+ for(let prismaName of Object.keys(includeConfig)){
214
+ if(!sc.hasOwn(transformed, prismaName)){
215
+ continue;
216
+ }
217
+ let relationValue = transformed[prismaName];
218
+ let nestedConfig = includeConfig[prismaName];
219
+ if(sc.isObject(nestedConfig) && sc.hasOwn(nestedConfig, 'include')){
220
+ relationValue = this.transformRelationResults(relationValue, nestedConfig.include, relationMapping);
221
+ }
222
+ let reldensName = sc.get(relationMapping, prismaName, null);
223
+ if(!reldensName){
224
+ reldensName = this.mapToReldensName(prismaName);
225
+ }
226
+ transformed[reldensName] = relationValue;
227
+ if(reldensName !== prismaName){
228
+ delete transformed[prismaName];
229
+ }
230
+ }
231
+ return transformed;
232
+ }
233
+
234
+ hasValidIds(relationData)
235
+ {
236
+ if(!sc.isArray(relationData)){
237
+ return sc.hasOwn(relationData, 'id') && !this.typeCaster.isNullOrEmpty(relationData.id);
238
+ }
239
+ for(let item of relationData){
240
+ if(!sc.hasOwn(item, 'id') || this.typeCaster.isNullOrEmpty(item.id)){
241
+ return false;
242
+ }
243
+ }
244
+ return true;
245
+ }
246
+
247
+ buildCreateDataWithRelations(params, relations)
248
+ {
249
+ let createData = {};
250
+ for(let relation of relations){
251
+ let prismaName = this.mapToPrismaName(relation, null);
252
+ if(!prismaName){
253
+ continue;
254
+ }
255
+ if(!sc.hasOwn(params, relation)){
256
+ continue;
257
+ }
258
+ let relationData = params[relation];
259
+ if(!this.typeCaster){
260
+ createData[prismaName] = sc.isArray(relationData)
261
+ ? {connect: relationData.map(item => ({id: item.id}))}
262
+ : {connect: {id: relationData.id}};
263
+ continue;
264
+ }
265
+ if(this.hasValidIds(relationData)){
266
+ if(sc.isArray(relationData)){
267
+ createData[prismaName] = {
268
+ connect: relationData.map(item => ({id: this.typeCaster.castToIdType(item.id)}))
269
+ };
270
+ continue;
271
+ }
272
+ createData[prismaName] = {
273
+ connect: {id: this.typeCaster.castToIdType(relationData.id)}
274
+ };
275
+ continue;
276
+ }
277
+ if(sc.isArray(relationData)){
278
+ createData[prismaName] = {
279
+ create: relationData.map(item => this.typeCaster.castFieldsInData(item))
280
+ };
281
+ continue;
282
+ }
283
+ createData[prismaName] = {
284
+ create: this.typeCaster.castFieldsInData(relationData)
285
+ };
286
+ }
287
+ return createData;
288
+ }
289
+
290
+ }
291
+
292
+ module.exports.PrismaRelationResolver = PrismaRelationResolver;