@rws-framework/db 3.2.0 → 3.3.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/dist/decorators/IdType.js +6 -0
- package/dist/decorators/InverseRelation.js +3 -4
- package/dist/decorators/Relation.js +2 -2
- package/dist/decorators/TrackType.js +2 -2
- package/dist/helper/DbHelper.d.ts +1 -1
- package/dist/helper/DbHelper.js +4 -4
- package/dist/helper/db/relation-manager.js +1 -1
- package/dist/helper/db/schema-generator.js +51 -20
- package/dist/helper/db/type-converter.d.ts +2 -1
- package/dist/helper/db/type-converter.js +10 -7
- package/dist/helper/db/utils.d.ts +7 -0
- package/dist/helper/db/utils.js +80 -26
- package/dist/models/core/RWSModel.js +29 -32
- package/dist/models/interfaces/IIdTypeOpts.d.ts +2 -0
- package/dist/models/interfaces/ITrackerOpts.d.ts +1 -0
- package/dist/models/utils/ModelUtils.js +8 -1
- package/dist/models/utils/PaginationUtils.js +5 -6
- package/dist/services/DBService.js +7 -8
- package/package.json +1 -1
- package/src/decorators/IdType.ts +10 -1
- package/src/decorators/InverseRelation.ts +2 -2
- package/src/decorators/Relation.ts +2 -2
- package/src/helper/DbHelper.ts +1 -1
- package/src/helper/db/schema-generator.ts +72 -28
- package/src/helper/db/type-converter.ts +10 -4
- package/src/helper/db/utils.ts +114 -40
- package/src/models/interfaces/IIdTypeOpts.ts +2 -0
- package/src/models/interfaces/ITrackerOpts.ts +1 -0
- package/src/models/utils/ModelUtils.ts +10 -1
- package/tsconfig.json +1 -1
|
@@ -3,6 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
require("reflect-metadata");
|
|
4
4
|
function IdType(type, opts = null, tags = []) {
|
|
5
5
|
const metaOpts = { type, dbOptions: opts && opts.dbOptions ? opts.dbOptions : null };
|
|
6
|
+
if (opts && opts.dbOptions) {
|
|
7
|
+
metaOpts.dbOptions = opts.dbOptions;
|
|
8
|
+
}
|
|
9
|
+
if (opts && opts.noAuto) {
|
|
10
|
+
metaOpts.noAuto = opts.noAuto;
|
|
11
|
+
}
|
|
6
12
|
return function (target, key) {
|
|
7
13
|
Reflect.defineMetadata(`IdType:${key}`, metaOpts, target);
|
|
8
14
|
};
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
require("reflect-metadata");
|
|
4
4
|
const ModelUtils_1 = require("../models/utils/ModelUtils");
|
|
5
5
|
function guessForeignKey(inversionModel, bindingModel, decoratorsData) {
|
|
6
|
-
var _a;
|
|
7
6
|
let key = null;
|
|
8
7
|
let defaultKey = `${bindingModel._collection}_id`;
|
|
9
8
|
const relDecorators = {};
|
|
@@ -21,7 +20,7 @@ function guessForeignKey(inversionModel, bindingModel, decoratorsData) {
|
|
|
21
20
|
}
|
|
22
21
|
}
|
|
23
22
|
for (const relKey of Object.keys(relDecorators)) {
|
|
24
|
-
const prodMeta =
|
|
23
|
+
const prodMeta = relDecorators[relKey]?.metadata;
|
|
25
24
|
if (prodMeta && prodMeta.relatedTo._collection === bindingModel._collection) {
|
|
26
25
|
return prodMeta.relationField;
|
|
27
26
|
}
|
|
@@ -41,8 +40,8 @@ function InverseRelation(inversionModel, sourceModel, relationOptions = null) {
|
|
|
41
40
|
foreignKey: relationOptions && relationOptions.foreignKey ? relationOptions.foreignKey : guessForeignKey(model, source, decoratorsData),
|
|
42
41
|
// Generate a unique relation name if one is not provided
|
|
43
42
|
relationName: relationOptions && relationOptions.relationName ?
|
|
44
|
-
relationOptions.relationName
|
|
45
|
-
|
|
43
|
+
relationOptions.relationName :
|
|
44
|
+
null
|
|
46
45
|
};
|
|
47
46
|
return metaOpts;
|
|
48
47
|
});
|
|
@@ -16,8 +16,8 @@ function Relation(theModel, relationOptions = _DEFAULTS) {
|
|
|
16
16
|
key,
|
|
17
17
|
// Generate a unique relation name if one is not provided
|
|
18
18
|
relationName: relationOptions.relationName ?
|
|
19
|
-
relationOptions.relationName
|
|
20
|
-
|
|
19
|
+
relationOptions.relationName :
|
|
20
|
+
null
|
|
21
21
|
};
|
|
22
22
|
if (relationOptions.required) {
|
|
23
23
|
metaOpts.cascade.onDelete = 'Restrict';
|
|
@@ -8,10 +8,10 @@ function TrackType(type, opts = null, tags = []) {
|
|
|
8
8
|
isArray: false
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
if (!
|
|
11
|
+
if (!opts?.required) {
|
|
12
12
|
opts.required = false;
|
|
13
13
|
}
|
|
14
|
-
if (!
|
|
14
|
+
if (!opts?.isArray) {
|
|
15
15
|
opts.isArray = false;
|
|
16
16
|
}
|
|
17
17
|
const required = opts.required;
|
|
@@ -60,7 +60,7 @@ export declare class DbHelper {
|
|
|
60
60
|
/**
|
|
61
61
|
* Convert a JavaScript type to a Prisma schema type
|
|
62
62
|
*/
|
|
63
|
-
static toConfigCase(modelType: any, dbType?:
|
|
63
|
+
static toConfigCase(modelType: any, dbType?: IDbConfigParams['db_type']): string;
|
|
64
64
|
/**
|
|
65
65
|
* Process type functions metadata to extract database-specific options
|
|
66
66
|
*/
|
package/dist/helper/DbHelper.js
CHANGED
|
@@ -10,6 +10,10 @@ const db_1 = require("./db");
|
|
|
10
10
|
* It delegates to the specialized modules for specific functionality.
|
|
11
11
|
*/
|
|
12
12
|
class DbHelper {
|
|
13
|
+
/**
|
|
14
|
+
* The environment variable name for the Prisma database URL
|
|
15
|
+
*/
|
|
16
|
+
static dbUrlVarName = db_1.SchemaGenerator.dbUrlVarName;
|
|
13
17
|
/**
|
|
14
18
|
* Install Prisma with the generated schema
|
|
15
19
|
* @param configService The configuration service
|
|
@@ -107,7 +111,3 @@ class DbHelper {
|
|
|
107
111
|
}
|
|
108
112
|
}
|
|
109
113
|
exports.DbHelper = DbHelper;
|
|
110
|
-
/**
|
|
111
|
-
* The environment variable name for the Prisma database URL
|
|
112
|
-
*/
|
|
113
|
-
DbHelper.dbUrlVarName = db_1.SchemaGenerator.dbUrlVarName;
|
|
@@ -5,6 +5,7 @@ exports.RelationManager = void 0;
|
|
|
5
5
|
* Manages database relations for schema generation
|
|
6
6
|
*/
|
|
7
7
|
class RelationManager {
|
|
8
|
+
static allRelations = new Map();
|
|
8
9
|
/**
|
|
9
10
|
* Mark a relation between two models
|
|
10
11
|
* @param relationKey A unique key for the relation
|
|
@@ -102,4 +103,3 @@ class RelationManager {
|
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
exports.RelationManager = RelationManager;
|
|
105
|
-
RelationManager.allRelations = new Map();
|
|
@@ -11,10 +11,13 @@ const _model_1 = require("../../models/_model");
|
|
|
11
11
|
const utils_1 = require("./utils");
|
|
12
12
|
const type_converter_1 = require("./type-converter");
|
|
13
13
|
const relation_manager_1 = require("./relation-manager");
|
|
14
|
+
const _EXECUTE_PRISMA_CMD = true;
|
|
15
|
+
const _REMOVE_SCHEMA_FILE = true;
|
|
14
16
|
/**
|
|
15
17
|
* Handles Prisma schema generation
|
|
16
18
|
*/
|
|
17
19
|
class SchemaGenerator {
|
|
20
|
+
static dbUrlVarName = 'PRISMA_DB_URL';
|
|
18
21
|
/**
|
|
19
22
|
* Generate the base schema for Prisma
|
|
20
23
|
* @param dbType The database type
|
|
@@ -39,21 +42,31 @@ datasource db {
|
|
|
39
42
|
* @returns The model section
|
|
40
43
|
*/
|
|
41
44
|
static async generateModelSections(model, configService) {
|
|
42
|
-
var _a, _b;
|
|
43
45
|
let section = '';
|
|
44
46
|
const modelMetadatas = await _model_1.RWSModel.getModelAnnotations(model);
|
|
45
47
|
const dbType = configService.get('db_type') || 'mongodb';
|
|
46
48
|
const modelName = model._collection;
|
|
47
49
|
section += `model ${modelName} {\n`;
|
|
48
|
-
|
|
50
|
+
let hasIdType = false;
|
|
51
|
+
let idFieldName;
|
|
52
|
+
for (const someModelMetaKey in modelMetadatas) {
|
|
53
|
+
const isIdTyped = modelMetadatas[someModelMetaKey].annotationType === 'IdType';
|
|
54
|
+
if (isIdTyped) {
|
|
55
|
+
hasIdType = true;
|
|
56
|
+
idFieldName = someModelMetaKey;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
let idGenerated = false;
|
|
60
|
+
if (!model._NO_ID && !hasIdType) {
|
|
49
61
|
section += `\t${utils_1.DbUtils.generateId(dbType, modelMetadatas)}\n`;
|
|
62
|
+
idGenerated = true;
|
|
50
63
|
}
|
|
51
64
|
for (const key in modelMetadatas) {
|
|
52
65
|
const modelMetadata = modelMetadatas[key].metadata;
|
|
53
66
|
let requiredString = modelMetadata.required ? '' : '?';
|
|
54
67
|
const annotationType = modelMetadatas[key].annotationType;
|
|
55
68
|
let indexedId = false;
|
|
56
|
-
if (model._NO_ID) {
|
|
69
|
+
if (model._NO_ID || hasIdType) {
|
|
57
70
|
indexedId = true;
|
|
58
71
|
requiredString = '';
|
|
59
72
|
}
|
|
@@ -65,17 +78,17 @@ datasource db {
|
|
|
65
78
|
const relatedModel = relationMeta.relatedTo;
|
|
66
79
|
const isMany = relationMeta.many;
|
|
67
80
|
const cascadeOpts = [];
|
|
68
|
-
if (
|
|
81
|
+
if (relationMeta.cascade?.onDelete) {
|
|
69
82
|
cascadeOpts.push(`onDelete: ${relationMeta.cascade.onDelete}`);
|
|
70
83
|
}
|
|
71
|
-
if (
|
|
84
|
+
if (relationMeta.cascade?.onUpdate) {
|
|
72
85
|
cascadeOpts.push(`onUpdate: ${relationMeta.cascade.onUpdate}`);
|
|
73
86
|
}
|
|
74
87
|
const relatedModelName = relatedModel._collection;
|
|
75
88
|
const relationKey = [modelName, relatedModelName].join('_');
|
|
76
89
|
const relationIndex = relation_manager_1.RelationManager.getRelationCounter(relationKey);
|
|
77
|
-
const relationName =
|
|
78
|
-
const mapName =
|
|
90
|
+
const relationName = relationMeta.relationName ? relationMeta.relationName : null;
|
|
91
|
+
const mapName = relationMeta.mappingName ? relationMeta.mappingName : null;
|
|
79
92
|
const relatedModelMetadatas = await _model_1.RWSModel.getModelAnnotations(relatedModel);
|
|
80
93
|
const relationFieldName = modelMetadata.relationField ? modelMetadata.relationField : key.toLowerCase() + '_' + modelMetadata.relationField.toLowerCase();
|
|
81
94
|
const relatedToField = modelMetadata.relatedToField || 'id';
|
|
@@ -86,10 +99,10 @@ datasource db {
|
|
|
86
99
|
const cascadeStr = cascadeOpts.length ? `, ${cascadeOpts.join(', ')}` : '';
|
|
87
100
|
if (isMany) {
|
|
88
101
|
// Add an inverse field to the related model if it doesn't exist
|
|
89
|
-
section += `\t${key} ${relatedModel._collection}[] @relation("${relationName}", fields: [${relationFieldName}], references: [${relatedToField}]
|
|
102
|
+
section += `\t${key} ${relatedModel._collection}[] @relation(${relationName ? `"${relationName}", ` : ''}fields: [${relationFieldName}], references: [${relatedToField}]${mapName ? `, map: "${mapName}"` : ''}${cascadeStr})\n`;
|
|
90
103
|
}
|
|
91
104
|
else {
|
|
92
|
-
section += `\t${key} ${relatedModel._collection}${requiredString} @relation("${relationName}", fields: [${relationFieldName}], references: [${relatedToField}]
|
|
105
|
+
section += `\t${key} ${relatedModel._collection}${requiredString} @relation(${relationName ? `"${relationName}", ` : ''}fields: [${relationFieldName}], references: [${relatedToField}]${mapName ? `, map: "${mapName}"` : ''}${cascadeStr})\n`;
|
|
93
106
|
if (!bindingFieldExists) {
|
|
94
107
|
const relatedFieldMeta = relatedModelMetadatas[relatedToField];
|
|
95
108
|
if (!relatedFieldMeta.metadata.required) {
|
|
@@ -131,8 +144,11 @@ datasource db {
|
|
|
131
144
|
const relationKey = [relatedModelName, modelName].join('_');
|
|
132
145
|
const relationIndex = relation_manager_1.RelationManager.getRelationCounter(relationKey, true);
|
|
133
146
|
const relationName = relation_manager_1.RelationManager.getShortenedRelationName(relatedModelName, modelName, relationIndex);
|
|
134
|
-
|
|
135
|
-
|
|
147
|
+
let relationTag = '';
|
|
148
|
+
if (relationMeta.relationName) {
|
|
149
|
+
relationTag = ` @relation("${relationMeta.relationName}")`;
|
|
150
|
+
}
|
|
151
|
+
section += `\t${key} ${relationMeta.inversionModel._collection}[]${relationTag}\n`;
|
|
136
152
|
relation_manager_1.RelationManager.completeRelation(relationKey, relationIndex, true);
|
|
137
153
|
}
|
|
138
154
|
else if (annotationType === 'InverseTimeSeries') {
|
|
@@ -151,10 +167,16 @@ datasource db {
|
|
|
151
167
|
section += `\t${key} String[]\n`;
|
|
152
168
|
}
|
|
153
169
|
}
|
|
154
|
-
else
|
|
170
|
+
else {
|
|
155
171
|
const trackMeta = modelMetadata;
|
|
156
|
-
const
|
|
157
|
-
|
|
172
|
+
const trackTags = trackMeta.tags || [];
|
|
173
|
+
const tags = trackTags.map((item) => '@' + item);
|
|
174
|
+
const isNoIdBehavior = model._NO_ID || idFieldName;
|
|
175
|
+
const isOverrideBehavior = (hasIdType && annotationType === 'IdType' && key === 'id' && idFieldName === 'id')
|
|
176
|
+
||
|
|
177
|
+
(model._NO_ID && model._SUPER_TAGS.some(a => a.fields.includes('id')) && key === 'id');
|
|
178
|
+
if (key === 'id' &&
|
|
179
|
+
isNoIdBehavior && !isOverrideBehavior) {
|
|
158
180
|
continue;
|
|
159
181
|
}
|
|
160
182
|
if (trackMeta.unique) {
|
|
@@ -173,7 +195,17 @@ datasource db {
|
|
|
173
195
|
// Process any database-specific options from the metadata
|
|
174
196
|
const dbSpecificTags = type_converter_1.TypeConverter.processTypeOptions(trackMeta, dbType);
|
|
175
197
|
tags.push(...dbSpecificTags);
|
|
176
|
-
|
|
198
|
+
const isIdTypeField = modelMetadatas[key].annotationType === 'IdType';
|
|
199
|
+
const fieldInUniqueSuperTag = model._SUPER_TAGS.some(st => st.tagType === 'unique' && st.fields.includes(key));
|
|
200
|
+
if (isIdTypeField) {
|
|
201
|
+
requiredString = '';
|
|
202
|
+
}
|
|
203
|
+
let trackField = `${key} ${type_converter_1.TypeConverter.toConfigCase(trackMeta, dbType, key === 'id', isOverrideBehavior)}${requiredString} ${tags.join(' ')}`;
|
|
204
|
+
if (isIdTypeField) {
|
|
205
|
+
trackField += utils_1.DbUtils.addIdPart(dbType, utils_1.DbUtils.doesUseUuid(modelMetadatas), trackMeta.noAuto);
|
|
206
|
+
idGenerated = true;
|
|
207
|
+
}
|
|
208
|
+
section += `\t${trackField}\n`;
|
|
177
209
|
}
|
|
178
210
|
}
|
|
179
211
|
if (model._SUPER_TAGS.length) {
|
|
@@ -250,11 +282,11 @@ datasource db {
|
|
|
250
282
|
fs_1.default.unlinkSync(schemaPath);
|
|
251
283
|
}
|
|
252
284
|
fs_1.default.writeFileSync(schemaPath, template);
|
|
253
|
-
|
|
254
|
-
|
|
285
|
+
if (_EXECUTE_PRISMA_CMD)
|
|
286
|
+
await console_1.rwsShell.runCommand(`${utils_1.DbUtils.detectInstaller()} prisma generate --schema=${schemaPath}`, process.cwd());
|
|
255
287
|
console.log(chalk_1.default.green('[RWS Init]') + ' prisma schema generated from ', schemaPath);
|
|
256
|
-
if (
|
|
257
|
-
|
|
288
|
+
if (_REMOVE_SCHEMA_FILE) {
|
|
289
|
+
fs_1.default.unlinkSync(schemaPath);
|
|
258
290
|
}
|
|
259
291
|
}
|
|
260
292
|
}
|
|
@@ -271,4 +303,3 @@ datasource db {
|
|
|
271
303
|
}
|
|
272
304
|
}
|
|
273
305
|
exports.SchemaGenerator = SchemaGenerator;
|
|
274
|
-
SchemaGenerator.dbUrlVarName = 'PRISMA_DB_URL';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { IDbOpts } from '../../models/interfaces/IDbOpts';
|
|
2
2
|
import { ITrackerMetaOpts } from '../../models/_model';
|
|
3
3
|
import { IIdMetaOpts } from 'src/decorators/IdType';
|
|
4
|
+
import { IDbConfigParams } from 'src/types/DbConfigHandler';
|
|
4
5
|
/**
|
|
5
6
|
* Handles type conversion for database schema generation
|
|
6
7
|
*/
|
|
@@ -8,7 +9,7 @@ export declare class TypeConverter {
|
|
|
8
9
|
/**
|
|
9
10
|
* Convert a JavaScript type to a Prisma schema type
|
|
10
11
|
*/
|
|
11
|
-
static toConfigCase(modelType: ITrackerMetaOpts | IIdMetaOpts, dbType?:
|
|
12
|
+
static toConfigCase(modelType: ITrackerMetaOpts | IIdMetaOpts, dbType?: IDbConfigParams['db_type'], isId?: boolean, isIdOverride?: boolean): string;
|
|
12
13
|
/**
|
|
13
14
|
* Process type functions metadata to extract database-specific options
|
|
14
15
|
* @param metadata The metadata from a type function
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TypeConverter = void 0;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
4
5
|
/**
|
|
5
6
|
* Handles type conversion for database schema generation
|
|
6
7
|
*/
|
|
@@ -8,7 +9,7 @@ class TypeConverter {
|
|
|
8
9
|
/**
|
|
9
10
|
* Convert a JavaScript type to a Prisma schema type
|
|
10
11
|
*/
|
|
11
|
-
static toConfigCase(modelType, dbType = 'mongodb', isId = false) {
|
|
12
|
+
static toConfigCase(modelType, dbType = 'mongodb', isId = false, isIdOverride = false) {
|
|
12
13
|
const type = modelType.type;
|
|
13
14
|
let input = type.name;
|
|
14
15
|
// Handle basic types
|
|
@@ -30,6 +31,9 @@ class TypeConverter {
|
|
|
30
31
|
input = 'Int';
|
|
31
32
|
}
|
|
32
33
|
}
|
|
34
|
+
if (input == 'BigInt') {
|
|
35
|
+
input = 'BigInt';
|
|
36
|
+
}
|
|
33
37
|
if (input == 'Object') {
|
|
34
38
|
input = 'Json';
|
|
35
39
|
}
|
|
@@ -53,8 +57,8 @@ class TypeConverter {
|
|
|
53
57
|
const firstChar = input.charAt(0).toUpperCase();
|
|
54
58
|
const restOfString = input.slice(1);
|
|
55
59
|
let resultField = firstChar + restOfString;
|
|
56
|
-
if (isId) {
|
|
57
|
-
return dbType
|
|
60
|
+
if (isId && !isIdOverride) {
|
|
61
|
+
return utils_1.DbUtils.getDefaultPrismaType(dbType, false);
|
|
58
62
|
}
|
|
59
63
|
const trackerModelType = modelType;
|
|
60
64
|
if (trackerModelType.isArray) {
|
|
@@ -81,7 +85,6 @@ class TypeConverter {
|
|
|
81
85
|
* @returns Array of tags to apply to the field
|
|
82
86
|
*/
|
|
83
87
|
static processTypeOptions(metadata, dbType) {
|
|
84
|
-
var _a, _b, _c, _d;
|
|
85
88
|
const tags = [...(metadata.tags || [])];
|
|
86
89
|
// Extract any database-specific options from the metadata
|
|
87
90
|
// and convert them to appropriate Prisma schema tags
|
|
@@ -91,7 +94,7 @@ class TypeConverter {
|
|
|
91
94
|
let tag = null;
|
|
92
95
|
if (metadata.dbOptions.mysql.useType && !metadata.dbOptions.mysql.useText) {
|
|
93
96
|
const tagName = metadata.dbOptions.mysql.useType === 'VarChar' ? 'db.' + metadata.dbOptions.mysql.useType : metadata.dbOptions.mysql.useType;
|
|
94
|
-
let tagParams = tagName === 'db.VarChar' && metadata.dbOptions.mysql.maxLength ? metadata.dbOptions.mysql.maxLength : (
|
|
97
|
+
let tagParams = tagName === 'db.VarChar' && metadata.dbOptions.mysql.maxLength ? metadata.dbOptions.mysql.maxLength : (metadata.dbOptions.mysql?.params?.join(', ') || '');
|
|
95
98
|
tag = `@${tagName}(${tagParams})`;
|
|
96
99
|
}
|
|
97
100
|
if (metadata.dbOptions.mysql.useText) {
|
|
@@ -100,7 +103,7 @@ class TypeConverter {
|
|
|
100
103
|
if (tag) {
|
|
101
104
|
tags.push(tag);
|
|
102
105
|
}
|
|
103
|
-
if (metadata.dbOptions.mysql.useUuid &&
|
|
106
|
+
if (metadata.dbOptions.mysql.useUuid && metadata.tags?.includes('id')) {
|
|
104
107
|
tags.push('default(uuid())');
|
|
105
108
|
}
|
|
106
109
|
}
|
|
@@ -109,7 +112,7 @@ class TypeConverter {
|
|
|
109
112
|
if (metadata.dbOptions.postgres.useText) {
|
|
110
113
|
tags.push('@db.Text');
|
|
111
114
|
}
|
|
112
|
-
if (metadata.dbOptions.postgres.useUuid &&
|
|
115
|
+
if (metadata.dbOptions.postgres.useUuid && metadata.tags?.includes('id')) {
|
|
113
116
|
tags.push('@default(uuid())');
|
|
114
117
|
tags.push('@db.Uuid');
|
|
115
118
|
}
|
|
@@ -19,6 +19,13 @@ export declare class DbUtils {
|
|
|
19
19
|
annotationType: string;
|
|
20
20
|
metadata: IIdMetaOpts;
|
|
21
21
|
}>, optional?: boolean): string;
|
|
22
|
+
static getDefaultPrismaType(dbType: IDbConfigParams['db_type'], useUuid: boolean): string;
|
|
23
|
+
static doesUseUuid(modelMeta: Record<string, {
|
|
24
|
+
annotationType: string;
|
|
25
|
+
metadata: IIdMetaOpts;
|
|
26
|
+
}>): boolean;
|
|
27
|
+
static addIdPart(dbType: IDbConfigParams['db_type'], useUuid: boolean, noAuto?: boolean): string;
|
|
28
|
+
static generateIdDefault(dbType: IDbConfigParams['db_type'], useUuid: boolean): string;
|
|
22
29
|
}
|
|
23
30
|
export declare const workspaceRootPath: string;
|
|
24
31
|
export declare const moduleDirPath: string;
|
package/dist/helper/db/utils.js
CHANGED
|
@@ -35,8 +35,7 @@ class DbUtils {
|
|
|
35
35
|
* Generate an ID field based on the database type
|
|
36
36
|
*/
|
|
37
37
|
static generateId(dbType, modelMeta, optional = false) {
|
|
38
|
-
|
|
39
|
-
let useUuid = false;
|
|
38
|
+
let useUuid = this.doesUseUuid(modelMeta);
|
|
40
39
|
let field = 'id';
|
|
41
40
|
const tags = [];
|
|
42
41
|
for (const key in modelMeta) {
|
|
@@ -47,50 +46,105 @@ class DbUtils {
|
|
|
47
46
|
const dbSpecificTags = type_converter_1.TypeConverter.processTypeOptions({ tags: [], dbOptions: modelMetadata.dbOptions }, dbType);
|
|
48
47
|
tags.push(...dbSpecificTags);
|
|
49
48
|
field = key;
|
|
50
|
-
if ((_b = (_a = modelMetadata.dbOptions) === null || _a === void 0 ? void 0 : _a.mysql) === null || _b === void 0 ? void 0 : _b.useUuid) {
|
|
51
|
-
useUuid = true;
|
|
52
|
-
}
|
|
53
|
-
if ((_d = (_c = modelMetadata.dbOptions) === null || _c === void 0 ? void 0 : _c.postgres) === null || _d === void 0 ? void 0 : _d.useUuid) {
|
|
54
|
-
useUuid = true;
|
|
55
|
-
}
|
|
56
|
-
if (modelMetadata.type.name === 'String') {
|
|
57
|
-
useUuid = true;
|
|
58
|
-
}
|
|
59
49
|
}
|
|
60
50
|
}
|
|
61
51
|
}
|
|
62
|
-
|
|
52
|
+
const idPrismaType = this.getDefaultPrismaType(dbType, useUuid);
|
|
63
53
|
let reqStr = '';
|
|
64
54
|
if (optional) {
|
|
65
55
|
reqStr = '?';
|
|
66
56
|
}
|
|
57
|
+
let idString = `${field} ${idPrismaType}${reqStr}`;
|
|
58
|
+
idString += this.addIdPart(dbType, useUuid, modelMeta[field].metadata.noAuto);
|
|
59
|
+
if (dbType === 'mongodb') {
|
|
60
|
+
tags.push('@map("_id")');
|
|
61
|
+
tags.push('@db.ObjectId');
|
|
62
|
+
}
|
|
63
|
+
if (tags.length) {
|
|
64
|
+
idString += ' ' + tags.join(' ');
|
|
65
|
+
}
|
|
66
|
+
if (!idString) {
|
|
67
|
+
throw new Error(`DB type "${dbType}" is not supported!`);
|
|
68
|
+
}
|
|
69
|
+
return idString;
|
|
70
|
+
}
|
|
71
|
+
static getDefaultPrismaType(dbType, useUuid) {
|
|
72
|
+
let idPrismaType = 'String';
|
|
67
73
|
switch (dbType) {
|
|
68
|
-
case 'mongodb':
|
|
69
|
-
idString = `${field} String${reqStr} @id @default(auto()) @map("_id") @db.ObjectId`;
|
|
70
|
-
break;
|
|
71
74
|
case 'mysql':
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
if (useUuid) {
|
|
76
|
+
idPrismaType = 'String';
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
idPrismaType = 'Int';
|
|
80
|
+
}
|
|
75
81
|
break;
|
|
76
82
|
case 'postgresql':
|
|
77
83
|
case 'postgres':
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
if (useUuid) {
|
|
85
|
+
idPrismaType = 'String';
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
idPrismaType = 'Int';
|
|
89
|
+
}
|
|
81
90
|
break;
|
|
82
91
|
case 'sqlite':
|
|
83
|
-
|
|
92
|
+
if (useUuid) {
|
|
93
|
+
idPrismaType = 'String';
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
idPrismaType = 'Int';
|
|
97
|
+
}
|
|
84
98
|
break;
|
|
85
99
|
}
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
return idPrismaType;
|
|
101
|
+
}
|
|
102
|
+
static doesUseUuid(modelMeta) {
|
|
103
|
+
let useUuid = false;
|
|
104
|
+
for (const key in modelMeta) {
|
|
105
|
+
const modelMetadata = modelMeta[key].metadata;
|
|
106
|
+
const annotationType = modelMeta[key].annotationType;
|
|
107
|
+
if (key !== 'id') {
|
|
108
|
+
if (annotationType == 'IdType') {
|
|
109
|
+
if (modelMetadata.dbOptions?.mysql?.useUuid) {
|
|
110
|
+
useUuid = true;
|
|
111
|
+
}
|
|
112
|
+
if (modelMetadata.dbOptions?.postgres?.useUuid) {
|
|
113
|
+
useUuid = true;
|
|
114
|
+
}
|
|
115
|
+
if (modelMetadata.type.name === 'String') {
|
|
116
|
+
useUuid = true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
88
120
|
}
|
|
89
|
-
|
|
90
|
-
|
|
121
|
+
return useUuid;
|
|
122
|
+
}
|
|
123
|
+
static addIdPart(dbType, useUuid, noAuto = false) {
|
|
124
|
+
let idString = ` @id${!noAuto ? ` @default(${this.generateIdDefault(dbType, useUuid)})` : ''}`;
|
|
125
|
+
if (dbType === 'mongodb') {
|
|
126
|
+
idString += ' @map("_id")';
|
|
127
|
+
idString += ' @db.ObjectId';
|
|
91
128
|
}
|
|
92
129
|
return idString;
|
|
93
130
|
}
|
|
131
|
+
static generateIdDefault(dbType, useUuid) {
|
|
132
|
+
switch (dbType) {
|
|
133
|
+
case 'mongodb':
|
|
134
|
+
return `auto()`;
|
|
135
|
+
case 'mysql':
|
|
136
|
+
return useUuid
|
|
137
|
+
? `uuid()`
|
|
138
|
+
: `autoincrement()`;
|
|
139
|
+
case 'postgresql':
|
|
140
|
+
case 'postgres':
|
|
141
|
+
return useUuid
|
|
142
|
+
? `uuid()`
|
|
143
|
+
: `autoincrement()`;
|
|
144
|
+
case 'sqlite':
|
|
145
|
+
return 'autoincrement()';
|
|
146
|
+
}
|
|
147
|
+
}
|
|
94
148
|
}
|
|
95
149
|
exports.DbUtils = DbUtils;
|
|
96
150
|
exports.workspaceRootPath = workspaceRoot;
|
|
@@ -17,6 +17,15 @@ const TimeSeriesUtils_1 = require("../utils/TimeSeriesUtils");
|
|
|
17
17
|
const ModelUtils_1 = require("../utils/ModelUtils");
|
|
18
18
|
const HydrateUtils_1 = require("../utils/HydrateUtils");
|
|
19
19
|
class RWSModel {
|
|
20
|
+
static services = {};
|
|
21
|
+
id;
|
|
22
|
+
static _collection = null;
|
|
23
|
+
static _RELATIONS = {};
|
|
24
|
+
static _NO_ID = false;
|
|
25
|
+
static _SUPER_TAGS = [];
|
|
26
|
+
static _BANNED_KEYS = ['_collection'];
|
|
27
|
+
static allModels = [];
|
|
28
|
+
static _CUT_KEYS = [];
|
|
20
29
|
constructor(data = null) {
|
|
21
30
|
if (!this.getCollection()) {
|
|
22
31
|
throw new Error('Model must have a collection defined');
|
|
@@ -110,12 +119,11 @@ class RWSModel {
|
|
|
110
119
|
return RelationUtils_1.RelationUtils.getRelationManyMeta(model, classFields);
|
|
111
120
|
}
|
|
112
121
|
static async paginate(paginateParams, findParams) {
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const fullData = (_e = findParams === null || findParams === void 0 ? void 0 : findParams.fullData) !== null && _e !== void 0 ? _e : false;
|
|
122
|
+
const conditions = findParams?.conditions ?? {};
|
|
123
|
+
const ordering = findParams?.ordering ?? null;
|
|
124
|
+
const fields = findParams?.fields ?? null;
|
|
125
|
+
const allowRelations = findParams?.allowRelations ?? true;
|
|
126
|
+
const fullData = findParams?.fullData ?? false;
|
|
119
127
|
const collection = Reflect.get(this, '_collection');
|
|
120
128
|
this.checkForInclusionWithThrow(this.name);
|
|
121
129
|
try {
|
|
@@ -231,12 +239,11 @@ class RWSModel {
|
|
|
231
239
|
return await this.services.dbService.watchCollection(collection, preRun);
|
|
232
240
|
}
|
|
233
241
|
static async findOneBy(findParams) {
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
const
|
|
237
|
-
const
|
|
238
|
-
const
|
|
239
|
-
const fullData = (_e = findParams === null || findParams === void 0 ? void 0 : findParams.fullData) !== null && _e !== void 0 ? _e : false;
|
|
242
|
+
const conditions = findParams?.conditions ?? {};
|
|
243
|
+
const ordering = findParams?.ordering ?? null;
|
|
244
|
+
const fields = findParams?.fields ?? null;
|
|
245
|
+
const allowRelations = findParams?.allowRelations ?? true;
|
|
246
|
+
const fullData = findParams?.fullData ?? false;
|
|
240
247
|
this.checkForInclusionWithThrow('');
|
|
241
248
|
const collection = Reflect.get(this, '_collection');
|
|
242
249
|
const dbData = await this.services.dbService.findOneBy(collection, conditions, fields, ordering, allowRelations);
|
|
@@ -247,11 +254,10 @@ class RWSModel {
|
|
|
247
254
|
return null;
|
|
248
255
|
}
|
|
249
256
|
static async find(id, findParams = null) {
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
const
|
|
253
|
-
const
|
|
254
|
-
const fullData = (_d = findParams === null || findParams === void 0 ? void 0 : findParams.fullData) !== null && _d !== void 0 ? _d : false;
|
|
257
|
+
const ordering = findParams?.ordering ?? null;
|
|
258
|
+
const fields = findParams?.fields ?? null;
|
|
259
|
+
const allowRelations = findParams?.allowRelations ?? true;
|
|
260
|
+
const fullData = findParams?.fullData ?? false;
|
|
255
261
|
const collection = Reflect.get(this, '_collection');
|
|
256
262
|
this.checkForInclusionWithThrow(this.name);
|
|
257
263
|
const dbData = await this.services.dbService.findOneBy(collection, { id }, fields, ordering, allowRelations);
|
|
@@ -262,16 +268,15 @@ class RWSModel {
|
|
|
262
268
|
return null;
|
|
263
269
|
}
|
|
264
270
|
static async findBy(findParams) {
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
const fullData = (_e = findParams === null || findParams === void 0 ? void 0 : findParams.fullData) !== null && _e !== void 0 ? _e : false;
|
|
271
|
+
const conditions = findParams?.conditions ?? {};
|
|
272
|
+
const ordering = findParams?.ordering ?? null;
|
|
273
|
+
const fields = findParams?.fields ?? null;
|
|
274
|
+
const allowRelations = findParams?.allowRelations ?? true;
|
|
275
|
+
const fullData = findParams?.fullData ?? false;
|
|
271
276
|
const collection = Reflect.get(this, '_collection');
|
|
272
277
|
this.checkForInclusionWithThrow(this.name);
|
|
273
278
|
try {
|
|
274
|
-
const paginateParams =
|
|
279
|
+
const paginateParams = findParams?.pagination ? findParams?.pagination : undefined;
|
|
275
280
|
const dbData = await this.services.dbService.findBy(collection, conditions, fields, ordering, paginateParams);
|
|
276
281
|
if (dbData.length) {
|
|
277
282
|
const instanced = [];
|
|
@@ -327,14 +332,6 @@ class RWSModel {
|
|
|
327
332
|
}
|
|
328
333
|
}
|
|
329
334
|
exports.RWSModel = RWSModel;
|
|
330
|
-
RWSModel.services = {};
|
|
331
|
-
RWSModel._collection = null;
|
|
332
|
-
RWSModel._RELATIONS = {};
|
|
333
|
-
RWSModel._NO_ID = false;
|
|
334
|
-
RWSModel._SUPER_TAGS = [];
|
|
335
|
-
RWSModel._BANNED_KEYS = ['_collection'];
|
|
336
|
-
RWSModel.allModels = [];
|
|
337
|
-
RWSModel._CUT_KEYS = [];
|
|
338
335
|
__decorate([
|
|
339
336
|
(0, decorators_1.TrackType)(String),
|
|
340
337
|
__metadata("design:type", Object)
|
|
@@ -6,8 +6,15 @@ class ModelUtils {
|
|
|
6
6
|
static async getModelAnnotations(constructor) {
|
|
7
7
|
const annotationsData = {};
|
|
8
8
|
const metadataKeys = Reflect.getMetadataKeys(constructor.prototype);
|
|
9
|
+
const filteredMetaKeys = metadataKeys.filter((metaKey) => {
|
|
10
|
+
const [annotationType, annotatedField] = metaKey.split(':');
|
|
11
|
+
if (annotationType === 'TrackType' && annotatedField === 'id' && metadataKeys.includes('IdType:' + annotatedField)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
|
+
});
|
|
9
16
|
// Process all metadata keys and collect promises
|
|
10
|
-
const metadataPromises =
|
|
17
|
+
const metadataPromises = filteredMetaKeys.map(async (fullKey) => {
|
|
11
18
|
const [annotationType, propertyKey] = fullKey.split(':');
|
|
12
19
|
const metadata = Reflect.getMetadata(fullKey, constructor.prototype);
|
|
13
20
|
if (metadata) {
|
|
@@ -3,12 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PaginationUtils = void 0;
|
|
4
4
|
class PaginationUtils {
|
|
5
5
|
static async paginate(paginationParams = { page: 0, per_page: 50 }, findParams = {}) {
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const fullData = (_e = findParams === null || findParams === void 0 ? void 0 : findParams.fullData) !== null && _e !== void 0 ? _e : false;
|
|
6
|
+
const conditions = findParams?.conditions ?? {};
|
|
7
|
+
const ordering = findParams?.ordering ?? null;
|
|
8
|
+
const fields = findParams?.fields ?? null;
|
|
9
|
+
const allowRelations = findParams?.allowRelations ?? true;
|
|
10
|
+
const fullData = findParams?.fullData ?? false;
|
|
12
11
|
const collection = Reflect.get(this, '_collection');
|
|
13
12
|
this.checkForInclusionWithThrow(this.name);
|
|
14
13
|
try {
|
|
@@ -8,10 +8,12 @@ const client_1 = require("@prisma/client");
|
|
|
8
8
|
const mongodb_1 = require("mongodb");
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
10
|
class DBService {
|
|
11
|
+
configService;
|
|
12
|
+
client;
|
|
13
|
+
opts = null;
|
|
14
|
+
connected = false;
|
|
11
15
|
constructor(configService) {
|
|
12
16
|
this.configService = configService;
|
|
13
|
-
this.opts = null;
|
|
14
|
-
this.connected = false;
|
|
15
17
|
}
|
|
16
18
|
connectToDB(opts = null) {
|
|
17
19
|
if (opts) {
|
|
@@ -50,15 +52,13 @@ class DBService {
|
|
|
50
52
|
return client;
|
|
51
53
|
}
|
|
52
54
|
async createBaseMongoClient() {
|
|
53
|
-
|
|
54
|
-
const dbUrl = ((_a = this.opts) === null || _a === void 0 ? void 0 : _a.dbUrl) || this.configService.get('db_url');
|
|
55
|
+
const dbUrl = this.opts?.dbUrl || this.configService.get('db_url');
|
|
55
56
|
const client = DBService.baseClientConstruct(dbUrl);
|
|
56
57
|
await client.connect();
|
|
57
58
|
return client;
|
|
58
59
|
}
|
|
59
60
|
async createBaseMongoClientDB() {
|
|
60
|
-
|
|
61
|
-
const dbName = ((_a = this.opts) === null || _a === void 0 ? void 0 : _a.dbName) || this.configService.get('db_name');
|
|
61
|
+
const dbName = this.opts?.dbName || this.configService.get('db_name');
|
|
62
62
|
const client = await this.createBaseMongoClient();
|
|
63
63
|
return [client, client.db(dbName)];
|
|
64
64
|
}
|
|
@@ -151,8 +151,7 @@ class DBService {
|
|
|
151
151
|
return retData;
|
|
152
152
|
}
|
|
153
153
|
async collectionExists(collection_name) {
|
|
154
|
-
|
|
155
|
-
const dbUrl = ((_a = this.opts) === null || _a === void 0 ? void 0 : _a.dbUrl) || this.configService.get('db_url');
|
|
154
|
+
const dbUrl = this.opts?.dbUrl || this.configService.get('db_url');
|
|
156
155
|
const client = new mongodb_1.MongoClient(dbUrl);
|
|
157
156
|
try {
|
|
158
157
|
await client.connect();
|
package/package.json
CHANGED
package/src/decorators/IdType.ts
CHANGED
|
@@ -8,7 +8,16 @@ export interface IIdMetaOpts extends IIdTypeOpts {
|
|
|
8
8
|
|
|
9
9
|
function IdType(type: any, opts: IIdTypeOpts | null = null, tags: string[] = []) {
|
|
10
10
|
const metaOpts: IIdMetaOpts = { type, dbOptions: opts && opts.dbOptions ? opts.dbOptions : null };
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
if(opts && opts.dbOptions){
|
|
13
|
+
metaOpts.dbOptions = opts.dbOptions;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if(opts && opts.noAuto){
|
|
17
|
+
metaOpts.noAuto = opts.noAuto;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return function(target: any, key: string) {
|
|
12
21
|
Reflect.defineMetadata(`IdType:${key}`, metaOpts, target);
|
|
13
22
|
};
|
|
14
23
|
}
|
|
@@ -64,8 +64,8 @@ function InverseRelation(inversionModel: () => OpModelType<RWSModel<any>>, sourc
|
|
|
64
64
|
foreignKey: relationOptions && relationOptions.foreignKey ? relationOptions.foreignKey : guessForeignKey(model, source, decoratorsData),
|
|
65
65
|
// Generate a unique relation name if one is not provided
|
|
66
66
|
relationName: relationOptions && relationOptions.relationName ?
|
|
67
|
-
relationOptions.relationName
|
|
68
|
-
|
|
67
|
+
relationOptions.relationName :
|
|
68
|
+
null
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
return metaOpts;
|
|
@@ -39,8 +39,8 @@ function Relation(theModel: () => OpModelType<RWSModel<any>>, relationOptions: P
|
|
|
39
39
|
key,
|
|
40
40
|
// Generate a unique relation name if one is not provided
|
|
41
41
|
relationName: relationOptions.relationName ?
|
|
42
|
-
relationOptions.relationName
|
|
43
|
-
|
|
42
|
+
relationOptions.relationName :
|
|
43
|
+
null
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
if(relationOptions.required){
|
package/src/helper/DbHelper.ts
CHANGED
|
@@ -101,7 +101,7 @@ export class DbHelper {
|
|
|
101
101
|
/**
|
|
102
102
|
* Convert a JavaScript type to a Prisma schema type
|
|
103
103
|
*/
|
|
104
|
-
static toConfigCase(modelType: any, dbType:
|
|
104
|
+
static toConfigCase(modelType: any, dbType: IDbConfigParams['db_type'] = 'mongodb'): string {
|
|
105
105
|
return TypeConverter.toConfigCase(modelType, dbType);
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -15,6 +15,8 @@ import { RelationManager } from './relation-manager';
|
|
|
15
15
|
import { ITrackerMetaOpts } from '../../decorators/TrackType';
|
|
16
16
|
import { IDbOpts } from '../../models/interfaces/IDbOpts';
|
|
17
17
|
|
|
18
|
+
const _EXECUTE_PRISMA_CMD = true;
|
|
19
|
+
const _REMOVE_SCHEMA_FILE = true;
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Handles Prisma schema generation
|
|
@@ -56,13 +58,28 @@ datasource db {
|
|
|
56
58
|
const dbType = configService.get('db_type') || 'mongodb';
|
|
57
59
|
const modelName: string = (model as any)._collection;
|
|
58
60
|
|
|
59
|
-
section += `model ${modelName} {\n`;
|
|
61
|
+
section += `model ${modelName} {\n`;
|
|
62
|
+
|
|
63
|
+
let hasIdType = false;
|
|
64
|
+
let idFieldName: string;
|
|
65
|
+
|
|
66
|
+
for(const someModelMetaKey in modelMetadatas){
|
|
67
|
+
const isIdTyped = modelMetadatas[someModelMetaKey].annotationType === 'IdType';
|
|
68
|
+
if(isIdTyped){
|
|
69
|
+
hasIdType = true;
|
|
70
|
+
idFieldName = someModelMetaKey;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let idGenerated = false;
|
|
75
|
+
|
|
60
76
|
|
|
61
77
|
if(
|
|
62
|
-
!model._NO_ID
|
|
63
|
-
){
|
|
64
|
-
section += `\t${DbUtils.generateId(dbType, modelMetadatas)}\n`;
|
|
65
|
-
|
|
78
|
+
!model._NO_ID && !hasIdType
|
|
79
|
+
){
|
|
80
|
+
section += `\t${DbUtils.generateId(dbType, modelMetadatas)}\n`;
|
|
81
|
+
idGenerated = true;
|
|
82
|
+
}
|
|
66
83
|
|
|
67
84
|
for (const key in modelMetadatas) {
|
|
68
85
|
const modelMetadata = modelMetadatas[key].metadata;
|
|
@@ -71,16 +88,15 @@ datasource db {
|
|
|
71
88
|
|
|
72
89
|
let indexedId = false;
|
|
73
90
|
|
|
74
|
-
if(model._NO_ID){
|
|
91
|
+
if(model._NO_ID || hasIdType){
|
|
75
92
|
indexedId = true;
|
|
76
93
|
requiredString = '';
|
|
77
|
-
}
|
|
78
|
-
|
|
94
|
+
}
|
|
79
95
|
|
|
80
96
|
if (key === 'id' && !indexedId) {
|
|
81
97
|
continue;
|
|
82
98
|
}
|
|
83
|
-
|
|
99
|
+
|
|
84
100
|
if (annotationType === 'Relation') {
|
|
85
101
|
const relationMeta = modelMetadata as IRelationOpts;
|
|
86
102
|
|
|
@@ -100,8 +116,9 @@ datasource db {
|
|
|
100
116
|
const relationKey = [modelName, relatedModelName].join('_');
|
|
101
117
|
|
|
102
118
|
const relationIndex = RelationManager.getRelationCounter(relationKey);
|
|
103
|
-
const relationName =
|
|
104
|
-
|
|
119
|
+
const relationName = relationMeta.relationName ? relationMeta.relationName : null;
|
|
120
|
+
|
|
121
|
+
const mapName = relationMeta.mappingName ? relationMeta.mappingName : null;
|
|
105
122
|
|
|
106
123
|
const relatedModelMetadatas: Record<string, { annotationType: string, metadata: ITrackerMetaOpts }> = await RWSModel.getModelAnnotations(relatedModel);
|
|
107
124
|
const relationFieldName = modelMetadata.relationField ? modelMetadata.relationField : key.toLowerCase() + '_' + modelMetadata.relationField.toLowerCase();
|
|
@@ -111,19 +128,19 @@ datasource db {
|
|
|
111
128
|
|
|
112
129
|
if(modelMetadata.required === false){
|
|
113
130
|
requiredString = '?';
|
|
114
|
-
}
|
|
131
|
+
}
|
|
115
132
|
|
|
116
133
|
const cascadeStr = cascadeOpts.length ? `, ${cascadeOpts.join(', ')}` : '' ;
|
|
117
134
|
|
|
118
135
|
if (isMany) {
|
|
119
136
|
// Add an inverse field to the related model if it doesn't exist
|
|
120
|
-
section += `\t${key} ${relatedModel._collection}[] @relation("${relationName}", fields: [${relationFieldName}], references: [${relatedToField}]
|
|
121
|
-
} else {
|
|
122
|
-
section += `\t${key} ${relatedModel._collection}${requiredString} @relation("${relationName}", fields: [${relationFieldName}], references: [${relatedToField}]
|
|
137
|
+
section += `\t${key} ${relatedModel._collection}[] @relation(${relationName ? `"${relationName}", ` : ''}fields: [${relationFieldName}], references: [${relatedToField}]${mapName ? `, map: "${mapName}"` : ''}${cascadeStr})\n`;
|
|
138
|
+
} else {
|
|
139
|
+
section += `\t${key} ${relatedModel._collection}${requiredString} @relation(${relationName ? `"${relationName}", ` : ''}fields: [${relationFieldName}], references: [${relatedToField}]${mapName ? `, map: "${mapName}"` : ''}${cascadeStr})\n`;
|
|
123
140
|
if(!bindingFieldExists){
|
|
124
141
|
const relatedFieldMeta = relatedModelMetadatas[relatedToField];
|
|
125
142
|
|
|
126
|
-
if(!relatedFieldMeta.metadata.required){
|
|
143
|
+
if(!relatedFieldMeta.metadata.required){
|
|
127
144
|
requiredString = '';
|
|
128
145
|
}
|
|
129
146
|
|
|
@@ -163,10 +180,15 @@ datasource db {
|
|
|
163
180
|
const relationKey = [relatedModelName, modelName].join('_');
|
|
164
181
|
const relationIndex = RelationManager.getRelationCounter(relationKey, true);
|
|
165
182
|
|
|
166
|
-
const relationName = RelationManager.getShortenedRelationName(relatedModelName, modelName, relationIndex);
|
|
167
|
-
|
|
183
|
+
const relationName = RelationManager.getShortenedRelationName(relatedModelName, modelName, relationIndex);
|
|
184
|
+
|
|
185
|
+
let relationTag = '';
|
|
168
186
|
|
|
169
|
-
|
|
187
|
+
if(relationMeta.relationName){
|
|
188
|
+
relationTag = ` @relation("${relationMeta.relationName}")`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
section += `\t${key} ${relationMeta.inversionModel._collection}[]${relationTag}\n`;
|
|
170
192
|
|
|
171
193
|
RelationManager.completeRelation(relationKey, relationIndex, true);
|
|
172
194
|
} else if (annotationType === 'InverseTimeSeries') {
|
|
@@ -181,14 +203,22 @@ datasource db {
|
|
|
181
203
|
} else {
|
|
182
204
|
section += `\t${key} String[]\n`;
|
|
183
205
|
}
|
|
184
|
-
} else
|
|
206
|
+
} else {
|
|
185
207
|
const trackMeta = modelMetadata as ITrackerMetaOpts;
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
208
|
+
const trackTags = trackMeta.tags || [];
|
|
209
|
+
const tags: string[] = trackTags.map((item: string) => '@' + item);
|
|
210
|
+
|
|
211
|
+
const isNoIdBehavior = model._NO_ID || idFieldName;
|
|
212
|
+
const isOverrideBehavior = (hasIdType && annotationType === 'IdType' && key === 'id' && idFieldName === 'id')
|
|
213
|
+
||
|
|
214
|
+
(model._NO_ID && model._SUPER_TAGS.some(a => a.fields.includes('id')) && key === 'id');
|
|
215
|
+
|
|
216
|
+
if(key === 'id' &&
|
|
217
|
+
isNoIdBehavior && !isOverrideBehavior
|
|
218
|
+
){
|
|
189
219
|
continue;
|
|
190
220
|
}
|
|
191
|
-
|
|
221
|
+
|
|
192
222
|
if(trackMeta.unique){
|
|
193
223
|
const fieldDetail: string | null = typeof trackMeta.unique === 'string' ? trackMeta.unique : null;
|
|
194
224
|
tags.push(`@unique(${fieldDetail ? `map: "${fieldDetail}"` : ''})`);
|
|
@@ -210,8 +240,21 @@ datasource db {
|
|
|
210
240
|
const dbSpecificTags = TypeConverter.processTypeOptions(trackMeta as { tags: string[], dbOptions: IDbOpts['dbOptions'] }, dbType);
|
|
211
241
|
tags.push(...dbSpecificTags);
|
|
212
242
|
|
|
243
|
+
const isIdTypeField = modelMetadatas[key].annotationType === 'IdType';
|
|
244
|
+
const fieldInUniqueSuperTag = model._SUPER_TAGS.some(st => st.tagType === 'unique' && st.fields.includes(key));
|
|
213
245
|
|
|
214
|
-
|
|
246
|
+
if(isIdTypeField){
|
|
247
|
+
requiredString = '';
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let trackField = `${key} ${TypeConverter.toConfigCase(trackMeta, dbType, key === 'id', isOverrideBehavior)}${requiredString} ${tags.join(' ')}`;
|
|
251
|
+
|
|
252
|
+
if(isIdTypeField){
|
|
253
|
+
trackField += DbUtils.addIdPart(dbType, DbUtils.doesUseUuid(modelMetadatas), trackMeta.noAuto);
|
|
254
|
+
idGenerated = true;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
section += `\t${trackField}\n`;
|
|
215
258
|
}
|
|
216
259
|
}
|
|
217
260
|
|
|
@@ -319,13 +362,14 @@ datasource db {
|
|
|
319
362
|
|
|
320
363
|
fs.writeFileSync(schemaPath, template);
|
|
321
364
|
|
|
365
|
+
if(_EXECUTE_PRISMA_CMD)
|
|
322
366
|
await rwsShell.runCommand(`${DbUtils.detectInstaller()} prisma generate --schema=${schemaPath}`, process.cwd());
|
|
323
367
|
|
|
324
|
-
|
|
368
|
+
|
|
325
369
|
console.log(chalk.green('[RWS Init]') + ' prisma schema generated from ', schemaPath);
|
|
326
370
|
|
|
327
|
-
if (
|
|
328
|
-
|
|
371
|
+
if (_REMOVE_SCHEMA_FILE) {
|
|
372
|
+
fs.unlinkSync(schemaPath);
|
|
329
373
|
}
|
|
330
374
|
}
|
|
331
375
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { IDbOpts } from '../../models/interfaces/IDbOpts';
|
|
2
2
|
import { ITrackerMetaOpts } from '../../models/_model';
|
|
3
3
|
import { IIdMetaOpts } from 'src/decorators/IdType';
|
|
4
|
+
import { DbUtils } from './utils';
|
|
5
|
+
import { IDbConfigParams } from 'src/types/DbConfigHandler';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Handles type conversion for database schema generation
|
|
@@ -9,7 +11,7 @@ export class TypeConverter {
|
|
|
9
11
|
/**
|
|
10
12
|
* Convert a JavaScript type to a Prisma schema type
|
|
11
13
|
*/
|
|
12
|
-
static toConfigCase(modelType: ITrackerMetaOpts | IIdMetaOpts, dbType:
|
|
14
|
+
static toConfigCase(modelType: ITrackerMetaOpts | IIdMetaOpts, dbType: IDbConfigParams['db_type'] = 'mongodb', isId: boolean = false, isIdOverride: boolean = false): string {
|
|
13
15
|
const type = modelType.type;
|
|
14
16
|
let input = type.name;
|
|
15
17
|
|
|
@@ -36,6 +38,10 @@ export class TypeConverter {
|
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
if (input == 'BigInt') {
|
|
42
|
+
input = 'BigInt';
|
|
43
|
+
}
|
|
44
|
+
|
|
39
45
|
if (input == 'Object') {
|
|
40
46
|
input = 'Json';
|
|
41
47
|
}
|
|
@@ -63,9 +69,9 @@ export class TypeConverter {
|
|
|
63
69
|
const restOfString = input.slice(1);
|
|
64
70
|
let resultField = firstChar + restOfString;
|
|
65
71
|
|
|
66
|
-
if(isId){
|
|
67
|
-
return dbType
|
|
68
|
-
}
|
|
72
|
+
if(isId && !isIdOverride){
|
|
73
|
+
return DbUtils.getDefaultPrismaType(dbType, false);
|
|
74
|
+
}
|
|
69
75
|
|
|
70
76
|
const trackerModelType = modelType as ITrackerMetaOpts;
|
|
71
77
|
|
package/src/helper/db/utils.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { IDbConfigParams } from '../../types/DbConfigHandler';
|
|
|
5
5
|
import { IIdMetaOpts, IIdTypeOpts } from '../../decorators/IdType';
|
|
6
6
|
import { TypeConverter } from './type-converter';
|
|
7
7
|
import { IDbOpts } from '../../models/interfaces/IDbOpts';
|
|
8
|
+
import { ITrackerMetaOpts } from '../../models/_model';
|
|
8
9
|
|
|
9
10
|
const workspaceRoot = rwsPath.findRootWorkspacePath();
|
|
10
11
|
const moduleDir = path.resolve(workspaceRoot, 'node_modules', '@rws-framework', 'db');
|
|
@@ -41,78 +42,151 @@ export class DbUtils {
|
|
|
41
42
|
dbType: IDbConfigParams['db_type'],
|
|
42
43
|
modelMeta: Record<string, { annotationType: string, metadata: IIdMetaOpts }>,
|
|
43
44
|
optional = false
|
|
44
|
-
): string {
|
|
45
|
-
let useUuid =
|
|
45
|
+
): string {
|
|
46
|
+
let useUuid = this.doesUseUuid(modelMeta);
|
|
46
47
|
let field = 'id';
|
|
47
48
|
const tags: string[] = [];
|
|
48
49
|
|
|
49
50
|
for (const key in modelMeta) {
|
|
50
|
-
const modelMetadata: IIdMetaOpts = modelMeta[key].metadata;
|
|
51
|
-
const annotationType: string = modelMeta[key].annotationType;
|
|
51
|
+
const modelMetadata: IIdMetaOpts = modelMeta[key].metadata;
|
|
52
|
+
const annotationType: string = modelMeta[key].annotationType;
|
|
52
53
|
|
|
53
|
-
if(key !== 'id'){
|
|
54
|
-
if(annotationType == 'IdType'){
|
|
54
|
+
if (key !== 'id') {
|
|
55
|
+
if (annotationType == 'IdType') {
|
|
55
56
|
const dbSpecificTags = TypeConverter.processTypeOptions({ tags: [], dbOptions: modelMetadata.dbOptions }, dbType);
|
|
56
|
-
tags.push(...dbSpecificTags);
|
|
57
|
-
|
|
58
|
-
field = key;
|
|
57
|
+
tags.push(...dbSpecificTags);
|
|
58
|
+
|
|
59
|
+
field = key;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const idPrismaType = this.getDefaultPrismaType(dbType, useUuid);
|
|
65
|
+
|
|
66
|
+
let reqStr = '';
|
|
67
|
+
|
|
68
|
+
if (optional) {
|
|
69
|
+
reqStr = '?';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let idString: string = `${field} ${idPrismaType}${reqStr}`;
|
|
73
|
+
|
|
74
|
+
idString += this.addIdPart(dbType, useUuid, modelMeta[field].metadata.noAuto);
|
|
75
|
+
|
|
76
|
+
if(dbType === 'mongodb'){
|
|
77
|
+
tags.push('@map("_id")');
|
|
78
|
+
tags.push('@db.ObjectId');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (tags.length) {
|
|
82
|
+
idString += ' ' + tags.join(' ');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!idString) {
|
|
86
|
+
throw new Error(`DB type "${dbType}" is not supported!`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
return idString;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static getDefaultPrismaType(dbType: IDbConfigParams['db_type'], useUuid: boolean): string
|
|
94
|
+
{
|
|
95
|
+
let idPrismaType = 'String';
|
|
96
|
+
|
|
97
|
+
switch (dbType) {
|
|
98
|
+
case 'mysql':
|
|
99
|
+
if (useUuid) {
|
|
100
|
+
idPrismaType = 'String';
|
|
101
|
+
} else {
|
|
102
|
+
idPrismaType = 'Int';
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
break;
|
|
106
|
+
|
|
107
|
+
case 'postgresql':
|
|
108
|
+
case 'postgres':
|
|
109
|
+
if (useUuid) {
|
|
110
|
+
idPrismaType = 'String';
|
|
111
|
+
} else {
|
|
112
|
+
idPrismaType = 'Int';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
break;
|
|
116
|
+
|
|
117
|
+
case 'sqlite':
|
|
118
|
+
if (useUuid) {
|
|
119
|
+
idPrismaType = 'String';
|
|
120
|
+
} else {
|
|
121
|
+
idPrismaType = 'Int';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return idPrismaType;
|
|
128
|
+
}
|
|
59
129
|
|
|
60
|
-
|
|
130
|
+
static doesUseUuid(modelMeta: Record<string, { annotationType: string, metadata: IIdMetaOpts }>): boolean
|
|
131
|
+
{
|
|
132
|
+
let useUuid = false;
|
|
133
|
+
|
|
134
|
+
for (const key in modelMeta) {
|
|
135
|
+
const modelMetadata: IIdMetaOpts = modelMeta[key].metadata;
|
|
136
|
+
const annotationType: string = modelMeta[key].annotationType;
|
|
137
|
+
|
|
138
|
+
if (key !== 'id') {
|
|
139
|
+
if (annotationType == 'IdType') {
|
|
140
|
+
if (modelMetadata.dbOptions?.mysql?.useUuid) {
|
|
61
141
|
useUuid = true;
|
|
62
142
|
}
|
|
63
143
|
|
|
64
|
-
if(modelMetadata.dbOptions?.postgres?.useUuid){
|
|
144
|
+
if (modelMetadata.dbOptions?.postgres?.useUuid) {
|
|
65
145
|
useUuid = true;
|
|
66
146
|
}
|
|
67
147
|
|
|
68
|
-
if(modelMetadata.type.name === 'String'){
|
|
148
|
+
if (modelMetadata.type.name === 'String') {
|
|
69
149
|
useUuid = true;
|
|
70
150
|
}
|
|
71
151
|
}
|
|
72
152
|
}
|
|
73
153
|
}
|
|
74
154
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
let reqStr = '';
|
|
155
|
+
return useUuid;
|
|
156
|
+
}
|
|
78
157
|
|
|
79
|
-
|
|
80
|
-
|
|
158
|
+
static addIdPart(dbType: IDbConfigParams['db_type'], useUuid: boolean, noAuto: boolean = false): string
|
|
159
|
+
{
|
|
160
|
+
let idString = ` @id${!noAuto ? ` @default(${this.generateIdDefault(dbType, useUuid)})` : ''}`;
|
|
161
|
+
|
|
162
|
+
if(dbType === 'mongodb'){
|
|
163
|
+
idString += ' @map("_id")';
|
|
164
|
+
idString += ' @db.ObjectId';
|
|
81
165
|
}
|
|
82
166
|
|
|
167
|
+
return idString;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
static generateIdDefault(dbType: IDbConfigParams['db_type'], useUuid: boolean): string
|
|
171
|
+
{
|
|
83
172
|
switch (dbType) {
|
|
84
173
|
case 'mongodb':
|
|
85
|
-
|
|
86
|
-
break;
|
|
174
|
+
return `auto()`;
|
|
87
175
|
|
|
88
176
|
case 'mysql':
|
|
89
|
-
|
|
90
|
-
?
|
|
91
|
-
:
|
|
92
|
-
break;
|
|
177
|
+
return useUuid
|
|
178
|
+
? `uuid()`
|
|
179
|
+
: `autoincrement()`;
|
|
93
180
|
|
|
94
181
|
case 'postgresql':
|
|
95
182
|
case 'postgres':
|
|
96
|
-
|
|
97
|
-
?
|
|
98
|
-
:
|
|
99
|
-
break;
|
|
183
|
+
return useUuid
|
|
184
|
+
? `uuid()`
|
|
185
|
+
: `autoincrement()`;
|
|
100
186
|
|
|
101
187
|
case 'sqlite':
|
|
102
|
-
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if(tags.length){
|
|
107
|
-
idString += ' '+tags.join(' ');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if(!idString){
|
|
111
|
-
throw new Error(`DB type "${dbType}" is not supported!`);
|
|
188
|
+
return 'autoincrement()';
|
|
112
189
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return idString;
|
|
116
190
|
}
|
|
117
191
|
}
|
|
118
192
|
|
|
@@ -6,9 +6,18 @@ export class ModelUtils {
|
|
|
6
6
|
const annotationsData: Record<string, {annotationType: string, metadata: any}> = {};
|
|
7
7
|
|
|
8
8
|
const metadataKeys = Reflect.getMetadataKeys(constructor.prototype);
|
|
9
|
+
|
|
10
|
+
const filteredMetaKeys = metadataKeys.filter((metaKey) => {
|
|
11
|
+
const [annotationType, annotatedField] = metaKey.split(':');
|
|
12
|
+
if(annotationType === 'TrackType' && annotatedField === 'id' && metadataKeys.includes('IdType:' + annotatedField)){
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return true;
|
|
17
|
+
});
|
|
9
18
|
|
|
10
19
|
// Process all metadata keys and collect promises
|
|
11
|
-
const metadataPromises =
|
|
20
|
+
const metadataPromises = filteredMetaKeys.map(async (fullKey: string) => {
|
|
12
21
|
const [annotationType, propertyKey] = fullKey.split(':');
|
|
13
22
|
const metadata = Reflect.getMetadata(fullKey, constructor.prototype);
|
|
14
23
|
|