@reldens/storage 0.88.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.
- package/.claude/driver-methods-complete-analysis.md +1978 -0
- package/.claude/test-architecture.md +973 -0
- package/CLAUDE.md +121 -1
- package/bin/reldens-storage.js +1 -1
- package/lib/base-driver.js +8 -0
- package/lib/entities-generator.js +1 -3
- package/lib/entity-templates/mikro-orm-model.template +2 -1
- package/lib/generators/models-generation.js +126 -5
- package/lib/mikro-orm/mikro-orm-data-server.js +24 -10
- package/lib/mikro-orm/mikro-orm-driver.js +184 -116
- package/lib/objection-js/objection-js-driver.js +111 -19
- package/lib/prisma/prisma-data-server.js +3 -2
- package/lib/prisma/prisma-driver.js +12 -10
- package/lib/prisma/prisma-filter-processor.js +193 -146
- package/lib/prisma/prisma-metadata-loader.js +0 -4
- package/lib/prisma/prisma-relation-resolver.js +292 -267
- package/lib/prisma/prisma-type-caster.js +280 -260
- package/package.json +15 -6
- package/tests/.env.test.example +7 -0
- package/tests/fixtures/categories-fixtures.js +164 -0
- package/tests/fixtures/expected-entities-mikro-orm/entities/test-categories-entity.js +70 -0
- package/tests/fixtures/expected-entities-mikro-orm/entities/test-products-entity.js +95 -0
- package/tests/fixtures/expected-entities-mikro-orm/entities/test-reviews-entity.js +84 -0
- package/tests/fixtures/expected-entities-mikro-orm/entities-config.js +17 -0
- package/tests/fixtures/expected-entities-mikro-orm/entities-translations.js +13 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/registered-models-mikro-orm.js +23 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-categories-model.js +57 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-products-model.js +79 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-reviews-model.js +70 -0
- package/tests/fixtures/expected-entities-objection-js/entities/test-categories-entity.js +70 -0
- package/tests/fixtures/expected-entities-objection-js/entities/test-products-entity.js +95 -0
- package/tests/fixtures/expected-entities-objection-js/entities/test-reviews-entity.js +84 -0
- package/tests/fixtures/expected-entities-objection-js/entities-config.js +17 -0
- package/tests/fixtures/expected-entities-objection-js/entities-translations.js +13 -0
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/registered-models-objection-js.js +23 -0
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-categories-model.js +33 -0
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-products-model.js +42 -0
- package/tests/fixtures/expected-entities-objection-js/models/objection-js/test-reviews-model.js +33 -0
- package/tests/fixtures/expected-entities-prisma/entities/test-categories-entity.js +70 -0
- package/tests/fixtures/expected-entities-prisma/entities/test-products-entity.js +95 -0
- package/tests/fixtures/expected-entities-prisma/entities/test-reviews-entity.js +84 -0
- package/tests/fixtures/expected-entities-prisma/entities-config.js +17 -0
- package/tests/fixtures/expected-entities-prisma/entities-translations.js +13 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/registered-models-prisma.js +23 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/test-categories-model.js +43 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/test-products-model.js +50 -0
- package/tests/fixtures/expected-entities-prisma/models/prisma/test-reviews-model.js +46 -0
- package/tests/fixtures/products-fixtures.js +119 -0
- package/tests/fixtures/reviews-fixtures.js +42 -0
- package/tests/fixtures/sql/test-schema.sql +59 -0
- package/tests/integration/test-drivers.js +257 -0
- package/tests/integration/test-nested-filters.js +408 -0
- package/tests/integration/test-relations.js +349 -0
- package/tests/run-tests.js +213 -0
- package/tests/unit/test-drivers.js +89 -0
- package/tests/unit/test-entities-generator.js +533 -0
- package/tests/unit/test-entity-manager.js +128 -0
- package/tests/unit/test-type-mapper.js +232 -0
- package/tests/utils/custom-reporter.js +73 -0
- package/tests/utils/driver-registry.js +144 -0
- package/tests/utils/prisma-subprocess-worker.js +150 -0
- package/tests/utils/test-helpers.js +726 -0
- package/tests/utils/test-runner.js +63 -0
|
@@ -67,14 +67,15 @@ class ObjectionJsDriver extends BaseDriver
|
|
|
67
67
|
async upsert(params, filters)
|
|
68
68
|
{
|
|
69
69
|
if(params.id){
|
|
70
|
-
let
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
let existent = await this.loadById(params.id);
|
|
71
|
+
if(existent){
|
|
72
|
+
return this.updateById(params.id, params);
|
|
73
|
+
}
|
|
73
74
|
}
|
|
74
75
|
if(filters){
|
|
75
76
|
let existent = await this.loadOne(filters);
|
|
76
77
|
if(existent){
|
|
77
|
-
return this.
|
|
78
|
+
return this.updateById(existent.id, params);
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
return this.create(params);
|
|
@@ -103,7 +104,7 @@ class ObjectionJsDriver extends BaseDriver
|
|
|
103
104
|
{
|
|
104
105
|
let queryBuilder = this.queryBuilder(true, true, true);
|
|
105
106
|
this.appendFilters(queryBuilder, filters);
|
|
106
|
-
let count = await this.appendRelationsToQuery(queryBuilder, relations).count().first();
|
|
107
|
+
let count = await this.appendRelationsToQuery(queryBuilder, relations, null, this.containsRelationFilters(filters)).count().first();
|
|
107
108
|
return count ? count['count(*)'] : 0;
|
|
108
109
|
}
|
|
109
110
|
|
|
@@ -128,7 +129,7 @@ class ObjectionJsDriver extends BaseDriver
|
|
|
128
129
|
{
|
|
129
130
|
let queryBuilder = this.queryBuilder(true, true, true);
|
|
130
131
|
this.appendFilters(queryBuilder, filters);
|
|
131
|
-
return this.appendRelationsToQuery(queryBuilder, relations);
|
|
132
|
+
return this.appendRelationsToQuery(queryBuilder, relations, null, this.containsRelationFilters(filters));
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
loadBy(field, fieldValue, operator = null)
|
|
@@ -171,7 +172,12 @@ class ObjectionJsDriver extends BaseDriver
|
|
|
171
172
|
{
|
|
172
173
|
let queryBuilder = this.queryBuilder(false, true, true);
|
|
173
174
|
this.appendFilters(queryBuilder, filters);
|
|
174
|
-
return this.appendRelationsToQuery(
|
|
175
|
+
return this.appendRelationsToQuery(
|
|
176
|
+
queryBuilder,
|
|
177
|
+
relations,
|
|
178
|
+
null,
|
|
179
|
+
this.containsRelationFilters(filters)
|
|
180
|
+
).limit(1).first();
|
|
175
181
|
}
|
|
176
182
|
|
|
177
183
|
loadOneBy(field, fieldValue, operator = null)
|
|
@@ -200,7 +206,7 @@ class ObjectionJsDriver extends BaseDriver
|
|
|
200
206
|
if(useOffset && 0 !== this.offset){
|
|
201
207
|
queryBuilder.offset(this.offset)
|
|
202
208
|
}
|
|
203
|
-
if(useSort &&
|
|
209
|
+
if(useSort && this.sortBy && this.sortDirection){
|
|
204
210
|
queryBuilder.orderBy(this.sortBy, this.sortDirection)
|
|
205
211
|
}
|
|
206
212
|
return queryBuilder;
|
|
@@ -231,6 +237,45 @@ class ObjectionJsDriver extends BaseDriver
|
|
|
231
237
|
queryBuilder.where(field, 'like', '%' + cleanValue + '%');
|
|
232
238
|
}
|
|
233
239
|
|
|
240
|
+
isRelationField(fieldName)
|
|
241
|
+
{
|
|
242
|
+
let relationMappings = sc.get(this.rawModel, 'relationMappings', false);
|
|
243
|
+
if(!relationMappings){
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
if('function' === typeof relationMappings){
|
|
247
|
+
relationMappings = relationMappings();
|
|
248
|
+
}
|
|
249
|
+
return sc.hasOwn(relationMappings, fieldName);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
flattenRelationFilters(relationName, nestedFilters)
|
|
253
|
+
{
|
|
254
|
+
let flattened = {};
|
|
255
|
+
for(let key of Object.keys(nestedFilters)){
|
|
256
|
+
let value = nestedFilters[key];
|
|
257
|
+
if('OR' === key && sc.isArray(value)){
|
|
258
|
+
flattened.OR = this.flattenOrConditions(relationName, value);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
flattened[relationName+'.'+key] = value;
|
|
262
|
+
}
|
|
263
|
+
return flattened;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
flattenOrConditions(relationName, conditions)
|
|
267
|
+
{
|
|
268
|
+
let flattenedConditions = [];
|
|
269
|
+
for(let condition of conditions){
|
|
270
|
+
let flatCondition = {};
|
|
271
|
+
for(let condKey of Object.keys(condition)){
|
|
272
|
+
flatCondition[relationName+'.'+condKey] = condition[condKey];
|
|
273
|
+
}
|
|
274
|
+
flattenedConditions.push(flatCondition);
|
|
275
|
+
}
|
|
276
|
+
return flattenedConditions;
|
|
277
|
+
}
|
|
278
|
+
|
|
234
279
|
appendFilters(queryBuilder, filters = {})
|
|
235
280
|
{
|
|
236
281
|
let filtersKeys = Object.keys(filters);
|
|
@@ -239,17 +284,25 @@ class ObjectionJsDriver extends BaseDriver
|
|
|
239
284
|
}
|
|
240
285
|
for(let i of filtersKeys){
|
|
241
286
|
let filter = filters[i];
|
|
287
|
+
if('AND' === i && sc.isArray(filter)){
|
|
288
|
+
for(let condition of filter){
|
|
289
|
+
queryBuilder.where((nestedQb) => {
|
|
290
|
+
this.appendFilters(nestedQb, condition);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
242
295
|
if('OR' === i && sc.isArray(filter)){
|
|
243
|
-
queryBuilder.where(
|
|
296
|
+
queryBuilder.where((outerQb) => {
|
|
244
297
|
for(let condition of filter){
|
|
245
|
-
|
|
246
|
-
this.appendFilters(
|
|
247
|
-
}
|
|
298
|
+
outerQb.orWhere((innerQb) => {
|
|
299
|
+
this.appendFilters(innerQb, condition);
|
|
300
|
+
});
|
|
248
301
|
}
|
|
249
|
-
}
|
|
302
|
+
});
|
|
250
303
|
continue;
|
|
251
304
|
}
|
|
252
|
-
let filterOperator = sc.get(filter, 'operator');
|
|
305
|
+
let filterOperator = sc.get(filter, 'operator', '').toUpperCase();
|
|
253
306
|
if(filterOperator){
|
|
254
307
|
if('OR' === filterOperator){
|
|
255
308
|
queryBuilder.orWhere(i, filter.value);
|
|
@@ -267,7 +320,13 @@ class ObjectionJsDriver extends BaseDriver
|
|
|
267
320
|
this.handleLikeFilter(queryBuilder, i, filter.value);
|
|
268
321
|
continue;
|
|
269
322
|
}
|
|
270
|
-
|
|
323
|
+
let sqlOperator = this.operatorsMap[filterOperator] || filter.operator;
|
|
324
|
+
queryBuilder.where(i, sqlOperator, filter.value);
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
if(this.isRelationField(i) && sc.isObject(filter) && !sc.isArray(filter)){
|
|
328
|
+
let flattenedFilters = this.flattenRelationFilters(i, filter);
|
|
329
|
+
this.appendFilters(queryBuilder, flattenedFilters);
|
|
271
330
|
continue;
|
|
272
331
|
}
|
|
273
332
|
queryBuilder.where(i, filter);
|
|
@@ -275,18 +334,51 @@ class ObjectionJsDriver extends BaseDriver
|
|
|
275
334
|
return queryBuilder;
|
|
276
335
|
}
|
|
277
336
|
|
|
278
|
-
|
|
337
|
+
containsRelationFilters(filters)
|
|
338
|
+
{
|
|
339
|
+
if(!sc.isObject(filters)){
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
for(let key of Object.keys(filters)){
|
|
343
|
+
if('AND' === key || 'OR' === key){
|
|
344
|
+
if(sc.isArray(filters[key])){
|
|
345
|
+
for(let condition of filters[key]){
|
|
346
|
+
if(this.containsRelationFilters(condition)){
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if(this.isRelationField(key)){
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
appendRelationsToQuery(queryBuilder, relations, relationsModifiers, useJoin = false)
|
|
279
361
|
{
|
|
280
362
|
if(!sc.isArray(relations) || 0 === relations.length){
|
|
281
363
|
relations = Object.keys(this.rawModel.relationMappings || {});
|
|
282
364
|
}
|
|
283
|
-
|
|
284
|
-
queryBuilder.withGraphFetched('['+relations.join(',')+']');
|
|
285
|
-
}
|
|
365
|
+
this.applyGraphMethod(relations, useJoin, queryBuilder);
|
|
286
366
|
this.appendRelationsModifiers(queryBuilder, relations, relationsModifiers);
|
|
287
367
|
return queryBuilder;
|
|
288
368
|
}
|
|
289
369
|
|
|
370
|
+
applyGraphMethod(relations, useJoin, queryBuilder)
|
|
371
|
+
{
|
|
372
|
+
if(0 === relations.length){
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
if(useJoin){
|
|
376
|
+
queryBuilder.withGraphJoined('[' + relations.join(',') + ']');
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
queryBuilder.withGraphFetched('[' + relations.join(',') + ']');
|
|
380
|
+
}
|
|
381
|
+
|
|
290
382
|
appendRelationsModifiers(queryBuilder, relations, relationsModifiers)
|
|
291
383
|
{
|
|
292
384
|
if(!sc.isArray(relationsModifiers)){
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
const { BaseDataServer } = require('../base-data-server');
|
|
8
8
|
const { PrismaDriver } = require('./prisma-driver');
|
|
9
9
|
const { MySQLTablesProvider } = require('../mysql-tables-provider');
|
|
10
|
-
const { PrismaClient } = require('@prisma/client');
|
|
10
|
+
const { PrismaClient, Prisma } = require('@prisma/client');
|
|
11
11
|
const { FileHandler } = require('@reldens/server-utils');
|
|
12
12
|
const { Logger, sc } = require('@reldens/utils');
|
|
13
13
|
|
|
@@ -91,7 +91,8 @@ class PrismaDataServer extends BaseDataServer
|
|
|
91
91
|
prisma: this.prisma,
|
|
92
92
|
model: prismaModel,
|
|
93
93
|
server: this,
|
|
94
|
-
allRelationMappings: this.allRelationMappings
|
|
94
|
+
allRelationMappings: this.allRelationMappings,
|
|
95
|
+
prismaDbNull: Prisma.DbNull
|
|
95
96
|
});
|
|
96
97
|
}
|
|
97
98
|
this.entityManager.setEntities(this.entities);
|
|
@@ -29,6 +29,7 @@ class PrismaDriver extends BaseDriver
|
|
|
29
29
|
this.prisma = props.prisma;
|
|
30
30
|
this.model = props.model;
|
|
31
31
|
this.allRelationMappings = sc.get(props, 'allRelationMappings', {});
|
|
32
|
+
this.prismaDbNull = sc.get(props, 'prismaDbNull', null);
|
|
32
33
|
this.initHelpers();
|
|
33
34
|
this.initModelMetadata();
|
|
34
35
|
}
|
|
@@ -46,7 +47,8 @@ class PrismaDriver extends BaseDriver
|
|
|
46
47
|
fieldDefaults: {},
|
|
47
48
|
requiredFields: [],
|
|
48
49
|
optionalFields: [],
|
|
49
|
-
rawModel: this.rawModel
|
|
50
|
+
rawModel: this.rawModel,
|
|
51
|
+
prismaDbNull: this.prismaDbNull
|
|
50
52
|
});
|
|
51
53
|
this.relationResolver = new PrismaRelationResolver({
|
|
52
54
|
relationMetadata: {},
|
|
@@ -60,7 +62,9 @@ class PrismaDriver extends BaseDriver
|
|
|
60
62
|
typeCaster: this.typeCaster,
|
|
61
63
|
fieldTypes: {},
|
|
62
64
|
referenceFields: {},
|
|
63
|
-
jsonFields: new Set()
|
|
65
|
+
jsonFields: new Set(),
|
|
66
|
+
operatorsMap: this.operatorsMap,
|
|
67
|
+
relationResolver: this.relationResolver
|
|
64
68
|
});
|
|
65
69
|
this.queryBuilder = new PrismaQueryBuilder();
|
|
66
70
|
}
|
|
@@ -237,6 +241,11 @@ class PrismaDriver extends BaseDriver
|
|
|
237
241
|
relations = this.getAllRelations();
|
|
238
242
|
}
|
|
239
243
|
let preparedData = this.prepareData(params);
|
|
244
|
+
for(let relation of relations){
|
|
245
|
+
if(sc.hasOwn(preparedData, relation)){
|
|
246
|
+
delete preparedData[relation];
|
|
247
|
+
}
|
|
248
|
+
}
|
|
240
249
|
this.ensureRequiredFields(preparedData);
|
|
241
250
|
let createData = {data: preparedData};
|
|
242
251
|
if(0 < relations.length){
|
|
@@ -367,16 +376,9 @@ class PrismaDriver extends BaseDriver
|
|
|
367
376
|
|
|
368
377
|
async countWithRelations(filters, relations)
|
|
369
378
|
{
|
|
370
|
-
if(!sc.isArray(relations) || 0 === relations.length){
|
|
371
|
-
relations = this.getAllRelations();
|
|
372
|
-
}
|
|
373
379
|
let processedFilters = this.filterProcessor.processFilters(filters);
|
|
374
|
-
let query = {where: processedFilters};
|
|
375
|
-
if(0 < relations.length){
|
|
376
|
-
query.include = this.relationResolver.buildIncludeObjectWithMapping(relations).include;
|
|
377
|
-
}
|
|
378
380
|
return this.executeWithNormalization(
|
|
379
|
-
() => this.model.count(
|
|
381
|
+
() => this.model.count({where: processedFilters}),
|
|
380
382
|
'Count with relations error: ',
|
|
381
383
|
0
|
|
382
384
|
);
|
|
@@ -1,146 +1,193 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
* Reldens - PrismaFilterProcessor
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { sc } = require('@reldens/utils');
|
|
8
|
-
|
|
9
|
-
class PrismaFilterProcessor
|
|
10
|
-
{
|
|
11
|
-
|
|
12
|
-
constructor(props)
|
|
13
|
-
{
|
|
14
|
-
this.typeCaster = sc.get(props, 'typeCaster', null);
|
|
15
|
-
this.fieldTypes = sc.get(props, 'fieldTypes', {});
|
|
16
|
-
this.referenceFields = sc.get(props, 'referenceFields', {});
|
|
17
|
-
this.jsonFields = sc.get(props, 'jsonFields', new Set());
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
this.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
{
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - PrismaFilterProcessor
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { sc } = require('@reldens/utils');
|
|
8
|
+
|
|
9
|
+
class PrismaFilterProcessor
|
|
10
|
+
{
|
|
11
|
+
|
|
12
|
+
constructor(props)
|
|
13
|
+
{
|
|
14
|
+
this.typeCaster = sc.get(props, 'typeCaster', null);
|
|
15
|
+
this.fieldTypes = sc.get(props, 'fieldTypes', {});
|
|
16
|
+
this.referenceFields = sc.get(props, 'referenceFields', {});
|
|
17
|
+
this.jsonFields = sc.get(props, 'jsonFields', new Set());
|
|
18
|
+
this.operatorsMap = sc.get(props, 'operatorsMap', {});
|
|
19
|
+
this.relationResolver = sc.get(props, 'relationResolver', null);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
updateMetadata(metadata)
|
|
23
|
+
{
|
|
24
|
+
this.fieldTypes = sc.get(metadata, 'fieldTypes', this.fieldTypes);
|
|
25
|
+
this.referenceFields = sc.get(metadata, 'referenceFields', this.referenceFields);
|
|
26
|
+
this.jsonFields = sc.get(metadata, 'jsonFields', this.jsonFields);
|
|
27
|
+
this.relationResolver = sc.get(metadata, 'relationResolver', this.relationResolver);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
processFilters(filters)
|
|
31
|
+
{
|
|
32
|
+
if(!sc.isObject(filters)){
|
|
33
|
+
return filters;
|
|
34
|
+
}
|
|
35
|
+
let processedFilters = {};
|
|
36
|
+
for(let key of Object.keys(filters)){
|
|
37
|
+
let value = filters[key];
|
|
38
|
+
if('AND' === key && sc.isArray(value)){
|
|
39
|
+
processedFilters.AND = value.map(condition => this.processFilters(condition));
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if('OR' === key && sc.isArray(value)){
|
|
43
|
+
processedFilters.OR = value.map(condition => this.processFilters(condition));
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
let translatedKey = this.translateRelationKey(key);
|
|
47
|
+
if(sc.hasOwn(value, 'operator') && sc.hasOwn(value, 'value')){
|
|
48
|
+
let operatorValue = this.processFilterValue(value.value, translatedKey);
|
|
49
|
+
processedFilters[translatedKey] = this.applyOperator(operatorValue, value.operator, translatedKey);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if(sc.isObject(value) && !sc.isArray(value)){
|
|
53
|
+
if(this.isRelationKey(key)){
|
|
54
|
+
processedFilters[translatedKey] = {
|
|
55
|
+
some: this.processFilters(value)
|
|
56
|
+
};
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
processedFilters[translatedKey] = this.processFilters(value);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
processedFilters[translatedKey] = this.processFilterValue(value, translatedKey);
|
|
63
|
+
}
|
|
64
|
+
return processedFilters;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
processFilterValue(value, key)
|
|
68
|
+
{
|
|
69
|
+
if(sc.isArray(value)){
|
|
70
|
+
if('id' === key){
|
|
71
|
+
return value.map(id => this.typeCaster.castToIdType(id)).filter(id => null !== id);
|
|
72
|
+
}
|
|
73
|
+
let fieldType = this.fieldTypes[key];
|
|
74
|
+
if(fieldType){
|
|
75
|
+
return value.map(val => this.typeCaster.castValue(val, fieldType, key))
|
|
76
|
+
.filter(val => this.typeCaster.isValidCastedValue(val));
|
|
77
|
+
}
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
if('id' === key){
|
|
81
|
+
return this.typeCaster.castToIdType(value);
|
|
82
|
+
}
|
|
83
|
+
let fieldType = this.fieldTypes[key];
|
|
84
|
+
if(!fieldType){
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
let castedValue = this.typeCaster.castValue(value, fieldType, key);
|
|
88
|
+
if(this.typeCaster.isValidCastedValue(castedValue)){
|
|
89
|
+
return castedValue;
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
applyOperator(value, operator, fieldName = null)
|
|
95
|
+
{
|
|
96
|
+
let upperOperator = operator ? operator.toUpperCase() : '';
|
|
97
|
+
if('LIKE' === upperOperator){
|
|
98
|
+
return this.handleLikeOperator(value, fieldName);
|
|
99
|
+
}
|
|
100
|
+
if('IN' === upperOperator){
|
|
101
|
+
return {in: value};
|
|
102
|
+
}
|
|
103
|
+
if('NOT IN' === upperOperator){
|
|
104
|
+
return {notIn: value};
|
|
105
|
+
}
|
|
106
|
+
if('NOT' === upperOperator){
|
|
107
|
+
return {not: value};
|
|
108
|
+
}
|
|
109
|
+
let sqlOperator = this.operatorsMap[upperOperator] || upperOperator;
|
|
110
|
+
let prismaSyntaxMap = {
|
|
111
|
+
'=': value,
|
|
112
|
+
'!=': {not: value},
|
|
113
|
+
'>': {gt: value},
|
|
114
|
+
'>=': {gte: value},
|
|
115
|
+
'<': {lt: value},
|
|
116
|
+
'<=': {lte: value}
|
|
117
|
+
};
|
|
118
|
+
return prismaSyntaxMap[sqlOperator] || value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
handleLikeOperator(value, fieldName)
|
|
122
|
+
{
|
|
123
|
+
let cleanValue = String(value).replace(/%/g, '');
|
|
124
|
+
if(this.isJsonField(fieldName)){
|
|
125
|
+
return this.handleJsonTextSearch(cleanValue);
|
|
126
|
+
}
|
|
127
|
+
if('id' === fieldName || sc.hasOwn(this.referenceFields, fieldName)){
|
|
128
|
+
let numericValue = Number(cleanValue);
|
|
129
|
+
if(!isNaN(numericValue)){
|
|
130
|
+
return numericValue;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return {contains: cleanValue};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
handleJsonTextSearch(searchValue)
|
|
137
|
+
{
|
|
138
|
+
return {
|
|
139
|
+
string_contains: searchValue
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
isJsonField(fieldName)
|
|
144
|
+
{
|
|
145
|
+
if(!this.jsonFields){
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
if(this.jsonFields.has(fieldName)){
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
return this.typeCaster && this.typeCaster.isJsonFieldByName(fieldName);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
createSingleFilter(field, fieldValue, operator = null)
|
|
155
|
+
{
|
|
156
|
+
let filter = {};
|
|
157
|
+
let processedValue = this.processFilterValue(fieldValue, field);
|
|
158
|
+
if(null === operator){
|
|
159
|
+
filter[field] = processedValue;
|
|
160
|
+
return filter;
|
|
161
|
+
}
|
|
162
|
+
filter[field] = {operator, value: processedValue};
|
|
163
|
+
return this.processFilters(filter);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
translateRelationKey(key)
|
|
167
|
+
{
|
|
168
|
+
if(!this.relationResolver){
|
|
169
|
+
return key;
|
|
170
|
+
}
|
|
171
|
+
let prismaName = this.relationResolver.mapToPrismaName(key, null);
|
|
172
|
+
if(prismaName){
|
|
173
|
+
return prismaName;
|
|
174
|
+
}
|
|
175
|
+
return key;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
isRelationKey(key)
|
|
179
|
+
{
|
|
180
|
+
if(!this.relationResolver){
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
let prismaName = this.relationResolver.mapToPrismaName(key, null);
|
|
184
|
+
if(prismaName && prismaName !== key){
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
let relationMetadata = sc.get(this.relationResolver, 'relationMetadata', {});
|
|
188
|
+
return sc.hasOwn(relationMetadata, key);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports.PrismaFilterProcessor = PrismaFilterProcessor;
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { Logger, sc } = require('@reldens/utils');
|
|
8
|
-
const { Prisma } = require('@prisma/client');
|
|
9
8
|
|
|
10
9
|
class PrismaMetadataLoader
|
|
11
10
|
{
|
|
@@ -28,9 +27,6 @@ class PrismaMetadataLoader
|
|
|
28
27
|
if(this.prisma._runtimeDataModel){
|
|
29
28
|
return this.prisma._runtimeDataModel;
|
|
30
29
|
}
|
|
31
|
-
if(Prisma.dmmf && Prisma.dmmf.datamodel){
|
|
32
|
-
return Prisma.dmmf.datamodel;
|
|
33
|
-
}
|
|
34
30
|
return null;
|
|
35
31
|
} catch(error) {
|
|
36
32
|
Logger.warning('Failed to access DMMF: '+error.message);
|