Skip to content

Commit bf0ccf6

Browse files
dextertanyjvaltyr
andauthored
fix: implicit many to many relation generation (#71)
* fix: implicit many to many relation generation * test: add test for filtering many to one relations * style: fix lint * Add changeset --------- Co-authored-by: Valtýr Örn Kjartansson <valtyr@gmail.com>
1 parent 766fd4d commit bf0ccf6

File tree

3 files changed

+199
-14
lines changed

3 files changed

+199
-14
lines changed

.changeset/thin-countries-work.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"prisma-kysely": minor
3+
---
4+
5+
Implicit many to many relations are finally fixed thanks to @dextertanyj 🇸🇬🎉🥂. Huge thanks to him!

src/helpers/generateImplicitManyToManyModels.test.ts

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,180 @@ test("it respects overrides when generating field types", () => {
113113
},
114114
]);
115115
});
116+
117+
test("it filters out many-to-one relations safely", () => {
118+
const newModels = generateImplicitManyToManyModels([
119+
{
120+
name: "Category",
121+
fields: [
122+
{
123+
name: "id",
124+
type: "String",
125+
isId: true,
126+
hasDefaultValue: true,
127+
isList: false,
128+
isReadOnly: false,
129+
isRequired: true,
130+
isUnique: true,
131+
kind: "scalar",
132+
},
133+
{
134+
name: "posts",
135+
kind: "object",
136+
isList: true,
137+
isRequired: true,
138+
isUnique: false,
139+
isId: false,
140+
isReadOnly: false,
141+
type: "Post",
142+
hasDefaultValue: false,
143+
relationName: "CategoryToPost",
144+
relationFromFields: [],
145+
relationToFields: [],
146+
isGenerated: false,
147+
isUpdatedAt: false,
148+
},
149+
],
150+
primaryKey: null,
151+
dbName: null,
152+
uniqueFields: [],
153+
uniqueIndexes: [],
154+
},
155+
{
156+
name: "Post",
157+
fields: [
158+
{
159+
name: "id",
160+
type: "String",
161+
isId: true,
162+
hasDefaultValue: true,
163+
isList: false,
164+
isReadOnly: false,
165+
isRequired: true,
166+
isUnique: true,
167+
kind: "scalar",
168+
},
169+
{
170+
name: "categories",
171+
kind: "object",
172+
isList: true,
173+
isRequired: true,
174+
isUnique: false,
175+
isId: false,
176+
isReadOnly: false,
177+
type: "Category",
178+
hasDefaultValue: false,
179+
relationName: "CategoryToPost",
180+
relationFromFields: [],
181+
relationToFields: [],
182+
isGenerated: false,
183+
isUpdatedAt: false,
184+
},
185+
{
186+
name: "userId",
187+
kind: "scalar",
188+
isList: false,
189+
isRequired: true,
190+
isUnique: false,
191+
isId: false,
192+
isReadOnly: true,
193+
hasDefaultValue: false,
194+
type: "String",
195+
isGenerated: false,
196+
isUpdatedAt: false,
197+
},
198+
{
199+
name: "user",
200+
kind: "object",
201+
isList: false,
202+
isRequired: true,
203+
isUnique: false,
204+
isId: false,
205+
isReadOnly: false,
206+
hasDefaultValue: false,
207+
type: "User",
208+
relationName: "PostToUser",
209+
relationFromFields: ["userId"],
210+
relationToFields: ["id"],
211+
relationOnDelete: "Cascade",
212+
isGenerated: false,
213+
isUpdatedAt: false,
214+
},
215+
],
216+
primaryKey: null,
217+
dbName: null,
218+
uniqueFields: [],
219+
uniqueIndexes: [],
220+
},
221+
{
222+
name: "User",
223+
fields: [
224+
{
225+
name: "id",
226+
type: "String",
227+
isId: true,
228+
hasDefaultValue: true,
229+
isList: false,
230+
isReadOnly: false,
231+
isRequired: true,
232+
isUnique: true,
233+
kind: "scalar",
234+
},
235+
{
236+
name: "posts",
237+
kind: "object",
238+
isList: true,
239+
isRequired: true,
240+
isUnique: false,
241+
isId: false,
242+
isReadOnly: false,
243+
type: "Post",
244+
hasDefaultValue: false,
245+
relationName: "PostToUser",
246+
relationFromFields: [],
247+
relationToFields: [],
248+
isGenerated: false,
249+
isUpdatedAt: false,
250+
},
251+
],
252+
primaryKey: null,
253+
dbName: null,
254+
uniqueFields: [],
255+
uniqueIndexes: [],
256+
},
257+
]);
258+
259+
expect(newModels).toEqual<DMMF.Model[]>([
260+
{
261+
name: "CategoryToPost",
262+
dbName: "_CategoryToPost",
263+
fields: [
264+
{
265+
hasDefaultValue: false,
266+
isId: false,
267+
isList: false,
268+
isReadOnly: true,
269+
isRequired: true,
270+
isUnique: false,
271+
kind: "scalar",
272+
name: "A",
273+
type: "String",
274+
},
275+
{
276+
hasDefaultValue: false,
277+
isId: false,
278+
isList: false,
279+
isReadOnly: true,
280+
isRequired: true,
281+
isUnique: false,
282+
kind: "scalar",
283+
name: "B",
284+
type: "String",
285+
},
286+
],
287+
primaryKey: null,
288+
uniqueFields: [],
289+
uniqueIndexes: [],
290+
},
291+
]);
292+
});

src/helpers/generateImplicitManyToManyModels.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -111,18 +111,21 @@ function getJoinIdType(joinField: DMMF.Field, models: DMMF.Model[]): string {
111111
return idField.type;
112112
}
113113

114-
function filterManyToManyRelationFields(models: DMMF.Model[]): DMMF.Field[] {
115-
return models
116-
.map((model) =>
117-
model.fields
118-
.filter(
119-
(field) =>
120-
field.relationName &&
121-
field.isList &&
122-
field.relationFromFields?.length === 0 &&
123-
field.relationToFields?.length === 0
124-
)
125-
.map((field) => field)
126-
)
127-
.flat();
114+
function filterManyToManyRelationFields(models: DMMF.Model[]) {
115+
const fields = models.flatMap((model) => model.fields);
116+
117+
const relationFields = fields.filter(
118+
(field): field is DMMF.Field & Required<Pick<DMMF.Field, "relationName">> =>
119+
!!field.relationName
120+
);
121+
122+
const nonManyToManyRelationNames = relationFields
123+
.filter((field) => !field.isList)
124+
.map((field) => field.relationName);
125+
126+
const notManyToMany = new Set<string>(nonManyToManyRelationNames);
127+
128+
return relationFields.filter(
129+
(field) => !notManyToMany.has(field.relationName)
130+
);
128131
}

0 commit comments

Comments
 (0)