Skip to content

Commit d7b9986

Browse files
authored
Merge pull request #1590 from liam-hq/fix/prisma-parser-support-@@Map
Prisma Parser - Support @@Map
2 parents 43e0660 + dae0df1 commit d7b9986

File tree

3 files changed

+189
-9
lines changed

3 files changed

+189
-9
lines changed

.changeset/breezy-beers-unite.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@liam-hq/db-structure": minor
3+
---
4+
5+
update the primsa parser to support the @@map

frontend/packages/db-structure/src/parser/prisma/index.test.ts

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,143 @@ describe(_processor, () => {
560560
expect(value).toEqual(expectedTables)
561561
})
562562

563+
it('@@map', async () => {
564+
const { value } = await processor(`
565+
model User {
566+
id Int @id @default(autoincrement()) @map("_id")
567+
posts Post[]
568+
email String @unique @map("raw_email_address")
569+
role Role @default(USER)
570+
571+
@@map("users")
572+
}
573+
574+
model Post {
575+
id Int @id @default(autoincrement())
576+
user User @relation(fields: [user_id], references: [id])
577+
user_id Int @map("raw_user_id")
578+
579+
@@map("posts")
580+
}
581+
582+
enum Role {
583+
USER
584+
ADMIN
585+
}
586+
`)
587+
588+
const expectedTables = aSchema({
589+
tables: {
590+
users: aTable({
591+
name: 'users',
592+
columns: {
593+
_id: aColumn({
594+
name: '_id',
595+
type: 'serial',
596+
default: 'autoincrement()',
597+
notNull: true,
598+
primary: true,
599+
unique: true,
600+
}),
601+
raw_email_address: aColumn({
602+
name: 'raw_email_address',
603+
type: 'text',
604+
notNull: true,
605+
unique: true,
606+
}),
607+
role: aColumn({
608+
name: 'role',
609+
type: 'Role',
610+
notNull: true,
611+
default: 'USER',
612+
}),
613+
},
614+
indexes: {
615+
users_pkey: anIndex({
616+
name: 'users_pkey',
617+
columns: ['_id'],
618+
unique: true,
619+
}),
620+
users_raw_email_address_key: anIndex({
621+
name: 'users_raw_email_address_key',
622+
columns: ['raw_email_address'],
623+
unique: true,
624+
}),
625+
},
626+
constraints: {
627+
PRIMARY__id: {
628+
type: 'PRIMARY KEY',
629+
name: 'PRIMARY__id',
630+
columnName: '_id',
631+
},
632+
UNIQUE_raw_email_address: {
633+
type: 'UNIQUE',
634+
name: 'UNIQUE_raw_email_address',
635+
columnName: 'raw_email_address',
636+
},
637+
},
638+
}),
639+
posts: aTable({
640+
name: 'posts',
641+
columns: {
642+
id: aColumn({
643+
name: 'id',
644+
type: 'serial',
645+
default: 'autoincrement()',
646+
notNull: true,
647+
primary: true,
648+
unique: true,
649+
}),
650+
raw_user_id: aColumn({
651+
name: 'raw_user_id',
652+
type: 'integer',
653+
notNull: true,
654+
unique: false,
655+
}),
656+
},
657+
indexes: {
658+
posts_pkey: anIndex({
659+
name: 'posts_pkey',
660+
columns: ['id'],
661+
unique: true,
662+
}),
663+
},
664+
constraints: {
665+
PRIMARY_id: {
666+
type: 'PRIMARY KEY',
667+
name: 'PRIMARY_id',
668+
columnName: 'id',
669+
},
670+
PostToUser: {
671+
type: 'FOREIGN KEY',
672+
name: 'PostToUser',
673+
columnName: 'raw_user_id',
674+
targetTableName: 'users',
675+
targetColumnName: '_id',
676+
updateConstraint: 'NO_ACTION',
677+
deleteConstraint: 'NO_ACTION',
678+
},
679+
},
680+
}),
681+
},
682+
})
683+
684+
expectedTables['relationships'] = {
685+
PostToUser: aRelationship({
686+
name: 'PostToUser',
687+
primaryTableName: 'users',
688+
primaryColumnName: '_id',
689+
foreignTableName: 'posts',
690+
foreignColumnName: 'raw_user_id',
691+
cardinality: 'ONE_TO_MANY',
692+
updateConstraint: 'NO_ACTION',
693+
deleteConstraint: 'NO_ACTION',
694+
}),
695+
}
696+
697+
expect(value).toEqual(expectedTables)
698+
})
699+
563700
it('relationship (implicit many-to-many)', async () => {
564701
const { value } = await processor(`
565702
model Post {

frontend/packages/db-structure/src/parser/prisma/parser.ts

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ function buildFieldRenamingMap(
6464
for (const model of models) {
6565
for (const field of model.fields) {
6666
if (field.dbName) {
67-
const fieldConversions = tableFieldRenaming[model.name] ?? {}
67+
const tableName = model.dbName || model.name
68+
const fieldConversions = tableFieldRenaming[tableName] ?? {}
6869
fieldConversions[field.name] = field.dbName
69-
tableFieldRenaming[model.name] = fieldConversions
70+
tableFieldRenaming[tableName] = fieldConversions
7071
}
7172
}
7273
}
@@ -90,7 +91,9 @@ function processModelField(
9091
if (field.relationName) return { column: null, constraint: null }
9192

9293
const defaultValue = extractDefaultValue(field)
93-
const fieldName = tableFieldRenaming[model.name]?.[field.name] ?? field.name
94+
95+
const fieldName =
96+
tableFieldRenaming[model.dbName || model.name]?.[field.name] ?? field.name
9497

9598
const column = {
9699
name: fieldName,
@@ -159,7 +162,7 @@ function processModel(
159162
}
160163

161164
return {
162-
name: model.name,
165+
name: model.dbName || model.name,
163166
columns,
164167
comment: model.documentation ?? null,
165168
indexes: {},
@@ -177,18 +180,30 @@ function processTables(
177180
const tables: Record<string, Table> = {}
178181

179182
for (const model of models) {
180-
tables[model.name] = processModel(model, tableFieldRenaming)
183+
tables[model.dbName || model.name] = processModel(model, tableFieldRenaming)
181184
}
182185

183186
return tables
184187
}
185188

189+
/**
190+
* Get Primary Table Name
191+
*/
192+
193+
function getPrimaryTableNameByType(
194+
fieldType: string,
195+
models: readonly DMMF.Model[],
196+
) {
197+
return models.find((model) => model.name === fieldType)?.dbName ?? fieldType
198+
}
199+
186200
/**
187201
* Process a relationship field
188202
*/
189203
function processRelationshipField(
190204
field: DMMF.Field,
191205
model: DMMF.Model,
206+
models: readonly DMMF.Model[],
192207
existingRelationships: Record<string, Relationship>,
193208
tableFieldRenaming: Record<string, Record<string, string>>,
194209
): {
@@ -204,16 +219,20 @@ function processRelationshipField(
204219
field.relationFromFields?.[0] &&
205220
(field.relationFromFields?.length ?? 0) > 0
206221

222+
// Get the primary table name
223+
const primaryTableName =
224+
isTargetField && getPrimaryTableNameByType(field.type, models)
225+
207226
// Get the column names with fallback to empty string
208227
const primaryColumnName = field.relationToFields?.[0] ?? ''
209228
const foreignColumnName = field.relationFromFields?.[0] ?? ''
210229

211230
const _relationship: Relationship = isTargetField
212231
? {
213232
name: field.relationName,
214-
primaryTableName: field.type,
233+
primaryTableName: primaryTableName || field.type,
215234
primaryColumnName,
216-
foreignTableName: model.name,
235+
foreignTableName: model.dbName || model.name,
217236
foreignColumnName,
218237
cardinality: existingRelationship?.cardinality ?? 'ONE_TO_MANY',
219238
updateConstraint: 'NO_ACTION',
@@ -289,6 +308,7 @@ function processModelRelationships(
289308
const { relationship, constraint } = processRelationshipField(
290309
field,
291310
model,
311+
models,
292312
relationships,
293313
tableFieldRenaming,
294314
)
@@ -348,10 +368,23 @@ function processRelationships(
348368
*/
349369
function processIndexes(
350370
indexes: readonly DMMF.Index[],
371+
models: readonly DMMF.Model[],
351372
tables: Record<string, Table>,
352373
tableFieldRenaming: Record<string, Record<string, string>>,
353374
): void {
354-
for (const index of indexes) {
375+
const updatedIndexes = indexes.map((index) => {
376+
const model = models.find((m) => m.name === index.model)
377+
return model
378+
? {
379+
model: model.dbName ?? model.name,
380+
type: index.type,
381+
isDefinedOnField: index.isDefinedOnField,
382+
fields: index.fields,
383+
}
384+
: index
385+
})
386+
387+
for (const index of updatedIndexes) {
355388
const table = tables[index.model]
356389
if (!table) continue
357390

@@ -470,7 +503,12 @@ async function parsePrismaSchema(schemaString: string): Promise<ProcessResult> {
470503
)
471504

472505
// Process indexes
473-
processIndexes(dmmf.datamodel.indexes, tables, tableFieldRenaming)
506+
processIndexes(
507+
dmmf.datamodel.indexes,
508+
dmmf.datamodel.models,
509+
tables,
510+
tableFieldRenaming,
511+
)
474512

475513
// Process many-to-many relationships
476514
const manyToManyRelationships = processManyToManyRelationships(

0 commit comments

Comments
 (0)