Skip to content

Commit 559a7a3

Browse files
authored
feat(referentialActions): add relationOnDelete (#16)
* feat(referentialActions): add relationOnDelete * test: add referential actions onDelete test
1 parent 13993a4 commit 559a7a3

File tree

10 files changed

+155
-114
lines changed

10 files changed

+155
-114
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
node_modules
22
dist
3-
prisma/dev.sqlite
3+
prisma/dev.db*
44
.env

__tests__/dbml.test.ts

+33
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
datamodelDbmlManyToManyInvalid,
77
datamodelDbmlManyToManyRenameRelation,
88
datamodelDbmlManyToManySelfRelation,
9+
datamodelDbmlReferentialActions,
910
datamodelDbmlRelations,
1011
} from './fixtures/dbml.datamodel';
1112
import { generateDMMF } from './utils/generateDMMF';
@@ -301,4 +302,36 @@ Table userReceivesPosts {
301302

302303
expect(dbml).toEqual(expectedDbml);
303304
});
305+
306+
test('generating dbml schema with referential actions', async () => {
307+
const dmmf = await generateDMMF(datamodelDbmlReferentialActions);
308+
309+
const expectedDbml = `${autoGeneratedComment}
310+
311+
Table User {
312+
id Int [pk, increment]
313+
profile Profile
314+
posts Post [not null]
315+
}
316+
317+
Table Profile {
318+
id Int [pk, increment]
319+
user User [not null]
320+
userId Int [unique, not null]
321+
}
322+
323+
Table Post {
324+
id Int [pk, increment]
325+
author User
326+
authorId Int
327+
}
328+
329+
Ref: Profile.userId - User.id [delete: Cascade]
330+
331+
Ref: Post.authorId > User.id [delete: SetNull]`;
332+
333+
const dbml = generateDBMLSchema(dmmf);
334+
335+
expect(dbml).toEqual(expectedDbml);
336+
});
304337
});

__tests__/fixtures/dbml.datamodel.ts

+18
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,21 @@ export const datamodelDbmlManyToManyRenameRelation = /* Prisma */ `
131131
receivedBy User[] @relation("userReceivesPosts")
132132
}
133133
`;
134+
135+
export const datamodelDbmlReferentialActions = /* Prisma */ `
136+
model User {
137+
id Int @id @default(autoincrement())
138+
profile Profile?
139+
posts Post[]
140+
}
141+
model Profile {
142+
id Int @id @default(autoincrement())
143+
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
144+
userId Int @unique
145+
}
146+
model Post {
147+
id Int @id @default(autoincrement())
148+
author User? @relation(fields: [authorId], references: [id], onDelete: SetNull, onUpdate: Cascade)
149+
authorId Int?
150+
}
151+
`;

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

prisma/dbml/schema.dbml

+2-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ Table User {
1010
name String
1111
posts Post [not null]
1212
profile Profile
13-
role Role [not null, default: 'USER', note: 'user\'s role']
1413
}
1514

1615
Table Profile {
@@ -53,11 +52,6 @@ Table CategoryToPost {
5352
postsId Int [ref: > Post.id]
5453
}
5554

56-
Enum Role {
57-
ADMIN
58-
USER
59-
}
60-
61-
Ref: Profile.userId - User.id
55+
Ref: Profile.userId - User.id [delete: Cascade]
6256

63-
Ref: Post.authorId > User.id
57+
Ref: Post.authorId > User.id [delete: SetNull]

prisma/migrations/20210121125958_init/migration.sql

-84
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
-- CreateTable
2+
CREATE TABLE "User" (
3+
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
5+
"updatedAt" DATETIME NOT NULL,
6+
"email" TEXT NOT NULL,
7+
"name" TEXT
8+
);
9+
10+
-- CreateTable
11+
CREATE TABLE "Profile" (
12+
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
13+
"bio" TEXT,
14+
"userId" INTEGER NOT NULL,
15+
FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
16+
);
17+
18+
-- CreateTable
19+
CREATE TABLE "Post" (
20+
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
21+
"title" TEXT NOT NULL DEFAULT '',
22+
"content" TEXT,
23+
"published" BOOLEAN NOT NULL DEFAULT false,
24+
"authorId" INTEGER,
25+
FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
26+
);
27+
28+
-- CreateTable
29+
CREATE TABLE "Category" (
30+
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
31+
"name" TEXT NOT NULL
32+
);
33+
34+
-- CreateTable
35+
CREATE TABLE "Token" (
36+
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
37+
"device" TEXT NOT NULL,
38+
"operatingSystem" TEXT NOT NULL
39+
);
40+
41+
-- CreateTable
42+
CREATE TABLE "_CategoryToPost" (
43+
"A" INTEGER NOT NULL,
44+
"B" INTEGER NOT NULL,
45+
FOREIGN KEY ("A") REFERENCES "Category" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
46+
FOREIGN KEY ("B") REFERENCES "Post" ("id") ON DELETE CASCADE ON UPDATE CASCADE
47+
);
48+
49+
-- CreateIndex
50+
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email");
51+
52+
-- CreateIndex
53+
CREATE UNIQUE INDEX "Profile.userId_unique" ON "Profile"("userId");
54+
55+
-- CreateIndex
56+
CREATE UNIQUE INDEX "Token.device_operatingSystem_unique" ON "Token"("device", "operatingSystem");
57+
58+
-- CreateIndex
59+
CREATE UNIQUE INDEX "_CategoryToPost_AB_unique" ON "_CategoryToPost"("A", "B");
60+
61+
-- CreateIndex
62+
CREATE INDEX "_CategoryToPost_B_index" ON "_CategoryToPost"("B");

prisma/migrations/migration_lock.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# Please do not edit this file manually
2-
provider = "postgresql"
2+
# It should be added in your version-control system (i.e. Git)
3+
provider = "sqlite"

prisma/schema.prisma

+5-13
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
// learn more about it in the docs: https://pris.ly/d/prisma-schema
33

44
datasource db {
5-
provider = "postgres"
6-
url = env("POSTGRESQL_URL")
5+
provider = "sqlite"
6+
url = env("DATABASE_URL")
77
}
88

99
generator client {
10-
provider = "prisma-client-js"
10+
provider = "prisma-client-js"
1111
}
1212

1313
generator dbml {
@@ -22,15 +22,13 @@ model User {
2222
name String?
2323
posts Post[]
2424
profile Profile?
25-
/// user's role
26-
role Role @default(USER)
2725
}
2826

2927
/// User profile
3028
model Profile {
3129
id Int @id @default(autoincrement())
3230
bio String?
33-
user User @relation(fields: [userId], references: [id])
31+
user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)
3432
userId Int @unique
3533
}
3634

@@ -39,7 +37,7 @@ model Post {
3937
title String @default("")
4038
content String?
4139
published Boolean @default(false)
42-
author User? @relation(fields: [authorId], references: [id])
40+
author User? @relation(fields: [authorId], references: [id], onDelete: SetNull)
4341
authorId Int?
4442
categories Category[]
4543
}
@@ -57,9 +55,3 @@ model Token {
5755
5856
@@unique([device, operatingSystem])
5957
}
60-
61-
/// user role
62-
enum Role {
63-
ADMIN /// allowed to do everything
64-
USER
65-
}

src/generator/relations.ts

+31-6
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,19 @@ export function generateRelations(models: DMMF.Model[]): string[] {
2424
relationTo
2525
);
2626

27-
refs.push(
28-
`Ref: ${relationFrom}.${combineKeys(
29-
field.relationFromFields!
30-
)} ${relationOperator} ${relationTo}.${combineKeys(
31-
field.relationToFields!!
32-
)}`
27+
const ref = `Ref: ${relationFrom}.${combineKeys(
28+
field.relationFromFields!
29+
)} ${relationOperator} ${relationTo}.${combineKeys(
30+
field.relationToFields!!
31+
)}`;
32+
33+
const referentialActions = getReferentialActions(
34+
models,
35+
relationFrom,
36+
relationTo
3337
);
38+
39+
refs.push(`${ref}${referentialActions}`);
3440
});
3541
});
3642
return refs;
@@ -51,3 +57,22 @@ const getRelationOperator = (
5157
const combineKeys = (keys: string[]): string => {
5258
return keys.length > 1 ? `(${keys.join(', ')})` : keys[0];
5359
};
60+
61+
const getReferentialActions = (
62+
models: DMMF.Model[],
63+
from: string,
64+
to: string
65+
): string => {
66+
const model = models.find((model) => model.name === from);
67+
const field = model?.fields.find((field) => field.type === to);
68+
const referentialActions: string[] = [];
69+
70+
if (field?.relationOnDelete) {
71+
referentialActions.push(`delete: ${field.relationOnDelete}`);
72+
}
73+
74+
if (referentialActions.length) {
75+
return ' [' + referentialActions.join(', ') + ']';
76+
}
77+
return '';
78+
};

0 commit comments

Comments
 (0)