@reldens/storage 0.78.0 → 0.80.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.
@@ -13,7 +13,7 @@ class {{modelClassName}} extends ObjectionJsRawModel
13
13
  {
14
14
  return '{{tableName}}';
15
15
  }
16
- {{modelRelations}}
16
+ {{idColumn}}{{modelRelations}}
17
17
  }
18
18
 
19
19
  module.exports.{{modelClassName}} = {{modelClassName}};
@@ -41,13 +41,15 @@ class ModelsGeneration extends BaseGenerator
41
41
  .join('\n ');
42
42
  let modelRelations = this.generateModelRelations(tableName, tableData, relationMetadata, driverKey);
43
43
  let entityPropertiesDefinition = this.getEntityPropertiesDefinition(tableData.columns, driverKey);
44
+ let idColumn = this.generateIdColumn(tableData.columns, driverKey);
44
45
  let replacements = {
45
46
  modelClassName,
46
47
  tableName: 'prisma' === driverKey ? tableName.toLowerCase() : tableName,
47
48
  modelPropertiesList,
48
49
  modelPropertiesConstructor,
49
50
  modelRelations,
50
- entityPropertiesDefinition
51
+ entityPropertiesDefinition,
52
+ idColumn
51
53
  };
52
54
  let modelContent = this.applyReplacements(modelTemplateContent, replacements);
53
55
  let fileName = sc.kebabCase(tableName)+'-model.js';
@@ -67,6 +69,29 @@ class ModelsGeneration extends BaseGenerator
67
69
  return true;
68
70
  }
69
71
 
72
+ generateIdColumn(columns, driverKey)
73
+ {
74
+ if('objection-js' !== driverKey){
75
+ return '';
76
+ }
77
+ let primaryKeyColumn = this.detectPrimaryKeyColumn(columns);
78
+ if(!primaryKeyColumn || 'id' === primaryKeyColumn){
79
+ return '';
80
+ }
81
+ return '\n static get idColumn()\n {\n return \''+primaryKeyColumn+'\';\n }\n';
82
+ }
83
+
84
+ detectPrimaryKeyColumn(columns)
85
+ {
86
+ for(let columnName of Object.keys(columns)){
87
+ let column = columns[columnName];
88
+ if('PRI' === column.key){
89
+ return columnName;
90
+ }
91
+ }
92
+ return null;
93
+ }
94
+
70
95
  setAllTablesData(allTablesData)
71
96
  {
72
97
  this.allTablesData = allTablesData;
@@ -120,7 +120,7 @@ class PrismaDataServer extends BaseDataServer
120
120
  return await this.prisma.$queryRawUnsafe(statement);
121
121
  }
122
122
  let result = await this.prisma.$executeRawUnsafe(statement);
123
- Logger.debug('Raw result from Prisma: ' + result);
123
+ //Logger.debug('Raw result from Prisma: ' + result);
124
124
  if(
125
125
  trimmedStatement.startsWith('CREATE')
126
126
  || trimmedStatement.startsWith('ALTER')
@@ -129,6 +129,29 @@ class PrismaDriver extends BaseDriver
129
129
  return Object.keys(this.relationMetadata || {});
130
130
  }
131
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
+
132
155
  isNumericFieldType(fieldType)
133
156
  {
134
157
  return ('Int' === fieldType || 'BigInt' === fieldType || 'Float' === fieldType || 'Decimal' === fieldType);
@@ -466,18 +489,18 @@ class PrismaDriver extends BaseDriver
466
489
  return value;
467
490
  }
468
491
 
469
- transformRelationResults(result, includeConfig)
492
+ transformRelationResults(result, includeConfig, relationMapping)
470
493
  {
471
494
  if(!result || !includeConfig){
472
495
  return result;
473
496
  }
474
497
  if(sc.isArray(result)){
475
- return result.map(item => this.transformSingleResult(item, includeConfig));
498
+ return result.map(item => this.transformSingleResult(item, includeConfig, relationMapping));
476
499
  }
477
- return this.transformSingleResult(result, includeConfig);
500
+ return this.transformSingleResult(result, includeConfig, relationMapping);
478
501
  }
479
502
 
480
- transformSingleResult(item, includeConfig)
503
+ transformSingleResult(item, includeConfig, relationMapping)
481
504
  {
482
505
  if(!item || !sc.isObject(item)){
483
506
  return item;
@@ -493,11 +516,15 @@ class PrismaDriver extends BaseDriver
493
516
  }
494
517
  let relationValue = transformed[relationName];
495
518
  if('one' === relationMeta.type && sc.isArray(relationValue)){
496
- transformed[relationName] = 0 < relationValue.length ? relationValue[0] : null;
497
- continue;
519
+ relationValue = 0 < relationValue.length ? [...relationValue].shift() : null;
498
520
  }
499
521
  if('many' === relationMeta.type && !sc.isArray(relationValue)){
500
- transformed[relationName] = relationValue ? [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];
501
528
  }
502
529
  }
503
530
  return transformed;
@@ -526,26 +553,33 @@ class PrismaDriver extends BaseDriver
526
553
  this.ensureRequiredFields(preparedData);
527
554
  let createData = {data: preparedData};
528
555
  if(0 < relations.length){
529
- let includeConfig = this.buildIncludeObject(relations);
530
- createData.include = includeConfig;
556
+ let includeData = this.buildIncludeObjectWithMapping(relations);
557
+ createData.include = includeData.include;
531
558
  for(let relation of relations){
532
- if(!sc.hasOwn(params, relation)){
559
+ let normalizedRelation = this.normalizeRelationName(relation);
560
+ if(!sc.hasOwn(params, relation) && !sc.hasOwn(params, normalizedRelation)){
533
561
  continue;
534
562
  }
535
- let relationData = params[relation];
563
+ let relationData = sc.get(params, relation, sc.get(params, normalizedRelation, null));
536
564
  if(sc.isArray(relationData)){
537
- createData.data[relation] = {
565
+ createData.data[normalizedRelation] = {
538
566
  connect: relationData.map(item => ({id: this.castToIdType(item.id)}))
539
567
  };
540
568
  continue;
541
569
  }
542
- createData.data[relation] = {
570
+ createData.data[normalizedRelation] = {
543
571
  connect: {id: this.castToIdType(relationData.id)}
544
572
  };
545
573
  }
574
+ try {
575
+ return this.transformRelationResults(await this.model.create(createData), includeData.include, includeData.mapping);
576
+ } catch(error) {
577
+ Logger.error('Create with relations error: '+error.message);
578
+ return false;
579
+ }
546
580
  }
547
581
  try {
548
- return this.transformRelationResults(await this.model.create(createData), createData.include);
582
+ return await this.model.create(createData);
549
583
  } catch(error) {
550
584
  Logger.error('Create with relations error: '+error.message);
551
585
  return false;
@@ -692,7 +726,7 @@ class PrismaDriver extends BaseDriver
692
726
  where: processedFilters
693
727
  };
694
728
  if(0 < relations.length){
695
- query.include = this.buildIncludeObject(relations);
729
+ query.include = this.buildIncludeObjectWithMapping(relations).include;
696
730
  }
697
731
  try {
698
732
  return await this.model.count(query);
@@ -714,22 +748,13 @@ class PrismaDriver extends BaseDriver
714
748
 
715
749
  async loadAllWithRelations(relations)
716
750
  {
717
- if(!sc.isArray(relations) || 0 === relations.length){
718
- relations = this.getAllRelations();
719
- }
720
- let query = this.buildQueryOptions();
721
- let includeConfig = {};
722
- if(0 < relations.length){
723
- includeConfig = this.buildIncludeObject(relations);
724
- query.include = includeConfig;
725
- }
726
- try {
727
- let results = await this.model.findMany(query);
728
- return this.transformRelationResults(results, includeConfig);
729
- } catch(error) {
730
- Logger.error('Load all with relations error: '+error.message);
731
- return [];
732
- }
751
+ return this.executeQueryWithRelations(
752
+ (q) => this.model.findMany(q),
753
+ this.buildQueryOptions(),
754
+ relations,
755
+ 'Load all with relations error: ',
756
+ []
757
+ );
733
758
  }
734
759
 
735
760
  async load(filters)
@@ -747,24 +772,15 @@ class PrismaDriver extends BaseDriver
747
772
 
748
773
  async loadWithRelations(filters, relations)
749
774
  {
750
- if(!sc.isArray(relations) || 0 === relations.length){
751
- relations = this.getAllRelations();
752
- }
753
- let processedFilters = this.processFilters(filters);
754
775
  let query = this.buildQueryOptions();
755
- query.where = processedFilters;
756
- let includeConfig = {};
757
- if(0 < relations.length){
758
- includeConfig = this.buildIncludeObject(relations);
759
- query.include = includeConfig;
760
- }
761
- try {
762
- let results = await this.model.findMany(query);
763
- return this.transformRelationResults(results, includeConfig);
764
- } catch(error) {
765
- Logger.error('Load with relations error: '+error.message);
766
- return [];
767
- }
776
+ query.where = this.processFilters(filters);
777
+ return this.executeQueryWithRelations(
778
+ (q) => this.model.findMany(q),
779
+ query,
780
+ relations,
781
+ 'Load with relations error: ',
782
+ []
783
+ );
768
784
  }
769
785
 
770
786
  async loadBy(field, fieldValue, operator = null)
@@ -782,24 +798,15 @@ class PrismaDriver extends BaseDriver
782
798
 
783
799
  async loadByWithRelations(field, fieldValue, relations, operator = null)
784
800
  {
785
- if(!sc.isArray(relations) || 0 === relations.length){
786
- relations = this.getAllRelations();
787
- }
788
- let filter = this.createSingleFilter(field, fieldValue, operator);
789
801
  let query = this.buildQueryOptions();
790
- query.where = filter;
791
- let includeConfig = {};
792
- if(0 < relations.length){
793
- includeConfig = this.buildIncludeObject(relations);
794
- query.include = includeConfig;
795
- }
796
- try {
797
- let results = await this.model.findMany(query);
798
- return this.transformRelationResults(results, includeConfig);
799
- } catch(error) {
800
- Logger.error('Load by with relations error: '+error.message);
801
- return [];
802
- }
802
+ query.where = this.createSingleFilter(field, fieldValue, operator);
803
+ return this.executeQueryWithRelations(
804
+ (q) => this.model.findMany(q),
805
+ query,
806
+ relations,
807
+ 'Load by with relations error: ',
808
+ []
809
+ );
803
810
  }
804
811
 
805
812
  async loadById(id)
@@ -817,25 +824,13 @@ class PrismaDriver extends BaseDriver
817
824
 
818
825
  async loadByIdWithRelations(id, relations)
819
826
  {
820
- if(!sc.isArray(relations) || 0 === relations.length){
821
- relations = this.getAllRelations();
822
- }
823
- let castedId = this.castToIdType(id);
824
- let query = {
825
- where: {id: castedId}
826
- };
827
- let includeConfig = {};
828
- if(0 < relations.length){
829
- includeConfig = this.buildIncludeObject(relations);
830
- query.include = includeConfig;
831
- }
832
- try {
833
- let result = await this.model.findUnique(query);
834
- return this.transformRelationResults(result, includeConfig);
835
- } catch(error) {
836
- Logger.error('Load by ID with relations error: '+error.message);
837
- return null;
838
- }
827
+ return this.executeQueryWithRelations(
828
+ (q) => this.model.findUnique(q),
829
+ {where: {id: this.castToIdType(id)}},
830
+ relations,
831
+ 'Load by ID with relations error: ',
832
+ null
833
+ );
839
834
  }
840
835
 
841
836
  async loadByIds(ids)
@@ -868,24 +863,15 @@ class PrismaDriver extends BaseDriver
868
863
 
869
864
  async loadOneWithRelations(filters, relations)
870
865
  {
871
- if(!sc.isArray(relations) || 0 === relations.length){
872
- relations = this.getAllRelations();
873
- }
874
- let processedFilters = this.processFilters(filters);
875
866
  let query = this.buildQueryOptions(false);
876
- query.where = processedFilters;
877
- let includeConfig = {};
878
- if(0 < relations.length){
879
- includeConfig = this.buildIncludeObject(relations);
880
- query.include = includeConfig;
881
- }
882
- try {
883
- let result = await this.model.findFirst(query);
884
- return this.transformRelationResults(result, includeConfig);
885
- } catch(error) {
886
- Logger.error('Load one with relations error: '+error.message);
887
- return null;
888
- }
867
+ query.where = this.processFilters(filters);
868
+ return this.executeQueryWithRelations(
869
+ (q) => this.model.findFirst(q),
870
+ query,
871
+ relations,
872
+ 'Load one with relations error: ',
873
+ null
874
+ );
889
875
  }
890
876
 
891
877
  async loadOneBy(field, fieldValue, operator = null)
@@ -903,24 +889,15 @@ class PrismaDriver extends BaseDriver
903
889
 
904
890
  async loadOneByWithRelations(field, fieldValue, relations, operator = null)
905
891
  {
906
- if(!sc.isArray(relations) || 0 === relations.length){
907
- relations = this.getAllRelations();
908
- }
909
- let filter = this.createSingleFilter(field, fieldValue, operator);
910
892
  let query = this.buildQueryOptions(false);
911
- query.where = filter;
912
- let includeConfig = {};
913
- if(0 < relations.length){
914
- includeConfig = this.buildIncludeObject(relations);
915
- query.include = includeConfig;
916
- }
917
- try {
918
- let result = await this.model.findFirst(query);
919
- return this.transformRelationResults(result, includeConfig);
920
- } catch(error) {
921
- Logger.error('Load one by with relations error: '+error.message);
922
- return null;
923
- }
893
+ query.where = this.createSingleFilter(field, fieldValue, operator);
894
+ return this.executeQueryWithRelations(
895
+ (q) => this.model.findFirst(q),
896
+ query,
897
+ relations,
898
+ 'Load one by with relations error: ',
899
+ null
900
+ );
924
901
  }
925
902
 
926
903
  buildQueryOptions(useLimit = true)
@@ -999,13 +976,39 @@ class PrismaDriver extends BaseDriver
999
976
  };
1000
977
  }
1001
978
 
1002
- buildIncludeObject(relations)
979
+ async executeQueryWithRelations(modelMethod, query, relations, errorMessage, defaultReturn)
980
+ {
981
+ if(!sc.isArray(relations) || 0 === relations.length){
982
+ relations = this.getAllRelations();
983
+ }
984
+ let includeData = this.buildIncludeObjectWithMapping(relations);
985
+ if(0 < Object.keys(includeData.include).length){
986
+ query.include = includeData.include;
987
+ }
988
+ try {
989
+ return this.transformRelationResults(await modelMethod(query), includeData.include, includeData.mapping);
990
+ } catch(error) {
991
+ Logger.error(errorMessage+error.message);
992
+ return defaultReturn;
993
+ }
994
+ }
995
+
996
+ buildIncludeObjectWithMapping(relations)
1003
997
  {
1004
998
  let include = {};
999
+ let mapping = {};
1005
1000
  for(let relation of relations){
1006
- include[relation] = true;
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
+ }
1007
1010
  }
1008
- return include;
1011
+ return {include, mapping};
1009
1012
  }
1010
1013
 
1011
1014
  }
@@ -1,182 +1,182 @@
1
- /**
2
- *
3
- * Reldens - PrismaSchemaGenerator
4
- *
5
- */
6
-
7
- const { execSync } = require('child_process');
8
- const { FileHandler } = require('@reldens/server-utils');
9
- const { Logger, sc } = require('@reldens/utils');
10
-
11
- class PrismaSchemaGenerator
12
- {
13
-
14
- constructor(props)
15
- {
16
- this.config = sc.get(props, 'config', {});
17
- this.client = sc.get(props, 'client', 'mysql');
18
- this.debug = sc.get(props, 'debug', false);
19
- this.dataProxy = sc.get(props, 'dataProxy', false);
20
- this.checkInterval = sc.get(props, 'checkInterval', 1000);
21
- this.maxWaitTime = sc.get(props, 'maxWaitTime', 30000);
22
- this.prismaSchemaPath = sc.get(props, 'prismaSchemaPath', FileHandler.joinPaths(process.cwd(), 'prisma'));
23
- this.schemaFilePath = FileHandler.joinPaths(this.prismaSchemaPath, 'schema.prisma');
24
- this.clientOutputPath = sc.get(props, 'clientOutputPath', '');
25
- this.generateBinaryTargets = sc.get(props, 'generateBinaryTargets', ['native', 'debian-openssl-1.1.x']);
26
- this.dbParams = sc.get(props, 'dbParams', process.env.RELDENS_DB_PARAMS || '');
27
- }
28
-
29
- async generate()
30
- {
31
- FileHandler.createFolder(this.prismaSchemaPath);
32
- this.generateSchemaFile();
33
- Logger.info('Running prisma introspect "npx prisma db pull"...');
34
- try {
35
- execSync('npx prisma db pull', { stdio: 'inherit' });
36
- } catch(error) {
37
- Logger.critical('Failed to pull database schema: ' + error.message);
38
- return false;
39
- }
40
- let generateCommand = 'npx prisma generate';
41
- if(this.dataProxy){
42
- generateCommand += ' --data-proxy';
43
- }
44
- Logger.info('Running prisma generate "'+generateCommand+'"...');
45
- try {
46
- execSync(generateCommand, { stdio: 'inherit' });
47
- } catch(error) {
48
- let errorString = error.toString();
49
- if(errorString.includes('EPERM') && errorString.includes('query_engine-windows.dll.node')){
50
- Logger.warning('Windows permission error detected with query engine file.');
51
- Logger.warning('This is a known Prisma issue on Windows.');
52
- Logger.warning('Please manually run: ' + generateCommand);
53
- Logger.warning('You may need to close any processes using the Prisma client first.');
54
- return false;
55
- }
56
- Logger.critical('Generate command failed: ' + error.message);
57
- return false;
58
- }
59
- await this.waitForSchemaGeneration();
60
- return true;
61
- }
62
-
63
- async waitForSchemaGeneration()
64
- {
65
- let clientPath = this.clientOutputPath || 'node_modules/.prisma/client';
66
- let generatedClientPath = this.resolveClientPath(clientPath);
67
- let clientIndexPath = FileHandler.joinPaths(generatedClientPath, 'index.js');
68
- let startTime = Date.now();
69
- return new Promise((resolve) => {
70
- let interval = setInterval(() => {
71
- let awaitTime = Date.now() - startTime;
72
- Logger.info('Awaiting on schema generation: ' + clientIndexPath, (awaitTime/1000).toFixed(0));
73
- if(FileHandler.exists(clientIndexPath)){
74
- clearInterval(interval);
75
- resolve();
76
- return;
77
- }
78
- if(awaitTime > this.maxWaitTime){
79
- clearInterval(interval);
80
- Logger.warning('Schema generation wait timeout reached.');
81
- resolve();
82
- }
83
- }, this.checkInterval);
84
- });
85
- }
86
-
87
- resolveClientPath(clientPath)
88
- {
89
- if(clientPath.match(/^[a-zA-Z]:/) || clientPath.startsWith('/')){
90
- return clientPath;
91
- }
92
- if(clientPath.startsWith('node_modules')){
93
- return FileHandler.joinPaths(process.cwd(), clientPath);
94
- }
95
- return FileHandler.joinPaths(this.prismaSchemaPath, clientPath);
96
- }
97
-
98
- generateSchemaFile()
99
- {
100
- let datasourceProvider = this.getDatasourceProvider();
101
- let schemaContent = this.buildSchemaContent(
102
- datasourceProvider,
103
- this.buildConnectionString(datasourceProvider),
104
- this.buildDirectConnectionString(datasourceProvider)
105
- );
106
- FileHandler.writeFile(this.schemaFilePath, schemaContent);
107
- Logger.info('Generated Prisma schema file at: '+this.schemaFilePath);
108
- }
109
-
110
- getDatasourceProvider()
111
- {
112
- if('postgresql' === this.client || 'postgres' === this.client){
113
- return 'postgresql';
114
- }
115
- if('mongodb' === this.client){
116
- return 'mongodb';
117
- }
118
- return 'mysql';
119
- }
120
-
121
- buildConnectionString(datasourceProvider)
122
- {
123
- if(this.dataProxy){
124
- datasourceProvider = 'prisma';
125
- }
126
- return datasourceProvider + this.buildConnectionDataString();
127
- }
128
-
129
- buildDirectConnectionString(datasourceProvider)
130
- {
131
- if(!this.dataProxy){
132
- return '';
133
- }
134
- return datasourceProvider + this.buildConnectionDataString();
135
- }
136
-
137
- buildConnectionDataString()
138
- {
139
- return '://' + this.config.user + ':' + this.config.password
140
- + '@' + this.config.host+':' + this.config.port
141
- + '/' + this.config.database
142
- + (this.dbParams ? '?' + this.dbParams : '');
143
- }
144
-
145
- buildSchemaContent(datasourceProvider, connectionString, directConnectionString)
146
- {
147
- return this.buildGeneratorBlock() + '\n\n'
148
- + this.buildDatasourceBlock(datasourceProvider, connectionString, directConnectionString) + '\n';
149
- }
150
-
151
- buildGeneratorBlock()
152
- {
153
- let generatorContent = 'generator client {\n';
154
- generatorContent += ' provider = "prisma-client-js"\n';
155
- if(this.clientOutputPath){
156
- let normalizedPath = this.clientOutputPath.replace(/\\/g, '/');
157
- generatorContent += ' output = "' + normalizedPath + '"\n';
158
- }
159
- if(0 < this.generateBinaryTargets.length){
160
- generatorContent += ' binaryTargets = [' +
161
- this.generateBinaryTargets.map(target => '"' + target + '"').join(', ')
162
- + ']\n';
163
- }
164
- generatorContent += '}';
165
- return generatorContent;
166
- }
167
-
168
- buildDatasourceBlock(datasourceProvider, connectionString, directConnectionString)
169
- {
170
- let datasourceContent = 'datasource db {\n';
171
- datasourceContent += ' provider = "' + datasourceProvider + '"\n';
172
- datasourceContent += ' url = "' + connectionString + '"\n';
173
- if('' !== directConnectionString){
174
- datasourceContent += ' directUrl = "' + directConnectionString + '"\n';
175
- }
176
- datasourceContent += '}';
177
- return datasourceContent;
178
- }
179
-
180
- }
181
-
182
- module.exports.PrismaSchemaGenerator = PrismaSchemaGenerator;
1
+ /**
2
+ *
3
+ * Reldens - PrismaSchemaGenerator
4
+ *
5
+ */
6
+
7
+ const { execSync } = require('child_process');
8
+ const { FileHandler } = require('@reldens/server-utils');
9
+ const { Logger, sc } = require('@reldens/utils');
10
+
11
+ class PrismaSchemaGenerator
12
+ {
13
+
14
+ constructor(props)
15
+ {
16
+ this.config = sc.get(props, 'config', {});
17
+ this.client = sc.get(props, 'client', 'mysql');
18
+ this.debug = sc.get(props, 'debug', false);
19
+ this.dataProxy = sc.get(props, 'dataProxy', false);
20
+ this.checkInterval = sc.get(props, 'checkInterval', 1000);
21
+ this.maxWaitTime = sc.get(props, 'maxWaitTime', 30000);
22
+ this.prismaSchemaPath = sc.get(props, 'prismaSchemaPath', FileHandler.joinPaths(process.cwd(), 'prisma'));
23
+ this.schemaFilePath = FileHandler.joinPaths(this.prismaSchemaPath, 'schema.prisma');
24
+ this.clientOutputPath = sc.get(props, 'clientOutputPath', '');
25
+ this.generateBinaryTargets = sc.get(props, 'generateBinaryTargets', ['native', 'debian-openssl-1.1.x']);
26
+ this.dbParams = sc.get(props, 'dbParams', process.env.RELDENS_DB_PARAMS || '');
27
+ }
28
+
29
+ async generate()
30
+ {
31
+ FileHandler.createFolder(this.prismaSchemaPath);
32
+ this.generateSchemaFile();
33
+ Logger.info('Running prisma introspect "npx prisma db pull"...');
34
+ try {
35
+ execSync('npx prisma db pull', { stdio: 'inherit' });
36
+ } catch(error) {
37
+ Logger.critical('Failed to pull database schema: ' + error.message);
38
+ return false;
39
+ }
40
+ let generateCommand = 'npx prisma generate';
41
+ if(this.dataProxy){
42
+ generateCommand += ' --data-proxy';
43
+ }
44
+ Logger.info('Running prisma generate "'+generateCommand+'"...');
45
+ try {
46
+ execSync(generateCommand, { stdio: 'inherit' });
47
+ } catch(error) {
48
+ let errorString = error.toString();
49
+ if(errorString.includes('EPERM') && errorString.includes('query_engine-windows.dll.node')){
50
+ Logger.warning('Windows permission error detected with query engine file.');
51
+ Logger.warning('This is a known Prisma issue on Windows.');
52
+ Logger.warning('Please manually run: ' + generateCommand);
53
+ Logger.warning('You may need to close any processes using the Prisma client first.');
54
+ return false;
55
+ }
56
+ Logger.critical('Generate command failed: ' + error.message);
57
+ return false;
58
+ }
59
+ await this.waitForSchemaGeneration();
60
+ return true;
61
+ }
62
+
63
+ async waitForSchemaGeneration()
64
+ {
65
+ let clientPath = this.clientOutputPath || 'node_modules/.prisma/client';
66
+ let generatedClientPath = this.resolveClientPath(clientPath);
67
+ let clientIndexPath = FileHandler.joinPaths(generatedClientPath, 'index.js');
68
+ let startTime = Date.now();
69
+ return new Promise((resolve) => {
70
+ let interval = setInterval(() => {
71
+ let awaitTime = Date.now() - startTime;
72
+ Logger.info('Awaiting on schema generation: ' + clientIndexPath, (awaitTime/1000).toFixed(0));
73
+ if(FileHandler.exists(clientIndexPath)){
74
+ clearInterval(interval);
75
+ resolve();
76
+ return;
77
+ }
78
+ if(awaitTime > this.maxWaitTime){
79
+ clearInterval(interval);
80
+ Logger.warning('Schema generation wait timeout reached.');
81
+ resolve();
82
+ }
83
+ }, this.checkInterval);
84
+ });
85
+ }
86
+
87
+ resolveClientPath(clientPath)
88
+ {
89
+ if(clientPath.match(/^[a-zA-Z]:/) || clientPath.startsWith('/')){
90
+ return clientPath;
91
+ }
92
+ if(clientPath.startsWith('node_modules')){
93
+ return FileHandler.joinPaths(process.cwd(), clientPath);
94
+ }
95
+ return FileHandler.joinPaths(this.prismaSchemaPath, clientPath);
96
+ }
97
+
98
+ generateSchemaFile()
99
+ {
100
+ let datasourceProvider = this.getDatasourceProvider();
101
+ let schemaContent = this.buildSchemaContent(
102
+ datasourceProvider,
103
+ this.buildConnectionString(datasourceProvider),
104
+ this.buildDirectConnectionString(datasourceProvider)
105
+ );
106
+ FileHandler.writeFile(this.schemaFilePath, schemaContent);
107
+ Logger.info('Generated Prisma schema file at: '+this.schemaFilePath);
108
+ }
109
+
110
+ getDatasourceProvider()
111
+ {
112
+ if('postgresql' === this.client || 'postgres' === this.client){
113
+ return 'postgresql';
114
+ }
115
+ if('mongodb' === this.client){
116
+ return 'mongodb';
117
+ }
118
+ return 'mysql';
119
+ }
120
+
121
+ buildConnectionString(datasourceProvider)
122
+ {
123
+ if(this.dataProxy){
124
+ datasourceProvider = 'prisma';
125
+ }
126
+ return datasourceProvider + this.buildConnectionDataString();
127
+ }
128
+
129
+ buildDirectConnectionString(datasourceProvider)
130
+ {
131
+ if(!this.dataProxy){
132
+ return '';
133
+ }
134
+ return datasourceProvider + this.buildConnectionDataString();
135
+ }
136
+
137
+ buildConnectionDataString()
138
+ {
139
+ return '://' + this.config.user + ':' + this.config.password
140
+ + '@' + this.config.host+':' + this.config.port
141
+ + '/' + this.config.database
142
+ + (this.dbParams ? '?' + this.dbParams : '');
143
+ }
144
+
145
+ buildSchemaContent(datasourceProvider, connectionString, directConnectionString)
146
+ {
147
+ return this.buildGeneratorBlock() + '\n\n'
148
+ + this.buildDatasourceBlock(datasourceProvider, connectionString, directConnectionString) + '\n';
149
+ }
150
+
151
+ buildGeneratorBlock()
152
+ {
153
+ let generatorContent = 'generator client {\n';
154
+ generatorContent += ' provider = "prisma-client-js"\n';
155
+ if(this.clientOutputPath){
156
+ let normalizedPath = this.clientOutputPath.replace(/\\/g, '/');
157
+ generatorContent += ' output = "' + normalizedPath + '"\n';
158
+ }
159
+ if(0 < this.generateBinaryTargets.length){
160
+ generatorContent += ' binaryTargets = [' +
161
+ this.generateBinaryTargets.map(target => '"' + target + '"').join(', ')
162
+ + ']\n';
163
+ }
164
+ generatorContent += '}';
165
+ return generatorContent;
166
+ }
167
+
168
+ buildDatasourceBlock(datasourceProvider, connectionString, directConnectionString)
169
+ {
170
+ let datasourceContent = 'datasource db {\n';
171
+ datasourceContent += ' provider = "' + datasourceProvider + '"\n';
172
+ datasourceContent += ' url = "' + connectionString + '"\n';
173
+ if('' !== directConnectionString){
174
+ datasourceContent += ' directUrl = "' + directConnectionString + '"\n';
175
+ }
176
+ datasourceContent += '}';
177
+ return datasourceContent;
178
+ }
179
+
180
+ }
181
+
182
+ module.exports.PrismaSchemaGenerator = PrismaSchemaGenerator;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reldens/storage",
3
3
  "scope": "@reldens",
4
- "version": "0.78.0",
4
+ "version": "0.80.0",
5
5
  "description": "Reldens - Storage",
6
6
  "author": "Damian A. Pastorini",
7
7
  "license": "MIT",
@@ -42,11 +42,11 @@
42
42
  "url": "https://github.com/damian-pastorini/reldens-storage/issues"
43
43
  },
44
44
  "dependencies": {
45
- "@mikro-orm/core": "6.6.0",
46
- "@mikro-orm/mongodb": "6.6.0",
47
- "@mikro-orm/mysql": "6.6.0",
45
+ "@mikro-orm/core": "6.6.1",
46
+ "@mikro-orm/mongodb": "6.6.1",
47
+ "@mikro-orm/mysql": "6.6.1",
48
48
  "@prisma/client": "6.19.0",
49
- "@reldens/server-utils": "^0.40.0",
49
+ "@reldens/server-utils": "^0.41.0",
50
50
  "@reldens/utils": "^0.54.0",
51
51
  "knex": "3.1.0",
52
52
  "mysql": "2.18.1",