@reldens/storage 0.117.0 → 0.118.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/.claude/test-architecture.md +27 -17
- package/CLAUDE.md +67 -11
- package/README.md +204 -22
- package/bin/reldens-storage.js +43 -10
- package/index.js +53 -12
- package/lib/drizzle/drizzle-data-server.js +123 -0
- package/lib/drizzle/drizzle-driver.js +228 -0
- package/lib/drizzle/drizzle-models-generation.js +49 -0
- package/lib/drizzle/drizzle-modules-loader.js +32 -0
- package/lib/drizzle/drizzle-modules-validator.js +74 -0
- package/lib/entities-generator.js +36 -7
- package/lib/entity-templates/drizzle-model.template +28 -0
- package/lib/entity-templates/query-builder-model.template +17 -0
- package/lib/generators/base-generator.js +13 -0
- package/lib/generators/entities-generation.js +0 -13
- package/lib/generators/models-generation.js +179 -680
- package/lib/generators/relations-detection.js +171 -0
- package/lib/knex/knex-data-server.js +128 -0
- package/lib/knex/knex-driver.js +157 -0
- package/lib/knex/knex-modules-loader.js +28 -0
- package/lib/knex/knex-modules-validator.js +45 -0
- package/lib/kysely/kysely-data-server.js +128 -0
- package/lib/kysely/kysely-driver.js +176 -0
- package/lib/kysely/kysely-modules-loader.js +28 -0
- package/lib/kysely/kysely-modules-validator.js +53 -0
- package/lib/mikro-orm/mikro-orm-data-server.js +48 -29
- package/lib/mikro-orm/mikro-orm-driver.js +317 -108
- package/lib/mikro-orm/mikro-orm-models-generation.js +178 -0
- package/lib/mikro-orm/mikro-orm-modules-loader.js +50 -0
- package/lib/mikro-orm/mikro-orm-modules-validator.js +46 -0
- package/lib/mysql2-connection-config.js +38 -0
- package/lib/objection-js/objection-js-data-server.js +71 -125
- package/lib/objection-js/objection-js-driver.js +4 -1
- package/lib/objection-js/objection-js-models-generation.js +98 -0
- package/lib/objection-js/objection-modules-loader.js +28 -0
- package/lib/objection-js/objection-modules-validator.js +31 -0
- package/lib/package-resolver.js +45 -0
- package/lib/prisma/prisma-client-loader.js +11 -1
- package/lib/prisma/prisma-driver.js +16 -2
- package/lib/prisma/prisma-filter-processor.js +5 -6
- package/lib/prisma/prisma-models-generation.js +236 -0
- package/lib/prisma/prisma-schema-generator.js +5 -1
- package/lib/prisma/prisma-type-caster.js +14 -0
- package/lib/query-builder-driver.js +210 -0
- package/lib/query-builder-models-generation.js +80 -0
- package/lib/relations-loader.js +192 -0
- package/lib/type-mapper.js +38 -0
- package/package.json +7 -7
- package/tests/.env.test.example +6 -0
- package/tests/fixtures/categories-fixtures.js +8 -0
- package/tests/fixtures/expected-entities/entities/test-product-details-entity.js +10 -1
- package/tests/fixtures/expected-entities/entities-translations.js +2 -0
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-categories-model.js +13 -8
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-product-details-model.js +30 -10
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-products-model.js +15 -14
- package/tests/fixtures/expected-entities-mikro-orm/models/mikro-orm/test-reviews-model.js +13 -12
- package/tests/fixtures/expected-entities-prisma/models/prisma/test-product-details-model.js +3 -1
- package/tests/fixtures/product-details-fixtures.js +4 -1
- package/tests/fixtures/reviews-fixtures.js +2 -1
- package/tests/fixtures/sql/test-schema.sql +11 -2
- package/tests/fixtures/table-columns-fixtures.js +97 -0
- package/tests/integration/test-cross-driver-equivalence.js +286 -0
- package/tests/integration/test-nested-filters.js +265 -408
- package/tests/integration/test-relations.js +135 -0
- package/tests/integration/test-reldens-sample-data.js +255 -0
- package/tests/integration/test-reldens-shape-relations.js +292 -0
- package/tests/run-tests.js +111 -65
- package/tests/unit/test-drivers.js +7 -1
- package/tests/unit/test-models-generation.js +28 -18
- package/tests/utils/column-value-assert.js +139 -0
- package/tests/utils/entity-projection.js +111 -0
- package/tests/utils/observed-value.js +41 -0
- package/tests/utils/projection-difference.js +151 -0
- package/tests/utils/relations-shape-support.js +104 -0
- package/tests/utils/reldens-schema-loader.js +274 -0
- package/tests/utils/test-helpers.js +143 -13
- package/tests/utils/test-runner.js +26 -5
- package/tests/utils/whole-result-assert.js +243 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - ObjectionModulesValidator
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
8
|
+
|
|
9
|
+
class ObjectionModulesValidator
|
|
10
|
+
{
|
|
11
|
+
|
|
12
|
+
static validate(objectionModules)
|
|
13
|
+
{
|
|
14
|
+
if(!sc.isObject(objectionModules)){
|
|
15
|
+
Logger.critical('Missing "objectionModules" object.');
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
if(!sc.isFunction(objectionModules.Model)){
|
|
19
|
+
Logger.critical('Invalid "objectionModules.Model".');
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if(!sc.isFunction(objectionModules.Model.knex)){
|
|
23
|
+
Logger.critical('Invalid "objectionModules.Model", "knex" not found.');
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports.ObjectionModulesValidator = ObjectionModulesValidator;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - PackageResolver
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { Logger } = require('@reldens/utils');
|
|
8
|
+
|
|
9
|
+
class PackageResolver
|
|
10
|
+
{
|
|
11
|
+
|
|
12
|
+
static loadPackage(packageName, projectPath)
|
|
13
|
+
{
|
|
14
|
+
try {
|
|
15
|
+
return require(PackageResolver.resolvePath(packageName, projectPath));
|
|
16
|
+
} catch(error) {
|
|
17
|
+
Logger.critical(
|
|
18
|
+
'Package "'+packageName+'" not found, install it in the project: npm install '+packageName,
|
|
19
|
+
error.message
|
|
20
|
+
);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static optionalPackage(packageName, projectPath)
|
|
26
|
+
{
|
|
27
|
+
try {
|
|
28
|
+
return require(PackageResolver.resolvePath(packageName, projectPath));
|
|
29
|
+
} catch(error) {
|
|
30
|
+
Logger.debug('Optional package "'+packageName+'" is not available.', error.message);
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static resolvePath(packageName, projectPath)
|
|
36
|
+
{
|
|
37
|
+
if(!projectPath){
|
|
38
|
+
return packageName;
|
|
39
|
+
}
|
|
40
|
+
return require.resolve(packageName, {paths: [projectPath]});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports.PackageResolver = PackageResolver;
|
|
@@ -40,7 +40,8 @@ class PrismaClientLoader
|
|
|
40
40
|
+(connectionData.password ? ':'+connectionData.password : '')
|
|
41
41
|
+'@'+connectionData.host
|
|
42
42
|
+':'+connectionData.port
|
|
43
|
-
+'/'+connectionData.database
|
|
43
|
+
+'/'+connectionData.database
|
|
44
|
+
+PrismaClientLoader.connectionLimitParameter(connectionData);
|
|
44
45
|
Logger.info('Creating PrismaClient with connection to: '+connectionData.database);
|
|
45
46
|
return PrismaClientLoader.createWithAdapter(completedModules, connectionString);
|
|
46
47
|
}
|
|
@@ -50,6 +51,15 @@ class PrismaClientLoader
|
|
|
50
51
|
* @param {string} connectionUrl
|
|
51
52
|
* @returns {Object|null}
|
|
52
53
|
*/
|
|
54
|
+
static connectionLimitParameter(connectionData)
|
|
55
|
+
{
|
|
56
|
+
let connectionLimit = sc.get(connectionData, 'connectionLimit', false);
|
|
57
|
+
if(!connectionLimit){
|
|
58
|
+
return '';
|
|
59
|
+
}
|
|
60
|
+
return '?connectionLimit='+connectionLimit;
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
static createWithAdapter(prismaModules, connectionUrl)
|
|
54
64
|
{
|
|
55
65
|
if(!PrismaModulesValidator.validate(prismaModules)){
|
|
@@ -237,6 +237,9 @@ class PrismaDriver extends BaseDriver
|
|
|
237
237
|
|
|
238
238
|
async createWithRelations(params, relations)
|
|
239
239
|
{
|
|
240
|
+
if(sc.isString(relations)){
|
|
241
|
+
relations = this.parseRelationsString(relations);
|
|
242
|
+
}
|
|
240
243
|
if(!sc.isArray(relations) || 0 === relations.length){
|
|
241
244
|
relations = this.getAllRelations();
|
|
242
245
|
}
|
|
@@ -343,11 +346,15 @@ class PrismaDriver extends BaseDriver
|
|
|
343
346
|
async delete(filters = {})
|
|
344
347
|
{
|
|
345
348
|
let processedFilters = this.filterProcessor.processFilters(filters);
|
|
346
|
-
|
|
349
|
+
let deleteResult = await this.executeWithNormalization(
|
|
347
350
|
() => this.model.deleteMany({where: processedFilters}),
|
|
348
351
|
'Delete error: ',
|
|
349
352
|
false
|
|
350
353
|
);
|
|
354
|
+
if(!deleteResult){
|
|
355
|
+
return deleteResult;
|
|
356
|
+
}
|
|
357
|
+
return sc.get(deleteResult, 'count', 0);
|
|
351
358
|
}
|
|
352
359
|
|
|
353
360
|
async deleteById(id)
|
|
@@ -357,11 +364,15 @@ class PrismaDriver extends BaseDriver
|
|
|
357
364
|
if(!found){
|
|
358
365
|
return false;
|
|
359
366
|
}
|
|
360
|
-
|
|
367
|
+
let deleteResult = await this.executeWithNormalization(
|
|
361
368
|
() => this.model.delete({where: {[this.idFieldName]: castedId}}),
|
|
362
369
|
'Delete by ID error: ',
|
|
363
370
|
false
|
|
364
371
|
);
|
|
372
|
+
if(!deleteResult){
|
|
373
|
+
return deleteResult;
|
|
374
|
+
}
|
|
375
|
+
return 1;
|
|
365
376
|
}
|
|
366
377
|
|
|
367
378
|
async count(filters)
|
|
@@ -540,6 +551,9 @@ class PrismaDriver extends BaseDriver
|
|
|
540
551
|
|
|
541
552
|
async executeQueryWithRelations(modelMethod, query, relations, errorMessage, defaultReturn)
|
|
542
553
|
{
|
|
554
|
+
if(sc.isString(relations)){
|
|
555
|
+
relations = this.parseRelationsString(relations);
|
|
556
|
+
}
|
|
543
557
|
if(!sc.isArray(relations) || 0 === relations.length){
|
|
544
558
|
relations = this.getAllRelations();
|
|
545
559
|
}
|
|
@@ -35,12 +35,11 @@ class PrismaFilterProcessor
|
|
|
35
35
|
let processedFilters = {};
|
|
36
36
|
for(let key of Object.keys(filters)){
|
|
37
37
|
let value = filters[key];
|
|
38
|
-
if('AND' === key && sc.isArray(value)){
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
processedFilters.OR = value.map(condition => this.processFilters(condition));
|
|
38
|
+
if(('AND' === key || 'OR' === key) && sc.isArray(value)){
|
|
39
|
+
if(0 === value.length){
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
processedFilters[key] = value.map(condition => this.processFilters(condition));
|
|
44
43
|
continue;
|
|
45
44
|
}
|
|
46
45
|
let translatedKey = this.translateRelationKey(key);
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - PrismaModelsGeneration
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { sc } = require('@reldens/utils');
|
|
8
|
+
const { TypeMapper } = require('../type-mapper');
|
|
9
|
+
const { RELATION_PREFIX } = require('../relation-key');
|
|
10
|
+
|
|
11
|
+
class PrismaModelsGeneration
|
|
12
|
+
{
|
|
13
|
+
|
|
14
|
+
constructor(relationsDetection)
|
|
15
|
+
{
|
|
16
|
+
this.relationsDetection = relationsDetection;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
modelTableName(tableName)
|
|
20
|
+
{
|
|
21
|
+
return tableName.toLowerCase();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
generateFkMappings(tableName, tableData)
|
|
25
|
+
{
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
generateIdColumn(columns)
|
|
30
|
+
{
|
|
31
|
+
let primaryKeyColumn = this.relationsDetection.detectPrimaryKeyColumn(columns);
|
|
32
|
+
if(!primaryKeyColumn || 'id' === primaryKeyColumn){
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
return '\n static get idColumn()\n {\n return \''+primaryKeyColumn+'\';\n }\n';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
generateEntityProperties(columns, tableName, tableData)
|
|
39
|
+
{
|
|
40
|
+
let prismaProps = [];
|
|
41
|
+
for(let columnName of Object.keys(columns)){
|
|
42
|
+
let column = columns[columnName];
|
|
43
|
+
let propDef = columnName+': {\n type: \''+TypeMapper.mapDbTypeToPrismaType(column.type)+'\'';
|
|
44
|
+
if('PRI' === column.key){
|
|
45
|
+
propDef += ',\n id: true';
|
|
46
|
+
}
|
|
47
|
+
if(column.nullable){
|
|
48
|
+
propDef += ',\n optional: true';
|
|
49
|
+
}
|
|
50
|
+
prismaProps.push(propDef+'\n }');
|
|
51
|
+
}
|
|
52
|
+
return prismaProps.join(',\n ');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
generateRelations(tableName, tableData, relationMetadata)
|
|
56
|
+
{
|
|
57
|
+
if(!relationMetadata || 0 === Object.keys(relationMetadata).length){
|
|
58
|
+
return '';
|
|
59
|
+
}
|
|
60
|
+
let relationTypesArray = [];
|
|
61
|
+
for(let prismaRelationName of Object.keys(relationMetadata)){
|
|
62
|
+
relationTypesArray.push(
|
|
63
|
+
' '+prismaRelationName+': \''+relationMetadata[prismaRelationName].type+'\''
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
let allMappings = Object.assign(
|
|
67
|
+
{},
|
|
68
|
+
this.buildForwardMappings(tableData, relationMetadata),
|
|
69
|
+
this.buildReverseMappings(tableName, relationMetadata)
|
|
70
|
+
);
|
|
71
|
+
let relationMappingsArray = [];
|
|
72
|
+
for(let reldensKey of Object.keys(allMappings)){
|
|
73
|
+
relationMappingsArray.push(' \''+reldensKey+'\': \''+allMappings[reldensKey]+'\'');
|
|
74
|
+
}
|
|
75
|
+
return this.staticGetter('relationTypes', relationTypesArray)
|
|
76
|
+
+this.staticGetter('relationMappings', relationMappingsArray);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
staticGetter(getterName, entries)
|
|
80
|
+
{
|
|
81
|
+
if(0 === entries.length){
|
|
82
|
+
return '';
|
|
83
|
+
}
|
|
84
|
+
return '\n\n static get '+getterName+'()\n {\n return {\n'+entries.join(',\n')+'\n };\n }';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
buildForwardMappings(tableData, relationMetadata)
|
|
88
|
+
{
|
|
89
|
+
let mappings = {};
|
|
90
|
+
let referenceCounts = this.relationsDetection.countReferencesPerTable(tableData);
|
|
91
|
+
for(let columnName of Object.keys(tableData.columns)){
|
|
92
|
+
let column = tableData.columns[columnName];
|
|
93
|
+
if(!sc.hasOwn(column, 'referencedTable')){
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
let prismaName = this.findRelationForFk(columnName, column.referencedTable, relationMetadata);
|
|
97
|
+
if(!prismaName){
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
let reldensKey = this.relationsDetection.generateForwardRelationKey(
|
|
101
|
+
column.referencedTable,
|
|
102
|
+
columnName,
|
|
103
|
+
referenceCounts
|
|
104
|
+
);
|
|
105
|
+
mappings[reldensKey] = prismaName;
|
|
106
|
+
}
|
|
107
|
+
return mappings;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
findRelationForFk(columnName, referencedTable, relationMetadata)
|
|
111
|
+
{
|
|
112
|
+
let lowerColumn = columnName.toLowerCase();
|
|
113
|
+
let candidates = this.singleRelationCandidates(referencedTable, relationMetadata);
|
|
114
|
+
for(let prismaName of candidates){
|
|
115
|
+
let lowerPrisma = prismaName.toLowerCase();
|
|
116
|
+
if(lowerPrisma.includes(lowerColumn.replace('_id', ''))){
|
|
117
|
+
return prismaName;
|
|
118
|
+
}
|
|
119
|
+
if(lowerPrisma.includes(lowerColumn)){
|
|
120
|
+
return prismaName;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if(0 === candidates.length){
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return [...candidates].shift();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
singleRelationCandidates(referencedTable, relationMetadata)
|
|
130
|
+
{
|
|
131
|
+
let lowerTable = referencedTable.toLowerCase();
|
|
132
|
+
let candidates = [];
|
|
133
|
+
for(let prismaName of Object.keys(relationMetadata)){
|
|
134
|
+
let relation = relationMetadata[prismaName];
|
|
135
|
+
if(relation.model.toLowerCase() !== lowerTable){
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if(relation.isList){
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
candidates.push(prismaName);
|
|
142
|
+
}
|
|
143
|
+
return candidates;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
buildReverseMappings(tableName, relationMetadata)
|
|
147
|
+
{
|
|
148
|
+
let mappings = {};
|
|
149
|
+
let reverseRelations = this.groupReverseRelationsByTable(relationMetadata);
|
|
150
|
+
for(let relatedTable of Object.keys(reverseRelations)){
|
|
151
|
+
Object.assign(
|
|
152
|
+
mappings,
|
|
153
|
+
this.tableReverseMappings(relatedTable, reverseRelations[relatedTable], tableName)
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return mappings;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
tableReverseMappings(relatedTable, relations, tableName)
|
|
160
|
+
{
|
|
161
|
+
let mappings = {};
|
|
162
|
+
for(let relInfo of relations){
|
|
163
|
+
let reldensKey = this.reverseRelationKey(relatedTable, relInfo.prismaName, tableName, relations.length);
|
|
164
|
+
mappings[reldensKey] = relInfo.prismaName;
|
|
165
|
+
}
|
|
166
|
+
return mappings;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
groupReverseRelationsByTable(relationMetadata)
|
|
170
|
+
{
|
|
171
|
+
let reverseRelations = {};
|
|
172
|
+
for(let prismaName of Object.keys(relationMetadata)){
|
|
173
|
+
let relation = relationMetadata[prismaName];
|
|
174
|
+
if(this.isForwardRelation(relation)){
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
let relatedTable = relation.model.toLowerCase();
|
|
178
|
+
if(!reverseRelations[relatedTable]){
|
|
179
|
+
reverseRelations[relatedTable] = [];
|
|
180
|
+
}
|
|
181
|
+
reverseRelations[relatedTable].push({prismaName, relation});
|
|
182
|
+
}
|
|
183
|
+
return reverseRelations;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
isForwardRelation(relation)
|
|
187
|
+
{
|
|
188
|
+
return sc.hasOwn(relation, 'foreignKeys')
|
|
189
|
+
&& sc.isArray(relation.foreignKeys)
|
|
190
|
+
&& 0 < relation.foreignKeys.length;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
reverseRelationKey(relatedTable, prismaName, tableName, referenceCount)
|
|
194
|
+
{
|
|
195
|
+
if(1 === referenceCount){
|
|
196
|
+
return RELATION_PREFIX+relatedTable;
|
|
197
|
+
}
|
|
198
|
+
let columnHint = this.extractColumnHint(prismaName, relatedTable, tableName);
|
|
199
|
+
if(columnHint){
|
|
200
|
+
return RELATION_PREFIX+relatedTable+'_'+columnHint;
|
|
201
|
+
}
|
|
202
|
+
return RELATION_PREFIX+relatedTable;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
extractColumnHint(prismaName, relatedTable, tableName)
|
|
206
|
+
{
|
|
207
|
+
let lowerName = prismaName.toLowerCase();
|
|
208
|
+
let lowerTable = tableName.toLowerCase();
|
|
209
|
+
let lowerRelated = relatedTable.toLowerCase();
|
|
210
|
+
let toSuffix = 'to'+lowerTable;
|
|
211
|
+
if(!lowerName.endsWith(toSuffix)){
|
|
212
|
+
toSuffix = 'to'+lowerRelated;
|
|
213
|
+
if(!lowerName.endsWith(toSuffix)){
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
let withoutSuffix = prismaName.slice(0, -toSuffix.length);
|
|
218
|
+
let prefixes = [
|
|
219
|
+
lowerRelated+'_'+lowerRelated+'_',
|
|
220
|
+
lowerRelated+'_',
|
|
221
|
+
lowerTable+'_'
|
|
222
|
+
];
|
|
223
|
+
for(let prefix of prefixes){
|
|
224
|
+
if(withoutSuffix.toLowerCase().startsWith(prefix)){
|
|
225
|
+
withoutSuffix = withoutSuffix.slice(prefix.length);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if(withoutSuffix.endsWith('_id')){
|
|
229
|
+
withoutSuffix = withoutSuffix.slice(0, -3);
|
|
230
|
+
}
|
|
231
|
+
return withoutSuffix.toLowerCase();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports.PrismaModelsGeneration = PrismaModelsGeneration;
|
|
@@ -139,7 +139,11 @@ class PrismaSchemaGenerator
|
|
|
139
139
|
|
|
140
140
|
generateConfigFile()
|
|
141
141
|
{
|
|
142
|
-
let configContent = '
|
|
142
|
+
let configContent = 'try {\n'
|
|
143
|
+
+' process.loadEnvFile(\'.env\');\n'
|
|
144
|
+
+'} catch(error) {\n'
|
|
145
|
+
+' process.env.RELDENS_PRISMA_ENV_ERROR = error.message;\n'
|
|
146
|
+
+'}\n'
|
|
143
147
|
+'module.exports = { datasource: { url: process.env.RELDENS_DB_URL } };\n';
|
|
144
148
|
let configPath = FileHandler.joinPaths(process.cwd(), 'prisma.config.js');
|
|
145
149
|
FileHandler.writeFile(configPath, configContent);
|
|
@@ -248,6 +248,17 @@ class PrismaTypeCaster
|
|
|
248
248
|
return casted;
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
isDecimalInstance(value)
|
|
252
|
+
{
|
|
253
|
+
if(!sc.isObject(value) || !sc.isFunction(value.toString)){
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
if(!sc.hasOwn(value, 'd') || !sc.hasOwn(value, 'e') || !sc.hasOwn(value, 's')){
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
return sc.isArray(value.d);
|
|
260
|
+
}
|
|
261
|
+
|
|
251
262
|
normalizeReturnData(data)
|
|
252
263
|
{
|
|
253
264
|
if(null === data || undefined === data){
|
|
@@ -262,6 +273,9 @@ class PrismaTypeCaster
|
|
|
262
273
|
if(data instanceof Date){
|
|
263
274
|
return data;
|
|
264
275
|
}
|
|
276
|
+
if(this.isDecimalInstance(data)){
|
|
277
|
+
return data.toString();
|
|
278
|
+
}
|
|
265
279
|
if(sc.isArray(data)){
|
|
266
280
|
return data.map(item => this.normalizeReturnData(item));
|
|
267
281
|
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - QueryBuilderDriver
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { BaseDriver } = require('./base-driver');
|
|
8
|
+
const { RelationsLoader } = require('./relations-loader');
|
|
9
|
+
const { ErrorManager, sc } = require('@reldens/utils');
|
|
10
|
+
|
|
11
|
+
class QueryBuilderDriver extends BaseDriver
|
|
12
|
+
{
|
|
13
|
+
|
|
14
|
+
constructor(props)
|
|
15
|
+
{
|
|
16
|
+
super(props);
|
|
17
|
+
this.server = sc.get(props, 'server', false);
|
|
18
|
+
this.relationsLoader = new RelationsLoader({driver: this, server: this.server});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
databaseName()
|
|
22
|
+
{
|
|
23
|
+
return sc.get(this.config, 'database', '');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
id()
|
|
27
|
+
{
|
|
28
|
+
return this.rawId || this.rawModel.tableName;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
name()
|
|
32
|
+
{
|
|
33
|
+
return this.rawName || this.rawModel.tableName;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
tableName()
|
|
37
|
+
{
|
|
38
|
+
return this.rawModel.tableName;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
idColumn()
|
|
42
|
+
{
|
|
43
|
+
return sc.get(this.rawModel, 'idColumn', 'id');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
property(propertyName)
|
|
47
|
+
{
|
|
48
|
+
return this.rawModel[propertyName] || null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
insertRow(data)
|
|
52
|
+
{
|
|
53
|
+
ErrorManager.error('QueryBuilderDriver insertRow() not implemented.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
selectRows(filters, options)
|
|
57
|
+
{
|
|
58
|
+
ErrorManager.error('QueryBuilderDriver selectRows() not implemented.');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
selectOptions(useLimit)
|
|
62
|
+
{
|
|
63
|
+
return {
|
|
64
|
+
select: this.select,
|
|
65
|
+
limit: useLimit ? this.limit : 0,
|
|
66
|
+
offset: this.offset,
|
|
67
|
+
sortBy: this.sortBy,
|
|
68
|
+
sortDirection: this.sortDirection
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
createSingleFilter(field, fieldValue, operator = null)
|
|
73
|
+
{
|
|
74
|
+
if(null === operator){
|
|
75
|
+
return {[field]: fieldValue};
|
|
76
|
+
}
|
|
77
|
+
return {[field]: {operator, value: fieldValue}};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async create(params)
|
|
81
|
+
{
|
|
82
|
+
let insertId = await this.insertRow(params);
|
|
83
|
+
let createdId = insertId ? insertId : sc.get(params, this.idColumn(), false);
|
|
84
|
+
if(!createdId){
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return await this.loadById(createdId);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
createWithRelations(params, relations)
|
|
91
|
+
{
|
|
92
|
+
return this.relationsLoader.createWithRelations(params, relations);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
updateBy(field, fieldValue, updatePatch, operator = null)
|
|
96
|
+
{
|
|
97
|
+
return this.update(this.createSingleFilter(field, fieldValue, operator), updatePatch);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async updateById(id, params)
|
|
101
|
+
{
|
|
102
|
+
await this.update({[this.idColumn()]: id}, params);
|
|
103
|
+
return await this.loadById(id);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async upsert(params, filters)
|
|
107
|
+
{
|
|
108
|
+
let idColumn = this.idColumn();
|
|
109
|
+
let providedId = sc.get(params, idColumn, false);
|
|
110
|
+
if(providedId){
|
|
111
|
+
let existentById = await this.loadById(providedId);
|
|
112
|
+
if(existentById){
|
|
113
|
+
return await this.updateById(providedId, params);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if(filters){
|
|
117
|
+
let existent = await this.loadOne(filters);
|
|
118
|
+
if(existent){
|
|
119
|
+
return await this.updateById(existent[idColumn], params);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return await this.create(params);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
deleteById(id)
|
|
126
|
+
{
|
|
127
|
+
return this.delete({[this.idColumn()]: id});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
countWithRelations(filters, relations)
|
|
131
|
+
{
|
|
132
|
+
return this.count(filters);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
loadAll()
|
|
136
|
+
{
|
|
137
|
+
return this.selectRows({}, this.selectOptions(true));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
load(filters = {})
|
|
141
|
+
{
|
|
142
|
+
return this.selectRows(filters, this.selectOptions(true));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async loadOne(filters)
|
|
146
|
+
{
|
|
147
|
+
let options = this.selectOptions(false);
|
|
148
|
+
options.limit = 1;
|
|
149
|
+
let rows = await this.selectRows(filters, options);
|
|
150
|
+
return rows.shift() || null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
loadBy(field, fieldValue, operator = null)
|
|
154
|
+
{
|
|
155
|
+
return this.load(this.createSingleFilter(field, fieldValue, operator));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
loadOneBy(field, fieldValue, operator = null)
|
|
159
|
+
{
|
|
160
|
+
return this.loadOne(this.createSingleFilter(field, fieldValue, operator));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
loadById(id)
|
|
164
|
+
{
|
|
165
|
+
return this.loadOne({[this.idColumn()]: id});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
loadByIds(ids)
|
|
169
|
+
{
|
|
170
|
+
return this.load({[this.idColumn()]: {operator: 'IN', value: ids}});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async loadAllWithRelations(relations)
|
|
174
|
+
{
|
|
175
|
+
return await this.relationsLoader.populate(await this.loadAll(), relations);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async loadWithRelations(filters, relations)
|
|
179
|
+
{
|
|
180
|
+
return await this.relationsLoader.populate(await this.load(filters), relations);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async loadByWithRelations(field, fieldValue, relations, operator = null)
|
|
184
|
+
{
|
|
185
|
+
return await this.relationsLoader.populate(await this.loadBy(field, fieldValue, operator), relations);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async loadByIdWithRelations(id, relations)
|
|
189
|
+
{
|
|
190
|
+
return await this.relationsLoader.populate(await this.loadById(id), relations);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async loadOneWithRelations(filters, relations)
|
|
194
|
+
{
|
|
195
|
+
return await this.relationsLoader.populate(await this.loadOne(filters), relations);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async loadOneByWithRelations(field, fieldValue, relations, operator = null)
|
|
199
|
+
{
|
|
200
|
+
return await this.relationsLoader.populate(await this.loadOneBy(field, fieldValue, operator), relations);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
rawQuery(content)
|
|
204
|
+
{
|
|
205
|
+
return this.server.rawQuery(content);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports.QueryBuilderDriver = QueryBuilderDriver;
|