@reldens/storage 0.55.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/entities-generator.js +145 -8
- package/lib/entity-templates/prisma-model.template +0 -15
- package/lib/generators/models-generation.js +144 -2
- package/lib/mysql-tables-provider.js +1 -1
- package/lib/prisma/prisma-data-server.js +18 -7
- package/lib/prisma/prisma-driver.js +107 -12
- package/lib/prisma/prisma-schema-generator.js +15 -4
- package/package.json +3 -3
|
@@ -48,10 +48,12 @@ class EntitiesGenerator
|
|
|
48
48
|
this.server = sc.get(props, 'server', false);
|
|
49
49
|
this.connectionData = sc.get(props, 'connectionData', false);
|
|
50
50
|
this.isOverride = sc.get(props, 'isOverride', false);
|
|
51
|
+
this.prismaClient = sc.get(props, 'prismaClient', false);
|
|
51
52
|
this.generatedEntities = {};
|
|
52
53
|
this.existingEntities = {};
|
|
53
54
|
this.existingEntityFields = {};
|
|
54
55
|
this.existingModels = {};
|
|
56
|
+
this.prismaRelationsMetadata = {};
|
|
55
57
|
this.entitiesGeneration = new EntitiesGeneration({
|
|
56
58
|
entitiesFolder: this.entitiesFolder,
|
|
57
59
|
templatePath: this.templates['entity']
|
|
@@ -163,6 +165,125 @@ class EntitiesGenerator
|
|
|
163
165
|
return false;
|
|
164
166
|
}
|
|
165
167
|
|
|
168
|
+
extractPrismaRelationsMetadata()
|
|
169
|
+
{
|
|
170
|
+
if(!this.prismaClient){
|
|
171
|
+
Logger.warning('Missing PrismaClient.');
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if(!this.server || !(this.server instanceof PrismaDataServer)){
|
|
175
|
+
Logger.warning('Server is not a PrismaDataServer instance.');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
let dmmf = this.prismaClient._runtimeDataModel || this.prismaClient._dmmf?.datamodel;
|
|
180
|
+
if(!dmmf || !dmmf.models){
|
|
181
|
+
Logger.warning('Could not extract Prisma DMMF data for relations metadata.');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if(sc.isArray(dmmf.models)){
|
|
185
|
+
this.processArrayModels(dmmf.models);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
this.processObjectModels(dmmf.models);
|
|
189
|
+
} catch(error) {
|
|
190
|
+
Logger.warning('Failed to extract Prisma relations metadata: ' + error.message);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
processArrayModels(models)
|
|
195
|
+
{
|
|
196
|
+
for(let model of models){
|
|
197
|
+
if(!model){
|
|
198
|
+
Logger.error('Missing model.');
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if(!model.name){
|
|
202
|
+
Logger.error('Missing model name.', model);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if(!sc.isArray(model.fields)){
|
|
206
|
+
Logger.error('Model fields not an array.', model);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
this.processModelForRelations(model.name, model, models);
|
|
210
|
+
}
|
|
211
|
+
Logger.info('Extracted relations metadata for ' + Object.keys(this.prismaRelationsMetadata).length + ' models.');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
processObjectModels(models)
|
|
215
|
+
{
|
|
216
|
+
for(let modelName of Object.keys(models)){
|
|
217
|
+
let model = models[modelName];
|
|
218
|
+
if(!model){
|
|
219
|
+
Logger.error('Missing model.');
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if(!sc.isArray(model.fields)){
|
|
223
|
+
Logger.error('Model fields not an array.', model);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
this.processModelForRelations(modelName, model, models);
|
|
227
|
+
}
|
|
228
|
+
Logger.info('Extracted relations metadata for ' + Object.keys(this.prismaRelationsMetadata).length + ' models.');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
processModelForRelations(modelName, model, allModels)
|
|
232
|
+
{
|
|
233
|
+
let tableName = modelName.toLowerCase();
|
|
234
|
+
this.prismaRelationsMetadata[tableName] = {};
|
|
235
|
+
for(let field of model.fields){
|
|
236
|
+
if('object' !== field.kind){
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
let relationType = 'many';
|
|
240
|
+
if(!field.isList){
|
|
241
|
+
relationType = this.inferRelationType(field, model, allModels, modelName);
|
|
242
|
+
}
|
|
243
|
+
this.prismaRelationsMetadata[tableName][field.name] = {
|
|
244
|
+
type: relationType,
|
|
245
|
+
model: field.type,
|
|
246
|
+
isList: field.isList,
|
|
247
|
+
isOptional: field.isOptional,
|
|
248
|
+
relationName: field.relationName
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
inferRelationType(field, model, allModels, modelName)
|
|
254
|
+
{
|
|
255
|
+
if(field.isList){
|
|
256
|
+
return 'many';
|
|
257
|
+
}
|
|
258
|
+
let relatedModel = this.findRelatedModel(allModels, field.type);
|
|
259
|
+
if(!relatedModel){
|
|
260
|
+
return 'one';
|
|
261
|
+
}
|
|
262
|
+
let backReference = relatedModel.fields.find(f =>
|
|
263
|
+
'object' === f.kind &&
|
|
264
|
+
f.type === modelName &&
|
|
265
|
+
f.relationName === field.relationName
|
|
266
|
+
);
|
|
267
|
+
if(!backReference){
|
|
268
|
+
return 'one';
|
|
269
|
+
}
|
|
270
|
+
if(field.relationFromFields && 0 < field.relationFromFields.length){
|
|
271
|
+
return 'one';
|
|
272
|
+
}
|
|
273
|
+
if(backReference.relationFromFields && 0 < backReference.relationFromFields.length){
|
|
274
|
+
return 'one';
|
|
275
|
+
}
|
|
276
|
+
return 'one';
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
findRelatedModel(allModels, fieldType)
|
|
280
|
+
{
|
|
281
|
+
if(sc.isArray(allModels)){
|
|
282
|
+
return allModels.find(m => m.name === fieldType);
|
|
283
|
+
}
|
|
284
|
+
return allModels[fieldType];
|
|
285
|
+
}
|
|
286
|
+
|
|
166
287
|
filterTablesToGenerate(tables, driverKey)
|
|
167
288
|
{
|
|
168
289
|
let filteredTables = {};
|
|
@@ -253,6 +374,18 @@ class EntitiesGenerator
|
|
|
253
374
|
Logger.critical('Failed to connect to database: '+error.message);
|
|
254
375
|
return false;
|
|
255
376
|
}
|
|
377
|
+
let driverKey = sc.get(
|
|
378
|
+
this.connectionData,
|
|
379
|
+
'driver',
|
|
380
|
+
(this.server ? sc.get(this.driversClassMap, this.server.constructor.name, false) : false)
|
|
381
|
+
);
|
|
382
|
+
if('prisma' === driverKey){
|
|
383
|
+
if(!this.prismaClient && this.server.prisma){
|
|
384
|
+
this.prismaClient = this.server.prisma;
|
|
385
|
+
}
|
|
386
|
+
Logger.debug('Extract Prisma relations metadata.');
|
|
387
|
+
this.extractPrismaRelationsMetadata();
|
|
388
|
+
}
|
|
256
389
|
let tables = await this.server.fetchEntitiesFromDatabase();
|
|
257
390
|
if(!tables){
|
|
258
391
|
Logger.critical('EntitiesGenerator tables fetch failed.');
|
|
@@ -263,15 +396,13 @@ class EntitiesGenerator
|
|
|
263
396
|
}
|
|
264
397
|
this.detectExistingEntities();
|
|
265
398
|
this.detectExistingModels();
|
|
266
|
-
let driverKey = sc.get(
|
|
267
|
-
this.connectionData,
|
|
268
|
-
'driver',
|
|
269
|
-
(this.server ? sc.get(this.driversClassMap, this.server.constructor.name, false) : false)
|
|
270
|
-
);
|
|
271
399
|
if(!driverKey){
|
|
272
400
|
Logger.critical('Failed to fetch driver key.');
|
|
273
401
|
return false;
|
|
274
402
|
}
|
|
403
|
+
if('objection-js' === driverKey){
|
|
404
|
+
this.modelsGeneration.setAllTablesData(tables);
|
|
405
|
+
}
|
|
275
406
|
let tablesToGenerate = this.filterTablesToGenerate(tables, driverKey);
|
|
276
407
|
if(0 === Object.keys(tablesToGenerate).length && !this.isOverride){
|
|
277
408
|
return true;
|
|
@@ -283,11 +414,13 @@ class EntitiesGenerator
|
|
|
283
414
|
continue;
|
|
284
415
|
}
|
|
285
416
|
let entityInfo = this.entitiesGeneration.generatedEntities[tableName];
|
|
417
|
+
let relationMetadata = sc.get(this.prismaRelationsMetadata, tableName, {});
|
|
286
418
|
let modelGenerated = await this.modelsGeneration.generateModelFile(
|
|
287
419
|
tableName,
|
|
288
420
|
tableData,
|
|
289
421
|
driverKey,
|
|
290
|
-
entityInfo
|
|
422
|
+
entityInfo,
|
|
423
|
+
relationMetadata
|
|
291
424
|
);
|
|
292
425
|
if(modelGenerated){
|
|
293
426
|
let modelInfo = this.modelsGeneration.generatedModels[tableName];
|
|
@@ -327,7 +460,7 @@ class EntitiesGenerator
|
|
|
327
460
|
Logger.critical('Unsupported driver: '+driverKey);
|
|
328
461
|
return false;
|
|
329
462
|
}
|
|
330
|
-
|
|
463
|
+
let serverConfig = {
|
|
331
464
|
client: sc.get(this.connectionData, 'client', 'mysql2'),
|
|
332
465
|
config: {
|
|
333
466
|
user: sc.get(this.connectionData, 'user', ''),
|
|
@@ -336,7 +469,11 @@ class EntitiesGenerator
|
|
|
336
469
|
host: sc.get(this.connectionData, 'host', 'localhost'),
|
|
337
470
|
port: sc.get(this.connectionData, 'port', 3306)
|
|
338
471
|
}
|
|
339
|
-
}
|
|
472
|
+
};
|
|
473
|
+
if('prisma' === driverKey && this.prismaClient){
|
|
474
|
+
serverConfig.prismaClient = this.prismaClient;
|
|
475
|
+
}
|
|
476
|
+
this.server = new driverClassMapped(serverConfig);
|
|
340
477
|
return this.server;
|
|
341
478
|
}
|
|
342
479
|
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const { PrismaClient } = require('@prisma/client');
|
|
8
|
-
|
|
9
7
|
class {{modelClassName}}
|
|
10
8
|
{
|
|
11
9
|
|
|
@@ -14,19 +12,6 @@ class {{modelClassName}}
|
|
|
14
12
|
{{modelPropertiesConstructor}}
|
|
15
13
|
}
|
|
16
14
|
|
|
17
|
-
static get client()
|
|
18
|
-
{
|
|
19
|
-
if(!this._client){
|
|
20
|
-
this._client = new PrismaClient();
|
|
21
|
-
}
|
|
22
|
-
return this._client;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
static get model()
|
|
26
|
-
{
|
|
27
|
-
return this.client['{{tableName}}'];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
15
|
static get tableName()
|
|
31
16
|
{
|
|
32
17
|
return '{{tableName}}';
|
|
@@ -18,9 +18,10 @@ class ModelsGeneration extends BaseGenerator
|
|
|
18
18
|
this.modelsFolder = sc.get(props, 'modelsFolder', '');
|
|
19
19
|
this.templates = sc.get(props, 'templates', {});
|
|
20
20
|
this.generatedModels = {};
|
|
21
|
+
this.allTablesData = {};
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
async generateModelFile(tableName, tableData, driverKey, entityInfo)
|
|
24
|
+
async generateModelFile(tableName, tableData, driverKey, entityInfo, relationMetadata = {})
|
|
24
25
|
{
|
|
25
26
|
let modelTemplatePath = this.templates[driverKey];
|
|
26
27
|
if(!FileHandler.exists(modelTemplatePath)){
|
|
@@ -37,7 +38,7 @@ class ModelsGeneration extends BaseGenerator
|
|
|
37
38
|
let modelPropertiesConstructor = Object.keys(tableData.columns)
|
|
38
39
|
.map(columnName => 'this.'+columnName+' = '+columnName+';')
|
|
39
40
|
.join('\n ');
|
|
40
|
-
let modelRelations =
|
|
41
|
+
let modelRelations = this.generateModelRelations(tableName, tableData, relationMetadata, driverKey);
|
|
41
42
|
let entityPropertiesDefinition = this.getEntityPropertiesDefinition(tableData.columns, driverKey);
|
|
42
43
|
let replacements = {
|
|
43
44
|
modelClassName,
|
|
@@ -65,6 +66,147 @@ class ModelsGeneration extends BaseGenerator
|
|
|
65
66
|
return true;
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
setAllTablesData(allTablesData)
|
|
70
|
+
{
|
|
71
|
+
this.allTablesData = allTablesData;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
generateModelRelations(tableName, tableData, relationMetadata, driverKey)
|
|
75
|
+
{
|
|
76
|
+
if('prisma' === driverKey){
|
|
77
|
+
return this.generatePrismaRelations(relationMetadata);
|
|
78
|
+
}
|
|
79
|
+
if('objection-js' === driverKey){
|
|
80
|
+
return this.generateObjectionJsRelations(tableName, tableData);
|
|
81
|
+
}
|
|
82
|
+
return '';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
generatePrismaRelations(relationMetadata)
|
|
86
|
+
{
|
|
87
|
+
if(!relationMetadata || 0 === Object.keys(relationMetadata).length){
|
|
88
|
+
return '';
|
|
89
|
+
}
|
|
90
|
+
let relationTypesArray = [];
|
|
91
|
+
for(let relationName of Object.keys(relationMetadata)){
|
|
92
|
+
let relation = relationMetadata[relationName];
|
|
93
|
+
relationTypesArray.push(' '+relationName+': \''+relation.type+'\'');
|
|
94
|
+
}
|
|
95
|
+
if(0 === relationTypesArray.length){
|
|
96
|
+
return '';
|
|
97
|
+
}
|
|
98
|
+
return '\n\n static get relationTypes()\n {\n return {\n '+relationTypesArray.join(',\n ')+'\n };\n }';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
generateObjectionJsRelations(tableName, tableData)
|
|
102
|
+
{
|
|
103
|
+
let relations = this.detectObjectionJsRelations(tableName, tableData);
|
|
104
|
+
let reverseRelations = this.detectReverseObjectionJsRelations(tableName);
|
|
105
|
+
let allRelations = Object.assign({}, relations, reverseRelations);
|
|
106
|
+
if(0 === Object.keys(allRelations).length){
|
|
107
|
+
return '';
|
|
108
|
+
}
|
|
109
|
+
let relationMappings = [];
|
|
110
|
+
let requiredModels = [];
|
|
111
|
+
for(let relationKey of Object.keys(allRelations)){
|
|
112
|
+
let relation = allRelations[relationKey];
|
|
113
|
+
let tableReference = relation.referencedTable || relation.referencingTable;
|
|
114
|
+
let relatedModelClass = sc.capitalizedCamelCase(tableReference)+'Model';
|
|
115
|
+
if(!requiredModels.find(m => m.modelClass === relatedModelClass)){
|
|
116
|
+
requiredModels.push({
|
|
117
|
+
modelClass: relatedModelClass,
|
|
118
|
+
fileName: sc.kebabCase(tableReference)+'-model'
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
let relationMapping = ' '+relationKey+': {\n';
|
|
122
|
+
relationMapping += ' relation: this.'+relation.relationType+',\n';
|
|
123
|
+
relationMapping += ' modelClass: '+relatedModelClass+',\n';
|
|
124
|
+
relationMapping += ' join: {\n';
|
|
125
|
+
relationMapping += ' from: this.tableName+\'.'+relation.fromColumn+'\',\n';
|
|
126
|
+
relationMapping += ' to: '+relatedModelClass+'.tableName+\'.'+relation.toColumn+'\'\n';
|
|
127
|
+
relationMapping += ' }\n';
|
|
128
|
+
relationMapping += ' }';
|
|
129
|
+
relationMappings.push(relationMapping);
|
|
130
|
+
}
|
|
131
|
+
if(0 === relationMappings.length){
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
134
|
+
let requireStatements = [];
|
|
135
|
+
for(let modelInfo of requiredModels){
|
|
136
|
+
requireStatements.push(' const { '+modelInfo.modelClass+' } = require(\'./'+modelInfo.fileName+'\');');
|
|
137
|
+
}
|
|
138
|
+
let relationMappingsMethod = '\n\n static get relationMappings()\n {\n';
|
|
139
|
+
relationMappingsMethod += requireStatements.join('\n')+'\n';
|
|
140
|
+
relationMappingsMethod += ' return {\n';
|
|
141
|
+
relationMappingsMethod += relationMappings.join(',\n');
|
|
142
|
+
relationMappingsMethod += '\n };\n';
|
|
143
|
+
relationMappingsMethod += ' }';
|
|
144
|
+
return relationMappingsMethod;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
detectObjectionJsRelations(tableName, tableData)
|
|
148
|
+
{
|
|
149
|
+
let relations = {};
|
|
150
|
+
for(let columnName of Object.keys(tableData.columns)){
|
|
151
|
+
let column = tableData.columns[columnName];
|
|
152
|
+
if(!sc.hasOwn(column, 'referencedTable')){
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
let relationKey = 'related_'+column.referencedTable;
|
|
156
|
+
let relationType = this.determineRelationType(column, true);
|
|
157
|
+
relations[relationKey] = {
|
|
158
|
+
fromColumn: columnName,
|
|
159
|
+
toColumn: column.referencedColumn,
|
|
160
|
+
referencedTable: column.referencedTable,
|
|
161
|
+
relationType: relationType
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return relations;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
detectReverseObjectionJsRelations(tableName)
|
|
168
|
+
{
|
|
169
|
+
let reverseRelations = {};
|
|
170
|
+
for(let otherTableName of Object.keys(this.allTablesData)){
|
|
171
|
+
if(otherTableName === tableName){
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
let otherTableData = this.allTablesData[otherTableName];
|
|
175
|
+
for(let columnName of Object.keys(otherTableData.columns)){
|
|
176
|
+
let column = otherTableData.columns[columnName];
|
|
177
|
+
if(!sc.hasOwn(column, 'referencedTable')){
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if(column.referencedTable !== tableName){
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
let relationKey = 'related_'+otherTableName;
|
|
184
|
+
let relationType = this.determineRelationType(column, false);
|
|
185
|
+
reverseRelations[relationKey] = {
|
|
186
|
+
fromColumn: column.referencedColumn,
|
|
187
|
+
toColumn: columnName,
|
|
188
|
+
referencingTable: otherTableName,
|
|
189
|
+
relationType: relationType
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return reverseRelations;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
determineRelationType(column, isForwardRelation)
|
|
197
|
+
{
|
|
198
|
+
if(isForwardRelation){
|
|
199
|
+
if('PRI' === column.key){
|
|
200
|
+
return 'HasOneRelation';
|
|
201
|
+
}
|
|
202
|
+
return 'BelongsToOneRelation';
|
|
203
|
+
}
|
|
204
|
+
if('PRI' === column.key){
|
|
205
|
+
return 'HasOneRelation';
|
|
206
|
+
}
|
|
207
|
+
return 'HasManyRelation';
|
|
208
|
+
}
|
|
209
|
+
|
|
68
210
|
getEntityPropertiesDefinition(columns, driverKey)
|
|
69
211
|
{
|
|
70
212
|
if('mikro-orm' === driverKey){
|
|
@@ -15,7 +15,7 @@ class MySQLTablesProvider
|
|
|
15
15
|
' TABLE_NAME, ' +
|
|
16
16
|
' COLUMN_NAME, ' +
|
|
17
17
|
' DATA_TYPE, ' +
|
|
18
|
-
' CHARACTER_MAXIMUM_LENGTH, ' +
|
|
18
|
+
' CAST(CHARACTER_MAXIMUM_LENGTH AS CHAR) as CHARACTER_MAXIMUM_LENGTH, ' +
|
|
19
19
|
' COLUMN_TYPE, ' +
|
|
20
20
|
' IS_NULLABLE, ' +
|
|
21
21
|
' COLUMN_KEY, ' +
|
|
@@ -8,7 +8,7 @@ const { BaseDataServer } = require('../base-data-server');
|
|
|
8
8
|
const { PrismaDriver } = require('./prisma-driver');
|
|
9
9
|
const { MySQLTablesProvider } = require('../mysql-tables-provider');
|
|
10
10
|
const { PrismaClient } = require('@prisma/client');
|
|
11
|
-
const { Logger } = require('@reldens/utils');
|
|
11
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
12
12
|
|
|
13
13
|
class PrismaDataServer extends BaseDataServer
|
|
14
14
|
{
|
|
@@ -16,7 +16,7 @@ class PrismaDataServer extends BaseDataServer
|
|
|
16
16
|
constructor(props)
|
|
17
17
|
{
|
|
18
18
|
super(props);
|
|
19
|
-
this.prisma = false;
|
|
19
|
+
this.prisma = sc.get(props, 'prismaClient', false);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
async connect()
|
|
@@ -25,10 +25,15 @@ class PrismaDataServer extends BaseDataServer
|
|
|
25
25
|
return this.initialized;
|
|
26
26
|
}
|
|
27
27
|
try {
|
|
28
|
-
this.prisma
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
if(!this.prisma){
|
|
29
|
+
this.prisma = new PrismaClient({
|
|
30
|
+
datasources: {db: {url: this.connectString}},
|
|
31
|
+
log: this.debug ? ['query', 'info', 'warn', 'error'] : ['error']
|
|
32
|
+
});
|
|
33
|
+
}
|
|
31
34
|
await this.prisma.$connect();
|
|
35
|
+
let dbTest = await this.prisma.$queryRaw`SELECT DATABASE() as current_db`;
|
|
36
|
+
Logger.info('Connected to database: ' + dbTest[0].current_db);
|
|
32
37
|
this.initialized = Date.now();
|
|
33
38
|
return this.initialized;
|
|
34
39
|
} catch(error) {
|
|
@@ -86,14 +91,16 @@ class PrismaDataServer extends BaseDataServer
|
|
|
86
91
|
continue;
|
|
87
92
|
}
|
|
88
93
|
try {
|
|
89
|
-
|
|
94
|
+
let result = await this.executeStatement(cleanStatement);
|
|
95
|
+
results.push(result);
|
|
96
|
+
//Logger.debug('Statement executed, result: ' + JSON.stringify(result));
|
|
90
97
|
} catch(stmtError) {
|
|
91
98
|
Logger.error('Statement "'+cleanStatement+'" execution failed: '+stmtError.message);
|
|
92
99
|
return false;
|
|
93
100
|
}
|
|
94
101
|
}
|
|
95
102
|
if(0 === results.length){
|
|
96
|
-
Logger.error('Statement results length is zero.', results);
|
|
103
|
+
//Logger.error('Statement results length is zero.', results);
|
|
97
104
|
return false;
|
|
98
105
|
}
|
|
99
106
|
return 1 === results.length ? results[0] : results;
|
|
@@ -109,7 +116,11 @@ class PrismaDataServer extends BaseDataServer
|
|
|
109
116
|
if(trimmedStatement.startsWith('SELECT')){
|
|
110
117
|
return await this.prisma.$queryRawUnsafe(statement);
|
|
111
118
|
}
|
|
119
|
+
if(trimmedStatement.startsWith('SHOW')){
|
|
120
|
+
return await this.prisma.$queryRawUnsafe(statement);
|
|
121
|
+
}
|
|
112
122
|
let result = await this.prisma.$executeRawUnsafe(statement);
|
|
123
|
+
Logger.debug('Raw result from Prisma: ' + result);
|
|
113
124
|
if(
|
|
114
125
|
trimmedStatement.startsWith('CREATE')
|
|
115
126
|
|| trimmedStatement.startsWith('ALTER')
|
|
@@ -33,7 +33,9 @@ class PrismaDriver extends BaseDriver
|
|
|
33
33
|
this.fieldTypes = {};
|
|
34
34
|
this.idFieldType = 'String';
|
|
35
35
|
this.referenceFields = {};
|
|
36
|
+
this.relationMetadata = {};
|
|
36
37
|
this.initFieldMetadata();
|
|
38
|
+
this.initRelationMetadata();
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
initFieldMetadata()
|
|
@@ -69,6 +71,44 @@ class PrismaDriver extends BaseDriver
|
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
initRelationMetadata()
|
|
75
|
+
{
|
|
76
|
+
try {
|
|
77
|
+
let dmmf = this.prisma._runtimeDataModel || Prisma.dmmf?.datamodel;
|
|
78
|
+
if(!dmmf){
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let modelName = this.tableName().toLowerCase();
|
|
82
|
+
let modelInfo = dmmf.models?.[modelName] || dmmf.models?.find(m => m.name.toLowerCase() === modelName);
|
|
83
|
+
if(!modelInfo){
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
for(let field of modelInfo.fields){
|
|
87
|
+
if('object' === field.kind){
|
|
88
|
+
let relationType = 'many';
|
|
89
|
+
if(!field.isList){
|
|
90
|
+
relationType = 'one';
|
|
91
|
+
}
|
|
92
|
+
this.relationMetadata[field.name] = {
|
|
93
|
+
type: relationType,
|
|
94
|
+
model: field.type,
|
|
95
|
+
isList: field.isList,
|
|
96
|
+
isOptional: field.isOptional
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if(this.rawModel && this.rawModel.relationTypes){
|
|
101
|
+
for(let relationName of Object.keys(this.rawModel.relationTypes)){
|
|
102
|
+
if(sc.hasOwn(this.relationMetadata, relationName)){
|
|
103
|
+
this.relationMetadata[relationName].type = this.rawModel.relationTypes[relationName];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch(error) {
|
|
108
|
+
Logger.warning('Could not initialize relation metadata: '+error.message);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
72
112
|
castIdValue(id)
|
|
73
113
|
{
|
|
74
114
|
if(null === id || undefined === id){
|
|
@@ -239,6 +279,43 @@ class PrismaDriver extends BaseDriver
|
|
|
239
279
|
return processedFilters;
|
|
240
280
|
}
|
|
241
281
|
|
|
282
|
+
transformRelationResults(result, includeConfig)
|
|
283
|
+
{
|
|
284
|
+
if(!result || !includeConfig){
|
|
285
|
+
return result;
|
|
286
|
+
}
|
|
287
|
+
if(sc.isArray(result)){
|
|
288
|
+
return result.map(item => this.transformSingleResult(item, includeConfig));
|
|
289
|
+
}
|
|
290
|
+
return this.transformSingleResult(result, includeConfig);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
transformSingleResult(item, includeConfig)
|
|
294
|
+
{
|
|
295
|
+
if(!item || !sc.isObject(item)){
|
|
296
|
+
return item;
|
|
297
|
+
}
|
|
298
|
+
let transformed = {...item};
|
|
299
|
+
for(let relationName of Object.keys(includeConfig)){
|
|
300
|
+
if(!sc.hasOwn(transformed, relationName)){
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
let relationMeta = this.relationMetadata[relationName];
|
|
304
|
+
if(!relationMeta){
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
let relationValue = transformed[relationName];
|
|
308
|
+
if('one' === relationMeta.type && sc.isArray(relationValue)){
|
|
309
|
+
transformed[relationName] = 0 < relationValue.length ? relationValue[0] : null;
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if('many' === relationMeta.type && !sc.isArray(relationValue)){
|
|
313
|
+
transformed[relationName] = relationValue ? [relationValue] : [];
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return transformed;
|
|
317
|
+
}
|
|
318
|
+
|
|
242
319
|
async create(params)
|
|
243
320
|
{
|
|
244
321
|
try {
|
|
@@ -439,10 +516,13 @@ class PrismaDriver extends BaseDriver
|
|
|
439
516
|
{
|
|
440
517
|
try {
|
|
441
518
|
let query = this.buildQueryOptions();
|
|
519
|
+
let includeConfig = {};
|
|
442
520
|
if(sc.isArray(relations) && 0 < relations.length){
|
|
443
|
-
|
|
521
|
+
includeConfig = this.buildIncludeObject(relations);
|
|
522
|
+
query.include = includeConfig;
|
|
444
523
|
}
|
|
445
|
-
|
|
524
|
+
let results = await this.model.findMany(query);
|
|
525
|
+
return this.transformRelationResults(results, includeConfig);
|
|
446
526
|
} catch(error) {
|
|
447
527
|
Logger.error('Load all with relations error: '+error.message);
|
|
448
528
|
return [];
|
|
@@ -468,10 +548,13 @@ class PrismaDriver extends BaseDriver
|
|
|
468
548
|
let processedFilters = this.processFilters(filters);
|
|
469
549
|
let query = this.buildQueryOptions();
|
|
470
550
|
query.where = processedFilters;
|
|
551
|
+
let includeConfig = {};
|
|
471
552
|
if(sc.isArray(relations) && 0 < relations.length){
|
|
472
|
-
|
|
553
|
+
includeConfig = this.buildIncludeObject(relations);
|
|
554
|
+
query.include = includeConfig;
|
|
473
555
|
}
|
|
474
|
-
|
|
556
|
+
let results = await this.model.findMany(query);
|
|
557
|
+
return this.transformRelationResults(results, includeConfig);
|
|
475
558
|
} catch(error) {
|
|
476
559
|
Logger.error('Load with relations error: '+error.message);
|
|
477
560
|
return [];
|
|
@@ -497,10 +580,13 @@ class PrismaDriver extends BaseDriver
|
|
|
497
580
|
let filter = this.createSingleFilter(field, fieldValue, operator);
|
|
498
581
|
let query = this.buildQueryOptions();
|
|
499
582
|
query.where = filter;
|
|
583
|
+
let includeConfig = {};
|
|
500
584
|
if(sc.isArray(relations) && 0 < relations.length){
|
|
501
|
-
|
|
585
|
+
includeConfig = this.buildIncludeObject(relations);
|
|
586
|
+
query.include = includeConfig;
|
|
502
587
|
}
|
|
503
|
-
|
|
588
|
+
let results = await this.model.findMany(query);
|
|
589
|
+
return this.transformRelationResults(results, includeConfig);
|
|
504
590
|
} catch(error) {
|
|
505
591
|
Logger.error('Load by with relations error: '+error.message);
|
|
506
592
|
return [];
|
|
@@ -527,10 +613,13 @@ class PrismaDriver extends BaseDriver
|
|
|
527
613
|
let query = {
|
|
528
614
|
where: {id: castedId}
|
|
529
615
|
};
|
|
616
|
+
let includeConfig = {};
|
|
530
617
|
if(sc.isArray(relations) && 0 < relations.length){
|
|
531
|
-
|
|
618
|
+
includeConfig = this.buildIncludeObject(relations);
|
|
619
|
+
query.include = includeConfig;
|
|
532
620
|
}
|
|
533
|
-
|
|
621
|
+
let result = await this.model.findUnique(query);
|
|
622
|
+
return this.transformRelationResults(result, includeConfig);
|
|
534
623
|
} catch(error) {
|
|
535
624
|
Logger.error('Load by ID with relations error: '+error.message);
|
|
536
625
|
return null;
|
|
@@ -571,10 +660,13 @@ class PrismaDriver extends BaseDriver
|
|
|
571
660
|
let processedFilters = this.processFilters(filters);
|
|
572
661
|
let query = this.buildQueryOptions(false);
|
|
573
662
|
query.where = processedFilters;
|
|
663
|
+
let includeConfig = {};
|
|
574
664
|
if(sc.isArray(relations) && 0 < relations.length){
|
|
575
|
-
|
|
665
|
+
includeConfig = this.buildIncludeObject(relations);
|
|
666
|
+
query.include = includeConfig;
|
|
576
667
|
}
|
|
577
|
-
|
|
668
|
+
let result = await this.model.findFirst(query);
|
|
669
|
+
return this.transformRelationResults(result, includeConfig);
|
|
578
670
|
} catch(error) {
|
|
579
671
|
Logger.error('Load one with relations error: '+error.message);
|
|
580
672
|
return null;
|
|
@@ -600,10 +692,13 @@ class PrismaDriver extends BaseDriver
|
|
|
600
692
|
let filter = this.createSingleFilter(field, fieldValue, operator);
|
|
601
693
|
let query = this.buildQueryOptions(false);
|
|
602
694
|
query.where = filter;
|
|
695
|
+
let includeConfig = {};
|
|
603
696
|
if(sc.isArray(relations) && 0 < relations.length){
|
|
604
|
-
|
|
697
|
+
includeConfig = this.buildIncludeObject(relations);
|
|
698
|
+
query.include = includeConfig;
|
|
605
699
|
}
|
|
606
|
-
|
|
700
|
+
let result = await this.model.findFirst(query);
|
|
701
|
+
return this.transformRelationResults(result, includeConfig);
|
|
607
702
|
} catch(error) {
|
|
608
703
|
Logger.error('Load one by with relations error: '+error.message);
|
|
609
704
|
return null;
|
|
@@ -62,19 +62,19 @@ class PrismaSchemaGenerator
|
|
|
62
62
|
async waitForSchemaGeneration()
|
|
63
63
|
{
|
|
64
64
|
let clientPath = this.clientOutputPath || 'node_modules/.prisma/client';
|
|
65
|
-
let generatedClientPath =
|
|
66
|
-
? FileHandler.joinPaths(process.cwd(), clientPath)
|
|
67
|
-
: FileHandler.joinPaths(this.prismaSchemaPath, clientPath);
|
|
65
|
+
let generatedClientPath = this.resolveClientPath(clientPath);
|
|
68
66
|
let clientIndexPath = FileHandler.joinPaths(generatedClientPath, 'index.js');
|
|
69
67
|
let startTime = Date.now();
|
|
70
68
|
return new Promise((resolve) => {
|
|
71
69
|
let interval = setInterval(() => {
|
|
70
|
+
let awaitTime = Date.now() - startTime;
|
|
71
|
+
Logger.info('Awaiting on schema generation: ' + clientIndexPath, (awaitTime/1000).toFixed(0));
|
|
72
72
|
if(FileHandler.exists(clientIndexPath)){
|
|
73
73
|
clearInterval(interval);
|
|
74
74
|
resolve();
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
|
-
if(
|
|
77
|
+
if(awaitTime > this.maxWaitTime){
|
|
78
78
|
clearInterval(interval);
|
|
79
79
|
Logger.warning('Schema generation wait timeout reached.');
|
|
80
80
|
resolve();
|
|
@@ -83,6 +83,17 @@ class PrismaSchemaGenerator
|
|
|
83
83
|
});
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
resolveClientPath(clientPath)
|
|
87
|
+
{
|
|
88
|
+
if(clientPath.match(/^[a-zA-Z]:/) || clientPath.startsWith('/')){
|
|
89
|
+
return clientPath;
|
|
90
|
+
}
|
|
91
|
+
if(clientPath.startsWith('node_modules')){
|
|
92
|
+
return FileHandler.joinPaths(process.cwd(), clientPath);
|
|
93
|
+
}
|
|
94
|
+
return FileHandler.joinPaths(this.prismaSchemaPath, clientPath);
|
|
95
|
+
}
|
|
96
|
+
|
|
86
97
|
generateSchemaFile()
|
|
87
98
|
{
|
|
88
99
|
let datasourceProvider = this.getDatasourceProvider();
|
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.57.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.
|
|
48
|
+
"@prisma/client": "^6.10.0",
|
|
49
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.10.0"
|
|
56
56
|
}
|
|
57
57
|
}
|