morio_bridge 0.1.0 → 0.1.2
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.
- checksums.yaml +4 -4
- data/bin/post-install.sh +1 -1
- data/morio_bridge.gemspec +4 -3
- data/server/bun.lock +116 -0
- data/server/db/pg-dev/client/client.ts +47 -0
- data/server/db/pg-dev/client/commonInputTypes.ts +141 -0
- data/server/db/pg-dev/client/enums.ts +13 -0
- data/server/db/pg-dev/client/index.ts +5 -0
- data/server/db/pg-dev/client/internal/class.ts +242 -0
- data/server/db/pg-dev/client/internal/prismaNamespace.ts +760 -0
- data/server/db/pg-dev/client/models/User.ts +1151 -0
- data/server/db/pg-dev/client/models.ts +11 -0
- data/server/db/pg-dev/schema.prisma +24 -0
- data/server/examples/commands.ts +95 -0
- data/server/examples/create-test.ts +21 -0
- data/server/index.ts +349 -0
- data/server/package.json +45 -0
- data/server/plugins/index.ts +26 -0
- data/server/plugins/lib/correct.schema.prisma +131 -0
- data/server/plugins/lib/schema-converter.ts +1393 -0
- data/server/plugins/lib/schema.ts +469 -0
- data/server/plugins/lib/util.ts +51 -0
- data/server/plugins/orm.ts +32 -0
- data/server/plugins/validator.ts +191 -0
- data/server/tsconfig.json +28 -0
- metadata +23 -1
@@ -0,0 +1,1393 @@
|
|
1
|
+
/**-------------------------------------------------------------------
|
2
|
+
*
|
3
|
+
* [ Schema Converter ]
|
4
|
+
*
|
5
|
+
* - Handles conversion between internal DSL schema and Prisma schema
|
6
|
+
* - Supports bidirectional conversion (DSL to Prisma and Prisma to DSL)
|
7
|
+
*
|
8
|
+
* -------------------------------------------------------------------*/
|
9
|
+
import { plural } from "pluralize";
|
10
|
+
import { QUERY } from "./queries";
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Converts internal DSL schema to Prisma schema format
|
14
|
+
*/
|
15
|
+
class DSLToPrismaConverter {
|
16
|
+
constructor() {}
|
17
|
+
|
18
|
+
/**
|
19
|
+
* Convert internal DSL schema to Prisma schema
|
20
|
+
*/
|
21
|
+
convertToPrisma(
|
22
|
+
dslSchema: DBConfigs,
|
23
|
+
options: ConversionOptions = {}
|
24
|
+
): string {
|
25
|
+
const {
|
26
|
+
provider = "postgresql",
|
27
|
+
connectionStringEnv = "DATABASE_URL",
|
28
|
+
includeComments = true,
|
29
|
+
} = options;
|
30
|
+
|
31
|
+
// Start with header and generator
|
32
|
+
let prismaSchema = this.generateHeader(includeComments);
|
33
|
+
prismaSchema += QUERY.SCHEMA_CONVERSION.PRISMA_GENERATOR;
|
34
|
+
|
35
|
+
// Add datasource
|
36
|
+
const datasource = QUERY.SCHEMA_CONVERSION.PRISMA_DATASOURCE.replace(
|
37
|
+
"{provider}",
|
38
|
+
provider
|
39
|
+
).replace("{connectionStringEnv}", connectionStringEnv);
|
40
|
+
prismaSchema += `\n${datasource}\n`;
|
41
|
+
|
42
|
+
// Process each schema in the DSL
|
43
|
+
const dbName = Object.keys(dslSchema)[0] as string;
|
44
|
+
const schemas = dslSchema[dbName]?.schemas || [];
|
45
|
+
|
46
|
+
// Track relationships to avoid duplicates
|
47
|
+
const processedRelations = new Set<string>();
|
48
|
+
|
49
|
+
// Convert each model
|
50
|
+
for (const schema of schemas) {
|
51
|
+
prismaSchema += this.convertModelToPrisma(
|
52
|
+
schema,
|
53
|
+
schemas,
|
54
|
+
processedRelations,
|
55
|
+
includeComments
|
56
|
+
);
|
57
|
+
}
|
58
|
+
|
59
|
+
return prismaSchema;
|
60
|
+
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
* Generate schema header with comments
|
64
|
+
*/
|
65
|
+
private generateHeader(includeComments: boolean): string {
|
66
|
+
if (!includeComments) return "";
|
67
|
+
|
68
|
+
return `// This is your Prisma schema file,
|
69
|
+
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
70
|
+
|
71
|
+
`;
|
72
|
+
}
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Convert a single model to Prisma format
|
76
|
+
*/
|
77
|
+
private convertModelToPrisma(
|
78
|
+
schema: Schema,
|
79
|
+
allSchemas: Schema[],
|
80
|
+
processedRelations: Set<string>,
|
81
|
+
includeComments: boolean
|
82
|
+
): string {
|
83
|
+
const modelName = schema.model;
|
84
|
+
let modelDefinition = includeComments
|
85
|
+
? `// ${schema.tableName} table\n`
|
86
|
+
: "";
|
87
|
+
|
88
|
+
modelDefinition += `model ${modelName} {\n`;
|
89
|
+
|
90
|
+
// Add fields
|
91
|
+
for (const field of schema.fields) {
|
92
|
+
modelDefinition += this.convertFieldToPrisma(field, schema.tableName);
|
93
|
+
}
|
94
|
+
|
95
|
+
// Add relations
|
96
|
+
if (schema.relations) {
|
97
|
+
for (const [relationName, relation] of Object.entries(schema.relations)) {
|
98
|
+
const relationDef = this.convertRelationToPrisma(
|
99
|
+
relationName,
|
100
|
+
relation,
|
101
|
+
schema,
|
102
|
+
allSchemas,
|
103
|
+
processedRelations
|
104
|
+
);
|
105
|
+
if (relationDef) {
|
106
|
+
modelDefinition += relationDef;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
// Add table mapping if table name differs from model name
|
112
|
+
if (schema.tableName.toLowerCase() !== modelName.toLowerCase()) {
|
113
|
+
modelDefinition += ` @@map("${schema.tableName}")\n`;
|
114
|
+
}
|
115
|
+
|
116
|
+
modelDefinition += `}\n\n`;
|
117
|
+
return modelDefinition;
|
118
|
+
}
|
119
|
+
|
120
|
+
/**
|
121
|
+
* Convert a field to Prisma format
|
122
|
+
*/
|
123
|
+
private convertFieldToPrisma(field: SchemaField, tableName: string): string {
|
124
|
+
const { name, type } = field;
|
125
|
+
const prismaType = this.mapTypeToPrisma(type, field);
|
126
|
+
|
127
|
+
let fieldDefinition = ` ${name} ${prismaType}`;
|
128
|
+
|
129
|
+
// Handle nullable
|
130
|
+
if (!field.nullable) {
|
131
|
+
fieldDefinition += " ";
|
132
|
+
} else {
|
133
|
+
fieldDefinition += "? ";
|
134
|
+
}
|
135
|
+
|
136
|
+
// Handle primary key
|
137
|
+
if (field.primaryKey) {
|
138
|
+
fieldDefinition += "@id ";
|
139
|
+
}
|
140
|
+
|
141
|
+
// Handle default values
|
142
|
+
if (field.default !== undefined || field.autoIncrement) {
|
143
|
+
fieldDefinition += this.generateDefaultAttribute(field);
|
144
|
+
}
|
145
|
+
|
146
|
+
// Handle unique constraint
|
147
|
+
if (field.unique && !field.primaryKey) {
|
148
|
+
fieldDefinition += "@unique ";
|
149
|
+
}
|
150
|
+
|
151
|
+
// Handle created at
|
152
|
+
if (
|
153
|
+
name.toLowerCase() === "createdat" &&
|
154
|
+
type.toLowerCase().includes("time")
|
155
|
+
) {
|
156
|
+
fieldDefinition += "@default(now()) ";
|
157
|
+
}
|
158
|
+
|
159
|
+
// Handle updated at
|
160
|
+
if (
|
161
|
+
name.toLowerCase() === "updatedat" &&
|
162
|
+
type.toLowerCase().includes("time")
|
163
|
+
) {
|
164
|
+
fieldDefinition += "@updatedAt ";
|
165
|
+
}
|
166
|
+
|
167
|
+
// Add column mapping if field name differs from column name in database
|
168
|
+
// This is a placeholder - in a real implementation you'd check if the field name
|
169
|
+
// differs from the column name in the database
|
170
|
+
// if (name !== databaseColumnName) {
|
171
|
+
// fieldDefinition += `@map("${databaseColumnName}") `;
|
172
|
+
// }
|
173
|
+
|
174
|
+
return `${fieldDefinition}\n`;
|
175
|
+
}
|
176
|
+
|
177
|
+
/**
|
178
|
+
* Generate default attribute for a field
|
179
|
+
*/
|
180
|
+
private generateDefaultAttribute(field: SchemaField): string {
|
181
|
+
if (field.autoIncrement) {
|
182
|
+
return "@default(autoincrement()) ";
|
183
|
+
}
|
184
|
+
|
185
|
+
if (
|
186
|
+
field.type.toLowerCase() === "uuid" ||
|
187
|
+
field.name.toLowerCase() === "id"
|
188
|
+
) {
|
189
|
+
return "@default(uuid()) ";
|
190
|
+
}
|
191
|
+
|
192
|
+
// Handle different types of defaults
|
193
|
+
if (field.default !== undefined) {
|
194
|
+
if (typeof field.default === "string") {
|
195
|
+
return `@default("${field.default}") `;
|
196
|
+
} else if (typeof field.default === "boolean") {
|
197
|
+
return `@default(${field.default}) `;
|
198
|
+
} else if (typeof field.default === "number") {
|
199
|
+
return `@default(${field.default}) `;
|
200
|
+
} else if (field.default === null) {
|
201
|
+
return "";
|
202
|
+
}
|
203
|
+
}
|
204
|
+
|
205
|
+
return "";
|
206
|
+
}
|
207
|
+
|
208
|
+
/**
|
209
|
+
* Convert a relation to Prisma format
|
210
|
+
*/
|
211
|
+
private convertRelationToPrisma(
|
212
|
+
relationName: string,
|
213
|
+
relation: SchemaRelation,
|
214
|
+
sourceSchema: Schema,
|
215
|
+
allSchemas: Schema[],
|
216
|
+
processedRelations: Set<string>
|
217
|
+
): string {
|
218
|
+
const { relatedTo, relationType, foreignKey } = relation;
|
219
|
+
|
220
|
+
// Find the related schema
|
221
|
+
const relatedSchema = allSchemas.find((s) => s.model === relatedTo);
|
222
|
+
if (!relatedSchema) return "";
|
223
|
+
|
224
|
+
// Create a unique identifier for this relation to avoid duplicates
|
225
|
+
const relationId = `${sourceSchema.model}:${relatedSchema.model}:${relationName}`;
|
226
|
+
const reverseRelationId = `${relatedSchema.model}:${sourceSchema.model}:${relationName}`;
|
227
|
+
|
228
|
+
// Skip if we've already processed this relation
|
229
|
+
if (
|
230
|
+
processedRelations.has(relationId) ||
|
231
|
+
processedRelations.has(reverseRelationId)
|
232
|
+
) {
|
233
|
+
return "";
|
234
|
+
}
|
235
|
+
|
236
|
+
processedRelations.add(relationId);
|
237
|
+
|
238
|
+
// Handle different relation types
|
239
|
+
switch (relationType) {
|
240
|
+
case "OneToOne":
|
241
|
+
return this.generateOneToOneRelation(
|
242
|
+
sourceSchema,
|
243
|
+
relatedSchema,
|
244
|
+
relation,
|
245
|
+
foreignKey
|
246
|
+
);
|
247
|
+
|
248
|
+
case "OneToMany":
|
249
|
+
return this.generateOneToManyRelation(
|
250
|
+
sourceSchema,
|
251
|
+
relatedSchema,
|
252
|
+
relation,
|
253
|
+
foreignKey
|
254
|
+
);
|
255
|
+
|
256
|
+
case "ManyToOne":
|
257
|
+
return this.generateManyToOneRelation(
|
258
|
+
sourceSchema,
|
259
|
+
relatedSchema,
|
260
|
+
relation,
|
261
|
+
foreignKey
|
262
|
+
);
|
263
|
+
|
264
|
+
case "ManyToMany":
|
265
|
+
return this.generateManyToManyRelation(
|
266
|
+
sourceSchema,
|
267
|
+
relatedSchema,
|
268
|
+
relation,
|
269
|
+
relationName
|
270
|
+
);
|
271
|
+
|
272
|
+
default:
|
273
|
+
return "";
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
/**
|
278
|
+
* Generate a one-to-one relation
|
279
|
+
*/
|
280
|
+
private generateOneToOneRelation(
|
281
|
+
sourceSchema: Schema,
|
282
|
+
relatedSchema: Schema,
|
283
|
+
relation: SchemaRelation,
|
284
|
+
foreignKey: string
|
285
|
+
): string {
|
286
|
+
const relatedModelName = relatedSchema.model;
|
287
|
+
const relationField = this.camelCase(relatedModelName);
|
288
|
+
|
289
|
+
return (
|
290
|
+
` ${relationField} ${relatedModelName}? @relation(fields: [${foreignKey}], references: [id])\n` +
|
291
|
+
` ${foreignKey} String? @unique\n`
|
292
|
+
);
|
293
|
+
}
|
294
|
+
|
295
|
+
/**
|
296
|
+
* Generate a one-to-many relation
|
297
|
+
*/
|
298
|
+
private generateOneToManyRelation(
|
299
|
+
sourceSchema: Schema,
|
300
|
+
relatedSchema: Schema,
|
301
|
+
relation: SchemaRelation,
|
302
|
+
foreignKey: string
|
303
|
+
): string {
|
304
|
+
const relatedModelName = relatedSchema.model;
|
305
|
+
const relationField = this.camelCase(plural(relatedModelName));
|
306
|
+
|
307
|
+
return ` ${relationField} ${relatedModelName}[]\n`;
|
308
|
+
}
|
309
|
+
|
310
|
+
/**
|
311
|
+
* Generate a many-to-one relation
|
312
|
+
*/
|
313
|
+
private generateManyToOneRelation(
|
314
|
+
sourceSchema: Schema,
|
315
|
+
relatedSchema: Schema,
|
316
|
+
relation: SchemaRelation,
|
317
|
+
foreignKey: string
|
318
|
+
): string {
|
319
|
+
const relatedModelName = relatedSchema.model;
|
320
|
+
const relationField = this.camelCase(relatedModelName);
|
321
|
+
|
322
|
+
return (
|
323
|
+
` ${relationField} ${relatedModelName} @relation(fields: [${foreignKey}], references: [id])\n` +
|
324
|
+
` ${foreignKey} String\n`
|
325
|
+
);
|
326
|
+
}
|
327
|
+
|
328
|
+
/**
|
329
|
+
* Generate a many-to-many relation
|
330
|
+
*/
|
331
|
+
private generateManyToManyRelation(
|
332
|
+
sourceSchema: Schema,
|
333
|
+
relatedSchema: Schema,
|
334
|
+
relation: SchemaRelation,
|
335
|
+
relationName: string
|
336
|
+
): string {
|
337
|
+
const relatedModelName = relatedSchema.model;
|
338
|
+
const relationField = this.camelCase(plural(relatedModelName));
|
339
|
+
|
340
|
+
return ` ${relationField} ${relatedModelName}[] @relation("${relationName}")\n`;
|
341
|
+
}
|
342
|
+
|
343
|
+
/**
|
344
|
+
* Map internal type to Prisma type
|
345
|
+
*/
|
346
|
+
private mapTypeToPrisma(type: string, field: SchemaField): string {
|
347
|
+
const typeMap: Record<string, string> = {
|
348
|
+
string: "String",
|
349
|
+
number: "Int",
|
350
|
+
bigint: "BigInt",
|
351
|
+
boolean: "Boolean",
|
352
|
+
Date: "DateTime",
|
353
|
+
DateTime: "DateTime",
|
354
|
+
json: "Json",
|
355
|
+
jsonb: "Json",
|
356
|
+
uuid: "String",
|
357
|
+
text: "String",
|
358
|
+
};
|
359
|
+
|
360
|
+
return typeMap[type.toLowerCase()] || "String";
|
361
|
+
}
|
362
|
+
|
363
|
+
/**
|
364
|
+
* Convert string to camelCase
|
365
|
+
*/
|
366
|
+
private camelCase(str: string): string {
|
367
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
368
|
+
}
|
369
|
+
}
|
370
|
+
|
371
|
+
/**
|
372
|
+
* Converts Prisma schema to internal DSL schema format
|
373
|
+
*/
|
374
|
+
class PrismaToDSLConverter {
|
375
|
+
constructor() {}
|
376
|
+
|
377
|
+
/**
|
378
|
+
* Convert Prisma schema to internal DSL format
|
379
|
+
*/
|
380
|
+
convertToDSL(prismaSchema: string, dbName: string): DBConfigs {
|
381
|
+
// Parse the Prisma schema
|
382
|
+
const models = this.parsePrismaSchema(prismaSchema);
|
383
|
+
|
384
|
+
// Convert models to schemas
|
385
|
+
const schemas: Schema[] = models.map((model) =>
|
386
|
+
this.convertModelToDSL(model)
|
387
|
+
);
|
388
|
+
|
389
|
+
// Create the DBConfigs object
|
390
|
+
const dbConfigs: DBConfigs = {
|
391
|
+
[dbName]: {
|
392
|
+
database: {
|
393
|
+
name: dbName,
|
394
|
+
type: this.detectDatabaseType(prismaSchema) as
|
395
|
+
| "postgres"
|
396
|
+
| "sqlite"
|
397
|
+
| "indexeddb"
|
398
|
+
| "memory"
|
399
|
+
| "localstorage"
|
400
|
+
| "valkey",
|
401
|
+
enabled: true,
|
402
|
+
connectionStringEnv: this.extractConnectionStringEnv(prismaSchema),
|
403
|
+
},
|
404
|
+
schemas,
|
405
|
+
},
|
406
|
+
};
|
407
|
+
|
408
|
+
return dbConfigs;
|
409
|
+
}
|
410
|
+
|
411
|
+
/**
|
412
|
+
* Parse Prisma schema into model objects
|
413
|
+
*/
|
414
|
+
private parsePrismaSchema(prismaSchema: string): PrismaModel[] {
|
415
|
+
const models: PrismaModel[] = [];
|
416
|
+
const modelRegex = /model\s+(\w+)\s+{([^}]*)}/g;
|
417
|
+
let match;
|
418
|
+
|
419
|
+
while ((match = modelRegex.exec(prismaSchema)) !== null) {
|
420
|
+
const modelName = match[1];
|
421
|
+
const modelBody = match[2];
|
422
|
+
|
423
|
+
// Parse fields
|
424
|
+
const fields: PrismaField[] = [];
|
425
|
+
const relations: PrismaRelation[] = [];
|
426
|
+
|
427
|
+
const lines = modelBody
|
428
|
+
?.split("\n")
|
429
|
+
.map((line) => line.trim())
|
430
|
+
.filter((line) => line);
|
431
|
+
|
432
|
+
for (const line of lines || []) {
|
433
|
+
// Skip comments and directives
|
434
|
+
if (line.startsWith("//") || line.startsWith("@@")) continue;
|
435
|
+
|
436
|
+
// Parse field
|
437
|
+
const fieldMatch = line.match(/(\w+)\s+(\w+)(\??)(\[\])?(\s+(.*))?/);
|
438
|
+
if (fieldMatch) {
|
439
|
+
const [_, fieldName, fieldType, nullable, isArray, __, attributes] =
|
440
|
+
fieldMatch;
|
441
|
+
|
442
|
+
// Check if it's a relation
|
443
|
+
if (attributes && attributes.includes("@relation")) {
|
444
|
+
const relationMatch = attributes.match(
|
445
|
+
/@relation\(fields:\s*\[(\w+)\],\s*references:\s*\[(\w+)\]\)/
|
446
|
+
);
|
447
|
+
if (relationMatch) {
|
448
|
+
const [_, foreignKey, referencedField] = relationMatch;
|
449
|
+
|
450
|
+
if (foreignKey && referencedField && fieldName && fieldType) {
|
451
|
+
relations.push({
|
452
|
+
name: fieldName,
|
453
|
+
type: fieldType,
|
454
|
+
foreignKey,
|
455
|
+
referencedField,
|
456
|
+
isArray: !!isArray,
|
457
|
+
isRequired: !nullable,
|
458
|
+
});
|
459
|
+
}
|
460
|
+
} else {
|
461
|
+
// Many-to-many relation
|
462
|
+
const manyToManyMatch = attributes.match(/@relation\("(\w+)"\)/);
|
463
|
+
if (manyToManyMatch) {
|
464
|
+
const [_, relationName] = manyToManyMatch;
|
465
|
+
|
466
|
+
if (relationName && fieldName && fieldType) {
|
467
|
+
relations.push({
|
468
|
+
name: fieldName,
|
469
|
+
type: fieldType,
|
470
|
+
relationName,
|
471
|
+
isArray: !!isArray,
|
472
|
+
isRequired: !nullable,
|
473
|
+
});
|
474
|
+
}
|
475
|
+
}
|
476
|
+
}
|
477
|
+
} else {
|
478
|
+
// Regular field
|
479
|
+
if (fieldName && fieldType) {
|
480
|
+
fields.push({
|
481
|
+
name: fieldName,
|
482
|
+
type: fieldType,
|
483
|
+
isArray: !!isArray,
|
484
|
+
isRequired: !nullable,
|
485
|
+
attributes: attributes || "",
|
486
|
+
});
|
487
|
+
}
|
488
|
+
}
|
489
|
+
}
|
490
|
+
}
|
491
|
+
|
492
|
+
// Extract table name from @@map directive
|
493
|
+
let tableName = modelName;
|
494
|
+
const mapMatch = modelBody?.match(/@@map\("([^"]+)"\)/);
|
495
|
+
if (mapMatch) {
|
496
|
+
tableName = mapMatch[1];
|
497
|
+
}
|
498
|
+
|
499
|
+
if (!tableName) {
|
500
|
+
throw new Error(`Table name not found for model ${modelName}`);
|
501
|
+
}
|
502
|
+
|
503
|
+
if (!modelName) {
|
504
|
+
throw new Error(`Model name not found for model ${modelName}`);
|
505
|
+
}
|
506
|
+
|
507
|
+
models.push({
|
508
|
+
name: modelName,
|
509
|
+
tableName,
|
510
|
+
fields,
|
511
|
+
relations,
|
512
|
+
});
|
513
|
+
}
|
514
|
+
|
515
|
+
return models;
|
516
|
+
}
|
517
|
+
|
518
|
+
/**
|
519
|
+
* Convert a Prisma model to internal DSL schema
|
520
|
+
*/
|
521
|
+
private convertModelToDSL(model: PrismaModel): Schema {
|
522
|
+
const fields: SchemaField[] = model.fields.map((field) =>
|
523
|
+
this.convertFieldToDSL(field)
|
524
|
+
);
|
525
|
+
const relations: SchemaRelation[] = [];
|
526
|
+
|
527
|
+
// Convert relations
|
528
|
+
for (const relation of model.relations) {
|
529
|
+
const relationKey = `${relation.name}Relation`;
|
530
|
+
relations.push(this.convertRelationToDSL(relation));
|
531
|
+
}
|
532
|
+
|
533
|
+
return {
|
534
|
+
model: model.name,
|
535
|
+
tableName: model.tableName,
|
536
|
+
fields,
|
537
|
+
relations:
|
538
|
+
Object.keys(relations).length > 0
|
539
|
+
? relations
|
540
|
+
: ([] as unknown as SchemaRelation[]),
|
541
|
+
};
|
542
|
+
}
|
543
|
+
|
544
|
+
/**
|
545
|
+
* Convert a Prisma field to internal DSL field
|
546
|
+
*/
|
547
|
+
private convertFieldToDSL(field: PrismaField): SchemaField {
|
548
|
+
const schemaField: SchemaField = {
|
549
|
+
name: field.name,
|
550
|
+
type: this.mapPrismaTypeToInternal(field.type),
|
551
|
+
nullable: !field.isRequired,
|
552
|
+
};
|
553
|
+
|
554
|
+
// Handle attributes
|
555
|
+
if (field.attributes) {
|
556
|
+
// Primary key
|
557
|
+
if (field.attributes.includes("@id")) {
|
558
|
+
schemaField.primaryKey = true;
|
559
|
+
}
|
560
|
+
|
561
|
+
// Unique constraint
|
562
|
+
if (field.attributes.includes("@unique")) {
|
563
|
+
schemaField.unique = true;
|
564
|
+
}
|
565
|
+
|
566
|
+
// Default values
|
567
|
+
const defaultMatch = field.attributes.match(/@default\(([^)]+)\)/);
|
568
|
+
if (defaultMatch) {
|
569
|
+
const defaultValue = defaultMatch[1];
|
570
|
+
|
571
|
+
if (defaultValue === "autoincrement()") {
|
572
|
+
schemaField.autoIncrement = true;
|
573
|
+
} else if (defaultValue === "uuid()" || defaultValue === "cuid()") {
|
574
|
+
// Handle UUID/CUID default
|
575
|
+
schemaField.default = "uuid";
|
576
|
+
} else if (defaultValue === "now()") {
|
577
|
+
// Handle DateTime default
|
578
|
+
schemaField.DateTime = new Date();
|
579
|
+
} else if (defaultValue === "true" || defaultValue === "false") {
|
580
|
+
// Handle boolean default
|
581
|
+
schemaField.default = defaultValue === "true";
|
582
|
+
} else if (!isNaN(Number(defaultValue))) {
|
583
|
+
// Handle numeric default
|
584
|
+
schemaField.default = Number(defaultValue);
|
585
|
+
} else {
|
586
|
+
// Handle string default (remove quotes)
|
587
|
+
schemaField.default = defaultValue?.replace(/^"(.*)"$/, "$1");
|
588
|
+
}
|
589
|
+
}
|
590
|
+
|
591
|
+
// Updated at
|
592
|
+
// if (field.attributes.includes("@updatedAt")) {
|
593
|
+
// schemaField.updatedAt = true;
|
594
|
+
// }
|
595
|
+
}
|
596
|
+
|
597
|
+
return schemaField;
|
598
|
+
}
|
599
|
+
|
600
|
+
/**
|
601
|
+
* Convert a Prisma relation to internal DSL relation
|
602
|
+
*/
|
603
|
+
private convertRelationToDSL(relation: PrismaRelation): SchemaRelation {
|
604
|
+
let relationType: "OneToOne" | "OneToMany" | "ManyToOne" | "ManyToMany";
|
605
|
+
|
606
|
+
if (relation.isArray) {
|
607
|
+
relationType = relation.relationName ? "ManyToMany" : "OneToMany";
|
608
|
+
} else {
|
609
|
+
relationType =
|
610
|
+
relation.foreignKey && relation.attributes?.includes("@unique")
|
611
|
+
? "OneToOne"
|
612
|
+
: "ManyToOne";
|
613
|
+
}
|
614
|
+
|
615
|
+
return {
|
616
|
+
relatedTo: relation.type,
|
617
|
+
relationType,
|
618
|
+
foreignKey: relation.foreignKey || "",
|
619
|
+
onDelete: "CASCADE", // Default value, could be extracted from attributes
|
620
|
+
};
|
621
|
+
}
|
622
|
+
|
623
|
+
/**
|
624
|
+
* Map Prisma type to internal type
|
625
|
+
*/
|
626
|
+
private mapPrismaTypeToInternal(prismaType: string): string {
|
627
|
+
const typeMap: Record<string, string> = {
|
628
|
+
String: "string",
|
629
|
+
Int: "number",
|
630
|
+
BigInt: "bigint",
|
631
|
+
Float: "number",
|
632
|
+
Decimal: "number",
|
633
|
+
Boolean: "boolean",
|
634
|
+
DateTime: "DateTime",
|
635
|
+
Date: "Date",
|
636
|
+
Json: "json",
|
637
|
+
Bytes: "string",
|
638
|
+
};
|
639
|
+
|
640
|
+
return typeMap[prismaType] || "string";
|
641
|
+
}
|
642
|
+
|
643
|
+
/**
|
644
|
+
* Detect database type from Prisma schema
|
645
|
+
*/
|
646
|
+
private detectDatabaseType(prismaSchema: string): string {
|
647
|
+
const providerMatch = prismaSchema.match(/provider\s*=\s*"([^"]+)"/);
|
648
|
+
if (providerMatch && providerMatch[1]) {
|
649
|
+
const provider = providerMatch[1];
|
650
|
+
|
651
|
+
switch (provider) {
|
652
|
+
case "postgresql":
|
653
|
+
return "postgres";
|
654
|
+
case "mysql":
|
655
|
+
return "mysql";
|
656
|
+
case "sqlite":
|
657
|
+
return "sqlite";
|
658
|
+
case "sqlserver":
|
659
|
+
return "sqlserver";
|
660
|
+
default:
|
661
|
+
return provider;
|
662
|
+
}
|
663
|
+
}
|
664
|
+
|
665
|
+
return "postgres"; // Default
|
666
|
+
}
|
667
|
+
|
668
|
+
/**
|
669
|
+
* Extract connection string environment variable from Prisma schema
|
670
|
+
*/
|
671
|
+
private extractConnectionStringEnv(prismaSchema: string): string {
|
672
|
+
const urlMatch = prismaSchema.match(/url\s*=\s*env\("([^"]+)"\)/);
|
673
|
+
return urlMatch?.[1] ?? "DATABASE_URL";
|
674
|
+
}
|
675
|
+
}
|
676
|
+
|
677
|
+
/**
|
678
|
+
* Schema Converter Factory
|
679
|
+
*/
|
680
|
+
class SchemaConverter {
|
681
|
+
private dslToPrismaConverter: DSLToPrismaConverter;
|
682
|
+
private prismaToDSLConverter: PrismaToDSLConverter;
|
683
|
+
|
684
|
+
constructor() {
|
685
|
+
this.dslToPrismaConverter = new DSLToPrismaConverter();
|
686
|
+
this.prismaToDSLConverter = new PrismaToDSLConverter();
|
687
|
+
}
|
688
|
+
|
689
|
+
/**
|
690
|
+
* Convert DSL schema to Prisma schema
|
691
|
+
*/
|
692
|
+
dslToPrisma(dslSchema: DBConfigs, options: ConversionOptions = {}): string {
|
693
|
+
return this.dslToPrismaConverter.convertToPrisma(dslSchema, options);
|
694
|
+
}
|
695
|
+
|
696
|
+
/**
|
697
|
+
* Convert Prisma schema to DSL schema
|
698
|
+
*/
|
699
|
+
prismaToDSL(prismaSchema: string, dbName: string): DBConfigs {
|
700
|
+
return this.prismaToDSLConverter.convertToDSL(prismaSchema, dbName);
|
701
|
+
}
|
702
|
+
}
|
703
|
+
|
704
|
+
/**
|
705
|
+
* Types
|
706
|
+
*/
|
707
|
+
interface ConversionOptions {
|
708
|
+
provider?: string;
|
709
|
+
connectionStringEnv?: string;
|
710
|
+
includeComments?: boolean;
|
711
|
+
}
|
712
|
+
|
713
|
+
interface PrismaModel {
|
714
|
+
name: string;
|
715
|
+
tableName: string;
|
716
|
+
fields: PrismaField[];
|
717
|
+
relations: PrismaRelation[];
|
718
|
+
}
|
719
|
+
|
720
|
+
interface PrismaField {
|
721
|
+
name: string;
|
722
|
+
type: string;
|
723
|
+
isArray: boolean;
|
724
|
+
isRequired: boolean;
|
725
|
+
attributes?: string;
|
726
|
+
}
|
727
|
+
|
728
|
+
interface PrismaRelation {
|
729
|
+
name: string;
|
730
|
+
type: string;
|
731
|
+
foreignKey?: string;
|
732
|
+
referencedField?: string;
|
733
|
+
relationName?: string;
|
734
|
+
isArray: boolean;
|
735
|
+
isRequired: boolean;
|
736
|
+
attributes?: string;
|
737
|
+
}
|
738
|
+
|
739
|
+
export { DSLToPrismaConverter, PrismaToDSLConverter, SchemaConverter };
|
740
|
+
|
741
|
+
/**
|
742
|
+
* Test
|
743
|
+
*/
|
744
|
+
|
745
|
+
const DSLSchema: DBConfigs = {
|
746
|
+
uwazi_dev: {
|
747
|
+
database: {
|
748
|
+
name: "uwazi_dev",
|
749
|
+
type: "postgres",
|
750
|
+
enabled: true,
|
751
|
+
connectionStringEnv: "UWAZI_SPRING_DATABASE_URL",
|
752
|
+
},
|
753
|
+
schemas: [
|
754
|
+
{
|
755
|
+
model: "AuditTrail",
|
756
|
+
tableName: "audit_trails",
|
757
|
+
fields: [
|
758
|
+
{
|
759
|
+
name: "log_ref",
|
760
|
+
type: "String",
|
761
|
+
length: 50,
|
762
|
+
nullable: false,
|
763
|
+
},
|
764
|
+
{
|
765
|
+
name: "user_name",
|
766
|
+
type: "String",
|
767
|
+
length: 50,
|
768
|
+
},
|
769
|
+
{
|
770
|
+
name: "active_page",
|
771
|
+
type: "String",
|
772
|
+
length: 255,
|
773
|
+
},
|
774
|
+
{
|
775
|
+
name: "activity_done",
|
776
|
+
type: "String",
|
777
|
+
length: 255,
|
778
|
+
},
|
779
|
+
{
|
780
|
+
name: "system_module",
|
781
|
+
type: "String",
|
782
|
+
length: 100,
|
783
|
+
},
|
784
|
+
{
|
785
|
+
name: "audit_date",
|
786
|
+
type: "DateTime",
|
787
|
+
default: "now",
|
788
|
+
},
|
789
|
+
{
|
790
|
+
name: "ip_address",
|
791
|
+
type: "String",
|
792
|
+
length: 50,
|
793
|
+
},
|
794
|
+
],
|
795
|
+
},
|
796
|
+
{
|
797
|
+
model: "Claim",
|
798
|
+
tableName: "claims",
|
799
|
+
fields: [
|
800
|
+
{
|
801
|
+
name: "claim_reference",
|
802
|
+
type: "String",
|
803
|
+
length: 50,
|
804
|
+
nullable: false,
|
805
|
+
index: true,
|
806
|
+
},
|
807
|
+
{
|
808
|
+
name: "invoice_number",
|
809
|
+
type: "String",
|
810
|
+
length: 50,
|
811
|
+
unique: true,
|
812
|
+
nullable: false,
|
813
|
+
index: true,
|
814
|
+
},
|
815
|
+
{
|
816
|
+
name: "policy_number",
|
817
|
+
type: "String",
|
818
|
+
length: 50,
|
819
|
+
},
|
820
|
+
{
|
821
|
+
name: "invoice_amount",
|
822
|
+
type: "Decimal",
|
823
|
+
nullable: false,
|
824
|
+
length: 15,
|
825
|
+
precision: 2,
|
826
|
+
},
|
827
|
+
{
|
828
|
+
name: "min_cost",
|
829
|
+
type: "Decimal",
|
830
|
+
length: 15,
|
831
|
+
precision: 2,
|
832
|
+
},
|
833
|
+
{
|
834
|
+
name: "maximum_cost",
|
835
|
+
type: "Decimal",
|
836
|
+
length: 15,
|
837
|
+
precision: 2,
|
838
|
+
},
|
839
|
+
{
|
840
|
+
name: "risk_classification",
|
841
|
+
type: "String",
|
842
|
+
length: 50,
|
843
|
+
},
|
844
|
+
{
|
845
|
+
name: "claim_narration",
|
846
|
+
type: "String",
|
847
|
+
length: 500,
|
848
|
+
},
|
849
|
+
{
|
850
|
+
name: "created_by",
|
851
|
+
type: "String",
|
852
|
+
length: 100,
|
853
|
+
},
|
854
|
+
{
|
855
|
+
name: "approved_by",
|
856
|
+
type: "String",
|
857
|
+
length: 100,
|
858
|
+
},
|
859
|
+
{
|
860
|
+
name: "created_at",
|
861
|
+
type: "DateTime",
|
862
|
+
default: "now",
|
863
|
+
},
|
864
|
+
{
|
865
|
+
name: "date_approved",
|
866
|
+
type: "DateTime",
|
867
|
+
},
|
868
|
+
{
|
869
|
+
name: "approval_remarks",
|
870
|
+
type: "String",
|
871
|
+
length: 255,
|
872
|
+
},
|
873
|
+
{
|
874
|
+
name: "status_code",
|
875
|
+
type: "String",
|
876
|
+
length: 50,
|
877
|
+
},
|
878
|
+
{
|
879
|
+
name: "status_description",
|
880
|
+
type: "Text",
|
881
|
+
},
|
882
|
+
],
|
883
|
+
relations: [
|
884
|
+
{
|
885
|
+
foreignKey: "treatment_id",
|
886
|
+
relatedTo: "Treatment",
|
887
|
+
relationType: "ManyToOne",
|
888
|
+
onDelete: "CASCADE",
|
889
|
+
},
|
890
|
+
{
|
891
|
+
foreignKey: "hospital_id",
|
892
|
+
relatedTo: "Organisation",
|
893
|
+
relationType: "ManyToOne",
|
894
|
+
onDelete: "CASCADE",
|
895
|
+
},
|
896
|
+
{
|
897
|
+
foreignKey: "insured_id",
|
898
|
+
relatedTo: "Organisation",
|
899
|
+
relationType: "ManyToOne",
|
900
|
+
onDelete: "CASCADE",
|
901
|
+
},
|
902
|
+
],
|
903
|
+
},
|
904
|
+
{
|
905
|
+
model: "Organisation",
|
906
|
+
tableName: "organisations",
|
907
|
+
fields: [
|
908
|
+
{
|
909
|
+
name: "code",
|
910
|
+
type: "String",
|
911
|
+
length: 50,
|
912
|
+
unique: true,
|
913
|
+
nullable: false,
|
914
|
+
index: true,
|
915
|
+
},
|
916
|
+
{
|
917
|
+
name: "name",
|
918
|
+
type: "String",
|
919
|
+
length: 255,
|
920
|
+
nullable: false,
|
921
|
+
},
|
922
|
+
{
|
923
|
+
name: "type",
|
924
|
+
type: "String",
|
925
|
+
length: 50,
|
926
|
+
nullable: false,
|
927
|
+
},
|
928
|
+
{
|
929
|
+
name: "kra_pin",
|
930
|
+
type: "String",
|
931
|
+
length: 50,
|
932
|
+
nullable: true,
|
933
|
+
},
|
934
|
+
{
|
935
|
+
name: "head_quarter_location",
|
936
|
+
type: "String",
|
937
|
+
nullable: true,
|
938
|
+
},
|
939
|
+
{
|
940
|
+
name: "email_address",
|
941
|
+
type: "String",
|
942
|
+
length: 255,
|
943
|
+
unique: true,
|
944
|
+
nullable: true,
|
945
|
+
},
|
946
|
+
{
|
947
|
+
name: "mobile_number",
|
948
|
+
type: "String",
|
949
|
+
length: 20,
|
950
|
+
nullable: true,
|
951
|
+
},
|
952
|
+
{
|
953
|
+
name: "hospital_category",
|
954
|
+
type: "String",
|
955
|
+
length: 50,
|
956
|
+
nullable: true,
|
957
|
+
},
|
958
|
+
{
|
959
|
+
name: "created_at",
|
960
|
+
type: "DateTime",
|
961
|
+
default: "now",
|
962
|
+
},
|
963
|
+
{
|
964
|
+
name: "updated_at",
|
965
|
+
type: "DateTime",
|
966
|
+
},
|
967
|
+
{
|
968
|
+
name: "created_by",
|
969
|
+
type: "String",
|
970
|
+
length: 100,
|
971
|
+
},
|
972
|
+
{
|
973
|
+
name: "approved_by",
|
974
|
+
type: "String",
|
975
|
+
length: 100,
|
976
|
+
},
|
977
|
+
{
|
978
|
+
name: "approved_at",
|
979
|
+
type: "DateTime",
|
980
|
+
default: "now",
|
981
|
+
},
|
982
|
+
{
|
983
|
+
name: "status_code",
|
984
|
+
type: "String",
|
985
|
+
length: 50,
|
986
|
+
nullable: false,
|
987
|
+
},
|
988
|
+
{
|
989
|
+
name: "status_description",
|
990
|
+
type: "Text",
|
991
|
+
nullable: true,
|
992
|
+
},
|
993
|
+
],
|
994
|
+
},
|
995
|
+
{
|
996
|
+
model: "Policy",
|
997
|
+
tableName: "policies",
|
998
|
+
fields: [
|
999
|
+
{
|
1000
|
+
name: "policy_number",
|
1001
|
+
type: "String",
|
1002
|
+
length: 50,
|
1003
|
+
unique: true,
|
1004
|
+
nullable: false,
|
1005
|
+
index: true,
|
1006
|
+
},
|
1007
|
+
{
|
1008
|
+
name: "policy_start_date",
|
1009
|
+
type: "Date",
|
1010
|
+
nullable: false,
|
1011
|
+
default: "now",
|
1012
|
+
},
|
1013
|
+
{
|
1014
|
+
name: "policy_end_date",
|
1015
|
+
type: "Date",
|
1016
|
+
nullable: false,
|
1017
|
+
default: "now",
|
1018
|
+
},
|
1019
|
+
{
|
1020
|
+
name: "premium_amount",
|
1021
|
+
type: "Decimal",
|
1022
|
+
length: 15,
|
1023
|
+
precision: 2,
|
1024
|
+
nullable: false,
|
1025
|
+
},
|
1026
|
+
{
|
1027
|
+
name: "remaining_limit",
|
1028
|
+
type: "Decimal",
|
1029
|
+
nullable: false,
|
1030
|
+
length: 15,
|
1031
|
+
precision: 2,
|
1032
|
+
},
|
1033
|
+
{
|
1034
|
+
name: "created_at",
|
1035
|
+
type: "DateTime",
|
1036
|
+
default: "now",
|
1037
|
+
},
|
1038
|
+
{
|
1039
|
+
name: "updated_at",
|
1040
|
+
type: "DateTime",
|
1041
|
+
},
|
1042
|
+
],
|
1043
|
+
relations: [
|
1044
|
+
{
|
1045
|
+
foreignKey: "user_id",
|
1046
|
+
relatedTo: "User",
|
1047
|
+
relationType: "ManyToOne",
|
1048
|
+
onDelete: "CASCADE",
|
1049
|
+
},
|
1050
|
+
],
|
1051
|
+
},
|
1052
|
+
{
|
1053
|
+
model: "Role",
|
1054
|
+
tableName: "roles",
|
1055
|
+
fields: [
|
1056
|
+
{
|
1057
|
+
name: "name",
|
1058
|
+
type: "String",
|
1059
|
+
length: 50,
|
1060
|
+
nullable: false,
|
1061
|
+
},
|
1062
|
+
{
|
1063
|
+
name: "description",
|
1064
|
+
type: "String",
|
1065
|
+
length: 255,
|
1066
|
+
nullable: false,
|
1067
|
+
},
|
1068
|
+
{
|
1069
|
+
name: "created_by",
|
1070
|
+
type: "String",
|
1071
|
+
},
|
1072
|
+
{
|
1073
|
+
name: "created_at",
|
1074
|
+
type: "DateTime",
|
1075
|
+
default: "now",
|
1076
|
+
},
|
1077
|
+
],
|
1078
|
+
},
|
1079
|
+
{
|
1080
|
+
model: "Token",
|
1081
|
+
tableName: "tokens",
|
1082
|
+
fields: [
|
1083
|
+
{
|
1084
|
+
name: "token",
|
1085
|
+
type: "Text",
|
1086
|
+
nullable: false,
|
1087
|
+
},
|
1088
|
+
{
|
1089
|
+
name: "token_type",
|
1090
|
+
type: "String",
|
1091
|
+
length: 20,
|
1092
|
+
nullable: false,
|
1093
|
+
},
|
1094
|
+
{
|
1095
|
+
name: "expires_at",
|
1096
|
+
type: "DateTime",
|
1097
|
+
nullable: false,
|
1098
|
+
},
|
1099
|
+
{
|
1100
|
+
name: "revoked",
|
1101
|
+
type: "Boolean",
|
1102
|
+
default: false,
|
1103
|
+
},
|
1104
|
+
{
|
1105
|
+
name: "created_at",
|
1106
|
+
type: "DateTime",
|
1107
|
+
default: "now",
|
1108
|
+
},
|
1109
|
+
],
|
1110
|
+
relations: [
|
1111
|
+
{
|
1112
|
+
foreignKey: "user_id",
|
1113
|
+
relatedTo: "User",
|
1114
|
+
relationType: "ManyToOne",
|
1115
|
+
onDelete: "CASCADE",
|
1116
|
+
},
|
1117
|
+
],
|
1118
|
+
},
|
1119
|
+
{
|
1120
|
+
model: "TreatmentCost",
|
1121
|
+
tableName: "treatment_costs",
|
1122
|
+
fields: [
|
1123
|
+
{
|
1124
|
+
name: "hospital_category",
|
1125
|
+
type: "String",
|
1126
|
+
length: 50,
|
1127
|
+
nullable: false,
|
1128
|
+
},
|
1129
|
+
{
|
1130
|
+
name: "min_cost",
|
1131
|
+
type: "Decimal",
|
1132
|
+
length: 15,
|
1133
|
+
precision: 2,
|
1134
|
+
nullable: false,
|
1135
|
+
},
|
1136
|
+
{
|
1137
|
+
name: "maximum_cost",
|
1138
|
+
type: "Decimal",
|
1139
|
+
length: 15,
|
1140
|
+
precision: 2,
|
1141
|
+
nullable: false,
|
1142
|
+
},
|
1143
|
+
{
|
1144
|
+
name: "created_by",
|
1145
|
+
type: "String",
|
1146
|
+
length: 100,
|
1147
|
+
},
|
1148
|
+
{
|
1149
|
+
name: "created_at",
|
1150
|
+
type: "DateTime",
|
1151
|
+
default: "now",
|
1152
|
+
},
|
1153
|
+
{
|
1154
|
+
name: "approved_by",
|
1155
|
+
type: "String",
|
1156
|
+
length: 100,
|
1157
|
+
},
|
1158
|
+
{
|
1159
|
+
name: "approved_at",
|
1160
|
+
type: "DateTime",
|
1161
|
+
},
|
1162
|
+
{
|
1163
|
+
name: "updated_by",
|
1164
|
+
type: "String",
|
1165
|
+
length: 100,
|
1166
|
+
},
|
1167
|
+
{
|
1168
|
+
name: "updated_at",
|
1169
|
+
type: "DateTime",
|
1170
|
+
},
|
1171
|
+
{
|
1172
|
+
name: "status_code",
|
1173
|
+
type: "String",
|
1174
|
+
length: 50,
|
1175
|
+
nullable: false,
|
1176
|
+
},
|
1177
|
+
{
|
1178
|
+
name: "status_description",
|
1179
|
+
type: "Text",
|
1180
|
+
},
|
1181
|
+
],
|
1182
|
+
relations: [
|
1183
|
+
{
|
1184
|
+
foreignKey: "treatment_id",
|
1185
|
+
relatedTo: "Treatment",
|
1186
|
+
relationType: "ManyToOne",
|
1187
|
+
onDelete: "CASCADE",
|
1188
|
+
},
|
1189
|
+
],
|
1190
|
+
},
|
1191
|
+
{
|
1192
|
+
model: "Treatment",
|
1193
|
+
tableName: "treatments",
|
1194
|
+
fields: [
|
1195
|
+
{
|
1196
|
+
name: "code",
|
1197
|
+
type: "String",
|
1198
|
+
length: 50,
|
1199
|
+
unique: true,
|
1200
|
+
nullable: false,
|
1201
|
+
index: true,
|
1202
|
+
},
|
1203
|
+
{
|
1204
|
+
name: "name",
|
1205
|
+
type: "String",
|
1206
|
+
length: 255,
|
1207
|
+
},
|
1208
|
+
{
|
1209
|
+
name: "description",
|
1210
|
+
type: "String",
|
1211
|
+
length: 500,
|
1212
|
+
},
|
1213
|
+
{
|
1214
|
+
name: "created_by",
|
1215
|
+
type: "String",
|
1216
|
+
length: 100,
|
1217
|
+
},
|
1218
|
+
{
|
1219
|
+
name: "created_at",
|
1220
|
+
type: "DateTime",
|
1221
|
+
default: "now",
|
1222
|
+
},
|
1223
|
+
{
|
1224
|
+
name: "approved_by",
|
1225
|
+
type: "String",
|
1226
|
+
length: 100,
|
1227
|
+
},
|
1228
|
+
{
|
1229
|
+
name: "approved_at",
|
1230
|
+
type: "DateTime",
|
1231
|
+
default: "now",
|
1232
|
+
},
|
1233
|
+
{
|
1234
|
+
name: "status_code",
|
1235
|
+
type: "String",
|
1236
|
+
length: 50,
|
1237
|
+
nullable: false,
|
1238
|
+
},
|
1239
|
+
{
|
1240
|
+
name: "status_description",
|
1241
|
+
type: "Text",
|
1242
|
+
},
|
1243
|
+
],
|
1244
|
+
},
|
1245
|
+
{
|
1246
|
+
model: "User",
|
1247
|
+
tableName: "users",
|
1248
|
+
fields: [
|
1249
|
+
{
|
1250
|
+
name: "user_name",
|
1251
|
+
type: "String",
|
1252
|
+
length: 100,
|
1253
|
+
unique: true,
|
1254
|
+
nullable: false,
|
1255
|
+
index: true,
|
1256
|
+
},
|
1257
|
+
{
|
1258
|
+
name: "first_name",
|
1259
|
+
type: "String",
|
1260
|
+
length: 100,
|
1261
|
+
nullable: false,
|
1262
|
+
},
|
1263
|
+
{
|
1264
|
+
name: "second_name",
|
1265
|
+
type: "String",
|
1266
|
+
length: 100,
|
1267
|
+
nullable: true,
|
1268
|
+
},
|
1269
|
+
{
|
1270
|
+
name: "last_name",
|
1271
|
+
type: "String",
|
1272
|
+
length: 100,
|
1273
|
+
nullable: false,
|
1274
|
+
},
|
1275
|
+
{
|
1276
|
+
name: "national_id",
|
1277
|
+
type: "String",
|
1278
|
+
length: 50,
|
1279
|
+
nullable: true,
|
1280
|
+
unique: true,
|
1281
|
+
},
|
1282
|
+
{
|
1283
|
+
name: "gender",
|
1284
|
+
type: "String",
|
1285
|
+
length: 10,
|
1286
|
+
nullable: true,
|
1287
|
+
},
|
1288
|
+
{
|
1289
|
+
name: "dob",
|
1290
|
+
type: "Date",
|
1291
|
+
nullable: true,
|
1292
|
+
},
|
1293
|
+
{
|
1294
|
+
name: "mobile_number",
|
1295
|
+
type: "String",
|
1296
|
+
length: 20,
|
1297
|
+
unique: true,
|
1298
|
+
nullable: true,
|
1299
|
+
},
|
1300
|
+
{
|
1301
|
+
name: "email",
|
1302
|
+
type: "String",
|
1303
|
+
length: 255,
|
1304
|
+
unique: true,
|
1305
|
+
nullable: false,
|
1306
|
+
index: true,
|
1307
|
+
},
|
1308
|
+
{
|
1309
|
+
name: "password_hash",
|
1310
|
+
type: "String",
|
1311
|
+
length: 255,
|
1312
|
+
nullable: false,
|
1313
|
+
},
|
1314
|
+
{
|
1315
|
+
name: "last_login_date",
|
1316
|
+
type: "DateTime",
|
1317
|
+
nullable: true,
|
1318
|
+
},
|
1319
|
+
{
|
1320
|
+
name: "login_trials",
|
1321
|
+
type: "Int",
|
1322
|
+
default: 3,
|
1323
|
+
nullable: true,
|
1324
|
+
},
|
1325
|
+
{
|
1326
|
+
name: "login_ip",
|
1327
|
+
type: "String",
|
1328
|
+
length: 50,
|
1329
|
+
nullable: true,
|
1330
|
+
},
|
1331
|
+
{
|
1332
|
+
name: "created_by",
|
1333
|
+
type: "String",
|
1334
|
+
length: 100,
|
1335
|
+
nullable: false,
|
1336
|
+
},
|
1337
|
+
{
|
1338
|
+
name: "created_at",
|
1339
|
+
type: "DateTime",
|
1340
|
+
default: "now",
|
1341
|
+
nullable: true,
|
1342
|
+
},
|
1343
|
+
{
|
1344
|
+
name: "approved_by",
|
1345
|
+
type: "String",
|
1346
|
+
length: 100,
|
1347
|
+
nullable: true,
|
1348
|
+
},
|
1349
|
+
{
|
1350
|
+
name: "approved_at",
|
1351
|
+
type: "DateTime",
|
1352
|
+
default: "now",
|
1353
|
+
nullable: true,
|
1354
|
+
},
|
1355
|
+
{
|
1356
|
+
name: "updated_at",
|
1357
|
+
type: "DateTime",
|
1358
|
+
nullable: true,
|
1359
|
+
},
|
1360
|
+
{
|
1361
|
+
name: "status_code",
|
1362
|
+
type: "String",
|
1363
|
+
length: 50,
|
1364
|
+
nullable: false,
|
1365
|
+
},
|
1366
|
+
{
|
1367
|
+
name: "status_description",
|
1368
|
+
type: "Text",
|
1369
|
+
nullable: true,
|
1370
|
+
},
|
1371
|
+
],
|
1372
|
+
relations: [
|
1373
|
+
{
|
1374
|
+
foreignKey: "role_id",
|
1375
|
+
relatedTo: "Role",
|
1376
|
+
relationType: "ManyToOne",
|
1377
|
+
},
|
1378
|
+
{
|
1379
|
+
foreignKey: "organisation_id",
|
1380
|
+
relatedTo: "Organisation",
|
1381
|
+
relationType: "ManyToOne",
|
1382
|
+
},
|
1383
|
+
],
|
1384
|
+
},
|
1385
|
+
],
|
1386
|
+
},
|
1387
|
+
};
|
1388
|
+
|
1389
|
+
const schemaConverter = new SchemaConverter();
|
1390
|
+
const prismaSchema = schemaConverter.dslToPrisma(DSLSchema);
|
1391
|
+
const dslSchema = schemaConverter.prismaToDSL(prismaSchema, "uwazi_dev");
|
1392
|
+
await Bun.write("schema.prisma", prismaSchema);
|
1393
|
+
await Bun.write("dsl-schema.json", JSON.stringify(dslSchema, null, 2));
|