@reldens/storage 0.80.0 → 0.81.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.
@@ -385,7 +385,7 @@ class EntitiesGenerator
385
385
  if(!this.prismaClient && this.server.prisma){
386
386
  this.prismaClient = this.server.prisma;
387
387
  }
388
- Logger.debug('Extract Prisma relations metadata.');
388
+ //Logger.debug('Extract Prisma relations metadata.');
389
389
  this.extractPrismaRelationsMetadata();
390
390
  }
391
391
  let tables = await this.server.fetchEntitiesFromDatabase();
@@ -5,8 +5,10 @@
5
5
  */
6
6
 
7
7
  const { BaseDriver } = require('../base-driver');
8
+ const { PrismaTypeCaster } = require('./prisma-type-caster');
9
+ const { PrismaMetadataLoader } = require('./prisma-metadata-loader');
10
+ const { PrismaRelationResolver } = require('./prisma-relation-resolver');
8
11
  const { Logger, sc } = require('@reldens/utils');
9
- const { Prisma } = require('@prisma/client');
10
12
 
11
13
  class PrismaDriver extends BaseDriver
12
14
  {
@@ -24,325 +26,59 @@ class PrismaDriver extends BaseDriver
24
26
  }
25
27
  this.prisma = props.prisma;
26
28
  this.model = props.model;
27
- this.requiredFields = [];
28
- this.optionalFields = [];
29
- this.fieldTypes = {};
30
- this.fieldDefaults = {};
31
- this.idFieldType = 'String';
32
- this.referenceFields = {};
33
- this.relationMetadata = {};
34
- this.foreignKeyMappings = {};
35
- this.jsonFields = new Set();
29
+ this.initHelpers();
36
30
  this.initModelMetadata();
37
31
  }
38
32
 
39
- initModelMetadata()
40
- {
41
- let dmmf = this.getDmmf();
42
- if(!dmmf){
43
- Logger.warning('Could not access Prisma DMMF metadata');
44
- return;
45
- }
46
- let modelName = this.tableName().toLowerCase();
47
- let modelInfo = dmmf.models?.[modelName] || dmmf.models?.find(m => m.name.toLowerCase() === modelName);
48
- if(!modelInfo){
49
- Logger.warning('Could not find model info for: '+modelName);
50
- return;
51
- }
52
- for(let field of modelInfo.fields){
53
- this.processFieldMetadata(field);
54
- }
55
- if(this.rawModel && this.rawModel.relationTypes){
56
- for(let relationName of Object.keys(this.rawModel.relationTypes)){
57
- if(sc.hasOwn(this.relationMetadata, relationName)){
58
- this.relationMetadata[relationName].type = this.rawModel.relationTypes[relationName];
59
- }
60
- }
61
- }
62
- }
63
-
64
- getDmmf()
65
- {
66
- try {
67
- if(this.prisma._dmmf){
68
- return this.prisma._dmmf.datamodel;
69
- }
70
- if(this.prisma._getDmmf && sc.isFunction(this.prisma._getDmmf)){
71
- return this.prisma._getDmmf().datamodel;
72
- }
73
- if(this.prisma._runtimeDataModel){
74
- return this.prisma._runtimeDataModel;
75
- }
76
- if(Prisma.dmmf && Prisma.dmmf.datamodel){
77
- return Prisma.dmmf.datamodel;
78
- }
79
- return null;
80
- } catch(error) {
81
- Logger.warning('Failed to access DMMF: '+error.message);
82
- return null;
83
- }
84
- }
85
-
86
- processFieldMetadata(field)
87
- {
88
- this.fieldTypes[field.name] = field.type;
89
- if('Json' === field.type){
90
- this.jsonFields.add(field.name);
91
- }
92
- if(field.isId){
93
- this.idFieldType = field.type;
94
- }
95
- if(field.hasDefaultValue){
96
- this.fieldDefaults[field.name] = field.default;
97
- }
98
- if(field.isRequired && !field.hasDefaultValue && !field.isId && 'object' !== field.kind){
99
- this.requiredFields.push(field.name);
100
- }
101
- if(field.isOptional || field.hasDefaultValue){
102
- this.optionalFields.push(field.name);
103
- }
104
- if(field.kind === 'scalar' && field.isList === false){
105
- let isNumericType = this.isNumericFieldType(field.type);
106
- let isReferenceField = field.relationFromFields && 0 < field.relationFromFields.length;
107
- if(isNumericType || isReferenceField){
108
- this.referenceFields[field.name] = field.type;
109
- }
110
- }
111
- if('object' === field.kind){
112
- let relationType = field.isList ? 'many' : 'one';
113
- this.relationMetadata[field.name] = {
114
- type: relationType,
115
- model: field.type,
116
- isList: field.isList,
117
- isOptional: field.isOptional
118
- };
119
- }
120
- if('object' === field.kind && sc.hasOwn(field, 'relationFromFields') && sc.isArray(field.relationFromFields)){
121
- for(let foreignKeyField of field.relationFromFields){
122
- this.foreignKeyMappings[foreignKeyField] = field.name;
123
- }
124
- }
125
- }
126
-
127
- getAllRelations()
128
- {
129
- return Object.keys(this.relationMetadata || {});
130
- }
131
-
132
- normalizeRelationName(relationName)
133
- {
134
- if(sc.hasOwn(this.relationMetadata, relationName)){
135
- return relationName;
136
- }
137
- if(relationName.startsWith('related_')){
138
- let withoutPrefix = relationName.substring(8);
139
- if(sc.hasOwn(this.relationMetadata, withoutPrefix)){
140
- return withoutPrefix;
141
- }
142
- }
143
- let availableRelations = Object.keys(this.relationMetadata);
144
- for(let availableRelation of availableRelations){
145
- if(relationName.endsWith(availableRelation)){
146
- return availableRelation;
147
- }
148
- if(availableRelation.endsWith(relationName.replace('related_', ''))){
149
- return availableRelation;
150
- }
151
- }
152
- return relationName;
153
- }
154
-
155
- isNumericFieldType(fieldType)
156
- {
157
- return ('Int' === fieldType || 'BigInt' === fieldType || 'Float' === fieldType || 'Decimal' === fieldType);
158
- }
159
-
160
- isBooleanFieldType(fieldType)
161
- {
162
- return 'Boolean' === fieldType;
163
- }
164
-
165
- isDateTimeFieldType(fieldType)
166
- {
167
- return 'DateTime' === fieldType || 'Date' === fieldType;
168
- }
169
-
170
- isJsonFieldType(fieldType)
171
- {
172
- return 'Json' === fieldType;
173
- }
174
-
175
- isJsonFieldByName(fieldName)
176
- {
177
- if(this.jsonFields.has(fieldName)){
178
- return true;
179
- }
180
- if(!this.rawModel || !this.rawModel.properties){
181
- return false;
182
- }
183
- return 'json' === sc.get(this.rawModel.properties[fieldName], 'dbType', '');
184
- }
185
-
186
- isStringFieldType(fieldType)
187
- {
188
- return 'String' === fieldType;
189
- }
190
-
191
- castValue(value, fieldType, fieldName = null)
192
- {
193
- if('id' === fieldName){
194
- return this.castToIdType(value);
195
- }
196
- if(this.isNullOrEmpty(value)){
197
- if(sc.hasOwn(this.fieldDefaults, fieldName)){
198
- return undefined;
199
- }
200
- if(-1 !== this.optionalFields.indexOf(fieldName)){
201
- return null;
202
- }
203
- if(-1 !== this.requiredFields.indexOf(fieldName)){
204
- Logger.warning('Required field '+fieldName+' cannot be null or empty');
205
- return undefined;
206
- }
207
- return null;
208
- }
209
- if(this.isJsonFieldByName(fieldName)){
210
- return this.castToJsonType(value);
211
- }
212
- if(this.isNumericFieldType(fieldType)){
213
- return this.castToNumericType(value, fieldType);
214
- }
215
- if(this.isBooleanFieldType(fieldType)){
216
- return this.castToBooleanType(value);
217
- }
218
- if(this.isDateTimeFieldType(fieldType)){
219
- return this.castToDateType(value);
220
- }
221
- if(this.isStringFieldType(fieldType)){
222
- return this.castToStringType(value);
223
- }
224
- return value;
225
- }
226
-
227
- isNullOrEmpty(value)
228
- {
229
- return null === value || undefined === value || '' === value;
230
- }
231
-
232
- castToIdType(value)
233
- {
234
- if(this.isNullOrEmpty(value)){
235
- return value;
236
- }
237
- if(this.isNumericFieldType(this.idFieldType)){
238
- let numeric = Number(value);
239
- if(!isNaN(numeric)){
240
- return ('Int' === this.idFieldType || 'BigInt' === this.idFieldType) ? Math.floor(numeric) : numeric;
241
- }
242
- }
243
- return String(value);
244
- }
245
-
246
- castToNumericType(value, fieldType)
247
- {
248
- if(this.isNullOrEmpty(value)){
249
- return null;
250
- }
251
- let numericValue = Number(value);
252
- if(isNaN(numericValue)){
253
- Logger.warning('Cannot convert value to numeric: '+value);
254
- return null;
255
- }
256
- if('Int' === fieldType || 'BigInt' === fieldType){
257
- return Math.floor(numericValue);
258
- }
259
- return numericValue;
260
- }
261
-
262
- castToBooleanType(value)
263
- {
264
- if(this.isNullOrEmpty(value)){
265
- return null;
266
- }
267
- if('boolean' === typeof value){
268
- return value;
269
- }
270
- if('number' === typeof value){
271
- return 0 !== value;
272
- }
273
- if('string' === typeof value){
274
- let lowerValue = value.toLowerCase();
275
- if('true' === lowerValue || '1' === lowerValue || 'yes' === lowerValue){
276
- return true;
277
- }
278
- if('false' === lowerValue || '0' === lowerValue || 'no' === lowerValue){
279
- return false;
280
- }
281
- }
282
- return Boolean(value);
33
+ initHelpers()
34
+ {
35
+ this.metadataLoader = new PrismaMetadataLoader({
36
+ prisma: this.prisma,
37
+ rawModel: this.rawModel
38
+ });
39
+ this.typeCaster = new PrismaTypeCaster({
40
+ idFieldType: 'String',
41
+ jsonFields: new Set(),
42
+ fieldTypes: {},
43
+ fieldDefaults: {},
44
+ requiredFields: [],
45
+ optionalFields: [],
46
+ rawModel: this.rawModel
47
+ });
48
+ this.relationResolver = new PrismaRelationResolver({
49
+ relationMetadata: {},
50
+ relationAliases: {},
51
+ metadataLoader: this.metadataLoader
52
+ });
283
53
  }
284
54
 
285
- castToDateType(dateValue)
286
- {
287
- if(dateValue instanceof Date){
288
- return dateValue;
289
- }
290
- if(this.isNullOrEmpty(dateValue)){
291
- return null;
292
- }
293
- let dateString = String(dateValue);
294
- if(dateString.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/)){
295
- return new Date(dateString.replace(' ', 'T') + 'Z');
296
- }
297
- if(dateString.match(/^\d{4}-\d{2}-\d{2}$/)){
298
- return new Date(dateString + 'T00:00:00Z');
299
- }
300
- try {
301
- let parsedDate = new Date(dateString);
302
- if(isNaN(parsedDate.getTime())){
303
- Logger.warning('Invalid date value: '+dateString);
304
- return null;
305
- }
306
- return parsedDate;
307
- } catch(error) {
308
- Logger.warning('Failed to convert date value: '+dateString);
309
- return null;
310
- }
311
- }
312
-
313
- castToJsonType(value)
314
- {
315
- if(this.isNullOrEmpty(value)){
316
- return Prisma.DbNull;
317
- }
318
- if('object' === typeof value){
319
- return value;
320
- }
321
- if('string' === typeof value){
322
- if(value.startsWith('%') && value.endsWith('%')){
323
- return value;
324
- }
325
- try {
326
- return JSON.parse(value);
327
- } catch(error) {
328
- return value;
329
- }
330
- }
331
- return value;
332
- }
333
-
334
- castToStringType(value)
55
+ initModelMetadata()
335
56
  {
336
- if(null === value || undefined === value){
337
- return null;
338
- }
339
- if('string' === typeof value){
340
- if('null' === value){
341
- return null;
342
- }
343
- return value;
57
+ let metadata = this.metadataLoader.loadModelMetadata(this.tableName());
58
+ if(!metadata){
59
+ return;
344
60
  }
345
- return String(value);
61
+ this.requiredFields = metadata.requiredFields;
62
+ this.optionalFields = metadata.optionalFields;
63
+ this.fieldTypes = metadata.fieldTypes;
64
+ this.fieldDefaults = metadata.fieldDefaults;
65
+ this.idFieldType = metadata.idFieldType;
66
+ this.referenceFields = metadata.referenceFields;
67
+ this.relationMetadata = metadata.relationMetadata;
68
+ this.foreignKeyMappings = metadata.foreignKeyMappings;
69
+ this.jsonFields = metadata.jsonFields;
70
+ this.typeCaster.updateMetadata({
71
+ idFieldType: this.idFieldType,
72
+ jsonFields: this.jsonFields,
73
+ fieldTypes: this.fieldTypes,
74
+ fieldDefaults: this.fieldDefaults,
75
+ requiredFields: this.requiredFields,
76
+ optionalFields: this.optionalFields
77
+ });
78
+ this.relationResolver.updateMetadata({
79
+ relationMetadata: this.relationMetadata
80
+ });
81
+ this.relationAliases = this.relationResolver.buildRelationAliases(this.tableName());
346
82
  }
347
83
 
348
84
  databaseName()
@@ -370,6 +106,11 @@ class PrismaDriver extends BaseDriver
370
106
  return this.rawModel[propertyName] || null;
371
107
  }
372
108
 
109
+ getAllRelations()
110
+ {
111
+ return this.relationResolver.getAllRelations();
112
+ }
113
+
373
114
  prepareData(params)
374
115
  {
375
116
  let data = {};
@@ -379,7 +120,7 @@ class PrismaDriver extends BaseDriver
379
120
  data[key] = params[key];
380
121
  continue;
381
122
  }
382
- let castedValue = this.castValue(params[key], fieldType, key);
123
+ let castedValue = this.typeCaster.castValue(params[key], fieldType, key);
383
124
  if(undefined !== castedValue){
384
125
  data[key] = castedValue;
385
126
  }
@@ -395,9 +136,9 @@ class PrismaDriver extends BaseDriver
395
136
  if(sc.hasOwn(this.foreignKeyMappings, key)){
396
137
  let relationName = this.foreignKeyMappings[key];
397
138
  let value = params[key];
398
- if(!this.isNullOrEmpty(value)){
139
+ if(!this.typeCaster.isNullOrEmpty(value)){
399
140
  relations[relationName] = {
400
- connect: {id: this.castToIdType(value)}
141
+ connect: {id: this.typeCaster.castToIdType(value)}
401
142
  };
402
143
  continue;
403
144
  }
@@ -410,14 +151,14 @@ class PrismaDriver extends BaseDriver
410
151
  }
411
152
  let fieldType = this.fieldTypes[key];
412
153
  if(!fieldType){
413
- if(!this.isNullOrEmpty(params[key])){
154
+ if(!this.typeCaster.isNullOrEmpty(params[key])){
414
155
  data[key] = params[key];
415
156
  }
416
157
  continue;
417
158
  }
418
- let castedValue = this.castValue(params[key], fieldType, key);
159
+ let castedValue = this.typeCaster.castValue(params[key], fieldType, key);
419
160
  if(undefined !== castedValue){
420
- if(this.isJsonFieldType(fieldType) && null === castedValue){
161
+ if(this.typeCaster.isJsonFieldType(fieldType) && null === castedValue){
421
162
  continue;
422
163
  }
423
164
  data[key] = castedValue;
@@ -470,64 +211,69 @@ class PrismaDriver extends BaseDriver
470
211
  {
471
212
  if(sc.isArray(value)){
472
213
  if('id' === key){
473
- return value.map(id => this.castToIdType(id));
214
+ return value.map(id => this.typeCaster.castToIdType(id)).filter(id => null !== id);
474
215
  }
475
216
  let fieldType = this.fieldTypes[key];
476
217
  if(fieldType){
477
- return value.map(val => this.castValue(val, fieldType, key)).filter(val => undefined !== val);
218
+ return value.map(val => this.typeCaster.castValue(val, fieldType, key)).filter(val => undefined !== val);
478
219
  }
479
220
  return value;
480
221
  }
481
222
  if('id' === key){
482
- return this.castToIdType(value);
223
+ return this.typeCaster.castToIdType(value);
483
224
  }
484
225
  let fieldType = this.fieldTypes[key];
485
226
  if(fieldType){
486
- let castedValue = this.castValue(value, fieldType, key);
227
+ let castedValue = this.typeCaster.castValue(value, fieldType, key);
487
228
  return undefined !== castedValue ? castedValue : value;
488
229
  }
489
230
  return value;
490
231
  }
491
232
 
492
- transformRelationResults(result, includeConfig, relationMapping)
233
+ applyOperator(value, operator, fieldName = null)
493
234
  {
494
- if(!result || !includeConfig){
495
- return result;
496
- }
497
- if(sc.isArray(result)){
498
- return result.map(item => this.transformSingleResult(item, includeConfig, relationMapping));
235
+ let upperOperator = operator ? operator.toUpperCase() : '';
236
+ if('LIKE' === upperOperator){
237
+ return this.handleLikeOperator(value, fieldName);
499
238
  }
500
- return this.transformSingleResult(result, includeConfig, relationMapping);
239
+ let operatorsMap = {
240
+ '=': value,
241
+ '!=': {not: value},
242
+ '>': {gt: value},
243
+ '>=': {gte: value},
244
+ '<': {lt: value},
245
+ '<=': {lte: value},
246
+ 'IN': {in: value},
247
+ 'NOT IN': {notIn: value}
248
+ };
249
+ return operatorsMap[upperOperator] || value;
501
250
  }
502
251
 
503
- transformSingleResult(item, includeConfig, relationMapping)
252
+ handleLikeOperator(value, fieldName)
504
253
  {
505
- if(!item || !sc.isObject(item)){
506
- return item;
254
+ let cleanValue = String(value).replace(/%/g, '');
255
+ if(this.typeCaster.isJsonFieldByName(fieldName) || this.isJsonField(fieldName)){
256
+ return this.handleJsonTextSearch(cleanValue);
507
257
  }
508
- let transformed = {...item};
509
- for(let relationName of Object.keys(includeConfig)){
510
- if(!sc.hasOwn(transformed, relationName)){
511
- continue;
512
- }
513
- let relationMeta = this.relationMetadata[relationName];
514
- if(!relationMeta){
515
- continue;
516
- }
517
- let relationValue = transformed[relationName];
518
- if('one' === relationMeta.type && sc.isArray(relationValue)){
519
- relationValue = 0 < relationValue.length ? [...relationValue].shift() : null;
520
- }
521
- if('many' === relationMeta.type && !sc.isArray(relationValue)){
522
- relationValue = relationValue ? [relationValue] : [];
523
- }
524
- let originalName = sc.get(relationMapping, relationName, relationName);
525
- transformed[originalName] = relationValue;
526
- if(originalName !== relationName){
527
- delete transformed[relationName];
258
+ if('id' === fieldName || sc.hasOwn(this.referenceFields, fieldName)){
259
+ let numericValue = Number(cleanValue);
260
+ if(!isNaN(numericValue)){
261
+ return numericValue;
528
262
  }
529
263
  }
530
- return transformed;
264
+ return {contains: cleanValue};
265
+ }
266
+
267
+ handleJsonTextSearch(searchValue)
268
+ {
269
+ return {
270
+ string_contains: searchValue
271
+ };
272
+ }
273
+
274
+ isJsonField(fieldName)
275
+ {
276
+ return this.jsonFields && this.jsonFields.has(fieldName);
531
277
  }
532
278
 
533
279
  async create(params)
@@ -553,26 +299,30 @@ class PrismaDriver extends BaseDriver
553
299
  this.ensureRequiredFields(preparedData);
554
300
  let createData = {data: preparedData};
555
301
  if(0 < relations.length){
556
- let includeData = this.buildIncludeObjectWithMapping(relations);
302
+ let includeData = this.relationResolver.buildIncludeObjectWithMapping(relations);
557
303
  createData.include = includeData.include;
558
304
  for(let relation of relations){
559
- let normalizedRelation = this.normalizeRelationName(relation);
305
+ let normalizedRelation = this.relationResolver.normalizeRelationName(relation);
560
306
  if(!sc.hasOwn(params, relation) && !sc.hasOwn(params, normalizedRelation)){
561
307
  continue;
562
308
  }
563
309
  let relationData = sc.get(params, relation, sc.get(params, normalizedRelation, null));
564
310
  if(sc.isArray(relationData)){
565
311
  createData.data[normalizedRelation] = {
566
- connect: relationData.map(item => ({id: this.castToIdType(item.id)}))
312
+ connect: relationData.map(item => ({id: this.typeCaster.castToIdType(item.id)}))
567
313
  };
568
314
  continue;
569
315
  }
570
316
  createData.data[normalizedRelation] = {
571
- connect: {id: this.castToIdType(relationData.id)}
317
+ connect: {id: this.typeCaster.castToIdType(relationData.id)}
572
318
  };
573
319
  }
574
320
  try {
575
- return this.transformRelationResults(await this.model.create(createData), includeData.include, includeData.mapping);
321
+ return this.relationResolver.transformRelationResults(
322
+ await this.model.create(createData),
323
+ includeData.include,
324
+ includeData.mapping
325
+ );
576
326
  } catch(error) {
577
327
  Logger.error('Create with relations error: '+error.message);
578
328
  return false;
@@ -618,7 +368,7 @@ class PrismaDriver extends BaseDriver
618
368
 
619
369
  async updateById(id, params)
620
370
  {
621
- let castedId = this.castToIdType(id);
371
+ let castedId = this.typeCaster.castToIdType(id);
622
372
  let found = await this.loadById(castedId);
623
373
  if(!found){
624
374
  return false;
@@ -640,7 +390,7 @@ class PrismaDriver extends BaseDriver
640
390
  let preparedData = this.prepareDataWithRelations(params);
641
391
  let existing = false;
642
392
  if(params.id){
643
- let castedId = this.castToIdType(params.id);
393
+ let castedId = this.typeCaster.castToIdType(params.id);
644
394
  existing = await this.loadById(castedId);
645
395
  if(existing){
646
396
  let patch = Object.assign({}, preparedData);
@@ -661,7 +411,7 @@ class PrismaDriver extends BaseDriver
661
411
  if(existing){
662
412
  try {
663
413
  return await this.model.update({
664
- where: {id: this.castToIdType(existing.id)},
414
+ where: {id: this.typeCaster.castToIdType(existing.id)},
665
415
  data: preparedData
666
416
  });
667
417
  } catch(error) {
@@ -688,7 +438,7 @@ class PrismaDriver extends BaseDriver
688
438
 
689
439
  async deleteById(id)
690
440
  {
691
- let castedId = this.castToIdType(id);
441
+ let castedId = this.typeCaster.castToIdType(id);
692
442
  let found = await this.loadById(castedId);
693
443
  if(!found){
694
444
  return false;
@@ -726,7 +476,7 @@ class PrismaDriver extends BaseDriver
726
476
  where: processedFilters
727
477
  };
728
478
  if(0 < relations.length){
729
- query.include = this.buildIncludeObjectWithMapping(relations).include;
479
+ query.include = this.relationResolver.buildIncludeObjectWithMapping(relations).include;
730
480
  }
731
481
  try {
732
482
  return await this.model.count(query);
@@ -811,7 +561,7 @@ class PrismaDriver extends BaseDriver
811
561
 
812
562
  async loadById(id)
813
563
  {
814
- let castedId = this.castToIdType(id);
564
+ let castedId = this.typeCaster.castToIdType(id);
815
565
  try {
816
566
  return await this.model.findUnique({
817
567
  where: {id: castedId}
@@ -826,7 +576,7 @@ class PrismaDriver extends BaseDriver
826
576
  {
827
577
  return this.executeQueryWithRelations(
828
578
  (q) => this.model.findUnique(q),
829
- {where: {id: this.castToIdType(id)}},
579
+ {where: {id: this.typeCaster.castToIdType(id)}},
830
580
  relations,
831
581
  'Load by ID with relations error: ',
832
582
  null
@@ -835,7 +585,10 @@ class PrismaDriver extends BaseDriver
835
585
 
836
586
  async loadByIds(ids)
837
587
  {
838
- let castedIds = ids.map(id => this.castToIdType(id));
588
+ let castedIds = ids.map(id => this.typeCaster.castToIdType(id)).filter(id => null !== id);
589
+ if(0 === castedIds.length){
590
+ return [];
591
+ }
839
592
  try {
840
593
  return await this.model.findMany({
841
594
  where: {
@@ -935,82 +688,23 @@ class PrismaDriver extends BaseDriver
935
688
  return this.processFilters(filter);
936
689
  }
937
690
 
938
- applyOperator(value, operator, fieldName = null)
939
- {
940
- let upperOperator = operator ? operator.toUpperCase() : '';
941
- if('LIKE' === upperOperator){
942
- return this.handleLikeOperator(value, fieldName);
943
- }
944
- let operatorsMap = {
945
- '=': value,
946
- '!=': {not: value},
947
- '>': {gt: value},
948
- '>=': {gte: value},
949
- '<': {lt: value},
950
- '<=': {lte: value},
951
- 'IN': {in: value},
952
- 'NOT IN': {notIn: value}
953
- };
954
- return operatorsMap[upperOperator] || value;
955
- }
956
-
957
- handleLikeOperator(value, fieldName)
958
- {
959
- let cleanValue = String(value).replace(/%/g, '');
960
- if(this.isJsonFieldByName(fieldName) || this.isJsonField(fieldName)){
961
- return this.handleJsonTextSearch(cleanValue);
962
- }
963
- if('id' === fieldName || sc.hasOwn(this.referenceFields, fieldName)){
964
- let numericValue = Number(cleanValue);
965
- if(!isNaN(numericValue)){
966
- return numericValue;
967
- }
968
- }
969
- return {contains: cleanValue};
970
- }
971
-
972
- handleJsonTextSearch(searchValue)
973
- {
974
- return {
975
- string_contains: searchValue
976
- };
977
- }
978
-
979
691
  async executeQueryWithRelations(modelMethod, query, relations, errorMessage, defaultReturn)
980
692
  {
981
693
  if(!sc.isArray(relations) || 0 === relations.length){
982
694
  relations = this.getAllRelations();
983
695
  }
984
- let includeData = this.buildIncludeObjectWithMapping(relations);
696
+ let includeData = this.relationResolver.buildIncludeObjectWithMapping(relations);
985
697
  if(0 < Object.keys(includeData.include).length){
986
698
  query.include = includeData.include;
987
699
  }
988
700
  try {
989
- return this.transformRelationResults(await modelMethod(query), includeData.include, includeData.mapping);
701
+ return this.relationResolver.transformRelationResults(await modelMethod(query), includeData.include, includeData.mapping);
990
702
  } catch(error) {
991
703
  Logger.error(errorMessage+error.message);
992
704
  return defaultReturn;
993
705
  }
994
706
  }
995
707
 
996
- buildIncludeObjectWithMapping(relations)
997
- {
998
- let include = {};
999
- let mapping = {};
1000
- for(let relation of relations){
1001
- let normalizedRelation = this.normalizeRelationName(relation);
1002
- if(!sc.hasOwn(this.relationMetadata, normalizedRelation)){
1003
- //Logger.debug('Relation "'+relation+'" (normalized: "'+normalizedRelation+'") not found in model '+this.tableName());
1004
- continue;
1005
- }
1006
- include[normalizedRelation] = true;
1007
- if(relation !== normalizedRelation){
1008
- mapping[normalizedRelation] = relation;
1009
- }
1010
- }
1011
- return {include, mapping};
1012
- }
1013
-
1014
708
  }
1015
709
 
1016
710
  module.exports.PrismaDriver = PrismaDriver;
@@ -0,0 +1,126 @@
1
+ /**
2
+ *
3
+ * Reldens - PrismaMetadataLoader
4
+ *
5
+ */
6
+
7
+ const { Logger, sc } = require('@reldens/utils');
8
+ const { Prisma } = require('@prisma/client');
9
+
10
+ class PrismaMetadataLoader
11
+ {
12
+
13
+ constructor(props)
14
+ {
15
+ this.prisma = sc.get(props, 'prisma', null);
16
+ this.rawModel = sc.get(props, 'rawModel', null);
17
+ }
18
+
19
+ getDmmf()
20
+ {
21
+ try {
22
+ if(this.prisma._dmmf){
23
+ return this.prisma._dmmf.datamodel;
24
+ }
25
+ if(this.prisma._getDmmf && sc.isFunction(this.prisma._getDmmf)){
26
+ return this.prisma._getDmmf().datamodel;
27
+ }
28
+ if(this.prisma._runtimeDataModel){
29
+ return this.prisma._runtimeDataModel;
30
+ }
31
+ if(Prisma.dmmf && Prisma.dmmf.datamodel){
32
+ return Prisma.dmmf.datamodel;
33
+ }
34
+ return null;
35
+ } catch(error) {
36
+ Logger.warning('Failed to access DMMF: '+error.message);
37
+ return null;
38
+ }
39
+ }
40
+
41
+ loadModelMetadata(tableName)
42
+ {
43
+ let dmmf = this.getDmmf();
44
+ if(!dmmf){
45
+ Logger.warning('Could not access Prisma DMMF metadata');
46
+ return null;
47
+ }
48
+ let modelName = tableName.toLowerCase();
49
+ let modelInfo = dmmf.models?.[modelName] || dmmf.models?.find(m => m.name.toLowerCase() === modelName);
50
+ if(!modelInfo){
51
+ Logger.warning('Could not find model info for: '+modelName);
52
+ return null;
53
+ }
54
+ let metadata = {
55
+ requiredFields: [],
56
+ optionalFields: [],
57
+ fieldTypes: {},
58
+ fieldDefaults: {},
59
+ idFieldType: 'String',
60
+ referenceFields: {},
61
+ relationMetadata: {},
62
+ foreignKeyMappings: {},
63
+ jsonFields: new Set()
64
+ };
65
+ for(let field of modelInfo.fields){
66
+ this.processFieldMetadata(field, metadata);
67
+ }
68
+ if(this.rawModel && this.rawModel.relationTypes){
69
+ for(let relationName of Object.keys(this.rawModel.relationTypes)){
70
+ if(sc.hasOwn(metadata.relationMetadata, relationName)){
71
+ metadata.relationMetadata[relationName].type = this.rawModel.relationTypes[relationName];
72
+ }
73
+ }
74
+ }
75
+ return metadata;
76
+ }
77
+
78
+ processFieldMetadata(field, metadata)
79
+ {
80
+ metadata.fieldTypes[field.name] = field.type;
81
+ if('Json' === field.type){
82
+ metadata.jsonFields.add(field.name);
83
+ }
84
+ if(field.isId){
85
+ metadata.idFieldType = field.type;
86
+ }
87
+ if(field.hasDefaultValue){
88
+ metadata.fieldDefaults[field.name] = field.default;
89
+ }
90
+ if(field.isRequired && !field.hasDefaultValue && !field.isId && 'object' !== field.kind){
91
+ metadata.requiredFields.push(field.name);
92
+ }
93
+ if(field.isOptional || field.hasDefaultValue){
94
+ metadata.optionalFields.push(field.name);
95
+ }
96
+ if(field.kind === 'scalar' && field.isList === false){
97
+ let isNumericType = this.isNumericFieldType(field.type);
98
+ let isReferenceField = field.relationFromFields && 0 < field.relationFromFields.length;
99
+ if(isNumericType || isReferenceField){
100
+ metadata.referenceFields[field.name] = field.type;
101
+ }
102
+ }
103
+ if('object' === field.kind){
104
+ let relationType = field.isList ? 'many' : 'one';
105
+ metadata.relationMetadata[field.name] = {
106
+ type: relationType,
107
+ model: field.type,
108
+ isList: field.isList,
109
+ isOptional: field.isOptional
110
+ };
111
+ }
112
+ if('object' === field.kind && sc.hasOwn(field, 'relationFromFields') && sc.isArray(field.relationFromFields)){
113
+ for(let foreignKeyField of field.relationFromFields){
114
+ metadata.foreignKeyMappings[foreignKeyField] = field.name;
115
+ }
116
+ }
117
+ }
118
+
119
+ isNumericFieldType(fieldType)
120
+ {
121
+ return ('Int' === fieldType || 'BigInt' === fieldType || 'Float' === fieldType || 'Decimal' === fieldType);
122
+ }
123
+
124
+ }
125
+
126
+ module.exports.PrismaMetadataLoader = PrismaMetadataLoader;
@@ -0,0 +1,267 @@
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.relationAliases = sc.get(props, 'relationAliases', {});
16
+ this.metadataLoader = sc.get(props, 'metadataLoader', null);
17
+ }
18
+
19
+ updateMetadata(metadata)
20
+ {
21
+ this.relationMetadata = sc.get(metadata, 'relationMetadata', this.relationMetadata);
22
+ this.relationAliases = sc.get(metadata, 'relationAliases', this.relationAliases);
23
+ }
24
+
25
+ buildRelationAliases(tableName)
26
+ {
27
+ let normalizedTableName = tableName.toLowerCase();
28
+ for(let prismaRelation of Object.keys(this.relationMetadata)){
29
+ let meta = this.relationMetadata[prismaRelation];
30
+ let relatedModel = meta.model.toLowerCase();
31
+ let aliases = this.generateRelationAliases(prismaRelation, relatedModel, normalizedTableName);
32
+ for(let alias of aliases){
33
+ this.relationAliases[alias] = prismaRelation;
34
+ }
35
+ }
36
+ return this.relationAliases;
37
+ }
38
+
39
+ generateRelationAliases(prismaRelation, relatedModel, tableName)
40
+ {
41
+ let aliases = [];
42
+ aliases.push('related_'+relatedModel);
43
+ aliases.push(relatedModel);
44
+ let fkHint = this.extractForeignKeyHint(prismaRelation, relatedModel, tableName);
45
+ if(fkHint){
46
+ aliases.push('related_'+relatedModel+'_'+fkHint);
47
+ aliases.push(relatedModel+'_'+fkHint);
48
+ }
49
+ return aliases;
50
+ }
51
+
52
+ extractForeignKeyHint(prismaRelation, relatedModel, tableName)
53
+ {
54
+ let toSuffix = 'To'+tableName;
55
+ if(!prismaRelation.toLowerCase().endsWith(toSuffix.toLowerCase())){
56
+ return null;
57
+ }
58
+ let withoutToSuffix = prismaRelation.slice(0, -toSuffix.length);
59
+ let prefix = relatedModel+'_';
60
+ if(withoutToSuffix.toLowerCase().startsWith(prefix.toLowerCase())){
61
+ withoutToSuffix = withoutToSuffix.slice(prefix.length);
62
+ if(withoutToSuffix.toLowerCase().startsWith(prefix.toLowerCase())){
63
+ withoutToSuffix = withoutToSuffix.slice(prefix.length);
64
+ }
65
+ }
66
+ if(withoutToSuffix.endsWith('_id')){
67
+ return withoutToSuffix.slice(0, -3);
68
+ }
69
+ return withoutToSuffix;
70
+ }
71
+
72
+ getAllRelations()
73
+ {
74
+ return Object.keys(this.relationMetadata || {});
75
+ }
76
+
77
+ normalizeRelationName(relationName)
78
+ {
79
+ if(sc.hasOwn(this.relationMetadata, relationName)){
80
+ return relationName;
81
+ }
82
+ if(sc.hasOwn(this.relationAliases, relationName)){
83
+ return this.relationAliases[relationName];
84
+ }
85
+ if(relationName.startsWith('related_')){
86
+ let withoutPrefix = relationName.substring(8);
87
+ if(sc.hasOwn(this.relationMetadata, withoutPrefix)){
88
+ return withoutPrefix;
89
+ }
90
+ if(sc.hasOwn(this.relationAliases, withoutPrefix)){
91
+ return this.relationAliases[withoutPrefix];
92
+ }
93
+ }
94
+ let availableRelations = Object.keys(this.relationMetadata);
95
+ for(let availableRelation of availableRelations){
96
+ if(relationName.endsWith(availableRelation)){
97
+ return availableRelation;
98
+ }
99
+ if(availableRelation.endsWith(relationName.replace('related_', ''))){
100
+ return availableRelation;
101
+ }
102
+ }
103
+ return relationName;
104
+ }
105
+
106
+ buildIncludeObjectWithMapping(relations)
107
+ {
108
+ let include = {};
109
+ let mapping = {};
110
+ for(let relation of relations){
111
+ if(-1 !== relation.indexOf('.')){
112
+ this.buildNestedInclude(relation, include, mapping);
113
+ continue;
114
+ }
115
+ let normalizedRelation = this.normalizeRelationName(relation);
116
+ if(!sc.hasOwn(this.relationMetadata, normalizedRelation)){
117
+ continue;
118
+ }
119
+ include[normalizedRelation] = true;
120
+ if(relation !== normalizedRelation){
121
+ mapping[normalizedRelation] = relation;
122
+ }
123
+ }
124
+ return {include, mapping};
125
+ }
126
+
127
+ buildNestedInclude(relationPath, include, mapping)
128
+ {
129
+ let parts = relationPath.split('.');
130
+ let rootPart = parts.shift();
131
+ let normalizedRoot = this.normalizeRelationName(rootPart);
132
+ if(!sc.hasOwn(this.relationMetadata, normalizedRoot)){
133
+ return;
134
+ }
135
+ if(!sc.hasOwn(include, normalizedRoot) || true === include[normalizedRoot]){
136
+ include[normalizedRoot] = {include: {}};
137
+ }
138
+ if(rootPart !== normalizedRoot){
139
+ mapping[normalizedRoot] = rootPart;
140
+ }
141
+ if(0 === parts.length){
142
+ return;
143
+ }
144
+ let nestedPath = parts.join('.');
145
+ let relatedModel = this.relationMetadata[normalizedRoot].model;
146
+ this.buildNestedIncludeForModel(nestedPath, include[normalizedRoot].include, mapping, relatedModel);
147
+ }
148
+
149
+ buildNestedIncludeForModel(relationPath, include, mapping, modelName)
150
+ {
151
+ if(!this.metadataLoader){
152
+ return;
153
+ }
154
+ let dmmf = this.metadataLoader.getDmmf();
155
+ if(!dmmf){
156
+ return;
157
+ }
158
+ let modelInfo = dmmf.models?.[modelName.toLowerCase()]
159
+ || dmmf.models?.find(m => m.name.toLowerCase() === modelName.toLowerCase());
160
+ if(!modelInfo){
161
+ return;
162
+ }
163
+ let relationFields = {};
164
+ let tableName = modelName.toLowerCase();
165
+ for(let field of modelInfo.fields){
166
+ if('object' === field.kind){
167
+ relationFields[field.name] = {
168
+ model: field.type,
169
+ isList: field.isList
170
+ };
171
+ }
172
+ }
173
+ let parts = relationPath.split('.');
174
+ let currentPart = parts.shift();
175
+ let normalizedPart = this.normalizeNestedRelation(currentPart, relationFields, tableName);
176
+ if(!normalizedPart){
177
+ return;
178
+ }
179
+ if(0 === parts.length){
180
+ include[normalizedPart] = true;
181
+ if(currentPart !== normalizedPart){
182
+ mapping[normalizedPart] = currentPart;
183
+ }
184
+ return;
185
+ }
186
+ if(!sc.hasOwn(include, normalizedPart) || true === include[normalizedPart]){
187
+ include[normalizedPart] = {include: {}};
188
+ }
189
+ if(currentPart !== normalizedPart){
190
+ mapping[normalizedPart] = currentPart;
191
+ }
192
+ let nestedPath = parts.join('.');
193
+ let nestedModel = relationFields[normalizedPart].model;
194
+ this.buildNestedIncludeForModel(nestedPath, include[normalizedPart].include, mapping, nestedModel);
195
+ }
196
+
197
+ normalizeNestedRelation(relationName, relationFields, tableName)
198
+ {
199
+ if(sc.hasOwn(relationFields, relationName)){
200
+ return relationName;
201
+ }
202
+ let cleanName = relationName.replace('related_', '');
203
+ if(sc.hasOwn(relationFields, cleanName)){
204
+ return cleanName;
205
+ }
206
+ for(let fieldName of Object.keys(relationFields)){
207
+ let relatedModel = relationFields[fieldName].model.toLowerCase();
208
+ let fkHint = this.extractForeignKeyHint(fieldName, relatedModel, tableName);
209
+ if(fkHint){
210
+ let possibleNames = [
211
+ 'related_'+relatedModel+'_'+fkHint,
212
+ relatedModel+'_'+fkHint,
213
+ 'related_'+relatedModel,
214
+ relatedModel
215
+ ];
216
+ if(-1 !== possibleNames.indexOf(cleanName) || -1 !== possibleNames.indexOf(relationName)){
217
+ return fieldName;
218
+ }
219
+ }
220
+ }
221
+ return null;
222
+ }
223
+
224
+ transformRelationResults(result, includeConfig, relationMapping)
225
+ {
226
+ if(!result || !includeConfig){
227
+ return result;
228
+ }
229
+ if(sc.isArray(result)){
230
+ return result.map(item => this.transformSingleResult(item, includeConfig, relationMapping));
231
+ }
232
+ return this.transformSingleResult(result, includeConfig, relationMapping);
233
+ }
234
+
235
+ transformSingleResult(item, includeConfig, relationMapping)
236
+ {
237
+ if(!item || !sc.isObject(item)){
238
+ return item;
239
+ }
240
+ let transformed = {...item};
241
+ for(let relationName of Object.keys(includeConfig)){
242
+ if(!sc.hasOwn(transformed, relationName)){
243
+ continue;
244
+ }
245
+ let relationMeta = this.relationMetadata[relationName];
246
+ if(!relationMeta){
247
+ continue;
248
+ }
249
+ let relationValue = transformed[relationName];
250
+ if('one' === relationMeta.type && sc.isArray(relationValue)){
251
+ relationValue = 0 < relationValue.length ? [...relationValue].shift() : null;
252
+ }
253
+ if('many' === relationMeta.type && !sc.isArray(relationValue)){
254
+ relationValue = relationValue ? [relationValue] : [];
255
+ }
256
+ let originalName = sc.get(relationMapping, relationName, relationName);
257
+ transformed[originalName] = relationValue;
258
+ if(originalName !== relationName){
259
+ delete transformed[relationName];
260
+ }
261
+ }
262
+ return transformed;
263
+ }
264
+
265
+ }
266
+
267
+ module.exports.PrismaRelationResolver = PrismaRelationResolver;
@@ -0,0 +1,231 @@
1
+ /**
2
+ *
3
+ * Reldens - PrismaTypeCaster
4
+ *
5
+ */
6
+
7
+ const { Logger, sc } = require('@reldens/utils');
8
+ const { Prisma } = require('@prisma/client');
9
+
10
+ class PrismaTypeCaster
11
+ {
12
+
13
+ constructor(props)
14
+ {
15
+ this.idFieldType = sc.get(props, 'idFieldType', 'String');
16
+ this.jsonFields = sc.get(props, 'jsonFields', new Set());
17
+ this.fieldTypes = sc.get(props, 'fieldTypes', {});
18
+ this.fieldDefaults = sc.get(props, 'fieldDefaults', {});
19
+ this.requiredFields = sc.get(props, 'requiredFields', []);
20
+ this.optionalFields = sc.get(props, 'optionalFields', []);
21
+ this.rawModel = sc.get(props, 'rawModel', false);
22
+ }
23
+
24
+ updateMetadata(metadata)
25
+ {
26
+ this.idFieldType = sc.get(metadata, 'idFieldType', this.idFieldType);
27
+ this.jsonFields = sc.get(metadata, 'jsonFields', this.jsonFields);
28
+ this.fieldTypes = sc.get(metadata, 'fieldTypes', this.fieldTypes);
29
+ this.fieldDefaults = sc.get(metadata, 'fieldDefaults', this.fieldDefaults);
30
+ this.requiredFields = sc.get(metadata, 'requiredFields', this.requiredFields);
31
+ this.optionalFields = sc.get(metadata, 'optionalFields', this.optionalFields);
32
+ }
33
+
34
+ isNumericFieldType(fieldType)
35
+ {
36
+ return ('Int' === fieldType || 'BigInt' === fieldType || 'Float' === fieldType || 'Decimal' === fieldType);
37
+ }
38
+
39
+ isBooleanFieldType(fieldType)
40
+ {
41
+ return 'Boolean' === fieldType;
42
+ }
43
+
44
+ isDateTimeFieldType(fieldType)
45
+ {
46
+ return 'DateTime' === fieldType || 'Date' === fieldType;
47
+ }
48
+
49
+ isJsonFieldType(fieldType)
50
+ {
51
+ return 'Json' === fieldType;
52
+ }
53
+
54
+ isJsonFieldByName(fieldName)
55
+ {
56
+ if(this.jsonFields.has(fieldName)){
57
+ return true;
58
+ }
59
+ if(!this.rawModel || !this.rawModel.properties){
60
+ return false;
61
+ }
62
+ return 'json' === sc.get(this.rawModel.properties[fieldName], 'dbType', '');
63
+ }
64
+
65
+ isStringFieldType(fieldType)
66
+ {
67
+ return 'String' === fieldType;
68
+ }
69
+
70
+ isNullOrEmpty(value)
71
+ {
72
+ return null === value || undefined === value || '' === value;
73
+ }
74
+
75
+ castValue(value, fieldType, fieldName = null)
76
+ {
77
+ if('id' === fieldName){
78
+ return this.castToIdType(value);
79
+ }
80
+ if(this.isNullOrEmpty(value)){
81
+ if(sc.hasOwn(this.fieldDefaults, fieldName)){
82
+ return undefined;
83
+ }
84
+ if(-1 !== this.optionalFields.indexOf(fieldName)){
85
+ return null;
86
+ }
87
+ if(-1 !== this.requiredFields.indexOf(fieldName)){
88
+ Logger.warning('Required field '+fieldName+' cannot be null or empty');
89
+ return undefined;
90
+ }
91
+ return null;
92
+ }
93
+ if(this.isJsonFieldByName(fieldName)){
94
+ return this.castToJsonType(value);
95
+ }
96
+ if(this.isNumericFieldType(fieldType)){
97
+ return this.castToNumericType(value, fieldType);
98
+ }
99
+ if(this.isBooleanFieldType(fieldType)){
100
+ return this.castToBooleanType(value);
101
+ }
102
+ if(this.isDateTimeFieldType(fieldType)){
103
+ return this.castToDateType(value);
104
+ }
105
+ if(this.isStringFieldType(fieldType)){
106
+ return this.castToStringType(value);
107
+ }
108
+ return value;
109
+ }
110
+
111
+ castToIdType(value)
112
+ {
113
+ if(this.isNullOrEmpty(value)){
114
+ return value;
115
+ }
116
+ if(this.isNumericFieldType(this.idFieldType)){
117
+ let numeric = Number(value);
118
+ if(!isNaN(numeric)){
119
+ return ('Int' === this.idFieldType || 'BigInt' === this.idFieldType) ? Math.floor(numeric) : numeric;
120
+ }
121
+ Logger.warning('Cannot cast non-numeric value "'+value+'" to ID type '+this.idFieldType);
122
+ return null;
123
+ }
124
+ return String(value);
125
+ }
126
+
127
+ castToNumericType(value, fieldType)
128
+ {
129
+ if(this.isNullOrEmpty(value)){
130
+ return null;
131
+ }
132
+ let numericValue = Number(value);
133
+ if(isNaN(numericValue)){
134
+ Logger.warning('Cannot convert value to numeric: '+value);
135
+ return null;
136
+ }
137
+ if('Int' === fieldType || 'BigInt' === fieldType){
138
+ return Math.floor(numericValue);
139
+ }
140
+ return numericValue;
141
+ }
142
+
143
+ castToBooleanType(value)
144
+ {
145
+ if(this.isNullOrEmpty(value)){
146
+ return null;
147
+ }
148
+ if('boolean' === typeof value){
149
+ return value;
150
+ }
151
+ if('number' === typeof value){
152
+ return 0 !== value;
153
+ }
154
+ if('string' === typeof value){
155
+ let lowerValue = value.toLowerCase();
156
+ if('true' === lowerValue || '1' === lowerValue || 'yes' === lowerValue){
157
+ return true;
158
+ }
159
+ if('false' === lowerValue || '0' === lowerValue || 'no' === lowerValue){
160
+ return false;
161
+ }
162
+ }
163
+ return Boolean(value);
164
+ }
165
+
166
+ castToDateType(dateValue)
167
+ {
168
+ if(dateValue instanceof Date){
169
+ return dateValue;
170
+ }
171
+ if(this.isNullOrEmpty(dateValue)){
172
+ return null;
173
+ }
174
+ let dateString = String(dateValue);
175
+ if(dateString.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/)){
176
+ return new Date(dateString.replace(' ', 'T')+'Z');
177
+ }
178
+ if(dateString.match(/^\d{4}-\d{2}-\d{2}$/)){
179
+ return new Date(dateString+'T00:00:00Z');
180
+ }
181
+ try {
182
+ let parsedDate = new Date(dateString);
183
+ if(isNaN(parsedDate.getTime())){
184
+ Logger.warning('Invalid date value: '+dateString);
185
+ return null;
186
+ }
187
+ return parsedDate;
188
+ } catch(error) {
189
+ Logger.warning('Failed to convert date value: '+dateString);
190
+ return null;
191
+ }
192
+ }
193
+
194
+ castToJsonType(value)
195
+ {
196
+ if(this.isNullOrEmpty(value)){
197
+ return Prisma.DbNull;
198
+ }
199
+ if('object' === typeof value){
200
+ return value;
201
+ }
202
+ if('string' === typeof value){
203
+ if(value.startsWith('%') && value.endsWith('%')){
204
+ return value;
205
+ }
206
+ try {
207
+ return JSON.parse(value);
208
+ } catch(error) {
209
+ return value;
210
+ }
211
+ }
212
+ return value;
213
+ }
214
+
215
+ castToStringType(value)
216
+ {
217
+ if(null === value || undefined === value){
218
+ return null;
219
+ }
220
+ if('string' === typeof value){
221
+ if('null' === value){
222
+ return null;
223
+ }
224
+ return value;
225
+ }
226
+ return String(value);
227
+ }
228
+
229
+ }
230
+
231
+ module.exports.PrismaTypeCaster = PrismaTypeCaster;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/storage",
3
3
  "scope": "@reldens",
4
- "version": "0.80.0",
4
+ "version": "0.81.0",
5
5
  "description": "Reldens - Storage",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",