@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.
@@ -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 = (_a = relDecorators[relKey]) === null || _a === void 0 ? void 0 : _a.metadata;
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.toLowerCase() :
45
- `${model._collection}_${key}_${source._collection}`.toLowerCase()
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.toLowerCase() :
20
- `${target.constructor.name.toLowerCase()}_${key}_${relatedTo._collection.toLowerCase()}`
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 (!(opts === null || opts === void 0 ? void 0 : opts.required)) {
11
+ if (!opts?.required) {
12
12
  opts.required = false;
13
13
  }
14
- if (!(opts === null || opts === void 0 ? void 0 : opts.isArray)) {
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?: string): string;
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
  */
@@ -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
- if (!model._NO_ID) {
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 ((_a = relationMeta.cascade) === null || _a === void 0 ? void 0 : _a.onDelete) {
81
+ if (relationMeta.cascade?.onDelete) {
69
82
  cascadeOpts.push(`onDelete: ${relationMeta.cascade.onDelete}`);
70
83
  }
71
- if ((_b = relationMeta.cascade) === null || _b === void 0 ? void 0 : _b.onUpdate) {
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 = relation_manager_1.RelationManager.getShortenedRelationName(modelName, relatedModelName, relationIndex);
78
- const mapName = relationName;
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}], map: "${mapName}"${cascadeStr})\n`;
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}], map: "${mapName}"${cascadeStr})\n`;
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
- const mapName = relationName;
135
- section += `\t${key} ${relationMeta.inversionModel._collection}[] @relation("${relationName}", map: "${mapName}")\n`;
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 if (annotationType === 'TrackType') {
170
+ else {
155
171
  const trackMeta = modelMetadata;
156
- const tags = trackMeta.tags.map((item) => '@' + item);
157
- if (key === 'id' && model._NO_ID && !model._SUPER_TAGS.some(tag => tag.tagType === 'id' && tag.fields.includes('id'))) {
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
- section += `\t${key} ${type_converter_1.TypeConverter.toConfigCase(trackMeta, dbType, key === 'id')}${requiredString} ${tags.join(' ')}\n`;
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
- await console_1.rwsShell.runCommand(`${utils_1.DbUtils.detectInstaller()} prisma generate --schema=${schemaPath}`, process.cwd());
254
- leaveFile = false;
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 (!leaveFile) {
257
- // fs.unlinkSync(schemaPath);
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?: string, isId?: boolean): string;
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 === 'mongodb' ? 'String' : 'Int';
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 : (((_b = (_a = metadata.dbOptions.mysql) === null || _a === void 0 ? void 0 : _a.params) === null || _b === void 0 ? void 0 : _b.join(', ')) || '');
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 && ((_c = metadata.tags) === null || _c === void 0 ? void 0 : _c.includes('id'))) {
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 && ((_d = metadata.tags) === null || _d === void 0 ? void 0 : _d.includes('id'))) {
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;
@@ -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
- var _a, _b, _c, _d;
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
- let idString;
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
- idString = useUuid
73
- ? `${field} String${reqStr} @id @default(uuid())`
74
- : `${field} Int${reqStr} @id @default(autoincrement())`;
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
- idString = useUuid
79
- ? `${field} String${reqStr} @id @default(uuid())`
80
- : `${field} Int${reqStr} @id @default(autoincrement())`;
84
+ if (useUuid) {
85
+ idPrismaType = 'String';
86
+ }
87
+ else {
88
+ idPrismaType = 'Int';
89
+ }
81
90
  break;
82
91
  case 'sqlite':
83
- idString = `${field} Int${reqStr} @id @default(autoincrement())`;
92
+ if (useUuid) {
93
+ idPrismaType = 'String';
94
+ }
95
+ else {
96
+ idPrismaType = 'Int';
97
+ }
84
98
  break;
85
99
  }
86
- if (tags.length) {
87
- idString += ' ' + tags.join(' ');
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
- if (!idString) {
90
- throw new Error(`DB type "${dbType}" is not supported!`);
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
- var _a, _b, _c, _d, _e;
114
- const conditions = (_a = findParams === null || findParams === void 0 ? void 0 : findParams.conditions) !== null && _a !== void 0 ? _a : {};
115
- const ordering = (_b = findParams === null || findParams === void 0 ? void 0 : findParams.ordering) !== null && _b !== void 0 ? _b : null;
116
- const fields = (_c = findParams === null || findParams === void 0 ? void 0 : findParams.fields) !== null && _c !== void 0 ? _c : null;
117
- const allowRelations = (_d = findParams === null || findParams === void 0 ? void 0 : findParams.allowRelations) !== null && _d !== void 0 ? _d : true;
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
- var _a, _b, _c, _d, _e;
235
- const conditions = (_a = findParams === null || findParams === void 0 ? void 0 : findParams.conditions) !== null && _a !== void 0 ? _a : {};
236
- const ordering = (_b = findParams === null || findParams === void 0 ? void 0 : findParams.ordering) !== null && _b !== void 0 ? _b : null;
237
- const fields = (_c = findParams === null || findParams === void 0 ? void 0 : findParams.fields) !== null && _c !== void 0 ? _c : null;
238
- const allowRelations = (_d = findParams === null || findParams === void 0 ? void 0 : findParams.allowRelations) !== null && _d !== void 0 ? _d : true;
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
- var _a, _b, _c, _d;
251
- const ordering = (_a = findParams === null || findParams === void 0 ? void 0 : findParams.ordering) !== null && _a !== void 0 ? _a : null;
252
- const fields = (_b = findParams === null || findParams === void 0 ? void 0 : findParams.fields) !== null && _b !== void 0 ? _b : null;
253
- const allowRelations = (_c = findParams === null || findParams === void 0 ? void 0 : findParams.allowRelations) !== null && _c !== void 0 ? _c : true;
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
- var _a, _b, _c, _d, _e;
266
- const conditions = (_a = findParams === null || findParams === void 0 ? void 0 : findParams.conditions) !== null && _a !== void 0 ? _a : {};
267
- const ordering = (_b = findParams === null || findParams === void 0 ? void 0 : findParams.ordering) !== null && _b !== void 0 ? _b : null;
268
- const fields = (_c = findParams === null || findParams === void 0 ? void 0 : findParams.fields) !== null && _c !== void 0 ? _c : null;
269
- const allowRelations = (_d = findParams === null || findParams === void 0 ? void 0 : findParams.allowRelations) !== null && _d !== void 0 ? _d : true;
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 = (findParams === null || findParams === void 0 ? void 0 : findParams.pagination) ? findParams === null || findParams === void 0 ? void 0 : findParams.pagination : undefined;
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)
@@ -1,3 +1,5 @@
1
1
  import { IDbOpts } from "./IDbOpts";
2
2
  export interface IIdTypeOpts extends IDbOpts {
3
+ unique?: boolean | string;
4
+ noAuto?: boolean;
3
5
  }
@@ -9,4 +9,5 @@ export interface ITrackerOpts extends IDbOpts {
9
9
  relatedTo?: OpModelType<any>;
10
10
  inversionModel?: OpModelType<any>;
11
11
  relationName?: string;
12
+ noAuto?: boolean;
12
13
  }
@@ -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 = metadataKeys.map(async (fullKey) => {
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
- var _a, _b, _c, _d, _e;
7
- const conditions = (_a = findParams === null || findParams === void 0 ? void 0 : findParams.conditions) !== null && _a !== void 0 ? _a : {};
8
- const ordering = (_b = findParams === null || findParams === void 0 ? void 0 : findParams.ordering) !== null && _b !== void 0 ? _b : null;
9
- const fields = (_c = findParams === null || findParams === void 0 ? void 0 : findParams.fields) !== null && _c !== void 0 ? _c : null;
10
- const allowRelations = (_d = findParams === null || findParams === void 0 ? void 0 : findParams.allowRelations) !== null && _d !== void 0 ? _d : true;
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
- var _a;
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
- var _a;
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
- var _a;
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rws-framework/db",
3
3
  "private": false,
4
- "version": "3.2.0",
4
+ "version": "3.3.0",
5
5
  "description": "",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -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
- return function(target: any, key: string) {
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.toLowerCase() :
68
- `${model._collection}_${key}_${source._collection}`.toLowerCase()
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.toLowerCase() :
43
- `${target.constructor.name.toLowerCase()}_${key}_${relatedTo._collection.toLowerCase()}`
42
+ relationOptions.relationName :
43
+ null
44
44
  };
45
45
 
46
46
  if(relationOptions.required){
@@ -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: string = 'mongodb'): string {
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 = RelationManager.getShortenedRelationName(modelName, relatedModelName, relationIndex);
104
- const mapName = relationName;
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}], map: "${mapName}"${cascadeStr})\n`;
121
- } else {
122
- section += `\t${key} ${relatedModel._collection}${requiredString} @relation("${relationName}", fields: [${relationFieldName}], references: [${relatedToField}], map: "${mapName}"${cascadeStr})\n`;
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
- const mapName = relationName;
183
+ const relationName = RelationManager.getShortenedRelationName(relatedModelName, modelName, relationIndex);
184
+
185
+ let relationTag = '';
168
186
 
169
- section += `\t${key} ${relationMeta.inversionModel._collection}[] @relation("${relationName}", map: "${mapName}")\n`;
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 if (annotationType === 'TrackType') {
206
+ } else {
185
207
  const trackMeta = modelMetadata as ITrackerMetaOpts;
186
- const tags: string[] = trackMeta.tags.map((item: string) => '@' + item);
187
-
188
- if(key === 'id' && model._NO_ID && !model._SUPER_TAGS.some(tag => tag.tagType === 'id' && tag.fields.includes('id'))){
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
- section += `\t${key} ${TypeConverter.toConfigCase(trackMeta, dbType, key === 'id')}${requiredString} ${tags.join(' ')}\n`;
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
- leaveFile = false;
368
+
325
369
  console.log(chalk.green('[RWS Init]') + ' prisma schema generated from ', schemaPath);
326
370
 
327
- if (!leaveFile) {
328
- // fs.unlinkSync(schemaPath);
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: string = 'mongodb', isId: boolean = false): string {
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 === 'mongodb' ? 'String' : 'Int';
68
- }
72
+ if(isId && !isIdOverride){
73
+ return DbUtils.getDefaultPrismaType(dbType, false);
74
+ }
69
75
 
70
76
  const trackerModelType = modelType as ITrackerMetaOpts;
71
77
 
@@ -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 = false;
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
- if(modelMetadata.dbOptions?.mysql?.useUuid){
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
- let idString: string;
76
-
77
- let reqStr = '';
155
+ return useUuid;
156
+ }
78
157
 
79
- if(optional){
80
- reqStr = '?';
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
- idString = `${field} String${reqStr} @id @default(auto()) @map("_id") @db.ObjectId`;
86
- break;
174
+ return `auto()`;
87
175
 
88
176
  case 'mysql':
89
- idString = useUuid
90
- ? `${field} String${reqStr} @id @default(uuid())`
91
- : `${field} Int${reqStr} @id @default(autoincrement())`;
92
- break;
177
+ return useUuid
178
+ ? `uuid()`
179
+ : `autoincrement()`;
93
180
 
94
181
  case 'postgresql':
95
182
  case 'postgres':
96
- idString = useUuid
97
- ? `${field} String${reqStr} @id @default(uuid())`
98
- : `${field} Int${reqStr} @id @default(autoincrement())`;
99
- break;
183
+ return useUuid
184
+ ? `uuid()`
185
+ : `autoincrement()`;
100
186
 
101
187
  case 'sqlite':
102
- idString = `${field} Int${reqStr} @id @default(autoincrement())`;
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
 
@@ -1,4 +1,6 @@
1
1
  import { IDbOpts } from "./IDbOpts";
2
2
 
3
3
  export interface IIdTypeOpts extends IDbOpts {
4
+ unique?: boolean | string;
5
+ noAuto?: boolean;
4
6
  }
@@ -10,4 +10,5 @@ export interface ITrackerOpts extends IDbOpts {
10
10
  relatedTo?: OpModelType<any>,
11
11
  inversionModel?: OpModelType<any>,
12
12
  relationName?: string
13
+ noAuto?: boolean;
13
14
  }
@@ -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 = metadataKeys.map(async (fullKey: string) => {
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
 
package/tsconfig.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "baseUrl": ".",
4
4
  "experimentalDecorators": true,
5
5
  "emitDecoratorMetadata": true,
6
- "target": "ES2018",
6
+ "target": "ES2022",
7
7
  "module": "commonjs",
8
8
  "moduleResolution": "node",
9
9
  "strict": true,