@reldens/storage 0.51.0 → 0.53.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.
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const { PrismaSchemaGenerator } = require('../lib/prisma/prisma-schema-generator');
|
|
10
|
+
const { FileHandler } = require('@reldens/server-utils');
|
|
10
11
|
const { Logger } = require('@reldens/utils');
|
|
11
12
|
|
|
12
13
|
let args = process.argv.slice(2);
|
|
@@ -70,7 +71,10 @@ if(!connectionData.config.user || !connectionData.config.database){
|
|
|
70
71
|
process.exit();
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
let generator = new PrismaSchemaGenerator({
|
|
74
|
+
let generator = new PrismaSchemaGenerator({
|
|
75
|
+
...connectionData,
|
|
76
|
+
prismaSchemaPath: FileHandler.joinPaths(projectPath, 'prisma')
|
|
77
|
+
});
|
|
74
78
|
|
|
75
79
|
generator.generate().then((success) => {
|
|
76
80
|
if(!success){
|
|
@@ -47,6 +47,8 @@ class EntitiesGenerator
|
|
|
47
47
|
this.isOverride = sc.get(props, 'isOverride', false);
|
|
48
48
|
this.generatedEntities = {};
|
|
49
49
|
this.existingEntities = {};
|
|
50
|
+
this.existingEntityFields = {};
|
|
51
|
+
this.existingModels = {};
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
async generateEntityFile(tableName, tableData)
|
|
@@ -57,7 +59,7 @@ class EntitiesGenerator
|
|
|
57
59
|
return false;
|
|
58
60
|
}
|
|
59
61
|
let entityTemplateContent = FileHandler.fetchFileContents(entityTemplatePath);
|
|
60
|
-
if(!entityTemplateContent)
|
|
62
|
+
if(!entityTemplateContent){
|
|
61
63
|
Logger.critical('Failed to read entity template file: '+entityTemplatePath);
|
|
62
64
|
return false;
|
|
63
65
|
}
|
|
@@ -158,7 +160,7 @@ class EntitiesGenerator
|
|
|
158
160
|
let propertiesConfig = [];
|
|
159
161
|
for(let columnName of Object.keys(columns)){
|
|
160
162
|
let column = columns[columnName];
|
|
161
|
-
let propertyKey = titleProperty &&
|
|
163
|
+
let propertyKey = titleProperty && titleProperty === columnName ? '[titleProperty]' : columnName;
|
|
162
164
|
if('id' === columnName){
|
|
163
165
|
propertiesConfig.push(columnName+': {}');
|
|
164
166
|
continue;
|
|
@@ -195,7 +197,7 @@ class EntitiesGenerator
|
|
|
195
197
|
props.push('type: \'datetime\'');
|
|
196
198
|
return;
|
|
197
199
|
}
|
|
198
|
-
if('text' === type || 'longtext' === type || 'mediumtext' === type || 'tinytext' === type){
|
|
200
|
+
if('text' === type || 'longtext' === type || 'mediumtext' === type || 'tinytext' === type || 'json' === type){
|
|
199
201
|
props.push('type: \'textarea\'');
|
|
200
202
|
return;
|
|
201
203
|
}
|
|
@@ -332,7 +334,7 @@ class EntitiesGenerator
|
|
|
332
334
|
let entityPropertiesDefinition = this.getEntityPropertiesDefinition(tableData.columns, driverKey);
|
|
333
335
|
let replacements = {
|
|
334
336
|
modelClassName,
|
|
335
|
-
tableName:
|
|
337
|
+
tableName: 'prisma' === driverKey ? tableName.toLowerCase() : tableName,
|
|
336
338
|
modelPropertiesList,
|
|
337
339
|
modelPropertiesConstructor,
|
|
338
340
|
modelRelations,
|
|
@@ -341,10 +343,7 @@ class EntitiesGenerator
|
|
|
341
343
|
let modelContent = this.applyReplacements(modelTemplateContent, replacements);
|
|
342
344
|
let fileName = sc.kebabCase(tableName)+'-model.js';
|
|
343
345
|
let driverFolder = FileHandler.joinPaths(this.modelsFolder, driverKey);
|
|
344
|
-
|
|
345
|
-
Logger.critical('Failed to create driver folder.');
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
346
|
+
FileHandler.createFolder(driverFolder);
|
|
348
347
|
let filePath = FileHandler.joinPaths(driverFolder, fileName);
|
|
349
348
|
if(!FileHandler.writeFile(filePath, modelContent)){
|
|
350
349
|
Logger.critical('Failed to write model file: '+fileName);
|
|
@@ -410,32 +409,166 @@ class EntitiesGenerator
|
|
|
410
409
|
}
|
|
411
410
|
let tableName = file.replace('-entity.js', '').replace(/-/g, '_');
|
|
412
411
|
this.existingEntities[tableName] = {entityFileName: file, tableName};
|
|
412
|
+
this.detectExistingEntityFields(tableName, file);
|
|
413
413
|
}
|
|
414
|
+
this.detectExistingModels();
|
|
414
415
|
Logger.info('Detected '+Object.keys(this.existingEntities).length+' existing entities.');
|
|
415
416
|
}
|
|
416
417
|
|
|
417
|
-
|
|
418
|
+
detectExistingModels()
|
|
418
419
|
{
|
|
419
|
-
if(this.
|
|
420
|
-
return
|
|
420
|
+
if(!FileHandler.exists(this.modelsFolder)){
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
let driverFolders = FileHandler.fetchSubFoldersList(this.modelsFolder);
|
|
424
|
+
for(let driverKey of driverFolders){
|
|
425
|
+
let driverFolder = FileHandler.joinPaths(this.modelsFolder, driverKey);
|
|
426
|
+
let modelFiles = FileHandler.getFilesInFolder(driverFolder, ['.js']);
|
|
427
|
+
for(let file of modelFiles){
|
|
428
|
+
if(!file.endsWith('-model.js')){
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
let tableName = file.replace('-model.js', '').replace(/-/g, '_');
|
|
432
|
+
if(!this.existingModels[tableName]){
|
|
433
|
+
this.existingModels[tableName] = {};
|
|
434
|
+
}
|
|
435
|
+
this.existingModels[tableName][driverKey] = {
|
|
436
|
+
modelFileName: file,
|
|
437
|
+
driverKey: driverKey
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
detectExistingEntityFields(tableName, fileName)
|
|
444
|
+
{
|
|
445
|
+
let filePath = FileHandler.joinPaths(this.entitiesFolder, fileName);
|
|
446
|
+
let fileContent = FileHandler.readFile(filePath);
|
|
447
|
+
if(!fileContent){
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
let fieldsMatch = fileContent.match(/properties\s*=\s*\{([^}]+)\}/s);
|
|
451
|
+
if(!fieldsMatch){
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
let propertiesContent = fieldsMatch[1];
|
|
455
|
+
let fieldMatches = propertiesContent.match(/(\w+):\s*\{[^}]*\}/g);
|
|
456
|
+
if(!fieldMatches){
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
this.existingEntityFields[tableName] = [];
|
|
460
|
+
for(let fieldMatch of fieldMatches){
|
|
461
|
+
let fieldName = fieldMatch.split(':')[0].trim();
|
|
462
|
+
this.existingEntityFields[tableName].push(fieldName);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
entityNeedsUpdate(tableName, tableData)
|
|
467
|
+
{
|
|
468
|
+
if(!this.existingEntityFields[tableName]){
|
|
469
|
+
return true;
|
|
421
470
|
}
|
|
471
|
+
let existingFields = this.existingEntityFields[tableName];
|
|
472
|
+
let databaseFields = Object.keys(tableData.columns);
|
|
473
|
+
for(let dbField of databaseFields){
|
|
474
|
+
if(!existingFields.includes(dbField)){
|
|
475
|
+
return true;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
for(let existingField of existingFields){
|
|
479
|
+
if('id' === existingField){
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
if(!databaseFields.includes(existingField)){
|
|
483
|
+
return true;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
filterTablesToGenerate(tables, driverKey)
|
|
490
|
+
{
|
|
422
491
|
let filteredTables = {};
|
|
423
492
|
let newTablesCount = 0;
|
|
493
|
+
let updateTablesCount = 0;
|
|
494
|
+
let missingConfigCount = 0;
|
|
495
|
+
let missingModelCount = 0;
|
|
424
496
|
for(let tableName of Object.keys(tables)){
|
|
497
|
+
let needsGeneration = false;
|
|
498
|
+
let reasons = [];
|
|
425
499
|
if(!this.existingEntities[tableName]){
|
|
426
|
-
|
|
500
|
+
needsGeneration = true;
|
|
427
501
|
newTablesCount++;
|
|
502
|
+
reasons.push('new entity');
|
|
503
|
+
}
|
|
504
|
+
if(this.existingEntities[tableName]){
|
|
505
|
+
if(this.entityNeedsUpdate(tableName, tables[tableName])){
|
|
506
|
+
needsGeneration = true;
|
|
507
|
+
updateTablesCount++;
|
|
508
|
+
reasons.push('fields mismatch');
|
|
509
|
+
}
|
|
510
|
+
if(!this.entityExistsInConfig(tableName)){
|
|
511
|
+
needsGeneration = true;
|
|
512
|
+
missingConfigCount++;
|
|
513
|
+
reasons.push('missing from config');
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
if(!this.existingModels[tableName] || !this.existingModels[tableName][driverKey]){
|
|
517
|
+
needsGeneration = true;
|
|
518
|
+
missingModelCount++;
|
|
519
|
+
reasons.push('missing model');
|
|
520
|
+
}
|
|
521
|
+
if(this.existingModels[tableName] && this.existingModels[tableName][driverKey]){
|
|
522
|
+
if(!this.modelExistsInRegistered(tableName, driverKey)){
|
|
523
|
+
needsGeneration = true;
|
|
524
|
+
reasons.push('missing from registered models');
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
if(needsGeneration || this.isOverride){
|
|
528
|
+
filteredTables[tableName] = tables[tableName];
|
|
529
|
+
if(0 < reasons.length){
|
|
530
|
+
Logger.info('Entity '+tableName+' needs generation: '+reasons.join(', '));
|
|
531
|
+
}
|
|
428
532
|
}
|
|
429
533
|
}
|
|
430
|
-
if(0 === newTablesCount){
|
|
431
|
-
Logger.info('No new tables found
|
|
534
|
+
if(0 === newTablesCount && 0 === updateTablesCount && 0 === missingConfigCount && 0 === missingModelCount){
|
|
535
|
+
Logger.info('No new tables found and all entities are properly configured.');
|
|
536
|
+
return filteredTables;
|
|
432
537
|
}
|
|
433
538
|
if(0 < newTablesCount){
|
|
434
539
|
Logger.info('Found '+newTablesCount+' new tables to generate entities for.');
|
|
435
540
|
}
|
|
541
|
+
if(0 < updateTablesCount){
|
|
542
|
+
Logger.info('Found '+updateTablesCount+' existing entities that need updates.');
|
|
543
|
+
}
|
|
544
|
+
if(0 < missingConfigCount){
|
|
545
|
+
Logger.info('Found '+missingConfigCount+' entities missing from config.');
|
|
546
|
+
}
|
|
547
|
+
if(0 < missingModelCount){
|
|
548
|
+
Logger.info('Found '+missingModelCount+' entities missing models.');
|
|
549
|
+
}
|
|
436
550
|
return filteredTables;
|
|
437
551
|
}
|
|
438
552
|
|
|
553
|
+
entityExistsInConfig(tableName)
|
|
554
|
+
{
|
|
555
|
+
if(!FileHandler.exists(this.entitiesConfigPath)){
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
let configContent = FileHandler.readFile(this.entitiesConfigPath);
|
|
559
|
+
return configContent && configContent.includes(sc.camelCase(tableName)+':');
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
modelExistsInRegistered(tableName, driverKey)
|
|
563
|
+
{
|
|
564
|
+
let registeredPath = FileHandler.joinPaths(this.modelsFolder, driverKey, 'registered-models-'+driverKey+'.js');
|
|
565
|
+
if(!FileHandler.exists(registeredPath)){
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
let registeredContent = FileHandler.readFile(registeredPath);
|
|
569
|
+
return registeredContent && registeredContent.includes(sc.camelCase(tableName)+':');
|
|
570
|
+
}
|
|
571
|
+
|
|
439
572
|
generateEntitiesConfigFile()
|
|
440
573
|
{
|
|
441
574
|
if(this.isOverride){
|
|
@@ -447,7 +580,7 @@ class EntitiesGenerator
|
|
|
447
580
|
regenerateEntitiesConfigFile()
|
|
448
581
|
{
|
|
449
582
|
let configTemplateContent = FileHandler.fetchFileContents(this.templates['entities-config']);
|
|
450
|
-
if(!configTemplateContent)
|
|
583
|
+
if(!configTemplateContent){
|
|
451
584
|
Logger.critical('Failed to read entities config template file: '+this.templates['entities-config']);
|
|
452
585
|
return false;
|
|
453
586
|
}
|
|
@@ -492,37 +625,66 @@ class EntitiesGenerator
|
|
|
492
625
|
if(!existingContent.includes(requireStatement)){
|
|
493
626
|
newRequires.push(requireStatement);
|
|
494
627
|
}
|
|
495
|
-
if(!
|
|
628
|
+
if(!this.entityExistsInConfig(tableName)){
|
|
496
629
|
newConfigs.push(configEntry);
|
|
497
630
|
}
|
|
498
631
|
}
|
|
499
632
|
if(0 === newRequires.length && 0 === newConfigs.length){
|
|
500
633
|
return true;
|
|
501
634
|
}
|
|
635
|
+
let normalizedContent = existingContent.replace(/\s+/g, ' ');
|
|
636
|
+
let entitiesConfigPattern = /let\s+entitiesConfig\s*=\s*\{/;
|
|
637
|
+
let entitiesConfigMatch = normalizedContent.match(entitiesConfigPattern);
|
|
638
|
+
if(!entitiesConfigMatch){
|
|
639
|
+
Logger.error('Could not find entitiesConfig variable in config file.');
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
let entitiesConfigStart = existingContent.indexOf(entitiesConfigMatch[0]);
|
|
643
|
+
let searchStart = entitiesConfigStart + entitiesConfigMatch[0].length;
|
|
644
|
+
let braceCount = 1;
|
|
645
|
+
let entitiesConfigEnd = -1;
|
|
646
|
+
for(let i = searchStart; i < existingContent.length; i++){
|
|
647
|
+
if('{' === existingContent[i]){
|
|
648
|
+
braceCount++;
|
|
649
|
+
}
|
|
650
|
+
if('}' === existingContent[i]){
|
|
651
|
+
braceCount--;
|
|
652
|
+
if(0 === braceCount){
|
|
653
|
+
entitiesConfigEnd = i;
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if(-1 === entitiesConfigEnd){
|
|
659
|
+
Logger.error('Could not find end of entitiesConfig object.');
|
|
660
|
+
return false;
|
|
661
|
+
}
|
|
502
662
|
let firstRequirePosition = existingContent.indexOf('const {');
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
Logger.error('Could not find insertion points in config file.');
|
|
663
|
+
if(-1 === firstRequirePosition){
|
|
664
|
+
Logger.error('Could not find require statements in config file.');
|
|
506
665
|
return false;
|
|
507
666
|
}
|
|
508
667
|
let beforeFirstRequire = existingContent.substring(0, firstRequirePosition);
|
|
509
668
|
let afterFirstRequire = existingContent.substring(firstRequirePosition, entitiesConfigEnd);
|
|
669
|
+
let afterConfig = existingContent.substring(entitiesConfigEnd);
|
|
510
670
|
let updatedContent = beforeFirstRequire;
|
|
511
671
|
if(0 < newRequires.length){
|
|
512
672
|
updatedContent += newRequires.join('\n') + '\n';
|
|
513
673
|
}
|
|
514
674
|
updatedContent += afterFirstRequire;
|
|
515
675
|
if(0 < newConfigs.length){
|
|
516
|
-
|
|
676
|
+
let trimmedContent = afterFirstRequire.trimEnd();
|
|
677
|
+
if(!trimmedContent.endsWith(',')){
|
|
517
678
|
updatedContent = updatedContent.trimEnd() + ',';
|
|
518
679
|
}
|
|
519
|
-
updatedContent += '\n ' + newConfigs.join(',\n ')
|
|
680
|
+
updatedContent += '\n ' + newConfigs.join(',\n ');
|
|
520
681
|
}
|
|
521
|
-
updatedContent +=
|
|
682
|
+
updatedContent += afterConfig;
|
|
522
683
|
if(!FileHandler.writeFile(this.entitiesConfigPath, updatedContent)){
|
|
523
684
|
Logger.error('Failed to append to entities config file: '+this.entitiesConfigPath);
|
|
524
685
|
return false;
|
|
525
686
|
}
|
|
687
|
+
Logger.info('Updated entities config file with '+newConfigs.length+' new entities.');
|
|
526
688
|
return true;
|
|
527
689
|
}
|
|
528
690
|
|
|
@@ -611,22 +773,45 @@ class EntitiesGenerator
|
|
|
611
773
|
if(0 === newTranslations.length){
|
|
612
774
|
return true;
|
|
613
775
|
}
|
|
614
|
-
let
|
|
615
|
-
|
|
616
|
-
|
|
776
|
+
let normalizedContent = existingContent.replace(/\s+/g, ' ');
|
|
777
|
+
let labelsPattern = /labels\s*:\s*\{/;
|
|
778
|
+
let labelsMatch = normalizedContent.match(labelsPattern);
|
|
779
|
+
if(!labelsMatch){
|
|
780
|
+
Logger.error('Could not find labels object in translations file.');
|
|
781
|
+
return false;
|
|
782
|
+
}
|
|
783
|
+
let labelsStart = existingContent.indexOf(labelsMatch[0]);
|
|
784
|
+
let searchStart = labelsStart + labelsMatch[0].length;
|
|
785
|
+
let braceCount = 1;
|
|
786
|
+
let labelsEnd = -1;
|
|
787
|
+
for(let i = searchStart; i < existingContent.length; i++){
|
|
788
|
+
if('{' === existingContent[i]){
|
|
789
|
+
braceCount++;
|
|
790
|
+
}
|
|
791
|
+
if('}' === existingContent[i]){
|
|
792
|
+
braceCount--;
|
|
793
|
+
if(0 === braceCount){
|
|
794
|
+
labelsEnd = i;
|
|
795
|
+
break;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if(-1 === labelsEnd){
|
|
800
|
+
Logger.error('Could not find end of labels object.');
|
|
617
801
|
return false;
|
|
618
802
|
}
|
|
619
803
|
let beforeLabelsEnd = existingContent.substring(0, labelsEnd);
|
|
620
|
-
|
|
621
|
-
|
|
804
|
+
let contentBeforeEnd = beforeLabelsEnd.trimEnd();
|
|
805
|
+
let lastChar = contentBeforeEnd[contentBeforeEnd.length - 1];
|
|
806
|
+
if('{' !== lastChar && ',' !== lastChar){
|
|
807
|
+
beforeLabelsEnd = contentBeforeEnd + ',';
|
|
622
808
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
beforeLabelsEnd + '\n ' + newTranslations.join(',\n ') + existingContent.substring(labelsEnd)
|
|
626
|
-
)){
|
|
809
|
+
let updatedContent = beforeLabelsEnd + '\n ' + newTranslations.join(',\n ') + existingContent.substring(labelsEnd);
|
|
810
|
+
if(!FileHandler.writeFile(this.entitiesTranslationsPath, updatedContent)){
|
|
627
811
|
Logger.error('Failed to append to entities translations file: '+this.entitiesTranslationsPath);
|
|
628
812
|
return false;
|
|
629
813
|
}
|
|
814
|
+
Logger.info('Updated translations file with '+newTranslations.length+' new labels.');
|
|
630
815
|
return true;
|
|
631
816
|
}
|
|
632
817
|
|
|
@@ -677,7 +862,9 @@ class EntitiesGenerator
|
|
|
677
862
|
for(let tableName of Object.keys(allEntities)){
|
|
678
863
|
let entity = allEntities[tableName];
|
|
679
864
|
if(entity.driverKey && driverKey !== entity.driverKey){
|
|
680
|
-
|
|
865
|
+
if(!this.existingModels[tableName] || !this.existingModels[tableName][driverKey]){
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
681
868
|
}
|
|
682
869
|
let modelClassName = entity.modelClassName || sc.capitalizedCamelCase(tableName)+'Model';
|
|
683
870
|
let modelFileName = entity.modelFileName || sc.kebabCase(tableName)+'-model.js';
|
|
@@ -694,7 +881,9 @@ class EntitiesGenerator
|
|
|
694
881
|
for(let tableName of Object.keys(allEntities)){
|
|
695
882
|
let entity = allEntities[tableName];
|
|
696
883
|
if(entity.driverKey && driverKey !== entity.driverKey){
|
|
697
|
-
|
|
884
|
+
if(!this.existingModels[tableName] || !this.existingModels[tableName][driverKey]){
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
698
887
|
}
|
|
699
888
|
let modelClassName = entity.modelClassName || sc.capitalizedCamelCase(tableName)+'Model';
|
|
700
889
|
registeredEntitiesObject.push(sc.camelCase(tableName)+': '+modelClassName);
|
|
@@ -731,10 +920,6 @@ class EntitiesGenerator
|
|
|
731
920
|
return false;
|
|
732
921
|
}
|
|
733
922
|
this.detectExistingEntities();
|
|
734
|
-
let tablesToGenerate = this.filterTablesToGenerate(tables);
|
|
735
|
-
if(0 === Object.keys(tablesToGenerate).length && !this.isOverride){
|
|
736
|
-
return true;
|
|
737
|
-
}
|
|
738
923
|
let driverKey = sc.get(
|
|
739
924
|
this.connectionData,
|
|
740
925
|
'driver',
|
|
@@ -744,6 +929,10 @@ class EntitiesGenerator
|
|
|
744
929
|
Logger.critical('Failed to fetch driver key.');
|
|
745
930
|
return false;
|
|
746
931
|
}
|
|
932
|
+
let tablesToGenerate = this.filterTablesToGenerate(tables, driverKey);
|
|
933
|
+
if(0 === Object.keys(tablesToGenerate).length && !this.isOverride){
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
747
936
|
for(let tableName of Object.keys(tablesToGenerate)){
|
|
748
937
|
let tableData = tablesToGenerate[tableName];
|
|
749
938
|
await this.generateEntityFile(tableName, tableData);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { BaseDriver } = require('../base-driver');
|
|
8
|
-
const {
|
|
8
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
9
9
|
const { Prisma } = require('@prisma/client');
|
|
10
10
|
|
|
11
11
|
class PrismaDriver extends BaseDriver
|
|
@@ -32,6 +32,7 @@ class PrismaDriver extends BaseDriver
|
|
|
32
32
|
this.requiredFields = [];
|
|
33
33
|
this.fieldTypes = {};
|
|
34
34
|
this.idFieldType = 'String';
|
|
35
|
+
this.referenceFields = {};
|
|
35
36
|
this.initFieldMetadata();
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -55,6 +56,13 @@ class PrismaDriver extends BaseDriver
|
|
|
55
56
|
if(field.isId){
|
|
56
57
|
this.idFieldType = field.type;
|
|
57
58
|
}
|
|
59
|
+
if(field.kind === 'scalar' && field.isList === false){
|
|
60
|
+
let isNumericType = ('Int' === field.type || 'BigInt' === field.type || 'Float' === field.type || 'Decimal' === field.type);
|
|
61
|
+
let isReferenceField = field.relationFromFields && 0 < field.relationFromFields.length;
|
|
62
|
+
if(isNumericType || isReferenceField){
|
|
63
|
+
this.referenceFields[field.name] = field.type;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
58
66
|
}
|
|
59
67
|
} catch(error) {
|
|
60
68
|
Logger.warning('Could not initialize field metadata: '+error.message);
|
|
@@ -75,6 +83,26 @@ class PrismaDriver extends BaseDriver
|
|
|
75
83
|
return String(id);
|
|
76
84
|
}
|
|
77
85
|
|
|
86
|
+
castNumericValue(value, fieldType)
|
|
87
|
+
{
|
|
88
|
+
if(null === value || undefined === value || '' === value){
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
if('Int' === fieldType || 'BigInt' === fieldType){
|
|
92
|
+
let numericValue = Number(value);
|
|
93
|
+
if(!isNaN(numericValue)){
|
|
94
|
+
return Math.floor(numericValue);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if('Float' === fieldType || 'Decimal' === fieldType){
|
|
98
|
+
let numericValue = Number(value);
|
|
99
|
+
if(!isNaN(numericValue)){
|
|
100
|
+
return numericValue;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
|
|
78
106
|
databaseName()
|
|
79
107
|
{
|
|
80
108
|
return this.config.database || '';
|
|
@@ -109,6 +137,10 @@ class PrismaDriver extends BaseDriver
|
|
|
109
137
|
}
|
|
110
138
|
if(this.isDateTimeField(key)){
|
|
111
139
|
data[key] = this.convertToDate(data[key], key);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if(sc.hasOwn(this.referenceFields, key)){
|
|
143
|
+
data[key] = this.castNumericValue(data[key], this.referenceFields[key]);
|
|
112
144
|
}
|
|
113
145
|
}
|
|
114
146
|
return data;
|
|
@@ -173,6 +205,12 @@ class PrismaDriver extends BaseDriver
|
|
|
173
205
|
if('id' === key && !sc.isArray(operatorValue)){
|
|
174
206
|
operatorValue = this.castIdValue(operatorValue);
|
|
175
207
|
}
|
|
208
|
+
if(sc.hasOwn(this.referenceFields, key) && sc.isArray(operatorValue)){
|
|
209
|
+
operatorValue = operatorValue.map(val => this.castNumericValue(val, this.referenceFields[key]));
|
|
210
|
+
}
|
|
211
|
+
if(sc.hasOwn(this.referenceFields, key) && !sc.isArray(operatorValue)){
|
|
212
|
+
operatorValue = this.castNumericValue(operatorValue, this.referenceFields[key]);
|
|
213
|
+
}
|
|
176
214
|
processedFilters[key] = this.applyOperator(operatorValue, value.operator);
|
|
177
215
|
continue;
|
|
178
216
|
}
|
|
@@ -188,6 +226,14 @@ class PrismaDriver extends BaseDriver
|
|
|
188
226
|
processedFilters[key] = this.castIdValue(value);
|
|
189
227
|
continue;
|
|
190
228
|
}
|
|
229
|
+
if(sc.hasOwn(this.referenceFields, key) && sc.isArray(value)){
|
|
230
|
+
processedFilters[key] = value.map(val => this.castNumericValue(val, this.referenceFields[key]));
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if(sc.hasOwn(this.referenceFields, key)){
|
|
234
|
+
processedFilters[key] = this.castNumericValue(value, this.referenceFields[key]);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
191
237
|
processedFilters[key] = value;
|
|
192
238
|
}
|
|
193
239
|
return processedFilters;
|
|
@@ -590,11 +636,18 @@ class PrismaDriver extends BaseDriver
|
|
|
590
636
|
createSingleFilter(field, fieldValue, operator = null)
|
|
591
637
|
{
|
|
592
638
|
let filter = {};
|
|
639
|
+
let processedValue = fieldValue;
|
|
640
|
+
if(sc.hasOwn(this.referenceFields, field)){
|
|
641
|
+
processedValue = this.castNumericValue(fieldValue, this.referenceFields[field]);
|
|
642
|
+
}
|
|
643
|
+
if('id' === field){
|
|
644
|
+
processedValue = this.castIdValue(fieldValue);
|
|
645
|
+
}
|
|
593
646
|
if(null === operator){
|
|
594
|
-
filter[field] =
|
|
647
|
+
filter[field] = processedValue;
|
|
595
648
|
return filter;
|
|
596
649
|
}
|
|
597
|
-
filter[field] = this.applyOperator(
|
|
650
|
+
filter[field] = this.applyOperator(processedValue, operator);
|
|
598
651
|
return filter;
|
|
599
652
|
}
|
|
600
653
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const { execSync } = require('child_process');
|
|
7
|
+
const { execSync, spawn } = require('child_process');
|
|
8
8
|
const { FileHandler } = require('@reldens/server-utils');
|
|
9
9
|
const { Logger, sc } = require('@reldens/utils');
|
|
10
10
|
|
|
@@ -26,24 +26,61 @@ class PrismaSchemaGenerator
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
async generate()
|
|
29
|
+
{
|
|
30
|
+
FileHandler.createFolder(this.prismaSchemaPath);
|
|
31
|
+
this.generateSchemaFile();
|
|
32
|
+
Logger.info('Running prisma introspect "npx prisma db pull"...');
|
|
33
|
+
execSync('npx prisma db pull', { stdio: 'inherit' });
|
|
34
|
+
let generateCommand = 'npx prisma generate';
|
|
35
|
+
if(this.dataProxy){
|
|
36
|
+
generateCommand += ' --data-proxy';
|
|
37
|
+
}
|
|
38
|
+
Logger.info('Running prisma generate "'+generateCommand+'"...');
|
|
39
|
+
await this.executeWithFallback(generateCommand);
|
|
40
|
+
await this.waitForSchemaGeneration();
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async executeWithFallback(command)
|
|
29
45
|
{
|
|
30
46
|
try {
|
|
31
|
-
|
|
32
|
-
this.generateSchemaFile();
|
|
33
|
-
Logger.info('Running prisma introspect "npx prisma db pull"...');
|
|
34
|
-
execSync('npx prisma db pull', { stdio: 'inherit' });
|
|
35
|
-
let generateCommand = 'npx prisma generate';
|
|
36
|
-
if(this.dataProxy){
|
|
37
|
-
generateCommand += ' --data-proxy';
|
|
38
|
-
}
|
|
39
|
-
Logger.info('Running prisma generate "'+generateCommand+'"...');
|
|
40
|
-
execSync(generateCommand, { stdio: 'inherit' });
|
|
41
|
-
await this.waitForSchemaGeneration();
|
|
42
|
-
return true;
|
|
47
|
+
execSync(command, { stdio: 'inherit' });
|
|
43
48
|
} catch(error) {
|
|
44
|
-
|
|
49
|
+
if(this.isWindowsPermissionError(error)){
|
|
50
|
+
Logger.warning('Windows permission error detected, retrying with detached process...');
|
|
51
|
+
await this.executeDetached(command);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
Logger.critical('Command failed: ' + error.message);
|
|
55
|
+
return false;
|
|
45
56
|
}
|
|
46
|
-
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
isWindowsPermissionError(error)
|
|
60
|
+
{
|
|
61
|
+
return error.message
|
|
62
|
+
&& error.message.includes('EPERM')
|
|
63
|
+
&& error.message.includes('rename')
|
|
64
|
+
&& error.message.includes('query_engine-windows.dll.node');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
executeDetached(command)
|
|
68
|
+
{
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
let child = spawn(command, [], {
|
|
71
|
+
stdio: 'inherit',
|
|
72
|
+
shell: true,
|
|
73
|
+
detached: true
|
|
74
|
+
});
|
|
75
|
+
child.on('close', (code) => {
|
|
76
|
+
if(0 === code){
|
|
77
|
+
resolve();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
reject(new Error('Command failed with exit code ' + code));
|
|
81
|
+
});
|
|
82
|
+
child.on('error', reject);
|
|
83
|
+
});
|
|
47
84
|
}
|
|
48
85
|
|
|
49
86
|
async waitForSchemaGeneration()
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reldens/storage",
|
|
3
3
|
"scope": "@reldens",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.53.0",
|
|
5
5
|
"description": "Reldens - Storage",
|
|
6
6
|
"author": "Damian A. Pastorini",
|
|
7
7
|
"license": "MIT",
|
|
@@ -45,13 +45,13 @@
|
|
|
45
45
|
"@mikro-orm/core": "^6.4.16",
|
|
46
46
|
"@mikro-orm/mongodb": "^6.4.16",
|
|
47
47
|
"@mikro-orm/mysql": "^6.4.16",
|
|
48
|
-
"@prisma/client": "^6.
|
|
49
|
-
"@reldens/server-utils": "^0.
|
|
48
|
+
"@prisma/client": "^6.9.0",
|
|
49
|
+
"@reldens/server-utils": "^0.19.0",
|
|
50
50
|
"@reldens/utils": "^0.50.0",
|
|
51
51
|
"knex": "^3.1.0",
|
|
52
52
|
"mysql": "^2.18.1",
|
|
53
53
|
"mysql2": "^3.14.1",
|
|
54
54
|
"objection": "^3.1.5",
|
|
55
|
-
"prisma": "^6.
|
|
55
|
+
"prisma": "^6.9.0"
|
|
56
56
|
}
|
|
57
57
|
}
|