Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions packages/runtime/src/client/crud-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -528,12 +528,9 @@ export type CreateArgs<Schema extends SchemaDef, Model extends GetModels<Schema>
omit?: OmitFields<Schema, Model>;
};

export type CreateManyArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = CreateManyPayload<
Schema,
Model
>;
export type CreateManyArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = CreateManyInput<Schema, Model>;

export type CreateManyAndReturnArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = CreateManyPayload<
export type CreateManyAndReturnArgs<Schema extends SchemaDef, Model extends GetModels<Schema>> = CreateManyInput<
Schema,
Model
> & {
Expand Down Expand Up @@ -597,15 +594,28 @@ type CreateRelationPayload<Schema extends SchemaDef, Model extends GetModels<Sch
}
>;

type CreateWithFKInput<Schema extends SchemaDef, Model extends GetModels<Schema>> = CreateScalarPayload<Schema, Model> &
CreateFKPayload<Schema, Model>;
type CreateWithFKInput<Schema extends SchemaDef, Model extends GetModels<Schema>> =
// scalar fields
CreateScalarPayload<Schema, Model> &
// fk fields
CreateFKPayload<Schema, Model> &
// non-owned relations
CreateWithNonOwnedRelationPayload<Schema, Model>;

type CreateWithRelationInput<Schema extends SchemaDef, Model extends GetModels<Schema>> = CreateScalarPayload<
Schema,
Model
> &
CreateRelationPayload<Schema, Model>;

type CreateWithNonOwnedRelationPayload<Schema extends SchemaDef, Model extends GetModels<Schema>> = OptionalWrap<
Schema,
Model,
{
[Key in NonOwnedRelationFields<Schema, Model>]: CreateRelationFieldPayload<Schema, Model, Key>;
}
>;

type ConnectOrCreatePayload<
Schema extends SchemaDef,
Model extends GetModels<Schema>,
Expand All @@ -615,7 +625,7 @@ type ConnectOrCreatePayload<
create: CreateInput<Schema, Model, Without>;
};

export type CreateManyPayload<
export type CreateManyInput<
Schema extends SchemaDef,
Model extends GetModels<Schema>,
Without extends string = never,
Expand Down Expand Up @@ -643,7 +653,7 @@ type NestedCreateManyInput<
Schema extends SchemaDef,
Model extends GetModels<Schema>,
Field extends RelationFields<Schema, Model>,
> = CreateManyPayload<Schema, RelationFieldType<Schema, Model, Field>, OppositeRelationAndFK<Schema, Model, Field>>;
> = CreateManyInput<Schema, RelationFieldType<Schema, Model, Field>, OppositeRelationAndFK<Schema, Model, Field>>;

//#endregion

Expand Down Expand Up @@ -1078,3 +1088,13 @@ type NestedDeleteManyInput<
> = OrArray<WhereInput<Schema, RelationFieldType<Schema, Model, Field>, true>>;

// #endregion

// #region Utilities

type NonOwnedRelationFields<Schema extends SchemaDef, Model extends GetModels<Schema>> = keyof {
[Key in RelationFields<Schema, Model> as GetField<Schema, Model, Key>['relation'] extends { references: unknown[] }
? never
: Key]: Key;
};

// #endregion
25 changes: 15 additions & 10 deletions packages/runtime/src/client/crud/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -650,8 +650,8 @@ export class InputValidator<Schema extends SchemaDef> {
withoutFields: string[] = [],
withoutRelationFields = false,
) {
const regularAndFkFields: any = {};
const regularAndRelationFields: any = {};
const uncheckedVariantFields: Record<string, ZodType> = {};
const checkedVariantFields: Record<string, ZodType> = {};
const modelDef = requireModel(this.schema, model);
const hasRelation =
!withoutRelationFields &&
Expand Down Expand Up @@ -705,7 +705,11 @@ export class InputValidator<Schema extends SchemaDef> {
if (fieldDef.optional && !fieldDef.array) {
fieldSchema = fieldSchema.nullable();
}
regularAndRelationFields[field] = fieldSchema;
checkedVariantFields[field] = fieldSchema;
if (fieldDef.array || !fieldDef.relation.references) {
// non-owned relation
uncheckedVariantFields[field] = fieldSchema;
}
} else {
let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type);

Expand All @@ -728,21 +732,22 @@ export class InputValidator<Schema extends SchemaDef> {
fieldSchema = fieldSchema.nullable();
}

regularAndFkFields[field] = fieldSchema;
uncheckedVariantFields[field] = fieldSchema;
if (!fieldDef.foreignKeyFor) {
regularAndRelationFields[field] = fieldSchema;
// non-fk field
checkedVariantFields[field] = fieldSchema;
}
}
});

if (!hasRelation) {
return this.orArray(z.object(regularAndFkFields).strict(), canBeArray);
return this.orArray(z.object(uncheckedVariantFields).strict(), canBeArray);
} else {
return z.union([
z.object(regularAndFkFields).strict(),
z.object(regularAndRelationFields).strict(),
...(canBeArray ? [z.array(z.object(regularAndFkFields).strict())] : []),
...(canBeArray ? [z.array(z.object(regularAndRelationFields).strict())] : []),
z.object(uncheckedVariantFields).strict(),
z.object(checkedVariantFields).strict(),
...(canBeArray ? [z.array(z.object(uncheckedVariantFields).strict())] : []),
...(canBeArray ? [z.array(z.object(checkedVariantFields).strict())] : []),
]);
}
}
Expand Down
29 changes: 29 additions & 0 deletions packages/runtime/test/client-api/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,33 @@ describe.each(createClientSpecs(PG_DB_NAME))('Client create tests', ({ createCli
expect(u3.posts).toHaveLength(3);
expect(u3.posts.map((p) => p.title)).toEqual(expect.arrayContaining(['Post1', 'Post2', 'Post4']));
});

it('complies with Prisma checked/unchecked typing', async () => {
const user = await client.user.create({
data: { email: 'u1@test.com' },
});

// fk and owned-relation are mutually exclusive
client.post.create({
// @ts-expect-error
data: {
authorId: user.id,
title: 'title',
author: { connect: { id: user.id } },
},
});

// fk can work with non-owned relation
await expect(
client.post.create({
data: {
authorId: user.id,
title: 'title',
comments: {
create: { content: 'comment' },
},
},
}),
).toResolveTruthy();
});
});