@reldens/storage 0.33.0 → 0.35.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/README.md +45 -2
- package/bin/generate-prisma-schema.js +85 -0
- package/bin/reldens-storage.js +1 -1
- package/index.js +9 -1
- package/lib/entities-generator.js +41 -20
- package/lib/entity-templates/prisma-model.template +32 -0
- package/lib/mikro-orm/mikro-orm-driver.js +1 -1
- package/lib/prisma/prisma-data-server.js +200 -0
- package/lib/prisma/prisma-driver.js +523 -0
- package/lib/prisma/prisma-schema-generator.js +69 -0
- package/lib/type-mapper.js +37 -0
- package/package.json +7 -4
- /package/lib/entity-templates/{mikro-orm-entity.template → mikro-orm-model.template} +0 -0
- /package/lib/entity-templates/{objection-js-entity.template → objection-js-model.template} +0 -0
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Reldens - Storage
|
|
4
4
|
|
|
5
5
|
## About this package
|
|
6
|
-
This package provides standardized database drivers for Reldens projects.
|
|
6
|
+
This package provides standardized database drivers for Reldens projects.
|
|
7
7
|
It ensures consistent data access methods across different database types and ORM implementations.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
@@ -11,6 +11,7 @@ It ensures consistent data access methods across different database types and OR
|
|
|
11
11
|
### ORM Support
|
|
12
12
|
- **Objection JS** (via Knex) - For SQL databases (recommended)
|
|
13
13
|
- **Mikro-ORM** - For MongoDB/NoSQL support
|
|
14
|
+
- **Prisma** - Modern database toolkit
|
|
14
15
|
|
|
15
16
|
### Entity Management
|
|
16
17
|
- Standardized CRUD operations
|
|
@@ -35,6 +36,20 @@ Options:
|
|
|
35
36
|
- `--client` - Database client (mysql|mysql2|mongodb)
|
|
36
37
|
- `--path` - Project path for output files
|
|
37
38
|
|
|
39
|
+
Generate Prisma schema for Prisma database:
|
|
40
|
+
```bash
|
|
41
|
+
npx reldens-storage-prisma --user=dbuser --pass=dbpass --database=dbname
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
- `--user` - Database username
|
|
46
|
+
- `--pass` - Database password
|
|
47
|
+
- `--host` - Database host (default: localhost)
|
|
48
|
+
- `--port` - Database port (default: 3306)
|
|
49
|
+
- `--database` - Database name
|
|
50
|
+
- `--client` - Database client (mysql|mysql2|postgresql|mongodb)
|
|
51
|
+
- `--path` - Project path for output files
|
|
52
|
+
|
|
38
53
|
## Usage Examples
|
|
39
54
|
|
|
40
55
|
### SQL with Objection JS
|
|
@@ -77,6 +92,35 @@ await server.connect();
|
|
|
77
92
|
const entities = server.generateEntities();
|
|
78
93
|
```
|
|
79
94
|
|
|
95
|
+
### Using Prisma
|
|
96
|
+
|
|
97
|
+
First, generate your Prisma schema:
|
|
98
|
+
```bash
|
|
99
|
+
npx reldens-storage-prisma --user=dbuser --pass=dbpass --database=dbname
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Then, use the PrismaDataServer in your code:
|
|
103
|
+
```javascript
|
|
104
|
+
const { PrismaDataServer } = require('@reldens/storage');
|
|
105
|
+
|
|
106
|
+
const server = new PrismaDataServer({
|
|
107
|
+
client: 'mysql2',
|
|
108
|
+
config: {
|
|
109
|
+
user: 'reldens',
|
|
110
|
+
password: 'reldens',
|
|
111
|
+
database: 'reldens',
|
|
112
|
+
host: 'localhost',
|
|
113
|
+
port: 3306
|
|
114
|
+
},
|
|
115
|
+
rawEntities: yourEntities
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await server.connect();
|
|
119
|
+
const entities = server.generateEntities();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Note: The PrismaDataServer requires the Prisma schema to be generated first. Make sure to run the `reldens-storage-prisma` command before using PrismaDataServer.
|
|
123
|
+
|
|
80
124
|
## Custom Drivers
|
|
81
125
|
|
|
82
126
|
You can create custom storage drivers by extending the base classes:
|
|
@@ -102,4 +146,3 @@ const appServer = new ServerManager(serverConfig, eventsManager, customDriver);
|
|
|
102
146
|
### [Reldens](https://www.reldens.com/ "Reldens")
|
|
103
147
|
|
|
104
148
|
##### [By DwDeveloper](https://www.dwdeveloper.com/ "DwDeveloper")
|
|
105
|
-
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* Reldens - Generate Prisma Schema CLI
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { PrismaSchemaGenerator } = require('../lib/prisma/prisma-schema-generator');
|
|
10
|
+
const { Logger } = require('@reldens/utils');
|
|
11
|
+
|
|
12
|
+
let args = process.argv.slice(2);
|
|
13
|
+
|
|
14
|
+
let connectionData = {
|
|
15
|
+
client: 'mysql2',
|
|
16
|
+
config: {
|
|
17
|
+
user: '',
|
|
18
|
+
password: '',
|
|
19
|
+
host: 'localhost',
|
|
20
|
+
database: '',
|
|
21
|
+
port: 3306
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
let projectPath = process.cwd();
|
|
26
|
+
for(let i = 0; i < args.length; i++){
|
|
27
|
+
let arg = args[i];
|
|
28
|
+
if(!arg.startsWith('--')){
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
let [key, value] = arg.substring(2).split('=');
|
|
32
|
+
if(!key || !value){
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if('pass' === key){
|
|
36
|
+
connectionData.config.password = value;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if('path' === key){
|
|
40
|
+
projectPath = value;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if('user' === key){
|
|
44
|
+
connectionData.config.user = value;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if('database' === key){
|
|
48
|
+
connectionData.config.database = value;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if('host' === key){
|
|
52
|
+
connectionData.config.host = value;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if('port' === key){
|
|
56
|
+
connectionData.config.port = value;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if('client' === key){
|
|
60
|
+
connectionData.client = value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if(!connectionData.config.user || !connectionData.config.database){
|
|
65
|
+
Logger.error('Required parameters missing.');
|
|
66
|
+
Logger.error(
|
|
67
|
+
'Usage:',
|
|
68
|
+
'npx reldens-storage generatePrismaSchema --user=[db-username] --pass=[db-password] --host=[db-host] --database=[db-name] --client=[db-client] --path=[project-path]'
|
|
69
|
+
);
|
|
70
|
+
process.exit();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let generator = new PrismaSchemaGenerator({...connectionData, prismaSchemaPath: projectPath+'/prisma'});
|
|
74
|
+
|
|
75
|
+
generator.generate().then((success) => {
|
|
76
|
+
if(!success){
|
|
77
|
+
Logger.error('Prisma schema generation failed.');
|
|
78
|
+
process.exit();
|
|
79
|
+
}
|
|
80
|
+
Logger.info('Prisma schema generation completed successfully!');
|
|
81
|
+
process.exit();
|
|
82
|
+
}).catch((error) => {
|
|
83
|
+
Logger.error('Error during Prisma schema generation: '+error.message);
|
|
84
|
+
process.exit();
|
|
85
|
+
});
|
package/bin/reldens-storage.js
CHANGED
|
@@ -65,7 +65,7 @@ let generator = new EntitiesGenerator({connectionData, projectPath});
|
|
|
65
65
|
generator.generate().then((success) => {
|
|
66
66
|
if(!success){
|
|
67
67
|
Logger.error('Entity generation failed.');
|
|
68
|
-
|
|
68
|
+
process.exit();
|
|
69
69
|
}
|
|
70
70
|
Logger.info('Entity generation completed successfully!');
|
|
71
71
|
process.exit();
|
package/index.js
CHANGED
|
@@ -16,6 +16,9 @@ const { EntitiesGenerator } = require('./lib/entities-generator');
|
|
|
16
16
|
const { EntityProperties } = require('./lib/entity-properties');
|
|
17
17
|
const { TypeMapper } = require('./lib/type-mapper');
|
|
18
18
|
const { MySQLTablesProvider } = require('./lib/mysql-tables-provider');
|
|
19
|
+
const { PrismaDriver } = require('./lib/prisma/prisma-driver');
|
|
20
|
+
const { PrismaDataServer } = require('./lib/prisma/prisma-data-server');
|
|
21
|
+
const { PrismaSchemaGenerator } = require('./lib/prisma/prisma-schema-generator');
|
|
19
22
|
|
|
20
23
|
module.exports = {
|
|
21
24
|
// base:
|
|
@@ -23,7 +26,8 @@ module.exports = {
|
|
|
23
26
|
BaseDriver,
|
|
24
27
|
DriversMap: {
|
|
25
28
|
'objection-js': ObjectionJsDataServer,
|
|
26
|
-
'mikro-orm': MikroOrmDataServer
|
|
29
|
+
'mikro-orm': MikroOrmDataServer,
|
|
30
|
+
'prisma': PrismaDataServer
|
|
27
31
|
},
|
|
28
32
|
// objection-js:
|
|
29
33
|
ObjectionJsDataServer,
|
|
@@ -33,6 +37,10 @@ module.exports = {
|
|
|
33
37
|
MikroOrmCore,
|
|
34
38
|
MikroOrmDataServer,
|
|
35
39
|
MikroOrmDriver,
|
|
40
|
+
// prisma:
|
|
41
|
+
PrismaDataServer,
|
|
42
|
+
PrismaDriver,
|
|
43
|
+
PrismaSchemaGenerator,
|
|
36
44
|
// entities:
|
|
37
45
|
EntitiesGenerator,
|
|
38
46
|
EntityProperties,
|
|
@@ -8,6 +8,7 @@ const { FileHandler } = require('@reldens/server-utils');
|
|
|
8
8
|
const { ErrorManager, Logger, sc } = require('@reldens/utils');
|
|
9
9
|
const { ObjectionJsDataServer } = require('./objection-js/objection-js-data-server');
|
|
10
10
|
const { MikroOrmDataServer } = require('./mikro-orm/mikro-orm-data-server');
|
|
11
|
+
const { PrismaDataServer } = require('./prisma/prisma-data-server');
|
|
11
12
|
const { TypeMapper } = require('./type-mapper');
|
|
12
13
|
|
|
13
14
|
class EntitiesGenerator
|
|
@@ -17,8 +18,9 @@ class EntitiesGenerator
|
|
|
17
18
|
{
|
|
18
19
|
this.templatesFolderPath = FileHandler.joinPaths(__dirname, 'entity-templates');
|
|
19
20
|
this.templates = {
|
|
20
|
-
'objection-js': FileHandler.joinPaths(this.templatesFolderPath, 'objection-js-
|
|
21
|
-
'mikro-orm': FileHandler.joinPaths(this.templatesFolderPath, 'mikro-orm-
|
|
21
|
+
'objection-js': FileHandler.joinPaths(this.templatesFolderPath, 'objection-js-model.template'),
|
|
22
|
+
'mikro-orm': FileHandler.joinPaths(this.templatesFolderPath, 'mikro-orm-model.template'),
|
|
23
|
+
'prisma': FileHandler.joinPaths(this.templatesFolderPath, 'prisma-model.template'),
|
|
22
24
|
'entity': FileHandler.joinPaths(this.templatesFolderPath, 'entity.template'),
|
|
23
25
|
'entities-config': FileHandler.joinPaths(this.templatesFolderPath, 'entities-config.template'),
|
|
24
26
|
'entities-translations': FileHandler.joinPaths(this.templatesFolderPath, 'entities-translations.template'),
|
|
@@ -32,7 +34,8 @@ class EntitiesGenerator
|
|
|
32
34
|
this.entitiesTranslationsPath = FileHandler.joinPaths(this.generationFolder, 'entities-translations.js');
|
|
33
35
|
this.driverMap = {
|
|
34
36
|
'objection-js': ObjectionJsDataServer,
|
|
35
|
-
'mikro-orm': MikroOrmDataServer
|
|
37
|
+
'mikro-orm': MikroOrmDataServer,
|
|
38
|
+
'prisma': PrismaDataServer
|
|
36
39
|
};
|
|
37
40
|
this.server = sc.get(props, 'server', false);
|
|
38
41
|
this.connectionData = sc.get(props, 'connectionData', false);
|
|
@@ -324,7 +327,7 @@ class EntitiesGenerator
|
|
|
324
327
|
let entityPropertiesDefinition = this.getEntityPropertiesDefinition(tableData.columns, driverKey);
|
|
325
328
|
let replacements = {
|
|
326
329
|
modelClassName,
|
|
327
|
-
tableName,
|
|
330
|
+
tableName: driverKey === 'prisma' ? tableName.toLowerCase() : tableName,
|
|
328
331
|
modelPropertiesList,
|
|
329
332
|
modelPropertiesConstructor,
|
|
330
333
|
modelRelations,
|
|
@@ -348,25 +351,43 @@ class EntitiesGenerator
|
|
|
348
351
|
|
|
349
352
|
getEntityPropertiesDefinition(columns, driverKey)
|
|
350
353
|
{
|
|
351
|
-
if('mikro-orm'
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
354
|
+
if('mikro-orm' === driverKey){
|
|
355
|
+
let entityProps = [];
|
|
356
|
+
for(let columnName in columns){
|
|
357
|
+
let column = columns[columnName];
|
|
358
|
+
let isPrimary = 'PRI' === column.key;
|
|
359
|
+
let type = TypeMapper.mapDbTypeToJsType(column.type);
|
|
360
|
+
let propDef = columnName+': { type: \''+type+'\'';
|
|
361
|
+
if(isPrimary){
|
|
362
|
+
propDef += ', primary: true';
|
|
363
|
+
}
|
|
364
|
+
if(column.nullable){
|
|
365
|
+
propDef += ', nullable: true';
|
|
366
|
+
}
|
|
367
|
+
propDef += ' }';
|
|
368
|
+
entityProps.push(propDef);
|
|
362
369
|
}
|
|
363
|
-
|
|
364
|
-
|
|
370
|
+
return entityProps.join(',\n ');
|
|
371
|
+
}
|
|
372
|
+
if('prisma' === driverKey){
|
|
373
|
+
let prismaProps = [];
|
|
374
|
+
for(let columnName in columns){
|
|
375
|
+
let column = columns[columnName];
|
|
376
|
+
let prismaType = TypeMapper.mapDbTypeToPrismaType(column.type);
|
|
377
|
+
let isPrimary = 'PRI' === column.key;
|
|
378
|
+
let propDef = columnName+': {\n type: \''+prismaType+'\'';
|
|
379
|
+
if(isPrimary){
|
|
380
|
+
propDef += ',\n id: true';
|
|
381
|
+
}
|
|
382
|
+
if(column.nullable){
|
|
383
|
+
propDef += ',\n optional: true';
|
|
384
|
+
}
|
|
385
|
+
propDef += '\n }';
|
|
386
|
+
prismaProps.push(propDef);
|
|
365
387
|
}
|
|
366
|
-
|
|
367
|
-
entityProps.push(propDef);
|
|
388
|
+
return prismaProps.join(',\n ');
|
|
368
389
|
}
|
|
369
|
-
return
|
|
390
|
+
return '';
|
|
370
391
|
}
|
|
371
392
|
|
|
372
393
|
generateEntitiesConfigFile()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - {{modelClassName}}
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { PrismaClient } = require('@prisma/client');
|
|
8
|
+
|
|
9
|
+
class {{modelClassName}}
|
|
10
|
+
{
|
|
11
|
+
|
|
12
|
+
constructor({{modelPropertiesList}})
|
|
13
|
+
{
|
|
14
|
+
{{modelPropertiesConstructor}}
|
|
15
|
+
}
|
|
16
|
+
|
|
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
|
+
{{modelRelations}}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports.{{modelClassName}} = {{modelClassName}};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - PrismaDataServer
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { BaseDataServer } = require('../base-data-server');
|
|
8
|
+
const { PrismaDriver } = require('./prisma-driver');
|
|
9
|
+
const { MySQLTablesProvider } = require('../mysql-tables-provider');
|
|
10
|
+
const { ErrorManager, Logger, sc } = require('@reldens/utils');
|
|
11
|
+
|
|
12
|
+
class PrismaDataServer extends BaseDataServer
|
|
13
|
+
{
|
|
14
|
+
|
|
15
|
+
constructor(props)
|
|
16
|
+
{
|
|
17
|
+
super(props);
|
|
18
|
+
this.prisma = false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async connect()
|
|
22
|
+
{
|
|
23
|
+
if(this.initialized){
|
|
24
|
+
return this.initialized;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
let { PrismaClient } = require('@prisma/client');
|
|
28
|
+
this.prisma = new PrismaClient({
|
|
29
|
+
log: this.debug ? ['query', 'info', 'warn', 'error'] : ['error']
|
|
30
|
+
});
|
|
31
|
+
await this.prisma.$connect();
|
|
32
|
+
this.initialized = Date.now();
|
|
33
|
+
return this.initialized;
|
|
34
|
+
} catch(error) {
|
|
35
|
+
Logger.critical('Connection failed, Prisma error: '+error.message);
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
generateEntities()
|
|
41
|
+
{
|
|
42
|
+
if(!this.initialized){
|
|
43
|
+
Logger.warning('Connection was not initialized, please use the connect method first.');
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
if(!this.rawEntities){
|
|
47
|
+
Logger.warning('Empty raw entities array, none entities generated.');
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
this.entities = {};
|
|
51
|
+
for(let i of Object.keys(this.rawEntities)){
|
|
52
|
+
let rawEntity = this.rawEntities[i];
|
|
53
|
+
let modelName = i.toLowerCase();
|
|
54
|
+
if(!this.prisma[modelName]){
|
|
55
|
+
Logger.critical('Invalid raw entity "'+i+'". No matching Prisma model found.');
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
this.entities[i] = new PrismaDriver({
|
|
59
|
+
rawModel: rawEntity,
|
|
60
|
+
id: i,
|
|
61
|
+
name: i,
|
|
62
|
+
config: this.config,
|
|
63
|
+
prisma: this.prisma,
|
|
64
|
+
model: this.prisma[modelName],
|
|
65
|
+
server: this
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
this.entityManager.setEntities(this.entities);
|
|
69
|
+
return this.entities;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
name()
|
|
73
|
+
{
|
|
74
|
+
return this.name || 'Prisma Data Server Driver';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async rawQuery(content)
|
|
78
|
+
{
|
|
79
|
+
try {
|
|
80
|
+
let statements = this.splitSqlStatements(content);
|
|
81
|
+
let results = [];
|
|
82
|
+
for(let statement of statements){
|
|
83
|
+
let cleanStatement = statement.trim();
|
|
84
|
+
if('' === cleanStatement){
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
let queryResult = await this.prisma.$executeRawUnsafe(cleanStatement);
|
|
89
|
+
results.push(queryResult);
|
|
90
|
+
} catch(stmtError) {
|
|
91
|
+
Logger.error('Statement "'+cleanStatement+'" execution failed: '+stmtError.message);
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if(0 === results.length){
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return 1 === results.length ? results[0] : results;
|
|
99
|
+
} catch(error) {
|
|
100
|
+
Logger.error('Raw query failed: '+error.message);
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
splitSqlStatements(sqlContent)
|
|
106
|
+
{
|
|
107
|
+
let statements = [];
|
|
108
|
+
let currentStatement = '';
|
|
109
|
+
let inQuote = false;
|
|
110
|
+
let quoteChar = '';
|
|
111
|
+
let inComment = false;
|
|
112
|
+
let commentType = '';
|
|
113
|
+
for(let i = 0; i < sqlContent.length; i++){
|
|
114
|
+
let char = sqlContent.charAt(i);
|
|
115
|
+
let nextChar = i < sqlContent.length - 1 ? sqlContent.charAt(i + 1) : '';
|
|
116
|
+
if(inComment){
|
|
117
|
+
let shouldEndComment = false;
|
|
118
|
+
if('*' === commentType && '*' === char && '/' === nextChar){
|
|
119
|
+
shouldEndComment = true;
|
|
120
|
+
i++;
|
|
121
|
+
}
|
|
122
|
+
if('-' === commentType && '\n' === char){
|
|
123
|
+
shouldEndComment = true;
|
|
124
|
+
}
|
|
125
|
+
if(shouldEndComment){
|
|
126
|
+
inComment = false;
|
|
127
|
+
}
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
let shouldStartComment = this.shouldStartComment(char, nextChar, inQuote);
|
|
131
|
+
if(shouldStartComment){
|
|
132
|
+
inComment = true;
|
|
133
|
+
commentType = '/' === char ? '*' : '-';
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
let isQuoteChar = ('"' === char || '\'' === char || '`' === char);
|
|
137
|
+
let isEscaped = (i > 0 && '\\' === sqlContent.charAt(i-1));
|
|
138
|
+
if(isQuoteChar && !isEscaped){
|
|
139
|
+
if(!inQuote){
|
|
140
|
+
inQuote = true;
|
|
141
|
+
quoteChar = char;
|
|
142
|
+
currentStatement += char;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
let shouldEndQuote = quoteChar === char;
|
|
146
|
+
if(shouldEndQuote){
|
|
147
|
+
inQuote = false;
|
|
148
|
+
}
|
|
149
|
+
currentStatement += char;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
let shouldEndStatement = (';' === char && !inQuote);
|
|
153
|
+
if(shouldEndStatement){
|
|
154
|
+
statements.push(currentStatement);
|
|
155
|
+
currentStatement = '';
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
currentStatement += char;
|
|
159
|
+
}
|
|
160
|
+
let hasRemainingStatement = '' !== currentStatement.trim();
|
|
161
|
+
if(hasRemainingStatement){
|
|
162
|
+
statements.push(currentStatement);
|
|
163
|
+
}
|
|
164
|
+
return statements;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
shouldStartComment(char, nextChar, inQuote)
|
|
168
|
+
{
|
|
169
|
+
if(inQuote){
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
let isBlockComment = ('/' === char && '*' === nextChar);
|
|
173
|
+
let isLineComment = ('-' === char && '-' === nextChar);
|
|
174
|
+
return isBlockComment || isLineComment;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async fetchEntitiesFromDatabase()
|
|
178
|
+
{
|
|
179
|
+
if(!this.initialized){
|
|
180
|
+
Logger.error('Connection was not initialized, please use the connect method first.');
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
return await MySQLTablesProvider.fetchTables(this);
|
|
185
|
+
} catch(error) {
|
|
186
|
+
ErrorManager.error('Failed to fetch tables from database: '+error.message);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async disconnect()
|
|
191
|
+
{
|
|
192
|
+
if(this.prisma){
|
|
193
|
+
await this.prisma.$disconnect();
|
|
194
|
+
}
|
|
195
|
+
this.initialized = false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports.PrismaDataServer = PrismaDataServer;
|
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - PrismaDriver
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { BaseDriver } = require('../base-driver');
|
|
8
|
+
const { ErrorManager, Logger, sc } = require('@reldens/utils');
|
|
9
|
+
|
|
10
|
+
class PrismaDriver extends BaseDriver
|
|
11
|
+
{
|
|
12
|
+
|
|
13
|
+
constructor(props)
|
|
14
|
+
{
|
|
15
|
+
super(props);
|
|
16
|
+
if(!props.prisma){
|
|
17
|
+
ErrorManager.error('Missing Prisma client on Prisma driver.');
|
|
18
|
+
}
|
|
19
|
+
if(!props.model){
|
|
20
|
+
ErrorManager.error('Missing Prisma model on Prisma driver.');
|
|
21
|
+
}
|
|
22
|
+
if(!props.server){
|
|
23
|
+
ErrorManager.error('Missing Server Driver on Prisma driver.');
|
|
24
|
+
}
|
|
25
|
+
this.prisma = props.prisma;
|
|
26
|
+
this.model = props.model;
|
|
27
|
+
this.server = props.server;
|
|
28
|
+
this.requiredFields = [];
|
|
29
|
+
this.fieldTypes = {};
|
|
30
|
+
this.initFieldMetadata();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
initFieldMetadata()
|
|
34
|
+
{
|
|
35
|
+
try {
|
|
36
|
+
let dmmf = this.prisma._baseDmmf;
|
|
37
|
+
if(!dmmf){
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
let modelInfo = dmmf.modelMap[this.tableName().toLowerCase()];
|
|
41
|
+
if(!modelInfo){
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
for(let field of modelInfo.fields){
|
|
45
|
+
if(field.isRequired && !field.hasDefaultValue && !field.isId){
|
|
46
|
+
this.requiredFields.push(field.name);
|
|
47
|
+
}
|
|
48
|
+
this.fieldTypes[field.name] = field.type;
|
|
49
|
+
}
|
|
50
|
+
} catch(error) {
|
|
51
|
+
Logger.warning('Could not initialize field metadata: '+error.message);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
databaseName()
|
|
56
|
+
{
|
|
57
|
+
return this.config.database || '';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
id()
|
|
61
|
+
{
|
|
62
|
+
return this.name || '';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
name()
|
|
66
|
+
{
|
|
67
|
+
return this.rawName || '';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
tableName()
|
|
71
|
+
{
|
|
72
|
+
return this.model._name || '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
property(propertyName)
|
|
76
|
+
{
|
|
77
|
+
return this.rawModel[propertyName] || null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
prepareData(params)
|
|
81
|
+
{
|
|
82
|
+
let data = {...params};
|
|
83
|
+
for(let key in data){
|
|
84
|
+
if('status' === key && 'number' === typeof data[key]){
|
|
85
|
+
data[key] = String(data[key]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return data;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
ensureRequiredFields(data)
|
|
92
|
+
{
|
|
93
|
+
let missingFields = [];
|
|
94
|
+
for(let field of this.requiredFields){
|
|
95
|
+
if(!sc.hasOwn(data, field)){
|
|
96
|
+
missingFields.push(field);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if(0 < missingFields.length){
|
|
100
|
+
Logger.warning('Missing required fields for '+this.tableName()+': '+missingFields.join(', '));
|
|
101
|
+
}
|
|
102
|
+
return data;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async create(params)
|
|
106
|
+
{
|
|
107
|
+
try {
|
|
108
|
+
let preparedData = this.prepareData(params);
|
|
109
|
+
this.ensureRequiredFields(preparedData);
|
|
110
|
+
return await this.model.create({
|
|
111
|
+
data: preparedData
|
|
112
|
+
});
|
|
113
|
+
} catch(error) {
|
|
114
|
+
Logger.error('Create error: '+error.message);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async createWithRelations(params, relations)
|
|
120
|
+
{
|
|
121
|
+
try {
|
|
122
|
+
let preparedData = this.prepareData(params);
|
|
123
|
+
this.ensureRequiredFields(preparedData);
|
|
124
|
+
let createData = {data: preparedData};
|
|
125
|
+
if(sc.isArray(relations) && 0 < relations.length){
|
|
126
|
+
for(let relation of relations){
|
|
127
|
+
if(!sc.hasOwn(params, relation)){
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
let relationData = params[relation];
|
|
131
|
+
if(sc.isArray(relationData)){
|
|
132
|
+
createData.data[relation] = {
|
|
133
|
+
connect: relationData.map(item => ({id: item.id}))
|
|
134
|
+
};
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
createData.data[relation] = {
|
|
138
|
+
connect: {id: relationData.id}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return await this.model.create(createData);
|
|
143
|
+
} catch(error) {
|
|
144
|
+
Logger.error('Create with relations error: '+error.message);
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async update(filters, updatePatch)
|
|
150
|
+
{
|
|
151
|
+
try {
|
|
152
|
+
let preparedData = this.prepareData(updatePatch);
|
|
153
|
+
return await this.model.updateMany({
|
|
154
|
+
where: filters,
|
|
155
|
+
data: preparedData
|
|
156
|
+
});
|
|
157
|
+
} catch(error) {
|
|
158
|
+
Logger.error('Update error: '+error.message);
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async updateBy(field, fieldValue, updatePatch, operator = null)
|
|
164
|
+
{
|
|
165
|
+
let filter = this.createSingleFilter(field, fieldValue, operator);
|
|
166
|
+
try {
|
|
167
|
+
let preparedData = this.prepareData(updatePatch);
|
|
168
|
+
return await this.model.updateMany({
|
|
169
|
+
where: filter,
|
|
170
|
+
data: preparedData
|
|
171
|
+
});
|
|
172
|
+
} catch(error) {
|
|
173
|
+
Logger.error('Update by error: '+error.message);
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async updateById(id, params)
|
|
179
|
+
{
|
|
180
|
+
try {
|
|
181
|
+
let found = await this.loadById(id);
|
|
182
|
+
if(!found){
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
let preparedData = this.prepareData(params);
|
|
186
|
+
return await this.model.update({
|
|
187
|
+
where: {id},
|
|
188
|
+
data: preparedData
|
|
189
|
+
});
|
|
190
|
+
} catch(error) {
|
|
191
|
+
Logger.error('Update by ID error: '+error.message);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async upsert(params, filters)
|
|
197
|
+
{
|
|
198
|
+
try {
|
|
199
|
+
let preparedData = this.prepareData(params);
|
|
200
|
+
let existing = false;
|
|
201
|
+
if(params.id){
|
|
202
|
+
existing = await this.loadById(params.id);
|
|
203
|
+
if(existing){
|
|
204
|
+
let patch = Object.assign({}, preparedData);
|
|
205
|
+
delete patch.id;
|
|
206
|
+
return await this.model.update({
|
|
207
|
+
where: {id: params.id},
|
|
208
|
+
data: patch
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if(filters){
|
|
213
|
+
let existing = await this.loadOne(filters);
|
|
214
|
+
if(existing){
|
|
215
|
+
return await this.model.update({
|
|
216
|
+
where: {id: existing.id},
|
|
217
|
+
data: preparedData
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return await this.create(preparedData);
|
|
222
|
+
} catch(error) {
|
|
223
|
+
Logger.error('Upsert error: '+error.message);
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async delete(filters = {})
|
|
229
|
+
{
|
|
230
|
+
try {
|
|
231
|
+
return await this.model.deleteMany({
|
|
232
|
+
where: filters
|
|
233
|
+
});
|
|
234
|
+
} catch(error) {
|
|
235
|
+
Logger.error('Delete error: '+error.message);
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async deleteById(id)
|
|
241
|
+
{
|
|
242
|
+
try {
|
|
243
|
+
let found = await this.loadById(id);
|
|
244
|
+
if(!found){
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
return await this.model.delete({
|
|
248
|
+
where: {id}
|
|
249
|
+
});
|
|
250
|
+
} catch(error) {
|
|
251
|
+
Logger.error('Delete by ID error: '+error.message);
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async count(filters)
|
|
257
|
+
{
|
|
258
|
+
try {
|
|
259
|
+
return await this.model.count({
|
|
260
|
+
where: filters
|
|
261
|
+
});
|
|
262
|
+
} catch(error) {
|
|
263
|
+
Logger.error('Count error: '+error.message);
|
|
264
|
+
return 0;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async countWithRelations(filters, relations)
|
|
269
|
+
{
|
|
270
|
+
try {
|
|
271
|
+
let query = {
|
|
272
|
+
where: filters
|
|
273
|
+
};
|
|
274
|
+
if(sc.isArray(relations) && 0 < relations.length){
|
|
275
|
+
query.include = this.buildIncludeObject(relations);
|
|
276
|
+
}
|
|
277
|
+
return await this.model.count(query);
|
|
278
|
+
} catch(error) {
|
|
279
|
+
Logger.error('Count with relations error: '+error.message);
|
|
280
|
+
return 0;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async loadAll()
|
|
285
|
+
{
|
|
286
|
+
try {
|
|
287
|
+
return await this.model.findMany(this.buildQueryOptions());
|
|
288
|
+
} catch(error) {
|
|
289
|
+
Logger.error('Load all error: '+error.message);
|
|
290
|
+
return [];
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async loadAllWithRelations(relations)
|
|
295
|
+
{
|
|
296
|
+
try {
|
|
297
|
+
let query = this.buildQueryOptions();
|
|
298
|
+
if(sc.isArray(relations) && 0 < relations.length){
|
|
299
|
+
query.include = this.buildIncludeObject(relations);
|
|
300
|
+
}
|
|
301
|
+
return await this.model.findMany(query);
|
|
302
|
+
} catch(error) {
|
|
303
|
+
Logger.error('Load all with relations error: '+error.message);
|
|
304
|
+
return [];
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async load(filters)
|
|
309
|
+
{
|
|
310
|
+
try {
|
|
311
|
+
let query = this.buildQueryOptions();
|
|
312
|
+
query.where = filters;
|
|
313
|
+
return await this.model.findMany(query);
|
|
314
|
+
} catch(error) {
|
|
315
|
+
Logger.error('Load error: '+error.message);
|
|
316
|
+
return [];
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async loadWithRelations(filters, relations)
|
|
321
|
+
{
|
|
322
|
+
try {
|
|
323
|
+
let query = this.buildQueryOptions();
|
|
324
|
+
query.where = filters;
|
|
325
|
+
if(sc.isArray(relations) && 0 < relations.length){
|
|
326
|
+
query.include = this.buildIncludeObject(relations);
|
|
327
|
+
}
|
|
328
|
+
return await this.model.findMany(query);
|
|
329
|
+
} catch(error) {
|
|
330
|
+
Logger.error('Load with relations error: '+error.message);
|
|
331
|
+
return [];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async loadBy(field, fieldValue, operator = null)
|
|
336
|
+
{
|
|
337
|
+
try {
|
|
338
|
+
let filter = this.createSingleFilter(field, fieldValue, operator);
|
|
339
|
+
let query = this.buildQueryOptions();
|
|
340
|
+
query.where = filter;
|
|
341
|
+
return await this.model.findMany(query);
|
|
342
|
+
} catch(error) {
|
|
343
|
+
Logger.error('Load by error: '+error.message);
|
|
344
|
+
return [];
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async loadByWithRelations(field, fieldValue, relations, operator = null)
|
|
349
|
+
{
|
|
350
|
+
try {
|
|
351
|
+
let filter = this.createSingleFilter(field, fieldValue, operator);
|
|
352
|
+
let query = this.buildQueryOptions();
|
|
353
|
+
query.where = filter;
|
|
354
|
+
if(sc.isArray(relations) && 0 < relations.length){
|
|
355
|
+
query.include = this.buildIncludeObject(relations);
|
|
356
|
+
}
|
|
357
|
+
return await this.model.findMany(query);
|
|
358
|
+
} catch(error) {
|
|
359
|
+
Logger.error('Load by with relations error: '+error.message);
|
|
360
|
+
return [];
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async loadById(id)
|
|
365
|
+
{
|
|
366
|
+
try {
|
|
367
|
+
return await this.model.findUnique({
|
|
368
|
+
where: {id}
|
|
369
|
+
});
|
|
370
|
+
} catch(error) {
|
|
371
|
+
Logger.error('Load by ID error: '+error.message);
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async loadByIdWithRelations(id, relations)
|
|
377
|
+
{
|
|
378
|
+
try {
|
|
379
|
+
let query = {
|
|
380
|
+
where: {id}
|
|
381
|
+
};
|
|
382
|
+
if(sc.isArray(relations) && 0 < relations.length){
|
|
383
|
+
query.include = this.buildIncludeObject(relations);
|
|
384
|
+
}
|
|
385
|
+
return await this.model.findUnique(query);
|
|
386
|
+
} catch(error) {
|
|
387
|
+
Logger.error('Load by ID with relations error: '+error.message);
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async loadByIds(ids)
|
|
393
|
+
{
|
|
394
|
+
try {
|
|
395
|
+
return await this.model.findMany({
|
|
396
|
+
where: {
|
|
397
|
+
id: {in: ids}
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
} catch(error) {
|
|
401
|
+
Logger.error('Load by IDs error: '+error.message);
|
|
402
|
+
return [];
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async loadOne(filters)
|
|
407
|
+
{
|
|
408
|
+
try {
|
|
409
|
+
let query = this.buildQueryOptions(false);
|
|
410
|
+
query.where = filters;
|
|
411
|
+
return await this.model.findFirst(query);
|
|
412
|
+
} catch(error) {
|
|
413
|
+
Logger.error('Load one error: '+error.message);
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
async loadOneWithRelations(filters, relations)
|
|
419
|
+
{
|
|
420
|
+
try {
|
|
421
|
+
let query = this.buildQueryOptions(false);
|
|
422
|
+
query.where = filters;
|
|
423
|
+
if(sc.isArray(relations) && 0 < relations.length){
|
|
424
|
+
query.include = this.buildIncludeObject(relations);
|
|
425
|
+
}
|
|
426
|
+
return await this.model.findFirst(query);
|
|
427
|
+
} catch(error) {
|
|
428
|
+
Logger.error('Load one with relations error: '+error.message);
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
async loadOneBy(field, fieldValue, operator = null)
|
|
434
|
+
{
|
|
435
|
+
try {
|
|
436
|
+
let filter = this.createSingleFilter(field, fieldValue, operator);
|
|
437
|
+
let query = this.buildQueryOptions(false);
|
|
438
|
+
query.where = filter;
|
|
439
|
+
return await this.model.findFirst(query);
|
|
440
|
+
} catch(error) {
|
|
441
|
+
Logger.error('Load one by error: '+error.message);
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
async loadOneByWithRelations(field, fieldValue, relations, operator = null)
|
|
447
|
+
{
|
|
448
|
+
try {
|
|
449
|
+
let filter = this.createSingleFilter(field, fieldValue, operator);
|
|
450
|
+
let query = this.buildQueryOptions(false);
|
|
451
|
+
query.where = filter;
|
|
452
|
+
if(sc.isArray(relations) && 0 < relations.length){
|
|
453
|
+
query.include = this.buildIncludeObject(relations);
|
|
454
|
+
}
|
|
455
|
+
return await this.model.findFirst(query);
|
|
456
|
+
} catch(error) {
|
|
457
|
+
Logger.error('Load one by with relations error: '+error.message);
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
buildQueryOptions(useLimit = true)
|
|
463
|
+
{
|
|
464
|
+
let queryOptions = {};
|
|
465
|
+
if(0 < this.select.length){
|
|
466
|
+
queryOptions.select = {};
|
|
467
|
+
for(let field of this.select){
|
|
468
|
+
queryOptions.select[field] = true;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if(useLimit && 0 !== this.limit){
|
|
472
|
+
queryOptions.take = this.limit;
|
|
473
|
+
}
|
|
474
|
+
if(0 !== this.offset){
|
|
475
|
+
queryOptions.skip = this.offset;
|
|
476
|
+
}
|
|
477
|
+
if(false !== this.sortBy && false !== this.sortDirection){
|
|
478
|
+
queryOptions.orderBy = {
|
|
479
|
+
[this.sortBy]: this.sortDirection.toLowerCase()
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
return queryOptions;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
createSingleFilter(field, fieldValue, operator = null)
|
|
486
|
+
{
|
|
487
|
+
let filter = {};
|
|
488
|
+
if(null === operator){
|
|
489
|
+
filter[field] = fieldValue;
|
|
490
|
+
return filter;
|
|
491
|
+
}
|
|
492
|
+
filter[field] = this.applyOperator(fieldValue, operator);
|
|
493
|
+
return filter;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
applyOperator(value, operator)
|
|
497
|
+
{
|
|
498
|
+
let operatorsMap = {
|
|
499
|
+
'=': value,
|
|
500
|
+
'!=': {not: value},
|
|
501
|
+
'>': {gt: value},
|
|
502
|
+
'>=': {gte: value},
|
|
503
|
+
'<': {lt: value},
|
|
504
|
+
'<=': {lte: value},
|
|
505
|
+
'LIKE': {contains: value},
|
|
506
|
+
'IN': {in: value},
|
|
507
|
+
'NOT IN': {notIn: value}
|
|
508
|
+
};
|
|
509
|
+
return operatorsMap[operator] || value;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
buildIncludeObject(relations)
|
|
513
|
+
{
|
|
514
|
+
let include = {};
|
|
515
|
+
for(let relation of relations){
|
|
516
|
+
include[relation] = true;
|
|
517
|
+
}
|
|
518
|
+
return include;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
module.exports.PrismaDriver = PrismaDriver;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - PrismaSchemaGenerator
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { ErrorManager, Logger, sc } = require('@reldens/utils');
|
|
8
|
+
const { FileHandler } = require('@reldens/server-utils');
|
|
9
|
+
const { execSync } = require('child_process');
|
|
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.prismaSchemaPath = sc.get(props, 'prismaSchemaPath', FileHandler.joinPaths(process.cwd(), 'prisma'));
|
|
20
|
+
this.schemaFilePath = FileHandler.joinPaths(this.prismaSchemaPath, 'schema.prisma');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async generate()
|
|
24
|
+
{
|
|
25
|
+
try {
|
|
26
|
+
FileHandler.createFolder(this.prismaSchemaPath, { recursive: true });
|
|
27
|
+
this.generateSchemaFile();
|
|
28
|
+
Logger.info('Running prisma introspect "npx prisma db pull"...');
|
|
29
|
+
execSync('npx prisma db pull', { stdio: 'inherit' });
|
|
30
|
+
Logger.info('Running prisma generate "npx prisma generate"...');
|
|
31
|
+
execSync('npx prisma generate', { stdio: 'inherit' });
|
|
32
|
+
return true;
|
|
33
|
+
} catch(error) {
|
|
34
|
+
ErrorManager.error('Failed to generate Prisma schema. '+error.message);
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
generateSchemaFile()
|
|
40
|
+
{
|
|
41
|
+
let datasourceProvider = 'mysql';
|
|
42
|
+
if('postgresql' === this.client || 'postgres' === this.client){
|
|
43
|
+
datasourceProvider = 'postgresql';
|
|
44
|
+
}
|
|
45
|
+
if('mongodb' === this.client){
|
|
46
|
+
datasourceProvider = 'mongodb';
|
|
47
|
+
}
|
|
48
|
+
let connectionString = datasourceProvider + '://'
|
|
49
|
+
+ this.config.user + ':' + this.config.password
|
|
50
|
+
+ '@' + this.config.host + ':' + this.config.port + '/'
|
|
51
|
+
+ this.config.database;
|
|
52
|
+
let schemaContent = `
|
|
53
|
+
generator client {
|
|
54
|
+
provider = "prisma-client-js"
|
|
55
|
+
output = "../node_modules/.prisma/client"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
datasource db {
|
|
59
|
+
provider = "${datasourceProvider}"
|
|
60
|
+
url = "${connectionString}"
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
FileHandler.writeFile(this.schemaFilePath, schemaContent);
|
|
64
|
+
Logger.info('Generated Prisma schema file at: '+this.schemaFilePath);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports.PrismaSchemaGenerator = PrismaSchemaGenerator;
|
package/lib/type-mapper.js
CHANGED
|
@@ -44,6 +44,43 @@ class TypeMapper
|
|
|
44
44
|
return typeMap[dbType.toLowerCase()];
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
mapDbTypeToPrismaType(dbType)
|
|
48
|
+
{
|
|
49
|
+
let typeMap = {
|
|
50
|
+
'int': 'Int',
|
|
51
|
+
'integer': 'Int',
|
|
52
|
+
'tinyint': 'Int',
|
|
53
|
+
'smallint': 'Int',
|
|
54
|
+
'mediumint': 'Int',
|
|
55
|
+
'bigint': 'BigInt',
|
|
56
|
+
'decimal': 'Decimal',
|
|
57
|
+
'float': 'Float',
|
|
58
|
+
'double': 'Float',
|
|
59
|
+
'varchar': 'String',
|
|
60
|
+
'char': 'String',
|
|
61
|
+
'text': 'String',
|
|
62
|
+
'tinytext': 'String',
|
|
63
|
+
'mediumtext': 'String',
|
|
64
|
+
'longtext': 'String',
|
|
65
|
+
'date': 'DateTime',
|
|
66
|
+
'datetime': 'DateTime',
|
|
67
|
+
'timestamp': 'DateTime',
|
|
68
|
+
'time': 'String',
|
|
69
|
+
'year': 'Int',
|
|
70
|
+
'boolean': 'Boolean',
|
|
71
|
+
'bool': 'Boolean',
|
|
72
|
+
'bit': 'Boolean',
|
|
73
|
+
'json': 'Json',
|
|
74
|
+
'binary': 'Bytes',
|
|
75
|
+
'varbinary': 'Bytes',
|
|
76
|
+
'blob': 'Bytes',
|
|
77
|
+
'tinyblob': 'Bytes',
|
|
78
|
+
'mediumblob': 'Bytes',
|
|
79
|
+
'longblob': 'Bytes'
|
|
80
|
+
};
|
|
81
|
+
return typeMap[dbType.toLowerCase()] || 'String';
|
|
82
|
+
}
|
|
83
|
+
|
|
47
84
|
}
|
|
48
85
|
|
|
49
86
|
module.exports.TypeMapper = new TypeMapper();
|
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.35.0",
|
|
5
5
|
"description": "Reldens - Storage",
|
|
6
6
|
"author": "Damian A. Pastorini",
|
|
7
7
|
"license": "MIT",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"source": true,
|
|
10
10
|
"main": "index.js",
|
|
11
11
|
"bin": {
|
|
12
|
-
"reldens-storage": "./bin/reldens-storage.js"
|
|
12
|
+
"reldens-storage": "./bin/reldens-storage.js",
|
|
13
|
+
"reldens-storage-prisma": "./bin/generate-prisma-schema.js"
|
|
13
14
|
},
|
|
14
15
|
"repository": {
|
|
15
16
|
"type": "git",
|
|
@@ -50,11 +51,13 @@
|
|
|
50
51
|
"@mikro-orm/core": "^6.4.15",
|
|
51
52
|
"@mikro-orm/mongodb": "^6.4.15",
|
|
52
53
|
"@mikro-orm/mysql": "^6.4.15",
|
|
53
|
-
"@
|
|
54
|
+
"@prisma/client": "^6.7.0",
|
|
54
55
|
"@reldens/server-utils": "^0.15.0",
|
|
56
|
+
"@reldens/utils": "^0.47.0",
|
|
55
57
|
"knex": "^3.1.0",
|
|
56
58
|
"mysql": "^2.18.1",
|
|
57
59
|
"mysql2": "^3.14.1",
|
|
58
|
-
"objection": "^3.1.5"
|
|
60
|
+
"objection": "^3.1.5",
|
|
61
|
+
"prisma": "^6.7.0"
|
|
59
62
|
}
|
|
60
63
|
}
|
|
File without changes
|
|
File without changes
|