@reldens/storage 0.117.0 → 0.119.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 +8 -8
- 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
package/bin/reldens-storage.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
const { EntitiesGenerator } = require('../lib/entities-generator');
|
|
10
10
|
const { PrismaClientLoader } = require('../lib/prisma/prisma-client-loader');
|
|
11
|
+
const { KyselyModulesLoader } = require('../lib/kysely/kysely-modules-loader');
|
|
12
|
+
const { DrizzleModulesLoader } = require('../lib/drizzle/drizzle-modules-loader');
|
|
13
|
+
const { MikroOrmModulesLoader } = require('../lib/mikro-orm/mikro-orm-modules-loader');
|
|
14
|
+
const { ObjectionModulesLoader } = require('../lib/objection-js/objection-modules-loader');
|
|
15
|
+
const { KnexModulesLoader } = require('../lib/knex/knex-modules-loader');
|
|
11
16
|
const { FileHandler } = require('@reldens/server-utils');
|
|
12
17
|
const { Logger, sc } = require('@reldens/utils');
|
|
13
18
|
|
|
@@ -65,7 +70,7 @@ class StorageEntitiesGenerator
|
|
|
65
70
|
continue;
|
|
66
71
|
}
|
|
67
72
|
if('port' === key){
|
|
68
|
-
this.config[key] =
|
|
73
|
+
this.config[key] = Number(value);
|
|
69
74
|
continue;
|
|
70
75
|
}
|
|
71
76
|
this.config[key] = value;
|
|
@@ -75,7 +80,7 @@ class StorageEntitiesGenerator
|
|
|
75
80
|
getConnectionData()
|
|
76
81
|
{
|
|
77
82
|
let connectionData = {
|
|
78
|
-
driver: sc.get(this.config, 'driver', '
|
|
83
|
+
driver: sc.get(this.config, 'driver', 'knex'),
|
|
79
84
|
client: sc.get(this.config, 'client', 'mysql2'),
|
|
80
85
|
user: sc.get(this.config, 'user', ''),
|
|
81
86
|
password: sc.get(this.config, 'password', ''),
|
|
@@ -89,6 +94,12 @@ class StorageEntitiesGenerator
|
|
|
89
94
|
if('prisma' === connectionData.driver && !this.config.client){
|
|
90
95
|
connectionData.client = 'mysql';
|
|
91
96
|
}
|
|
97
|
+
if('kysely' === connectionData.driver && !this.config.client){
|
|
98
|
+
connectionData.client = 'mysql';
|
|
99
|
+
}
|
|
100
|
+
if('drizzle' === connectionData.driver && !this.config.client){
|
|
101
|
+
connectionData.client = 'mysql';
|
|
102
|
+
}
|
|
92
103
|
return connectionData;
|
|
93
104
|
}
|
|
94
105
|
|
|
@@ -110,7 +121,7 @@ class StorageEntitiesGenerator
|
|
|
110
121
|
+' --pass=[db-password]'
|
|
111
122
|
+' --host=[db-host]'
|
|
112
123
|
+' --database=[db-name]'
|
|
113
|
-
+' --driver=[
|
|
124
|
+
+' --driver=[knex|kysely|drizzle|objection-js|mikro-orm|prisma] (default: knex)'
|
|
114
125
|
+' --client=[db-client]'
|
|
115
126
|
+' --prismaClientPath=[path-to-prisma-client]'
|
|
116
127
|
+' --prismaAdapter=[prisma-adapter-package-or-path]'
|
|
@@ -154,6 +165,32 @@ class StorageEntitiesGenerator
|
|
|
154
165
|
return loadedModules;
|
|
155
166
|
}
|
|
156
167
|
|
|
168
|
+
appendDriverModules(generatorProps, connectionData)
|
|
169
|
+
{
|
|
170
|
+
if('prisma' === connectionData.driver){
|
|
171
|
+
generatorProps.prismaModules = this.loadPrismaModules(connectionData);
|
|
172
|
+
return Boolean(generatorProps.prismaModules);
|
|
173
|
+
}
|
|
174
|
+
if('kysely' === connectionData.driver){
|
|
175
|
+
generatorProps.kyselyModules = KyselyModulesLoader.load(this.projectPath);
|
|
176
|
+
return Boolean(generatorProps.kyselyModules);
|
|
177
|
+
}
|
|
178
|
+
if('drizzle' === connectionData.driver){
|
|
179
|
+
generatorProps.drizzleModules = DrizzleModulesLoader.load(this.projectPath);
|
|
180
|
+
return Boolean(generatorProps.drizzleModules);
|
|
181
|
+
}
|
|
182
|
+
if('mikro-orm' === connectionData.driver){
|
|
183
|
+
generatorProps.mikroOrmModules = MikroOrmModulesLoader.load(this.projectPath, connectionData.client);
|
|
184
|
+
return Boolean(generatorProps.mikroOrmModules);
|
|
185
|
+
}
|
|
186
|
+
if('objection-js' === connectionData.driver){
|
|
187
|
+
generatorProps.objectionModules = ObjectionModulesLoader.load(this.projectPath);
|
|
188
|
+
return Boolean(generatorProps.objectionModules);
|
|
189
|
+
}
|
|
190
|
+
generatorProps.knexModules = KnexModulesLoader.load(this.projectPath);
|
|
191
|
+
return Boolean(generatorProps.knexModules);
|
|
192
|
+
}
|
|
193
|
+
|
|
157
194
|
async run()
|
|
158
195
|
{
|
|
159
196
|
if(!this.validateCommand()){
|
|
@@ -168,12 +205,8 @@ class StorageEntitiesGenerator
|
|
|
168
205
|
projectPath: this.projectPath,
|
|
169
206
|
isOverride: this.isOverride
|
|
170
207
|
};
|
|
171
|
-
if(
|
|
172
|
-
|
|
173
|
-
if(!prismaModules){
|
|
174
|
-
return false;
|
|
175
|
-
}
|
|
176
|
-
generatorProps.prismaModules = prismaModules;
|
|
208
|
+
if(!this.appendDriverModules(generatorProps, connectionData)){
|
|
209
|
+
return false;
|
|
177
210
|
}
|
|
178
211
|
let generator = new EntitiesGenerator(generatorProps);
|
|
179
212
|
let success = await generator.generate();
|
package/index.js
CHANGED
|
@@ -8,10 +8,13 @@ const { BaseDataServer } = require('./lib/base-data-server');
|
|
|
8
8
|
const { BaseDriver } = require('./lib/base-driver');
|
|
9
9
|
const { ObjectionJsDriver } = require('./lib/objection-js/objection-js-driver');
|
|
10
10
|
const { ObjectionJsDataServer } = require('./lib/objection-js/objection-js-data-server');
|
|
11
|
-
const {
|
|
11
|
+
const { ObjectionModulesValidator } = require('./lib/objection-js/objection-modules-validator');
|
|
12
|
+
const { ObjectionModulesLoader } = require('./lib/objection-js/objection-modules-loader');
|
|
12
13
|
const { MikroOrmDriver } = require('./lib/mikro-orm/mikro-orm-driver');
|
|
13
14
|
const { MikroOrmDataServer } = require('./lib/mikro-orm/mikro-orm-data-server');
|
|
14
|
-
const
|
|
15
|
+
const { MikroOrmModulesValidator } = require('./lib/mikro-orm/mikro-orm-modules-validator');
|
|
16
|
+
const { MikroOrmModulesLoader } = require('./lib/mikro-orm/mikro-orm-modules-loader');
|
|
17
|
+
const { PackageResolver } = require('./lib/package-resolver');
|
|
15
18
|
const { EntitiesGenerator } = require('./lib/entities-generator');
|
|
16
19
|
const { EntityProperties } = require('./lib/entity-properties');
|
|
17
20
|
const { TypeMapper } = require('./lib/type-mapper');
|
|
@@ -21,41 +24,79 @@ const { PrismaDataServer } = require('./lib/prisma/prisma-data-server');
|
|
|
21
24
|
const { PrismaSchemaGenerator } = require('./lib/prisma/prisma-schema-generator');
|
|
22
25
|
const { PrismaClientLoader } = require('./lib/prisma/prisma-client-loader');
|
|
23
26
|
const { PrismaModulesValidator } = require('./lib/prisma/prisma-modules-validator');
|
|
27
|
+
const { QueryBuilderDriver } = require('./lib/query-builder-driver');
|
|
28
|
+
const { RelationsLoader } = require('./lib/relations-loader');
|
|
29
|
+
const { Mysql2ConnectionConfig } = require('./lib/mysql2-connection-config');
|
|
30
|
+
const { KnexDriver } = require('./lib/knex/knex-driver');
|
|
31
|
+
const { KnexDataServer } = require('./lib/knex/knex-data-server');
|
|
32
|
+
const { KnexModulesValidator } = require('./lib/knex/knex-modules-validator');
|
|
33
|
+
const { KnexModulesLoader } = require('./lib/knex/knex-modules-loader');
|
|
34
|
+
const { KyselyDriver } = require('./lib/kysely/kysely-driver');
|
|
35
|
+
const { KyselyDataServer } = require('./lib/kysely/kysely-data-server');
|
|
36
|
+
const { KyselyModulesValidator } = require('./lib/kysely/kysely-modules-validator');
|
|
37
|
+
const { KyselyModulesLoader } = require('./lib/kysely/kysely-modules-loader');
|
|
38
|
+
const { DrizzleDriver } = require('./lib/drizzle/drizzle-driver');
|
|
39
|
+
const { DrizzleDataServer } = require('./lib/drizzle/drizzle-data-server');
|
|
40
|
+
const { DrizzleModulesValidator } = require('./lib/drizzle/drizzle-modules-validator');
|
|
41
|
+
const { DrizzleModulesLoader } = require('./lib/drizzle/drizzle-modules-loader');
|
|
24
42
|
const { RELATION_PREFIX } = require('./lib/relation-key');
|
|
25
43
|
|
|
44
|
+
let objectionPackage = PackageResolver.optionalPackage('objection');
|
|
45
|
+
let mikroOrmCorePackage = PackageResolver.optionalPackage('@mikro-orm/core');
|
|
46
|
+
|
|
26
47
|
module.exports = {
|
|
27
|
-
// base:
|
|
28
48
|
BaseDataServer,
|
|
29
49
|
BaseDriver,
|
|
30
50
|
DriversMap: {
|
|
31
51
|
'objection-js': ObjectionJsDataServer,
|
|
32
52
|
'mikro-orm': MikroOrmDataServer,
|
|
33
|
-
'prisma': PrismaDataServer
|
|
53
|
+
'prisma': PrismaDataServer,
|
|
54
|
+
'knex': KnexDataServer,
|
|
55
|
+
'kysely': KyselyDataServer,
|
|
56
|
+
'drizzle': DrizzleDataServer
|
|
34
57
|
},
|
|
35
58
|
DriversClassMap: {
|
|
36
59
|
'ObjectionJsDataServer': 'objection-js',
|
|
37
60
|
'MikroOrmDataServer': 'mikro-orm',
|
|
38
|
-
'PrismaDataServer': 'prisma'
|
|
61
|
+
'PrismaDataServer': 'prisma',
|
|
62
|
+
'KnexDataServer': 'knex',
|
|
63
|
+
'KyselyDataServer': 'kysely',
|
|
64
|
+
'DrizzleDataServer': 'drizzle'
|
|
39
65
|
},
|
|
40
|
-
|
|
66
|
+
QueryBuilderDriver,
|
|
67
|
+
RelationsLoader,
|
|
68
|
+
Mysql2ConnectionConfig,
|
|
69
|
+
KnexDataServer,
|
|
70
|
+
KnexDriver,
|
|
71
|
+
KyselyDataServer,
|
|
72
|
+
KyselyDriver,
|
|
73
|
+
KyselyModulesValidator,
|
|
74
|
+
KyselyModulesLoader,
|
|
75
|
+
DrizzleDataServer,
|
|
76
|
+
DrizzleDriver,
|
|
77
|
+
DrizzleModulesValidator,
|
|
78
|
+
DrizzleModulesLoader,
|
|
41
79
|
ObjectionJsDataServer,
|
|
42
80
|
ObjectionJsDriver,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
81
|
+
ObjectionModulesValidator,
|
|
82
|
+
ObjectionModulesLoader,
|
|
83
|
+
ObjectionJsRawModel: objectionPackage ? objectionPackage.Model : false,
|
|
84
|
+
MikroOrmCore: mikroOrmCorePackage,
|
|
46
85
|
MikroOrmDataServer,
|
|
47
86
|
MikroOrmDriver,
|
|
48
|
-
|
|
87
|
+
MikroOrmModulesValidator,
|
|
88
|
+
MikroOrmModulesLoader,
|
|
89
|
+
PackageResolver,
|
|
90
|
+
KnexModulesValidator,
|
|
91
|
+
KnexModulesLoader,
|
|
49
92
|
PrismaDataServer,
|
|
50
93
|
PrismaDriver,
|
|
51
94
|
PrismaSchemaGenerator,
|
|
52
95
|
PrismaClientLoader,
|
|
53
96
|
PrismaModulesValidator,
|
|
54
|
-
// entities:
|
|
55
97
|
EntitiesGenerator,
|
|
56
98
|
EntityProperties,
|
|
57
99
|
TypeMapper,
|
|
58
100
|
MySQLTablesProvider,
|
|
59
|
-
// relation key:
|
|
60
101
|
RELATION_PREFIX
|
|
61
102
|
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - DrizzleDataServer
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { BaseDataServer } = require('../base-data-server');
|
|
8
|
+
const { DrizzleDriver } = require('./drizzle-driver');
|
|
9
|
+
const { DrizzleModulesValidator } = require('./drizzle-modules-validator');
|
|
10
|
+
const { MySQLTablesProvider } = require('../mysql-tables-provider');
|
|
11
|
+
const { Mysql2ConnectionConfig } = require('../mysql2-connection-config');
|
|
12
|
+
const { createPool } = require('mysql2/promise');
|
|
13
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
14
|
+
|
|
15
|
+
class DrizzleDataServer extends BaseDataServer
|
|
16
|
+
{
|
|
17
|
+
|
|
18
|
+
constructor(props)
|
|
19
|
+
{
|
|
20
|
+
super(props);
|
|
21
|
+
this.drizzleModules = sc.get(props, 'drizzleModules', false);
|
|
22
|
+
this.db = sc.get(this.drizzleModules, 'db', false);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async connect()
|
|
26
|
+
{
|
|
27
|
+
if(this.initialized){
|
|
28
|
+
return this.initialized;
|
|
29
|
+
}
|
|
30
|
+
if(!DrizzleModulesValidator.validate(this.drizzleModules)){
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
if(!this.db){
|
|
35
|
+
this.db = this.drizzleModules.drizzle({client: createPool(this.poolConfiguration())});
|
|
36
|
+
this.drizzleModules.db = this.db;
|
|
37
|
+
}
|
|
38
|
+
await this.db.execute(this.drizzleModules.orm.sql.raw('SELECT 1'));
|
|
39
|
+
this.initialized = Date.now();
|
|
40
|
+
} catch(error) {
|
|
41
|
+
Logger.critical('Connection failed, Drizzle error: '+error.message);
|
|
42
|
+
}
|
|
43
|
+
return this.initialized;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
poolConfiguration()
|
|
47
|
+
{
|
|
48
|
+
let poolConfiguration = Mysql2ConnectionConfig.sanitize(this.config);
|
|
49
|
+
if(this.multipleStatements){
|
|
50
|
+
poolConfiguration.multipleStatements = true;
|
|
51
|
+
}
|
|
52
|
+
return poolConfiguration;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async disconnect()
|
|
56
|
+
{
|
|
57
|
+
if(!this.db){
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
await this.db.$client.end();
|
|
61
|
+
this.db = false;
|
|
62
|
+
this.initialized = false;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
generateEntities()
|
|
67
|
+
{
|
|
68
|
+
if(!this.rawEntities){
|
|
69
|
+
Logger.warning('Empty raw entities array, none entities generated.');
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
this.entities = {};
|
|
73
|
+
for(let i of Object.keys(this.rawEntities)){
|
|
74
|
+
let rawEntity = this.rawEntities[i];
|
|
75
|
+
if(!sc.isString(sc.get(rawEntity, 'tableName', false))){
|
|
76
|
+
Logger.critical('Invalid raw entity "'+i+'".');
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if(!sc.isObject(sc.get(rawEntity, 'table', false))){
|
|
80
|
+
Logger.critical('Missing Drizzle table on raw entity "'+i+'".');
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
this.entities[i] = new DrizzleDriver({
|
|
84
|
+
rawModel: rawEntity,
|
|
85
|
+
id: i,
|
|
86
|
+
name: i,
|
|
87
|
+
config: this.config,
|
|
88
|
+
server: this,
|
|
89
|
+
db: this.db,
|
|
90
|
+
orm: this.drizzleModules.orm
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
this.entityManager.setEntities(this.entities);
|
|
94
|
+
return this.entities;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
name()
|
|
98
|
+
{
|
|
99
|
+
return this.name || 'Drizzle Data Server Driver';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async rawQuery(content)
|
|
103
|
+
{
|
|
104
|
+
return (await this.db.execute(this.drizzleModules.orm.sql.raw(content))).shift() || false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async fetchEntitiesFromDatabase()
|
|
108
|
+
{
|
|
109
|
+
if(!this.initialized){
|
|
110
|
+
Logger.critical('Connection was not initialized, please use the connect method first.');
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
return await MySQLTablesProvider.fetchTables(this);
|
|
115
|
+
} catch(error) {
|
|
116
|
+
Logger.critical('Drizzle Data Server tables fetch failed. '+error.message);
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports.DrizzleDataServer = DrizzleDataServer;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - DrizzleDriver
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { QueryBuilderDriver } = require('../query-builder-driver');
|
|
8
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
9
|
+
|
|
10
|
+
class DrizzleDriver extends QueryBuilderDriver
|
|
11
|
+
{
|
|
12
|
+
|
|
13
|
+
constructor(props)
|
|
14
|
+
{
|
|
15
|
+
super(props);
|
|
16
|
+
this.db = sc.get(props, 'db', false);
|
|
17
|
+
this.orm = sc.get(props, 'orm', false);
|
|
18
|
+
this.table = sc.get(this.rawModel, 'table', false);
|
|
19
|
+
this.comparisonFunctions = {'=': 'eq', '!=': 'ne', '>': 'gt', '>=': 'gte', '<': 'lt', '<=': 'lte'};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
column(field)
|
|
23
|
+
{
|
|
24
|
+
let column = sc.get(this.table, field, false);
|
|
25
|
+
if(!column){
|
|
26
|
+
Logger.error('Column "'+field+'" not defined on table "'+this.tableName()+'".');
|
|
27
|
+
}
|
|
28
|
+
return column;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
prepareData(data)
|
|
32
|
+
{
|
|
33
|
+
let prepared = Object.assign({}, data);
|
|
34
|
+
for(let key of Object.keys(prepared)){
|
|
35
|
+
if(!sc.isString(prepared[key])){
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if('json' !== sc.get(this.column(key), 'dataType', '')){
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
prepared[key] = sc.parseJson(prepared[key]);
|
|
42
|
+
}
|
|
43
|
+
return prepared;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async insertRow(data)
|
|
47
|
+
{
|
|
48
|
+
return Number(sc.get((await this.db.insert(this.table).values(this.prepareData(data))).shift(), 'insertId', 0));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
selectRows(filters, options)
|
|
52
|
+
{
|
|
53
|
+
let query = 0 < options.select.length
|
|
54
|
+
? this.db.select(this.selectedColumns(options.select))
|
|
55
|
+
: this.db.select();
|
|
56
|
+
query = this.appendCondition(query.from(this.table), filters);
|
|
57
|
+
if(0 < options.limit){
|
|
58
|
+
query = query.limit(options.limit);
|
|
59
|
+
}
|
|
60
|
+
if(0 < options.offset){
|
|
61
|
+
query = query.offset(options.offset);
|
|
62
|
+
}
|
|
63
|
+
if(options.sortBy && options.sortDirection){
|
|
64
|
+
query = query.orderBy(this.orderByExpression(options));
|
|
65
|
+
}
|
|
66
|
+
return query;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
orderByExpression(options)
|
|
70
|
+
{
|
|
71
|
+
if('DESC' === options.sortDirection.toUpperCase()){
|
|
72
|
+
return this.orm.desc(this.column(options.sortBy));
|
|
73
|
+
}
|
|
74
|
+
return this.orm.asc(this.column(options.sortBy));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
selectedColumns(select)
|
|
78
|
+
{
|
|
79
|
+
let columns = {};
|
|
80
|
+
for(let field of select){
|
|
81
|
+
columns[field] = this.column(field);
|
|
82
|
+
}
|
|
83
|
+
return columns;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
appendCondition(query, filters)
|
|
87
|
+
{
|
|
88
|
+
let condition = this.filtersCondition(filters);
|
|
89
|
+
if(!condition){
|
|
90
|
+
return query;
|
|
91
|
+
}
|
|
92
|
+
return query.where(condition);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async update(filters, updatePatch)
|
|
96
|
+
{
|
|
97
|
+
let query = this.db.update(this.table).set(this.prepareData(updatePatch));
|
|
98
|
+
return Number(sc.get((await this.appendCondition(query, filters)).shift(), 'affectedRows', 0));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async delete(filters = {})
|
|
102
|
+
{
|
|
103
|
+
let deleteResult = await this.appendCondition(this.db.delete(this.table), filters);
|
|
104
|
+
return Number(sc.get(deleteResult.shift(), 'affectedRows', 0));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async count(filters = {})
|
|
108
|
+
{
|
|
109
|
+
let query = this.db.select({count: this.orm.count()}).from(this.table);
|
|
110
|
+
let rows = await this.appendCondition(query, filters);
|
|
111
|
+
let firstRow = rows.shift();
|
|
112
|
+
if(!firstRow){
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
return Number(firstRow.count);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
filtersCondition(filters = {})
|
|
119
|
+
{
|
|
120
|
+
let conditions = [];
|
|
121
|
+
for(let key of Object.keys(filters)){
|
|
122
|
+
conditions = this.appendFilterCondition(conditions, key, filters[key]);
|
|
123
|
+
}
|
|
124
|
+
if(0 === conditions.length){
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return this.orm.and(...conditions);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
appendFilterCondition(conditions, key, filter)
|
|
131
|
+
{
|
|
132
|
+
if(('AND' === key || 'OR' === key) && sc.isArray(filter)){
|
|
133
|
+
return this.appendGroupCondition(conditions, key, filter);
|
|
134
|
+
}
|
|
135
|
+
let operator = sc.get(filter, 'operator', '').toUpperCase();
|
|
136
|
+
if('OR' === operator){
|
|
137
|
+
return this.appendOrValueCondition(conditions, key, filter);
|
|
138
|
+
}
|
|
139
|
+
if(operator){
|
|
140
|
+
conditions.push(this.operatorCondition(key, operator, filter));
|
|
141
|
+
return conditions;
|
|
142
|
+
}
|
|
143
|
+
if(sc.hasOwn(this.relationsLoader.relationMappings, key) && sc.isObject(filter)){
|
|
144
|
+
let mapping = this.relationsLoader.relationMappings[key];
|
|
145
|
+
conditions.push(this.orm.inArray(this.column(mapping.from), this.relationSubQuery(mapping, filter)));
|
|
146
|
+
return conditions;
|
|
147
|
+
}
|
|
148
|
+
if(null === filter){
|
|
149
|
+
conditions.push(this.orm.isNull(this.column(key)));
|
|
150
|
+
return conditions;
|
|
151
|
+
}
|
|
152
|
+
conditions.push(this.orm.eq(this.column(key), filter));
|
|
153
|
+
return conditions;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
appendGroupCondition(conditions, key, group)
|
|
157
|
+
{
|
|
158
|
+
let nested = [];
|
|
159
|
+
for(let condition of group){
|
|
160
|
+
let nestedCondition = this.filtersCondition(condition);
|
|
161
|
+
if(nestedCondition){
|
|
162
|
+
nested.push(nestedCondition);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if(0 === nested.length){
|
|
166
|
+
return conditions;
|
|
167
|
+
}
|
|
168
|
+
conditions.push('AND' === key ? this.orm.and(...nested) : this.orm.or(...nested));
|
|
169
|
+
return conditions;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
appendOrValueCondition(conditions, key, filter)
|
|
173
|
+
{
|
|
174
|
+
let orCondition = this.orm.eq(this.column(key), filter.value);
|
|
175
|
+
if(0 === conditions.length){
|
|
176
|
+
return [orCondition];
|
|
177
|
+
}
|
|
178
|
+
return [this.orm.or(this.orm.and(...conditions), orCondition)];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
operatorCondition(field, operator, filter)
|
|
182
|
+
{
|
|
183
|
+
let column = this.column(field);
|
|
184
|
+
if('IN' === operator){
|
|
185
|
+
return this.orm.inArray(column, filter.value);
|
|
186
|
+
}
|
|
187
|
+
if('NOT' === operator){
|
|
188
|
+
return this.orm.not(this.orm.eq(column, filter.value));
|
|
189
|
+
}
|
|
190
|
+
if('LIKE' === operator){
|
|
191
|
+
return this.likeCondition(column, field, filter.value);
|
|
192
|
+
}
|
|
193
|
+
let sqlOperator = sc.get(this.operatorsMap, operator, filter.operator);
|
|
194
|
+
let comparisonFunction = sc.get(this.comparisonFunctions, sqlOperator, false);
|
|
195
|
+
if(!comparisonFunction){
|
|
196
|
+
Logger.error('Unsupported operator "'+filter.operator+'" on Drizzle driver, "=" used instead.');
|
|
197
|
+
return this.orm.eq(column, filter.value);
|
|
198
|
+
}
|
|
199
|
+
return this.orm[comparisonFunction](column, filter.value);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
likeCondition(column, field, value)
|
|
203
|
+
{
|
|
204
|
+
let likeValue = '%'+String(value).replace(/%/g, '')+'%';
|
|
205
|
+
if(this.isJsonField(field)){
|
|
206
|
+
let castExpression = 'CAST(`'+this.tableName()+'`.`'+field+'` AS CHAR)';
|
|
207
|
+
return this.orm.like(this.orm.sql.raw(castExpression), likeValue);
|
|
208
|
+
}
|
|
209
|
+
return this.orm.like(column, likeValue);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
relationSubQuery(mapping, nestedFilters)
|
|
213
|
+
{
|
|
214
|
+
let relatedDriver = this.relationsLoader.relatedDriver(mapping);
|
|
215
|
+
if(!relatedDriver){
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
let subQuery = this.db.select({value: relatedDriver.column(mapping.to)}).from(relatedDriver.table);
|
|
219
|
+
let condition = relatedDriver.filtersCondition(nestedFilters);
|
|
220
|
+
if(!condition){
|
|
221
|
+
return subQuery;
|
|
222
|
+
}
|
|
223
|
+
return subQuery.where(condition);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
module.exports.DrizzleDriver = DrizzleDriver;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - DrizzleModelsGeneration
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { QueryBuilderModelsGeneration } = require('../query-builder-models-generation');
|
|
8
|
+
const { TypeMapper } = require('../type-mapper');
|
|
9
|
+
|
|
10
|
+
class DrizzleModelsGeneration extends QueryBuilderModelsGeneration
|
|
11
|
+
{
|
|
12
|
+
|
|
13
|
+
generateEntityProperties(columns, tableName, tableData)
|
|
14
|
+
{
|
|
15
|
+
let columnLines = [];
|
|
16
|
+
for(let columnName of Object.keys(columns)){
|
|
17
|
+
columnLines.push(columnName+': '+this.columnBuilder(columns[columnName]));
|
|
18
|
+
}
|
|
19
|
+
return columnLines.join(',\n ');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
columnBuilder(column)
|
|
23
|
+
{
|
|
24
|
+
let builderName = TypeMapper.mapDbTypeToDrizzleBuilder(column.type);
|
|
25
|
+
let builder = 'DrizzleMysqlCore.'+builderName+'('+this.builderArguments(builderName, column)+')';
|
|
26
|
+
if('PRI' === column.key){
|
|
27
|
+
return builder+'.primaryKey()';
|
|
28
|
+
}
|
|
29
|
+
return builder;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
builderArguments(builderName, column)
|
|
33
|
+
{
|
|
34
|
+
let builderArguments = '\''+column.name+'\'';
|
|
35
|
+
if('mysqlEnum' === builderName){
|
|
36
|
+
return builderArguments+', [\''+this.parseEnumValues(column.columnType).join('\', \'')+'\']';
|
|
37
|
+
}
|
|
38
|
+
if('varchar' === builderName || 'char' === builderName || 'varbinary' === builderName){
|
|
39
|
+
return builderArguments+', {length: '+column.length+'}';
|
|
40
|
+
}
|
|
41
|
+
if('bigint' === builderName){
|
|
42
|
+
return builderArguments+', {mode: \'number\'}';
|
|
43
|
+
}
|
|
44
|
+
return builderArguments;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports.DrizzleModelsGeneration = DrizzleModelsGeneration;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - Storage - DrizzleModulesLoader
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { DrizzleModulesValidator } = require('./drizzle-modules-validator');
|
|
8
|
+
const { PackageResolver } = require('../package-resolver');
|
|
9
|
+
|
|
10
|
+
class DrizzleModulesLoader
|
|
11
|
+
{
|
|
12
|
+
|
|
13
|
+
static load(projectPath)
|
|
14
|
+
{
|
|
15
|
+
let orm = PackageResolver.loadPackage('drizzle-orm', projectPath);
|
|
16
|
+
if(!orm){
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
let mysql2Driver = PackageResolver.loadPackage('drizzle-orm/mysql2', projectPath);
|
|
20
|
+
if(!mysql2Driver){
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
let drizzleModules = {drizzle: mysql2Driver.drizzle, orm};
|
|
24
|
+
if(!DrizzleModulesValidator.validate(drizzleModules)){
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return drizzleModules;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports.DrizzleModulesLoader = DrizzleModulesLoader;
|