@rws-framework/db 2.4.5 → 3.0.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/.bin/add-v.sh +9 -9
- package/.bin/emerge.sh +10 -10
- package/.eslintrc.json +53 -53
- package/README.md +404 -404
- package/dist/decorators/IdType.d.ts +8 -0
- package/dist/decorators/IdType.js +10 -0
- package/dist/decorators/InverseRelation.d.ts +2 -2
- package/dist/decorators/InverseRelation.js +5 -1
- package/dist/decorators/InverseTimeSeries.d.ts +0 -0
- package/dist/decorators/InverseTimeSeries.js +0 -0
- package/dist/decorators/RWSCollection.d.ts +7 -0
- package/dist/decorators/RWSCollection.js +6 -0
- package/dist/decorators/Relation.d.ts +8 -6
- package/dist/decorators/Relation.js +8 -1
- package/dist/decorators/TrackType.d.ts +2 -14
- package/dist/decorators/TrackType.js +7 -0
- package/dist/decorators/TypeFunctions.d.ts +44 -0
- package/dist/decorators/TypeFunctions.js +174 -0
- package/dist/decorators/index.d.ts +4 -2
- package/dist/decorators/index.js +3 -1
- package/dist/helper/DbHelper.d.ts +76 -5
- package/dist/helper/DbHelper.js +93 -154
- package/dist/helper/FieldsHelper.d.ts +0 -0
- package/dist/helper/FieldsHelper.js +0 -0
- package/dist/helper/db/index.d.ts +9 -0
- package/dist/helper/db/index.js +18 -0
- package/dist/helper/db/relation-manager.d.ts +45 -0
- package/dist/helper/db/relation-manager.js +105 -0
- package/dist/helper/db/schema-generator.d.ts +37 -0
- package/dist/helper/db/schema-generator.js +243 -0
- package/dist/helper/db/type-converter.d.ts +22 -0
- package/dist/helper/db/type-converter.js +106 -0
- package/dist/helper/db/utils.d.ts +24 -0
- package/dist/helper/db/utils.js +99 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -1
- package/dist/models/TimeSeriesModel.d.ts +7 -7
- package/dist/models/TimeSeriesModel.js +33 -33
- package/dist/models/_model.d.ts +2 -2
- package/dist/models/_model.js +0 -0
- package/dist/models/core/RWSModel.d.ts +4 -1
- package/dist/models/core/RWSModel.js +6 -4
- package/dist/models/core/TimeSeriesModel.d.ts +0 -0
- package/dist/models/core/TimeSeriesModel.js +0 -0
- package/dist/models/index.d.ts +2 -2
- package/dist/models/index.js +0 -0
- package/dist/models/interfaces/IDbOpts.d.ts +17 -0
- package/dist/models/interfaces/IDbOpts.js +2 -0
- package/dist/models/interfaces/IIdOpts.d.ts +0 -0
- package/dist/models/interfaces/IIdOpts.js +1 -0
- package/dist/models/interfaces/IIdTypeOpts.d.ts +3 -0
- package/dist/models/interfaces/IIdTypeOpts.js +2 -0
- package/dist/models/interfaces/IModel.d.ts +0 -0
- package/dist/models/interfaces/IModel.js +0 -0
- package/dist/models/interfaces/IRWSModelServices.d.ts +0 -0
- package/dist/models/interfaces/IRWSModelServices.js +0 -0
- package/dist/models/interfaces/ITrackerOpts.d.ts +12 -0
- package/dist/models/interfaces/ITrackerOpts.js +2 -0
- package/dist/models/interfaces/OpModelType.d.ts +3 -0
- package/dist/models/interfaces/OpModelType.js +0 -0
- package/dist/models/types/RelationTypes.d.ts +0 -0
- package/dist/models/types/RelationTypes.js +0 -0
- package/dist/models/utils/ModelUtils.d.ts +0 -0
- package/dist/models/utils/ModelUtils.js +0 -0
- package/dist/models/utils/PaginationUtils.d.ts +0 -0
- package/dist/models/utils/PaginationUtils.js +0 -0
- package/dist/models/utils/RelationUtils.d.ts +1 -1
- package/dist/models/utils/RelationUtils.js +2 -2
- package/dist/models/utils/TimeSeriesUtils.d.ts +0 -0
- package/dist/models/utils/TimeSeriesUtils.js +0 -0
- package/dist/services/DBService.d.ts +0 -0
- package/dist/services/DBService.js +0 -0
- package/dist/types/DbConfigHandler.d.ts +1 -1
- package/dist/types/DbConfigHandler.js +0 -0
- package/dist/types/FindParams.d.ts +0 -0
- package/dist/types/FindParams.js +0 -0
- package/dist/types/IRWSModel.d.ts +1 -1
- package/dist/types/IRWSModel.js +0 -0
- package/dist/types/ITimeSeries.d.ts +0 -0
- package/dist/types/ITimeSeries.js +0 -0
- package/exec/console.js +110 -110
- package/exec/db.rws.webpack.config.js +168 -168
- package/exec/src/cli.ts +73 -75
- package/exec/tsconfig.json +32 -32
- package/exec/webpackFilters.js +17 -17
- package/package.json +36 -36
- package/src/decorators/IdType.ts +17 -0
- package/src/decorators/InverseRelation.ts +41 -37
- package/src/decorators/InverseTimeSeries.ts +21 -21
- package/src/decorators/RWSCollection.ts +45 -27
- package/src/decorators/Relation.ts +61 -48
- package/src/decorators/TrackType.ts +65 -69
- package/src/decorators/index.ts +16 -8
- package/src/empty.js +0 -0
- package/src/helper/DbHelper.ts +133 -223
- package/src/helper/FieldsHelper.ts +34 -34
- package/src/helper/db/index.ts +10 -0
- package/src/helper/db/relation-manager.ts +119 -0
- package/src/helper/db/schema-generator.ts +302 -0
- package/src/helper/db/type-converter.ts +119 -0
- package/src/helper/db/utils.ts +120 -0
- package/src/index.ts +47 -38
- package/src/models/_model.ts +29 -29
- package/src/models/core/RWSModel.ts +523 -520
- package/src/models/core/TimeSeriesModel.ts +19 -19
- package/src/models/index.ts +20 -20
- package/src/models/interfaces/IDbOpts.ts +17 -0
- package/src/models/interfaces/IIdTypeOpts.ts +4 -0
- package/src/models/interfaces/IModel.ts +12 -12
- package/src/models/interfaces/IRWSModelServices.ts +7 -7
- package/src/models/interfaces/ITrackerOpts.ts +13 -0
- package/src/models/interfaces/OpModelType.ts +52 -49
- package/src/models/types/RelationTypes.ts +25 -25
- package/src/models/utils/ModelUtils.ts +65 -65
- package/src/models/utils/PaginationUtils.ts +42 -42
- package/src/models/utils/RelationUtils.ts +76 -76
- package/src/models/utils/TimeSeriesUtils.ts +38 -38
- package/src/services/DBService.ts +277 -277
- package/src/types/DbConfigHandler.ts +17 -17
- package/src/types/FindParams.ts +13 -13
- package/src/types/IRWSModel.ts +2 -2
- package/src/types/ITimeSeries.ts +5 -5
- package/tsconfig.json +22 -22
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { rwsShell } from '@rws-framework/console';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
import { IDbConfigHandler } from '../../types/DbConfigHandler';
|
|
7
|
+
import { OpModelType, RWSModel } from '../../models/_model';
|
|
8
|
+
import { DBService } from '../../services/DBService';
|
|
9
|
+
import { IRelationOpts } from '../../decorators/Relation';
|
|
10
|
+
import { InverseRelationOpts } from '../../decorators/InverseRelation';
|
|
11
|
+
|
|
12
|
+
import { DbUtils } from './utils';
|
|
13
|
+
import { TypeConverter } from './type-converter';
|
|
14
|
+
import { RelationManager } from './relation-manager';
|
|
15
|
+
import { ITrackerMetaOpts } from '../../decorators/TrackType';
|
|
16
|
+
import { IDbOpts } from '../../models/interfaces/IDbOpts';
|
|
17
|
+
|
|
18
|
+
const log = console.log;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Handles Prisma schema generation
|
|
22
|
+
*/
|
|
23
|
+
export class SchemaGenerator {
|
|
24
|
+
static dbUrlVarName: string = 'PRISMA_DB_URL';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Generate the base schema for Prisma
|
|
28
|
+
* @param dbType The database type
|
|
29
|
+
* @param dbUrl The database URL
|
|
30
|
+
* @returns The base schema
|
|
31
|
+
*/
|
|
32
|
+
static generateBaseSchema(dbType: string, dbUrl: string): string {
|
|
33
|
+
process.env = { ...process.env, [this.dbUrlVarName]: dbUrl };
|
|
34
|
+
|
|
35
|
+
return `generator client {
|
|
36
|
+
provider = "prisma-client-js"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
datasource db {
|
|
40
|
+
provider = "${dbType}"
|
|
41
|
+
url = env("${this.dbUrlVarName}")
|
|
42
|
+
}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Generate model sections for the schema
|
|
47
|
+
* @param model The model to generate a section for
|
|
48
|
+
* @param configService The configuration service
|
|
49
|
+
* @returns The model section
|
|
50
|
+
*/
|
|
51
|
+
static async generateModelSections(
|
|
52
|
+
model: OpModelType<any>,
|
|
53
|
+
configService: IDbConfigHandler
|
|
54
|
+
): Promise<string> {
|
|
55
|
+
let section = '';
|
|
56
|
+
const modelMetadatas: Record<string, { annotationType: string, metadata: any }> = await RWSModel.getModelAnnotations(model);
|
|
57
|
+
const dbType = configService.get('db_type') || 'mongodb';
|
|
58
|
+
const modelName: string = (model as any)._collection;
|
|
59
|
+
|
|
60
|
+
section += `model ${modelName} {\n`;
|
|
61
|
+
|
|
62
|
+
if(
|
|
63
|
+
!model._NO_ID
|
|
64
|
+
){
|
|
65
|
+
section += `\t${DbUtils.generateId(dbType, modelMetadatas, false)}\n`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for (const key in modelMetadatas) {
|
|
69
|
+
const modelMetadata = modelMetadatas[key].metadata;
|
|
70
|
+
let requiredString = modelMetadata.required ? '' : '?';
|
|
71
|
+
const annotationType: string = modelMetadatas[key].annotationType;
|
|
72
|
+
|
|
73
|
+
let indexedId = false;
|
|
74
|
+
|
|
75
|
+
if(model._NO_ID){
|
|
76
|
+
indexedId = true;
|
|
77
|
+
requiredString = '';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if (key === 'id' && !indexedId) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (annotationType === 'Relation') {
|
|
86
|
+
const relationMeta = modelMetadata as IRelationOpts;
|
|
87
|
+
|
|
88
|
+
const relatedModel = relationMeta.relatedTo as OpModelType<any>;
|
|
89
|
+
const isMany = relationMeta.many;
|
|
90
|
+
const cascadeOpts = [];
|
|
91
|
+
|
|
92
|
+
if (relationMeta.cascade?.onDelete) {
|
|
93
|
+
cascadeOpts.push(`onDelete: ${relationMeta.cascade.onDelete}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (relationMeta.cascade?.onUpdate) {
|
|
97
|
+
cascadeOpts.push(`onUpdate: ${relationMeta.cascade.onUpdate}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const relatedModelName = relatedModel._collection;
|
|
101
|
+
const relationKey = [modelName, relatedModelName].join('_');
|
|
102
|
+
|
|
103
|
+
const relationIndex = RelationManager.getRelationCounter(relationKey);
|
|
104
|
+
const relationName = RelationManager.getShortenedRelationName(modelName, relatedModelName, relationIndex);
|
|
105
|
+
const mapName = relationName;
|
|
106
|
+
|
|
107
|
+
const relatedModelMetadatas: Record<string, { annotationType: string, metadata: ITrackerMetaOpts }> = await RWSModel.getModelAnnotations(relatedModel);
|
|
108
|
+
const relationFieldName = modelMetadata.relationField ? modelMetadata.relationField : key.toLowerCase() + '_' + modelMetadata.relationField.toLowerCase();
|
|
109
|
+
|
|
110
|
+
const relatedToField = modelMetadata.relatedToField || 'id';
|
|
111
|
+
const bindingFieldExists = !!modelMetadatas[relationFieldName];
|
|
112
|
+
|
|
113
|
+
if (isMany) {
|
|
114
|
+
// Add an inverse field to the related model if it doesn't exist
|
|
115
|
+
section += `\t${key} ${relatedModel._collection}[] @relation("${relationName}", fields: [${relationFieldName}], references: [${relatedToField}], map: "${mapName}", ${cascadeOpts.join(', ')})\n`;
|
|
116
|
+
} else {
|
|
117
|
+
section += `\t${key} ${relatedModel._collection}${requiredString} @relation("${relationName}", fields: [${relationFieldName}], references: [${relatedToField}], map: "${mapName}", ${cascadeOpts.join(', ')})\n`;
|
|
118
|
+
if(!bindingFieldExists){
|
|
119
|
+
const relatedFieldMeta = relatedModelMetadatas[relatedToField];
|
|
120
|
+
|
|
121
|
+
if(!relatedFieldMeta.metadata.required){
|
|
122
|
+
requiredString = '';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let relatedFieldType = TypeConverter.toConfigCase(relatedFieldMeta.metadata, dbType, true);
|
|
126
|
+
|
|
127
|
+
if(relatedToField === 'id' && dbType !== 'mongodb'){
|
|
128
|
+
relatedFieldType = 'Int';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Add relation field with appropriate type based on database
|
|
132
|
+
if (dbType === 'mongodb') {
|
|
133
|
+
section += `\t${relationFieldName} String${requiredString} @db.ObjectId\n`;
|
|
134
|
+
} else if (dbType === 'mysql') {
|
|
135
|
+
// For MySQL, determine the type based on the related model's ID type
|
|
136
|
+
section += `\t${relationFieldName} ${relatedFieldType}${requiredString}\n`;
|
|
137
|
+
} else if (dbType === 'postgresql' || dbType === 'postgres') {
|
|
138
|
+
if (relatedFieldType === 'String') {
|
|
139
|
+
section += `\t${relationFieldName} ${relatedFieldType}${requiredString} @db.Uuid\n`;
|
|
140
|
+
} else {
|
|
141
|
+
section += `\t${relationFieldName} ${relatedFieldType}${requiredString}\n`;
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
section += `\t${relationFieldName} String${requiredString}\n`;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
RelationManager.completeRelation(relationKey, relationIndex);
|
|
150
|
+
} else if (annotationType === 'InverseRelation') {
|
|
151
|
+
const relationMeta = modelMetadata as InverseRelationOpts;
|
|
152
|
+
|
|
153
|
+
// Check if we need a custom relation name (if there are multiple relations to the same model)
|
|
154
|
+
const relatedModelName = relationMeta.inversionModel._collection;
|
|
155
|
+
|
|
156
|
+
// Generate a very short relation name using a counter
|
|
157
|
+
// Use a consistent key for both sides of the relation to ensure the same name is used
|
|
158
|
+
const relationKey = [relatedModelName, modelName].join('_');
|
|
159
|
+
const relationIndex = RelationManager.getRelationCounter(relationKey, true);
|
|
160
|
+
|
|
161
|
+
const relationName = RelationManager.getShortenedRelationName(relatedModelName, modelName, relationIndex);
|
|
162
|
+
const mapName = relationName;
|
|
163
|
+
|
|
164
|
+
section += `\t${key} ${relationMeta.inversionModel._collection}[] @relation("${relationName}", map: "${mapName}")\n`;
|
|
165
|
+
|
|
166
|
+
RelationManager.completeRelation(relationKey, relationIndex, true);
|
|
167
|
+
} else if (annotationType === 'InverseTimeSeries') {
|
|
168
|
+
if (dbType === 'mongodb') {
|
|
169
|
+
section += `\t${key} String[] @db.ObjectId\n`;
|
|
170
|
+
} else if (dbType === 'mysql') {
|
|
171
|
+
// For MySQL, we need a different approach for arrays
|
|
172
|
+
section += `\t${key} Json\n`;
|
|
173
|
+
} else if (dbType === 'postgresql' || dbType === 'postgres') {
|
|
174
|
+
// PostgreSQL supports arrays natively
|
|
175
|
+
section += `\t${key} String[]\n`;
|
|
176
|
+
} else {
|
|
177
|
+
section += `\t${key} String[]\n`;
|
|
178
|
+
}
|
|
179
|
+
} else if (annotationType === 'TrackType') {
|
|
180
|
+
const trackMeta = modelMetadata as ITrackerMetaOpts;
|
|
181
|
+
const tags: string[] = trackMeta.tags.map((item: string) => '@' + item);
|
|
182
|
+
|
|
183
|
+
if(trackMeta.unique){
|
|
184
|
+
const fieldDetail: string | null = typeof trackMeta.unique === 'string' ? trackMeta.unique : null;
|
|
185
|
+
tags.push(`@unique(${fieldDetail ? `map: "${fieldDetail}"` : ''})`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (trackMeta.isArray || trackMeta.type.name === 'Array') {
|
|
189
|
+
requiredString = '';
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Process any database-specific options from the metadata
|
|
193
|
+
const dbSpecificTags = TypeConverter.processTypeOptions(trackMeta as { tags: string[], dbOptions: IDbOpts['dbOptions'] }, dbType);
|
|
194
|
+
tags.push(...dbSpecificTags);
|
|
195
|
+
|
|
196
|
+
section += `\t${key} ${TypeConverter.toConfigCase(trackMeta, dbType, key === 'id')}${requiredString} ${tags.join(' ')}\n`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
for(const superTag of model._SUPER_TAGS){
|
|
201
|
+
|
|
202
|
+
const mapStr = superTag.map ? `, map: "${superTag.map}"` : '';
|
|
203
|
+
|
|
204
|
+
section += `\t@@${superTag.tagType}([${superTag.fields.join(', ')}]${mapStr})\n`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
section += '}\n';
|
|
208
|
+
return section;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Install Prisma with the generated schema
|
|
213
|
+
* @param configService The configuration service
|
|
214
|
+
* @param dbService The database service
|
|
215
|
+
* @param leaveFile Whether to leave the schema file after generation
|
|
216
|
+
*/
|
|
217
|
+
static async installPrisma(configService: IDbConfigHandler, dbService: DBService, leaveFile = false): Promise<void> {
|
|
218
|
+
const dbUrl = configService.get('db_url');
|
|
219
|
+
const dbType = configService.get('db_type') || 'mongodb';
|
|
220
|
+
|
|
221
|
+
let template: string = this.generateBaseSchema(dbType, dbUrl);
|
|
222
|
+
|
|
223
|
+
const dbModels: OpModelType<unknown>[] | null = configService.get('db_models');
|
|
224
|
+
|
|
225
|
+
if (dbModels) {
|
|
226
|
+
for (const model of dbModels) {
|
|
227
|
+
const modelName = (model as any)._collection;
|
|
228
|
+
const modelMetadatas: Record<string, { annotationType: string, metadata: any }> = await RWSModel.getModelAnnotations(model);
|
|
229
|
+
|
|
230
|
+
for (const key in modelMetadatas) {
|
|
231
|
+
const annotationType: string = modelMetadatas[key].annotationType;
|
|
232
|
+
|
|
233
|
+
if (annotationType === 'Relation') {
|
|
234
|
+
const relationMeta = modelMetadatas[key].metadata as IRelationOpts;
|
|
235
|
+
|
|
236
|
+
const relatedModel = relationMeta.relatedTo as OpModelType<any>;
|
|
237
|
+
const relatedModelName = relatedModel._collection;
|
|
238
|
+
|
|
239
|
+
const relationKey = [modelName, relatedModelName].join('_');
|
|
240
|
+
|
|
241
|
+
// Add this relation to the map
|
|
242
|
+
RelationManager.markRelation(relationKey);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if(annotationType === 'InverseRelation'){
|
|
246
|
+
const relationMeta = modelMetadatas[key].metadata as InverseRelationOpts;
|
|
247
|
+
const relatedModel = relationMeta.inversionModel as OpModelType<any>;
|
|
248
|
+
|
|
249
|
+
const relatedModelName = relatedModel._collection;
|
|
250
|
+
|
|
251
|
+
const relationKey = [relatedModelName, modelName].join('_');
|
|
252
|
+
RelationManager.markRelation(relationKey, true);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Now generate the model sections with all relations
|
|
258
|
+
for (const model of dbModels) {
|
|
259
|
+
const modelSection = await SchemaGenerator.generateModelSections(model, configService);
|
|
260
|
+
|
|
261
|
+
template += '\n\n' + modelSection;
|
|
262
|
+
|
|
263
|
+
log(chalk.green('[RWS]'), chalk.blue('Building DB Model'), model.name);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const [schemaDir, schemaPath] = DbUtils.getSchemaDir();
|
|
267
|
+
|
|
268
|
+
if (!fs.existsSync(schemaDir)) {
|
|
269
|
+
fs.mkdirSync(schemaDir);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (fs.existsSync(schemaPath)) {
|
|
273
|
+
fs.unlinkSync(schemaPath);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
fs.writeFileSync(schemaPath, template);
|
|
277
|
+
|
|
278
|
+
await rwsShell.runCommand(`${DbUtils.detectInstaller()} prisma generate --schema=${schemaPath}`, process.cwd());
|
|
279
|
+
|
|
280
|
+
leaveFile = false;
|
|
281
|
+
log(chalk.green('[RWS Init]') + ' prisma schema generated from ', schemaPath);
|
|
282
|
+
|
|
283
|
+
if (!leaveFile) {
|
|
284
|
+
// fs.unlinkSync(schemaPath);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Push database models to the database
|
|
291
|
+
* @param configService The configuration service
|
|
292
|
+
* @param dbService The database service
|
|
293
|
+
* @param leaveFile Whether to leave the schema file after generation
|
|
294
|
+
*/
|
|
295
|
+
static async pushDBModels(configService: IDbConfigHandler, dbService: DBService, leaveFile = false): Promise<void> {
|
|
296
|
+
process.env = { ...process.env, [this.dbUrlVarName]: configService.get('db_url') };
|
|
297
|
+
|
|
298
|
+
const [_, schemaPath] = DbUtils.getSchemaDir();
|
|
299
|
+
|
|
300
|
+
await rwsShell.runCommand(`${DbUtils.detectInstaller()} prisma db push --schema=${schemaPath}`, process.cwd());
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { IDbOpts } from '../../models/interfaces/IDbOpts';
|
|
2
|
+
import { ITrackerMetaOpts } from '../../models/_model';
|
|
3
|
+
import { IIdMetaOpts } from 'src/decorators/IdType';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handles type conversion for database schema generation
|
|
7
|
+
*/
|
|
8
|
+
export class TypeConverter {
|
|
9
|
+
/**
|
|
10
|
+
* Convert a JavaScript type to a Prisma schema type
|
|
11
|
+
*/
|
|
12
|
+
static toConfigCase(modelType: ITrackerMetaOpts | IIdMetaOpts, dbType: string = 'mongodb', isId: boolean = false): string {
|
|
13
|
+
const type = modelType.type;
|
|
14
|
+
let input = type.name;
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
// Handle basic types
|
|
18
|
+
if (input == 'Number') {
|
|
19
|
+
input = 'Int';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (input == 'Object') {
|
|
23
|
+
input = 'Json';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (input == 'Date') {
|
|
27
|
+
input = 'DateTime';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (input == 'Boolean') {
|
|
31
|
+
// Ensure Boolean is properly handled for all database types
|
|
32
|
+
input = 'Boolean';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (input == 'Array') {
|
|
36
|
+
// Handle arrays differently based on database type
|
|
37
|
+
if (dbType === 'mysql') {
|
|
38
|
+
// MySQL doesn't support native arrays, use Json instead
|
|
39
|
+
input = 'Json';
|
|
40
|
+
} else {
|
|
41
|
+
input = 'Json[]';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const firstChar = input.charAt(0).toUpperCase();
|
|
46
|
+
const restOfString = input.slice(1);
|
|
47
|
+
let resultField = firstChar + restOfString;
|
|
48
|
+
|
|
49
|
+
if(isId){
|
|
50
|
+
return dbType === 'mongodb' ? 'String' : 'Int';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const trackerModelType = modelType as ITrackerMetaOpts;
|
|
54
|
+
|
|
55
|
+
if (trackerModelType.isArray) {
|
|
56
|
+
// Handle arrays differently based on database type
|
|
57
|
+
if (dbType === 'mysql') {
|
|
58
|
+
// For MySQL, we don't append [] as it doesn't support native arrays
|
|
59
|
+
// Instead, we'll store arrays as JSON
|
|
60
|
+
resultField = 'Json';
|
|
61
|
+
} else if (dbType === 'postgresql' || dbType === 'postgres') {
|
|
62
|
+
// PostgreSQL supports arrays natively
|
|
63
|
+
resultField += '[]';
|
|
64
|
+
} else {
|
|
65
|
+
resultField += '[]';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return resultField;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Process type functions metadata to extract database-specific options
|
|
74
|
+
* @param metadata The metadata from a type function
|
|
75
|
+
* @param dbType The database type
|
|
76
|
+
* @returns Array of tags to apply to the field
|
|
77
|
+
*/
|
|
78
|
+
static processTypeOptions(metadata: { tags: string[], dbOptions: IDbOpts['dbOptions'] }, dbType: string): string[] {
|
|
79
|
+
const tags: string[] = [...(metadata.tags || [])];
|
|
80
|
+
|
|
81
|
+
// Extract any database-specific options from the metadata
|
|
82
|
+
// and convert them to appropriate Prisma schema tags
|
|
83
|
+
if (metadata.dbOptions) {
|
|
84
|
+
// Handle MySQL-specific options
|
|
85
|
+
if (dbType === 'mysql' && metadata.dbOptions.mysql) {
|
|
86
|
+
if (metadata.dbOptions.mysql.useText) {
|
|
87
|
+
tags.push('@db.Text');
|
|
88
|
+
} else if (metadata.dbOptions.mysql.maxLength) {
|
|
89
|
+
tags.push(`@db.VarChar(${metadata.dbOptions.mysql.maxLength})`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (metadata.dbOptions.mysql.useUuid && metadata.tags?.includes('id')) {
|
|
93
|
+
tags.push('default(uuid())');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Handle PostgreSQL-specific options
|
|
98
|
+
if ((dbType === 'postgresql' || dbType === 'postgres') && metadata.dbOptions.postgres) {
|
|
99
|
+
if (metadata.dbOptions.postgres.useText) {
|
|
100
|
+
tags.push('@db.Text');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (metadata.dbOptions.postgres.useUuid && metadata.tags?.includes('id')) {
|
|
104
|
+
tags.push('@default(uuid())');
|
|
105
|
+
tags.push('@db.Uuid');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Handle MongoDB-specific options
|
|
110
|
+
if (dbType === 'mongodb' && metadata.dbOptions.mongodb) {
|
|
111
|
+
if (metadata.dbOptions.mongodb.customType) {
|
|
112
|
+
tags.push(`@db.${metadata.dbOptions.mongodb.customType}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return tags;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { rwsPath } from '@rws-framework/console';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import { IDbConfigParams } from '../../types/DbConfigHandler';
|
|
5
|
+
import { IIdMetaOpts, IIdTypeOpts } from '../../decorators/IdType';
|
|
6
|
+
import { TypeConverter } from './type-converter';
|
|
7
|
+
import { IDbOpts } from '../../models/interfaces/IDbOpts';
|
|
8
|
+
|
|
9
|
+
const workspaceRoot = rwsPath.findRootWorkspacePath();
|
|
10
|
+
const moduleDir = path.resolve(workspaceRoot, 'node_modules', '@rws-framework', 'db');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Utility functions for database operations
|
|
14
|
+
*/
|
|
15
|
+
export class DbUtils {
|
|
16
|
+
/**
|
|
17
|
+
* Get the directory and path for the Prisma schema file
|
|
18
|
+
*/
|
|
19
|
+
static getSchemaDir(): [string, string] {
|
|
20
|
+
const schemaDir = path.join(moduleDir, 'prisma');
|
|
21
|
+
const schemaPath = path.join(schemaDir, 'schema.prisma');
|
|
22
|
+
|
|
23
|
+
return [schemaDir, schemaPath];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Detect the package installer (yarn or npx)
|
|
28
|
+
*/
|
|
29
|
+
static detectInstaller(): string {
|
|
30
|
+
if (fs.existsSync(path.join(workspaceRoot, 'yarn.lock'))) {
|
|
31
|
+
return 'yarn';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return 'npx';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Generate an ID field based on the database type
|
|
39
|
+
*/
|
|
40
|
+
static generateId(
|
|
41
|
+
dbType: IDbConfigParams['db_type'],
|
|
42
|
+
modelMeta: Record<string, { annotationType: string, metadata: IIdMetaOpts }>,
|
|
43
|
+
debug = false
|
|
44
|
+
): string {
|
|
45
|
+
let useUuid = false;
|
|
46
|
+
let field = 'id';
|
|
47
|
+
const tags: string[] = [];
|
|
48
|
+
|
|
49
|
+
for (const key in modelMeta) {
|
|
50
|
+
const modelMetadata: IIdMetaOpts = modelMeta[key].metadata;
|
|
51
|
+
const annotationType: string = modelMeta[key].annotationType;
|
|
52
|
+
|
|
53
|
+
if(key !== 'id'){
|
|
54
|
+
if(annotationType == 'IdType'){
|
|
55
|
+
const dbSpecificTags = TypeConverter.processTypeOptions({ tags: [], dbOptions: modelMetadata.dbOptions }, dbType);
|
|
56
|
+
tags.push(...dbSpecificTags);
|
|
57
|
+
if(debug){
|
|
58
|
+
console.log({modelMetadata: modelMetadata.dbOptions});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
field = key;
|
|
62
|
+
|
|
63
|
+
if(modelMetadata.dbOptions?.mysql?.useUuid){
|
|
64
|
+
useUuid = true;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if(modelMetadata.dbOptions?.postgres?.useUuid){
|
|
68
|
+
useUuid = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if(modelMetadata.type.name === 'String'){
|
|
72
|
+
useUuid = true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let idString: string;
|
|
79
|
+
|
|
80
|
+
switch (dbType) {
|
|
81
|
+
case 'mongodb':
|
|
82
|
+
idString = `${field} String @id @default(auto()) @map("_id") @db.ObjectId`;
|
|
83
|
+
break;
|
|
84
|
+
|
|
85
|
+
case 'mysql':
|
|
86
|
+
idString = useUuid
|
|
87
|
+
? `${field} String @id @default(uuid())`
|
|
88
|
+
: `${field} Int @id @default(autoincrement())`;
|
|
89
|
+
break;
|
|
90
|
+
|
|
91
|
+
case 'postgresql':
|
|
92
|
+
case 'postgres':
|
|
93
|
+
idString = useUuid
|
|
94
|
+
? `${field} String @id @default(uuid())`
|
|
95
|
+
: `${field} Int @id @default(autoincrement())`;
|
|
96
|
+
break;
|
|
97
|
+
|
|
98
|
+
case 'sqlite':
|
|
99
|
+
idString = `${field} Int @id @default(autoincrement())`;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if(tags.length){
|
|
104
|
+
idString += ' '+tags.join(' ');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if(!idString){
|
|
108
|
+
throw new Error(`DB type "${dbType}" is not supported!`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if(debug){
|
|
112
|
+
console.log({idString, useUuid});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return idString;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const workspaceRootPath = workspaceRoot;
|
|
120
|
+
export const moduleDirPath = moduleDir;
|
package/src/index.ts
CHANGED
|
@@ -1,38 +1,47 @@
|
|
|
1
|
-
import { DBService } from "./services/DBService";
|
|
2
|
-
import { RWSModel, OpModelType } from "./models/_model";
|
|
3
|
-
// import TimeSeriesModel from './models/core/TimeSeriesModel';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1
|
+
import { DBService } from "./services/DBService";
|
|
2
|
+
import { RWSModel, OpModelType } from "./models/_model";
|
|
3
|
+
// import TimeSeriesModel from './models/core/TimeSeriesModel';
|
|
4
|
+
import {
|
|
5
|
+
InverseRelation,
|
|
6
|
+
Relation,
|
|
7
|
+
TrackType,
|
|
8
|
+
IdType,
|
|
9
|
+
InverseTimeSeries,
|
|
10
|
+
ITrackerMetaOpts,
|
|
11
|
+
ITrackerOpts
|
|
12
|
+
} from './decorators';
|
|
13
|
+
|
|
14
|
+
import { DbHelper } from './helper/DbHelper';
|
|
15
|
+
import { FieldsHelper } from './helper/FieldsHelper';
|
|
16
|
+
|
|
17
|
+
import type { FindByType } from './types/FindParams';
|
|
18
|
+
import type { ITimeSeries } from './types/ITimeSeries';
|
|
19
|
+
import type { IDbConfigHandler, IDbConfigParams } from './types/DbConfigHandler';
|
|
20
|
+
import type { IRWSModel } from './types/IRWSModel';
|
|
21
|
+
import { RWSCollection, IRWSCollectionMeta, IRWSCollectionOpts } from "./decorators/RWSCollection";
|
|
22
|
+
|
|
23
|
+
export type {
|
|
24
|
+
IRWSCollectionMeta, IRWSCollectionOpts,
|
|
25
|
+
IRWSModel,
|
|
26
|
+
ITrackerMetaOpts,
|
|
27
|
+
ITrackerOpts,
|
|
28
|
+
OpModelType,
|
|
29
|
+
IDbConfigHandler,
|
|
30
|
+
IDbConfigParams,
|
|
31
|
+
ITimeSeries,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
RWSModel,
|
|
36
|
+
RWSCollection,
|
|
37
|
+
|
|
38
|
+
DBService,
|
|
39
|
+
|
|
40
|
+
FindByType,
|
|
41
|
+
// TimeSeriesModel,
|
|
42
|
+
|
|
43
|
+
InverseRelation, Relation, TrackType, InverseTimeSeries, IdType,
|
|
44
|
+
|
|
45
|
+
DbHelper,
|
|
46
|
+
FieldsHelper
|
|
47
|
+
};
|
package/src/models/_model.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @deprecated This file is deprecated. Import from 'models' directory instead.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
IModel,
|
|
7
|
-
IRWSModelServices,
|
|
8
|
-
OpModelType,
|
|
9
|
-
RelationBindType,
|
|
10
|
-
RelOneMetaType,
|
|
11
|
-
RelManyMetaType,
|
|
12
|
-
RWSModel,
|
|
13
|
-
TrackType,
|
|
14
|
-
|
|
15
|
-
} from './index';
|
|
16
|
-
|
|
17
|
-
export {
|
|
18
|
-
RWSModel,
|
|
19
|
-
TrackType
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export type {
|
|
23
|
-
IModel,
|
|
24
|
-
IRWSModelServices,
|
|
25
|
-
OpModelType,
|
|
26
|
-
RelationBindType,
|
|
27
|
-
RelOneMetaType,
|
|
28
|
-
RelManyMetaType,
|
|
29
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated This file is deprecated. Import from 'models' directory instead.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
IModel,
|
|
7
|
+
IRWSModelServices,
|
|
8
|
+
OpModelType,
|
|
9
|
+
RelationBindType,
|
|
10
|
+
RelOneMetaType,
|
|
11
|
+
RelManyMetaType,
|
|
12
|
+
RWSModel,
|
|
13
|
+
TrackType,
|
|
14
|
+
ITrackerMetaOpts
|
|
15
|
+
} from './index';
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
RWSModel,
|
|
19
|
+
TrackType
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type {
|
|
23
|
+
IModel,
|
|
24
|
+
IRWSModelServices,
|
|
25
|
+
OpModelType,
|
|
26
|
+
RelationBindType,
|
|
27
|
+
RelOneMetaType,
|
|
28
|
+
RelManyMetaType,
|
|
29
|
+
ITrackerMetaOpts
|
|
30
30
|
};
|