Skip to content

Commit 5355474

Browse files
authored
fix(orm): typing issue with relation fields with fk having default value (#371)
1 parent e7f191c commit 5355474

File tree

7 files changed

+268
-1
lines changed

7 files changed

+268
-1
lines changed

packages/schema/src/schema.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export type RelationInfo = {
5252
name?: string;
5353
fields?: string[];
5454
references?: string[];
55+
hasDefault?: boolean;
5556
opposite?: string;
5657
onDelete?: CascadeAction;
5758
onUpdate?: CascadeAction;
@@ -253,7 +254,9 @@ export type FieldHasDefault<
253254
? true
254255
: GetModelField<Schema, Model, Field>['updatedAt'] extends true
255256
? true
256-
: false;
257+
: GetModelField<Schema, Model, Field>['relation'] extends { hasDefault: true }
258+
? true
259+
: false;
257260

258261
export type FieldIsRelationArray<
259262
Schema extends SchemaDef,

packages/sdk/src/ts-schema-generator.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,12 +707,16 @@ export class TsSchemaGenerator {
707707
}
708708

709709
const relation = getAttribute(field, '@relation');
710+
const fkFields: string[] = [];
710711
if (relation) {
711712
for (const arg of relation.args) {
712713
const param = arg.$resolvedParam.name;
713714
if (param === 'fields' || param === 'references') {
714715
const fieldNames = this.getReferenceNames(arg.value);
715716
if (fieldNames) {
717+
if (param === 'fields') {
718+
fkFields.push(...fieldNames);
719+
}
716720
relationFields.push(
717721
ts.factory.createPropertyAssignment(
718722
param,
@@ -733,6 +737,17 @@ export class TsSchemaGenerator {
733737
}
734738
}
735739

740+
// check if all fk fields have default values
741+
if (fkFields.length > 0) {
742+
const allHaveDefault = fkFields.every((fieldName) => {
743+
const fieldDef = field.$container.fields.find((f) => f.name === fieldName);
744+
return fieldDef && hasAttribute(fieldDef, '@default');
745+
});
746+
if (allHaveDefault) {
747+
relationFields.push(ts.factory.createPropertyAssignment('hasDefault', ts.factory.createTrue()));
748+
}
749+
}
750+
736751
return ts.factory.createObjectLiteralExpression(relationFields);
737752
}
738753

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { createTestClient } from '@zenstackhq/testtools';
2+
import { describe, expect, it } from 'vitest';
3+
import { schema } from '../schemas/default-auth/schema';
4+
5+
describe('Auth as default value tests', () => {
6+
it('should create without requiring the default auth field', async () => {
7+
const db = await createTestClient(schema);
8+
const user1 = await db.user.create({ data: {} });
9+
await expect(db.$setAuth(user1).profile.create({ data: { bio: 'My bio' } })).resolves.toMatchObject({
10+
userId: user1.id,
11+
});
12+
13+
const address = await db.address.create({ data: { city: 'Seattle ' } });
14+
const user2 = await db.user.create({ data: {} });
15+
await expect(
16+
db.$setAuth(user2).profile.create({ data: { bio: 'My bio', address: { connect: { id: address.id } } } }),
17+
).resolves.toMatchObject({
18+
userId: user2.id,
19+
});
20+
});
21+
});
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//////////////////////////////////////////////////////////////////////////////////////////////
2+
// DO NOT MODIFY THIS FILE //
3+
// This file is automatically generated by ZenStack CLI and should not be manually updated. //
4+
//////////////////////////////////////////////////////////////////////////////////////////////
5+
6+
/* eslint-disable */
7+
8+
import { type SchemaType as $Schema } from "./schema";
9+
import type { FindManyArgs as $FindManyArgs, FindUniqueArgs as $FindUniqueArgs, FindFirstArgs as $FindFirstArgs, CreateArgs as $CreateArgs, CreateManyArgs as $CreateManyArgs, CreateManyAndReturnArgs as $CreateManyAndReturnArgs, UpdateArgs as $UpdateArgs, UpdateManyArgs as $UpdateManyArgs, UpdateManyAndReturnArgs as $UpdateManyAndReturnArgs, UpsertArgs as $UpsertArgs, DeleteArgs as $DeleteArgs, DeleteManyArgs as $DeleteManyArgs, CountArgs as $CountArgs, AggregateArgs as $AggregateArgs, GroupByArgs as $GroupByArgs, WhereInput as $WhereInput, SelectInput as $SelectInput, IncludeInput as $IncludeInput, OmitInput as $OmitInput } from "@zenstackhq/orm";
10+
import type { SimplifiedModelResult as $SimplifiedModelResult, SelectIncludeOmit as $SelectIncludeOmit } from "@zenstackhq/orm";
11+
export type UserFindManyArgs = $FindManyArgs<$Schema, "User">;
12+
export type UserFindUniqueArgs = $FindUniqueArgs<$Schema, "User">;
13+
export type UserFindFirstArgs = $FindFirstArgs<$Schema, "User">;
14+
export type UserCreateArgs = $CreateArgs<$Schema, "User">;
15+
export type UserCreateManyArgs = $CreateManyArgs<$Schema, "User">;
16+
export type UserCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "User">;
17+
export type UserUpdateArgs = $UpdateArgs<$Schema, "User">;
18+
export type UserUpdateManyArgs = $UpdateManyArgs<$Schema, "User">;
19+
export type UserUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "User">;
20+
export type UserUpsertArgs = $UpsertArgs<$Schema, "User">;
21+
export type UserDeleteArgs = $DeleteArgs<$Schema, "User">;
22+
export type UserDeleteManyArgs = $DeleteManyArgs<$Schema, "User">;
23+
export type UserCountArgs = $CountArgs<$Schema, "User">;
24+
export type UserAggregateArgs = $AggregateArgs<$Schema, "User">;
25+
export type UserGroupByArgs = $GroupByArgs<$Schema, "User">;
26+
export type UserWhereInput = $WhereInput<$Schema, "User">;
27+
export type UserSelect = $SelectInput<$Schema, "User">;
28+
export type UserInclude = $IncludeInput<$Schema, "User">;
29+
export type UserOmit = $OmitInput<$Schema, "User">;
30+
export type UserGetPayload<Args extends $SelectIncludeOmit<$Schema, "User", true>> = $SimplifiedModelResult<$Schema, "User", Args>;
31+
export type ProfileFindManyArgs = $FindManyArgs<$Schema, "Profile">;
32+
export type ProfileFindUniqueArgs = $FindUniqueArgs<$Schema, "Profile">;
33+
export type ProfileFindFirstArgs = $FindFirstArgs<$Schema, "Profile">;
34+
export type ProfileCreateArgs = $CreateArgs<$Schema, "Profile">;
35+
export type ProfileCreateManyArgs = $CreateManyArgs<$Schema, "Profile">;
36+
export type ProfileCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Profile">;
37+
export type ProfileUpdateArgs = $UpdateArgs<$Schema, "Profile">;
38+
export type ProfileUpdateManyArgs = $UpdateManyArgs<$Schema, "Profile">;
39+
export type ProfileUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Profile">;
40+
export type ProfileUpsertArgs = $UpsertArgs<$Schema, "Profile">;
41+
export type ProfileDeleteArgs = $DeleteArgs<$Schema, "Profile">;
42+
export type ProfileDeleteManyArgs = $DeleteManyArgs<$Schema, "Profile">;
43+
export type ProfileCountArgs = $CountArgs<$Schema, "Profile">;
44+
export type ProfileAggregateArgs = $AggregateArgs<$Schema, "Profile">;
45+
export type ProfileGroupByArgs = $GroupByArgs<$Schema, "Profile">;
46+
export type ProfileWhereInput = $WhereInput<$Schema, "Profile">;
47+
export type ProfileSelect = $SelectInput<$Schema, "Profile">;
48+
export type ProfileInclude = $IncludeInput<$Schema, "Profile">;
49+
export type ProfileOmit = $OmitInput<$Schema, "Profile">;
50+
export type ProfileGetPayload<Args extends $SelectIncludeOmit<$Schema, "Profile", true>> = $SimplifiedModelResult<$Schema, "Profile", Args>;
51+
export type AddressFindManyArgs = $FindManyArgs<$Schema, "Address">;
52+
export type AddressFindUniqueArgs = $FindUniqueArgs<$Schema, "Address">;
53+
export type AddressFindFirstArgs = $FindFirstArgs<$Schema, "Address">;
54+
export type AddressCreateArgs = $CreateArgs<$Schema, "Address">;
55+
export type AddressCreateManyArgs = $CreateManyArgs<$Schema, "Address">;
56+
export type AddressCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Address">;
57+
export type AddressUpdateArgs = $UpdateArgs<$Schema, "Address">;
58+
export type AddressUpdateManyArgs = $UpdateManyArgs<$Schema, "Address">;
59+
export type AddressUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Address">;
60+
export type AddressUpsertArgs = $UpsertArgs<$Schema, "Address">;
61+
export type AddressDeleteArgs = $DeleteArgs<$Schema, "Address">;
62+
export type AddressDeleteManyArgs = $DeleteManyArgs<$Schema, "Address">;
63+
export type AddressCountArgs = $CountArgs<$Schema, "Address">;
64+
export type AddressAggregateArgs = $AggregateArgs<$Schema, "Address">;
65+
export type AddressGroupByArgs = $GroupByArgs<$Schema, "Address">;
66+
export type AddressWhereInput = $WhereInput<$Schema, "Address">;
67+
export type AddressSelect = $SelectInput<$Schema, "Address">;
68+
export type AddressInclude = $IncludeInput<$Schema, "Address">;
69+
export type AddressOmit = $OmitInput<$Schema, "Address">;
70+
export type AddressGetPayload<Args extends $SelectIncludeOmit<$Schema, "Address", true>> = $SimplifiedModelResult<$Schema, "Address", Args>;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//////////////////////////////////////////////////////////////////////////////////////////////
2+
// DO NOT MODIFY THIS FILE //
3+
// This file is automatically generated by ZenStack CLI and should not be manually updated. //
4+
//////////////////////////////////////////////////////////////////////////////////////////////
5+
6+
/* eslint-disable */
7+
8+
import { type SchemaType as $Schema } from "./schema";
9+
import { type ModelResult as $ModelResult } from "@zenstackhq/orm";
10+
export type User = $ModelResult<$Schema, "User">;
11+
export type Profile = $ModelResult<$Schema, "Profile">;
12+
export type Address = $ModelResult<$Schema, "Address">;
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//////////////////////////////////////////////////////////////////////////////////////////////
2+
// DO NOT MODIFY THIS FILE //
3+
// This file is automatically generated by ZenStack CLI and should not be manually updated. //
4+
//////////////////////////////////////////////////////////////////////////////////////////////
5+
6+
/* eslint-disable */
7+
8+
import { type SchemaDef, ExpressionUtils } from "@zenstackhq/orm/schema";
9+
export const schema = {
10+
provider: {
11+
type: "sqlite"
12+
},
13+
models: {
14+
User: {
15+
name: "User",
16+
fields: {
17+
id: {
18+
name: "id",
19+
type: "Int",
20+
id: true,
21+
attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }],
22+
default: ExpressionUtils.call("autoincrement")
23+
},
24+
profile: {
25+
name: "profile",
26+
type: "Profile",
27+
optional: true,
28+
relation: { opposite: "user" }
29+
}
30+
},
31+
idFields: ["id"],
32+
uniqueFields: {
33+
id: { type: "Int" }
34+
}
35+
},
36+
Profile: {
37+
name: "Profile",
38+
fields: {
39+
id: {
40+
name: "id",
41+
type: "Int",
42+
id: true,
43+
attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }],
44+
default: ExpressionUtils.call("autoincrement")
45+
},
46+
bio: {
47+
name: "bio",
48+
type: "String",
49+
optional: true
50+
},
51+
userId: {
52+
name: "userId",
53+
type: "Int",
54+
unique: true,
55+
attributes: [{ name: "@unique" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.member(ExpressionUtils.call("auth"), ["id"]) }] }],
56+
default: ExpressionUtils.member(ExpressionUtils.call("auth"), ["id"]),
57+
foreignKeyFor: [
58+
"user"
59+
]
60+
},
61+
user: {
62+
name: "user",
63+
type: "User",
64+
attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array([ExpressionUtils.field("userId")]) }, { name: "references", value: ExpressionUtils.array([ExpressionUtils.field("id")]) }] }],
65+
relation: { opposite: "profile", fields: ["userId"], references: ["id"], hasDefault: true }
66+
},
67+
address: {
68+
name: "address",
69+
type: "Address",
70+
optional: true,
71+
attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array([ExpressionUtils.field("addressId")]) }, { name: "references", value: ExpressionUtils.array([ExpressionUtils.field("id")]) }] }],
72+
relation: { opposite: "profile", fields: ["addressId"], references: ["id"] }
73+
},
74+
addressId: {
75+
name: "addressId",
76+
type: "Int",
77+
unique: true,
78+
optional: true,
79+
attributes: [{ name: "@unique" }],
80+
foreignKeyFor: [
81+
"address"
82+
]
83+
}
84+
},
85+
idFields: ["id"],
86+
uniqueFields: {
87+
id: { type: "Int" },
88+
userId: { type: "Int" },
89+
addressId: { type: "Int" }
90+
}
91+
},
92+
Address: {
93+
name: "Address",
94+
fields: {
95+
id: {
96+
name: "id",
97+
type: "Int",
98+
id: true,
99+
attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }],
100+
default: ExpressionUtils.call("autoincrement")
101+
},
102+
city: {
103+
name: "city",
104+
type: "String"
105+
},
106+
profile: {
107+
name: "profile",
108+
type: "Profile",
109+
optional: true,
110+
relation: { opposite: "address" }
111+
}
112+
},
113+
idFields: ["id"],
114+
uniqueFields: {
115+
id: { type: "Int" }
116+
}
117+
}
118+
},
119+
authType: "User",
120+
plugins: {}
121+
} as const satisfies SchemaDef;
122+
export type SchemaType = typeof schema;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
datasource db {
2+
provider = "sqlite"
3+
url = "file:./dev.db"
4+
}
5+
6+
model User {
7+
id Int @id @default(autoincrement())
8+
profile Profile?
9+
}
10+
11+
model Profile {
12+
id Int @id @default(autoincrement())
13+
bio String?
14+
userId Int @unique @default(auth().id)
15+
user User @relation(fields: [userId], references: [id])
16+
address Address? @relation(fields: [addressId], references: [id])
17+
addressId Int? @unique
18+
}
19+
20+
model Address {
21+
id Int @id @default(autoincrement())
22+
city String
23+
profile Profile?
24+
}

0 commit comments

Comments
 (0)