@reldens/storage 0.44.0 → 0.45.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.
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  const { FileHandler } = require('@reldens/server-utils');
8
- const { ErrorManager, Logger, sc } = require('@reldens/utils');
8
+ const { Logger, sc } = require('@reldens/utils');
9
9
  const { ObjectionJsDataServer } = require('./objection-js/objection-js-data-server');
10
10
  const { MikroOrmDataServer } = require('./mikro-orm/mikro-orm-data-server');
11
11
  const { PrismaDataServer } = require('./prisma/prisma-data-server');
@@ -61,7 +61,7 @@ class EntitiesGenerator
61
61
  }
62
62
  let entityClassName = sc.capitalizedCamelCase(tableName)+'Entity';
63
63
  let titleProperty = this.determineTitleProperty(tableData.columns);
64
- for(let columnName in tableData.columns){
64
+ for(let columnName of Object.keys(tableData.columns)){
65
65
  tableData.columns[columnName].tableName = tableName;
66
66
  }
67
67
  let propertiesConfig = this.generatePropertiesConfig(tableData.columns, titleProperty);
@@ -76,16 +76,19 @@ class EntitiesGenerator
76
76
  }
77
77
  let editPropertiesRemoval = this.getEditPropertiesRemoval(fieldsToRemoveFromEdit);
78
78
  let listPropertiesDeclaration = this.getListPropertiesDeclaration(tableData.columns);
79
+ let showPropertiesDeclaration = this.getShowPropertiesDeclaration(tableData.columns);
79
80
  let needsSc = listPropertiesDeclaration.includes('sc.removeFromArray')
80
- || editPropertiesRemoval.includes('sc.removeFromArray');
81
+ || editPropertiesRemoval.includes('sc.removeFromArray')
82
+ || showPropertiesDeclaration.includes('sc.removeFromArray');
81
83
  let scRequire = needsSc ? '\nconst { sc } = require(\'@reldens/utils\');' : '';
82
84
  let replacements = {
83
85
  entityClassName,
84
86
  propertiesConfig,
85
87
  titlePropertyDeclaration,
86
88
  titlePropertyReturn,
87
- listPropertiesDeclaration,
89
+ showPropertiesDeclaration,
88
90
  editPropertiesRemoval,
91
+ listPropertiesDeclaration,
89
92
  scRequire
90
93
  };
91
94
  let entityContent = this.applyReplacements(entityTemplateContent, replacements);
@@ -105,14 +108,30 @@ class EntitiesGenerator
105
108
  return true;
106
109
  }
107
110
 
111
+ getFieldsToRemoveFromList(columns)
112
+ {
113
+ let fieldsToRemove = [];
114
+ for(let columnName of Object.keys(columns)){
115
+ let column = columns[columnName];
116
+ let type = column.type.toLowerCase();
117
+ if(['text', 'json', 'tinytext', 'mediumtext', 'longtext'].includes(type)){
118
+ fieldsToRemove.push(columnName);
119
+ }
120
+ if('password' === columnName.toLowerCase()){
121
+ fieldsToRemove.push(columnName);
122
+ }
123
+ }
124
+ return fieldsToRemove;
125
+ }
126
+
108
127
  getEditPropertiesRemoval(fieldsToRemove)
109
128
  {
110
129
  if(1 === fieldsToRemove.length){
111
- return 'let editProperties = [...showProperties];\n editProperties.splice(editProperties.indexOf(\''+
130
+ return 'let editProperties = [...propertiesKeys];\n editProperties.splice(editProperties.indexOf(\''+
112
131
  fieldsToRemove[0]
113
132
  +'\'), 1);';
114
133
  }
115
- return 'let editProperties = sc.removeFromArray([...showProperties], [\''+fieldsToRemove.join('\', \'')+'\']);';
134
+ return 'let editProperties = sc.removeFromArray([...propertiesKeys], [\''+fieldsToRemove.join('\', \'')+'\']);';
116
135
  }
117
136
 
118
137
  determineTitleProperty(columns)
@@ -135,7 +154,7 @@ class EntitiesGenerator
135
154
  generatePropertiesConfig(columns, titleProperty)
136
155
  {
137
156
  let propertiesConfig = [];
138
- for(let columnName in columns){
157
+ for(let columnName of Object.keys(columns)){
139
158
  let column = columns[columnName];
140
159
  let propertyKey = titleProperty && columnName === titleProperty ? '[titleProperty]' : columnName;
141
160
  if('id' === columnName){
@@ -247,31 +266,53 @@ class EntitiesGenerator
247
266
  return refTableName;
248
267
  }
249
268
 
250
- getListPropertiesDeclaration(columns)
269
+ getShowPropertiesDeclaration(columns)
251
270
  {
252
- let fieldsToRemove = this.getTextAndJsonFields(columns);
253
- if(0 === fieldsToRemove.length){
254
- return 'let listProperties = showProperties;';
271
+ let passwordFields = this.getPasswordFields(columns);
272
+ if(0 === passwordFields.length){
273
+ return 'let showProperties = propertiesKeys;';
255
274
  }
256
- if(1 === fieldsToRemove.length){
257
- return 'let listProperties = [...showProperties];\n listProperties.splice(listProperties.indexOf(\''+
258
- fieldsToRemove[0]+
275
+ if(1 === passwordFields.length){
276
+ return 'let showProperties = [...propertiesKeys];\n showProperties.splice(showProperties.indexOf(\''+
277
+ passwordFields[0]+
259
278
  '\'), 1);';
260
279
  }
261
- return 'let listProperties = sc.removeFromArray([...showProperties], [\''+fieldsToRemove.join('\', \'')+'\']);';
280
+ return 'let showProperties = sc.removeFromArray([...propertiesKeys], [\''+passwordFields.join('\', \'')+'\']);';
262
281
  }
263
282
 
264
- getTextAndJsonFields(columns)
283
+ getPasswordFields(columns)
265
284
  {
266
- let fieldsToRemove = [];
267
- for(let columnName in columns){
268
- let column = columns[columnName];
269
- let type = column.type.toLowerCase();
270
- if(['text', 'json', 'tinytext', 'mediumtext', 'longtext'].includes(type)){
271
- fieldsToRemove.push(columnName);
285
+ let passwordFields = [];
286
+ for(let columnName of Object.keys(columns)){
287
+ if('password' === columnName.toLowerCase()){
288
+ passwordFields.push(columnName);
272
289
  }
273
290
  }
274
- return fieldsToRemove;
291
+ return passwordFields;
292
+ }
293
+
294
+ getEditPropertiesRemoval(fieldsToRemove)
295
+ {
296
+ if(1 === fieldsToRemove.length){
297
+ return 'let editProperties = [...propertiesKeys];\n editProperties.splice(editProperties.indexOf(\''+
298
+ fieldsToRemove[0]
299
+ +'\'), 1);';
300
+ }
301
+ return 'let editProperties = sc.removeFromArray([...propertiesKeys], [\''+fieldsToRemove.join('\', \'')+'\']);';
302
+ }
303
+
304
+ getListPropertiesDeclaration(columns)
305
+ {
306
+ let fieldsToRemove = this.getFieldsToRemoveFromList(columns);
307
+ if(0 === fieldsToRemove.length){
308
+ return 'let listProperties = propertiesKeys;';
309
+ }
310
+ if(1 === fieldsToRemove.length){
311
+ return 'let listProperties = [...propertiesKeys];\n listProperties.splice(listProperties.indexOf(\''+
312
+ fieldsToRemove[0]+
313
+ '\'), 1);';
314
+ }
315
+ return 'let listProperties = sc.removeFromArray([...propertiesKeys], [\''+fieldsToRemove.join('\', \'')+'\']);';
275
316
  }
276
317
 
277
318
  async generateModelFile(tableName, tableData, driverKey)
@@ -324,7 +365,7 @@ class EntitiesGenerator
324
365
  {
325
366
  if('mikro-orm' === driverKey){
326
367
  let entityProps = [];
327
- for(let columnName in columns){
368
+ for(let columnName of Object.keys(columns)){
328
369
  let column = columns[columnName];
329
370
  let isPrimary = 'PRI' === column.key;
330
371
  let type = TypeMapper.mapDbTypeToJsType(column.type);
@@ -342,7 +383,7 @@ class EntitiesGenerator
342
383
  }
343
384
  if('prisma' === driverKey){
344
385
  let prismaProps = [];
345
- for(let columnName in columns){
386
+ for(let columnName of Object.keys(columns)){
346
387
  let column = columns[columnName];
347
388
  let prismaType = TypeMapper.mapDbTypeToPrismaType(column.type);
348
389
  let isPrimary = 'PRI' === column.key;
@@ -391,7 +432,7 @@ class EntitiesGenerator
391
432
  getRequireStatements()
392
433
  {
393
434
  let requireStatements = [];
394
- for(let tableName in this.generatedEntities){
435
+ for(let tableName of Object.keys(this.generatedEntities)){
395
436
  let entity = this.generatedEntities[tableName];
396
437
  let nameWithoutExtension = entity.entityFileName.replace('.js', '');
397
438
  requireStatements.push(
@@ -404,13 +445,9 @@ class EntitiesGenerator
404
445
  getEntitiesConfigExport()
405
446
  {
406
447
  let entitiesConfigExport = [];
407
- for(let tableName in this.generatedEntities){
448
+ for(let tableName of Object.keys(this.generatedEntities)){
408
449
  let entity = this.generatedEntities[tableName];
409
- let camelCaseKey = sc.camelCase(tableName);
410
- let capitalizedCase = sc.capitalizedCamelCase(tableName);
411
- entitiesConfigExport.push(
412
- camelCaseKey+': '+entity.entityClassName+'.propertiesConfig({parentItemLabel: \''+capitalizedCase+'\'})'
413
- );
450
+ entitiesConfigExport.push(sc.camelCase(tableName)+': '+entity.entityClassName+'.propertiesConfig()');
414
451
  }
415
452
  return entitiesConfigExport.join(',\n ');
416
453
  }
@@ -440,7 +477,7 @@ class EntitiesGenerator
440
477
  getTranslationLabels()
441
478
  {
442
479
  let labels = [];
443
- for(let tableName in this.generatedEntities){
480
+ for(let tableName of Object.keys(this.generatedEntities)){
444
481
  let humanReadable = tableName.split('_').map(word => word.charAt(0).toUpperCase()+word.slice(1)).join(' ');
445
482
  labels.push('\''+tableName+'\': \''+humanReadable+'\'');
446
483
  }
@@ -477,7 +514,7 @@ class EntitiesGenerator
477
514
  getRegisteredModels(driverKey)
478
515
  {
479
516
  let registeredModels = [];
480
- for(let tableName in this.generatedEntities){
517
+ for(let tableName of Object.keys(this.generatedEntities)){
481
518
  let entity = this.generatedEntities[tableName];
482
519
  if(driverKey !== entity.driverKey){
483
520
  continue;
@@ -492,7 +529,7 @@ class EntitiesGenerator
492
529
  getRegisteredEntitiesObject(driverKey)
493
530
  {
494
531
  let registeredEntitiesObject = [];
495
- for(let tableName in this.generatedEntities){
532
+ for(let tableName of Object.keys(this.generatedEntities)){
496
533
  let entity = this.generatedEntities[tableName];
497
534
  if(driverKey !== entity.driverKey){
498
535
  continue;
@@ -511,7 +548,6 @@ class EntitiesGenerator
511
548
  return result;
512
549
  }
513
550
 
514
-
515
551
  async generate()
516
552
  {
517
553
  if(!this.server){
@@ -540,7 +576,7 @@ class EntitiesGenerator
540
576
  Logger.critical('Failed to fetch driver key.');
541
577
  return false;
542
578
  }
543
- for(let tableName in tables){
579
+ for(let tableName of Object.keys(tables)){
544
580
  let tableData = tables[tableName];
545
581
  await this.generateEntityFile(tableName, tableData);
546
582
  await this.generateModelFile(tableName, tableData, driverKey);
@@ -14,7 +14,8 @@ class {{entityClassName}} extends EntityProperties
14
14
  let properties = {
15
15
  {{propertiesConfig}}
16
16
  };
17
- let showProperties = Object.keys(properties);
17
+ let propertiesKeys = Object.keys(properties);
18
+ {{showPropertiesDeclaration}}
18
19
  {{editPropertiesRemoval}}
19
20
  {{listPropertiesDeclaration}}
20
21
  return {
@@ -64,7 +64,7 @@ class MySQLTablesProvider
64
64
  ' AND REFERENCED_TABLE_NAME IS NOT NULL;';
65
65
 
66
66
  let foreignKeysResult = await server.rawQuery(foreignKeysQuery);
67
- if(!sc.isArray(foreignKeysResult) || 0 < foreignKeysResult.length){
67
+ if(!sc.isArray(foreignKeysResult) || 0 === foreignKeysResult.length){
68
68
  return tables;
69
69
  }
70
70
  for(let row of foreignKeysResult){
@@ -7,6 +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
11
  const { Logger } = require('@reldens/utils');
11
12
 
12
13
  class PrismaDataServer extends BaseDataServer
@@ -24,7 +25,6 @@ class PrismaDataServer extends BaseDataServer
24
25
  return this.initialized;
25
26
  }
26
27
  try {
27
- let { PrismaClient } = require('@prisma/client');
28
28
  this.prisma = new PrismaClient({
29
29
  log: this.debug ? ['query', 'info', 'warn', 'error'] : ['error']
30
30
  });
@@ -93,6 +93,7 @@ class PrismaDataServer extends BaseDataServer
93
93
  }
94
94
  }
95
95
  if(0 === results.length){
96
+ Logger.error('Statement results length is zero.', results);
96
97
  return false;
97
98
  }
98
99
  return 1 === results.length ? results[0] : results;
@@ -104,10 +105,19 @@ class PrismaDataServer extends BaseDataServer
104
105
 
105
106
  async executeStatement(statement)
106
107
  {
107
- if(statement.trim().toUpperCase().startsWith('SELECT')){
108
+ let trimmedStatement = statement.trim().toUpperCase();
109
+ if(trimmedStatement.startsWith('SELECT')){
108
110
  return await this.prisma.$queryRawUnsafe(statement);
109
111
  }
110
- return await this.prisma.$executeRawUnsafe(statement);
112
+ let result = await this.prisma.$executeRawUnsafe(statement);
113
+ if(
114
+ trimmedStatement.startsWith('CREATE')
115
+ || trimmedStatement.startsWith('ALTER')
116
+ || trimmedStatement.startsWith('DROP')
117
+ ){
118
+ return { affectedRows: result };
119
+ }
120
+ return result;
111
121
  }
112
122
 
113
123
  splitSqlStatements(sqlContent)
@@ -6,6 +6,7 @@
6
6
 
7
7
  const { BaseDriver } = require('../base-driver');
8
8
  const { ErrorManager, Logger, sc } = require('@reldens/utils');
9
+ const { Prisma } = require('@prisma/client');
9
10
 
10
11
  class PrismaDriver extends BaseDriver
11
12
  {
@@ -14,30 +15,35 @@ class PrismaDriver extends BaseDriver
14
15
  {
15
16
  super(props);
16
17
  if(!props.prisma){
17
- ErrorManager.error('Missing Prisma client on Prisma driver.');
18
+ Logger.critical('Missing Prisma client on Prisma driver.');
19
+ return false;
18
20
  }
19
21
  if(!props.model){
20
- ErrorManager.error('Missing Prisma model on Prisma driver.');
22
+ Logger.critical('Missing Prisma model on Prisma driver.');
23
+ return false;
21
24
  }
22
25
  if(!props.server){
23
- ErrorManager.error('Missing Server Driver on Prisma driver.');
26
+ Logger.critical('Missing Server Driver on Prisma driver.');
27
+ return false;
24
28
  }
25
29
  this.prisma = props.prisma;
26
30
  this.model = props.model;
27
31
  this.server = props.server;
28
32
  this.requiredFields = [];
29
33
  this.fieldTypes = {};
34
+ this.idFieldType = 'String';
30
35
  this.initFieldMetadata();
31
36
  }
32
37
 
33
38
  initFieldMetadata()
34
39
  {
35
40
  try {
36
- let dmmf = this.prisma._baseDmmf;
41
+ let dmmf = this.prisma._runtimeDataModel || Prisma.dmmf?.datamodel;
37
42
  if(!dmmf){
38
43
  return;
39
44
  }
40
- let modelInfo = dmmf.modelMap[this.tableName().toLowerCase()];
45
+ let modelName = this.tableName().toLowerCase();
46
+ let modelInfo = dmmf.models?.[modelName] || dmmf.models?.find(m => m.name.toLowerCase() === modelName);
41
47
  if(!modelInfo){
42
48
  return;
43
49
  }
@@ -46,12 +52,29 @@ class PrismaDriver extends BaseDriver
46
52
  this.requiredFields.push(field.name);
47
53
  }
48
54
  this.fieldTypes[field.name] = field.type;
55
+ if(field.isId){
56
+ this.idFieldType = field.type;
57
+ }
49
58
  }
50
59
  } catch(error) {
51
60
  Logger.warning('Could not initialize field metadata: '+error.message);
52
61
  }
53
62
  }
54
63
 
64
+ castIdValue(id)
65
+ {
66
+ if(null === id || undefined === id){
67
+ return id;
68
+ }
69
+ if('Int' === this.idFieldType || 'BigInt' === this.idFieldType){
70
+ let numericId = Number(id);
71
+ if(!isNaN(numericId)){
72
+ return numericId;
73
+ }
74
+ }
75
+ return String(id);
76
+ }
77
+
55
78
  databaseName()
56
79
  {
57
80
  return this.config.database || '';
@@ -69,7 +92,7 @@ class PrismaDriver extends BaseDriver
69
92
 
70
93
  tableName()
71
94
  {
72
- return this.model._name || '';
95
+ return this.model.name || '';
73
96
  }
74
97
 
75
98
  property(propertyName)
@@ -80,14 +103,46 @@ class PrismaDriver extends BaseDriver
80
103
  prepareData(params)
81
104
  {
82
105
  let data = {...params};
83
- for(let key in data){
106
+ for(let key of Object.keys(data)){
84
107
  if('status' === key && 'number' === typeof data[key]){
85
108
  data[key] = String(data[key]);
86
109
  }
110
+ if(this.isDateTimeField(key)){
111
+ data[key] = this.convertToDate(data[key], key);
112
+ }
87
113
  }
88
114
  return data;
89
115
  }
90
116
 
117
+ isDateTimeField(fieldName)
118
+ {
119
+ let fieldType = this.fieldTypes[fieldName];
120
+ return fieldType === 'DateTime' || fieldType === 'Date';
121
+ }
122
+
123
+ convertToDate(dateValue, fieldName)
124
+ {
125
+ if(dateValue instanceof Date){
126
+ return dateValue;
127
+ }
128
+ if(!dateValue || '' === dateValue){
129
+ return null;
130
+ }
131
+ let dateString = String(dateValue);
132
+ if(dateString.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/)){
133
+ return new Date(dateString.replace(' ', 'T') + 'Z');
134
+ }
135
+ if(dateString.match(/^\d{4}-\d{2}-\d{2}$/)){
136
+ return new Date(dateString + 'T00:00:00Z');
137
+ }
138
+ try {
139
+ return new Date(dateString);
140
+ } catch(error) {
141
+ Logger.warning('Failed to convert date value: '+dateString);
142
+ return dateValue;
143
+ }
144
+ }
145
+
91
146
  ensureRequiredFields(data)
92
147
  {
93
148
  let missingFields = [];
@@ -130,12 +185,12 @@ class PrismaDriver extends BaseDriver
130
185
  let relationData = params[relation];
131
186
  if(sc.isArray(relationData)){
132
187
  createData.data[relation] = {
133
- connect: relationData.map(item => ({id: item.id}))
188
+ connect: relationData.map(item => ({id: this.castIdValue(item.id)}))
134
189
  };
135
190
  continue;
136
191
  }
137
192
  createData.data[relation] = {
138
- connect: {id: relationData.id}
193
+ connect: {id: this.castIdValue(relationData.id)}
139
194
  };
140
195
  }
141
196
  }
@@ -178,13 +233,14 @@ class PrismaDriver extends BaseDriver
178
233
  async updateById(id, params)
179
234
  {
180
235
  try {
181
- let found = await this.loadById(id);
236
+ let castedId = this.castIdValue(id);
237
+ let found = await this.loadById(castedId);
182
238
  if(!found){
183
239
  return false;
184
240
  }
185
241
  let preparedData = this.prepareData(params);
186
242
  return await this.model.update({
187
- where: {id},
243
+ where: {id: castedId},
188
244
  data: preparedData
189
245
  });
190
246
  } catch(error) {
@@ -199,12 +255,13 @@ class PrismaDriver extends BaseDriver
199
255
  let preparedData = this.prepareData(params);
200
256
  let existing = false;
201
257
  if(params.id){
202
- existing = await this.loadById(params.id);
258
+ let castedId = this.castIdValue(params.id);
259
+ existing = await this.loadById(castedId);
203
260
  if(existing){
204
261
  let patch = Object.assign({}, preparedData);
205
262
  delete patch.id;
206
263
  return await this.model.update({
207
- where: {id: params.id},
264
+ where: {id: castedId},
208
265
  data: patch
209
266
  });
210
267
  }
@@ -213,7 +270,7 @@ class PrismaDriver extends BaseDriver
213
270
  let existing = await this.loadOne(filters);
214
271
  if(existing){
215
272
  return await this.model.update({
216
- where: {id: existing.id},
273
+ where: {id: this.castIdValue(existing.id)},
217
274
  data: preparedData
218
275
  });
219
276
  }
@@ -240,12 +297,13 @@ class PrismaDriver extends BaseDriver
240
297
  async deleteById(id)
241
298
  {
242
299
  try {
243
- let found = await this.loadById(id);
300
+ let castedId = this.castIdValue(id);
301
+ let found = await this.loadById(castedId);
244
302
  if(!found){
245
303
  return false;
246
304
  }
247
305
  return await this.model.delete({
248
- where: {id}
306
+ where: {id: castedId}
249
307
  });
250
308
  } catch(error) {
251
309
  Logger.error('Delete by ID error: '+error.message);
@@ -364,8 +422,9 @@ class PrismaDriver extends BaseDriver
364
422
  async loadById(id)
365
423
  {
366
424
  try {
425
+ let castedId = this.castIdValue(id);
367
426
  return await this.model.findUnique({
368
- where: {id}
427
+ where: {id: castedId}
369
428
  });
370
429
  } catch(error) {
371
430
  Logger.error('Load by ID error: '+error.message);
@@ -376,8 +435,9 @@ class PrismaDriver extends BaseDriver
376
435
  async loadByIdWithRelations(id, relations)
377
436
  {
378
437
  try {
438
+ let castedId = this.castIdValue(id);
379
439
  let query = {
380
- where: {id}
440
+ where: {id: castedId}
381
441
  };
382
442
  if(sc.isArray(relations) && 0 < relations.length){
383
443
  query.include = this.buildIncludeObject(relations);
@@ -392,9 +452,10 @@ class PrismaDriver extends BaseDriver
392
452
  async loadByIds(ids)
393
453
  {
394
454
  try {
455
+ let castedIds = ids.map(id => this.castIdValue(id));
395
456
  return await this.model.findMany({
396
457
  where: {
397
- id: {in: ids}
458
+ id: {in: castedIds}
398
459
  }
399
460
  });
400
461
  } catch(error) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/storage",
3
3
  "scope": "@reldens",
4
- "version": "0.44.0",
4
+ "version": "0.45.0",
5
5
  "description": "Reldens - Storage",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",