diff --git a/packages/plugins/openapi/src/rpc-generator.ts b/packages/plugins/openapi/src/rpc-generator.ts index 86c7197ad..cd8ef133e 100644 --- a/packages/plugins/openapi/src/rpc-generator.ts +++ b/packages/plugins/openapi/src/rpc-generator.ts @@ -175,7 +175,15 @@ export class RPCOpenAPIGenerator extends OpenAPIGeneratorBase { type: 'object', required: ['data'], properties: { - data: this.ref(`${modelName}CreateManyInput`), + data: this.oneOf( + this.ref(`${modelName}CreateManyInput`), + this.array(this.ref(`${modelName}CreateManyInput`)) + ), + skipDuplicates: { + type: 'boolean', + description: + 'Do not insert records with unique fields or ID fields that already exist.', + }, meta: this.ref('_Meta'), }, }, diff --git a/packages/plugins/openapi/tests/baseline/rpc-3.0.0.baseline.yaml b/packages/plugins/openapi/tests/baseline/rpc-3.0.0.baseline.yaml index 3a82c7f0d..68885daed 100644 --- a/packages/plugins/openapi/tests/baseline/rpc-3.0.0.baseline.yaml +++ b/packages/plugins/openapi/tests/baseline/rpc-3.0.0.baseline.yaml @@ -3275,7 +3275,15 @@ components: - data properties: data: - $ref: '#/components/schemas/UserCreateManyInput' + oneOf: + - $ref: '#/components/schemas/UserCreateManyInput' + - type: array + items: + $ref: '#/components/schemas/UserCreateManyInput' + skipDuplicates: + type: boolean + description: Do not insert records with unique fields or ID fields that already + exist. meta: $ref: '#/components/schemas/_Meta' UserFindUniqueArgs: @@ -3455,7 +3463,15 @@ components: - data properties: data: - $ref: '#/components/schemas/ProfileCreateManyInput' + oneOf: + - $ref: '#/components/schemas/ProfileCreateManyInput' + - type: array + items: + $ref: '#/components/schemas/ProfileCreateManyInput' + skipDuplicates: + type: boolean + description: Do not insert records with unique fields or ID fields that already + exist. meta: $ref: '#/components/schemas/_Meta' ProfileFindUniqueArgs: @@ -3635,7 +3651,15 @@ components: - data properties: data: - $ref: '#/components/schemas/Post_ItemCreateManyInput' + oneOf: + - $ref: '#/components/schemas/Post_ItemCreateManyInput' + - type: array + items: + $ref: '#/components/schemas/Post_ItemCreateManyInput' + skipDuplicates: + type: boolean + description: Do not insert records with unique fields or ID fields that already + exist. meta: $ref: '#/components/schemas/_Meta' Post_ItemFindUniqueArgs: diff --git a/packages/plugins/openapi/tests/baseline/rpc-3.1.0.baseline.yaml b/packages/plugins/openapi/tests/baseline/rpc-3.1.0.baseline.yaml index dbf05a4dc..0f36abca2 100644 --- a/packages/plugins/openapi/tests/baseline/rpc-3.1.0.baseline.yaml +++ b/packages/plugins/openapi/tests/baseline/rpc-3.1.0.baseline.yaml @@ -3339,7 +3339,15 @@ components: - data properties: data: - $ref: '#/components/schemas/UserCreateManyInput' + oneOf: + - $ref: '#/components/schemas/UserCreateManyInput' + - type: array + items: + $ref: '#/components/schemas/UserCreateManyInput' + skipDuplicates: + type: boolean + description: Do not insert records with unique fields or ID fields that already + exist. meta: $ref: '#/components/schemas/_Meta' UserFindUniqueArgs: @@ -3519,7 +3527,15 @@ components: - data properties: data: - $ref: '#/components/schemas/ProfileCreateManyInput' + oneOf: + - $ref: '#/components/schemas/ProfileCreateManyInput' + - type: array + items: + $ref: '#/components/schemas/ProfileCreateManyInput' + skipDuplicates: + type: boolean + description: Do not insert records with unique fields or ID fields that already + exist. meta: $ref: '#/components/schemas/_Meta' ProfileFindUniqueArgs: @@ -3699,7 +3715,15 @@ components: - data properties: data: - $ref: '#/components/schemas/Post_ItemCreateManyInput' + oneOf: + - $ref: '#/components/schemas/Post_ItemCreateManyInput' + - type: array + items: + $ref: '#/components/schemas/Post_ItemCreateManyInput' + skipDuplicates: + type: boolean + description: Do not insert records with unique fields or ID fields that already + exist. meta: $ref: '#/components/schemas/_Meta' Post_ItemFindUniqueArgs: diff --git a/packages/plugins/openapi/tests/baseline/rpc-type-coverage-3.0.0.baseline.yaml b/packages/plugins/openapi/tests/baseline/rpc-type-coverage-3.0.0.baseline.yaml index f9da29092..495ebd42b 100644 --- a/packages/plugins/openapi/tests/baseline/rpc-type-coverage-3.0.0.baseline.yaml +++ b/packages/plugins/openapi/tests/baseline/rpc-type-coverage-3.0.0.baseline.yaml @@ -1922,7 +1922,15 @@ components: - data properties: data: - $ref: '#/components/schemas/FooCreateManyInput' + oneOf: + - $ref: '#/components/schemas/FooCreateManyInput' + - type: array + items: + $ref: '#/components/schemas/FooCreateManyInput' + skipDuplicates: + type: boolean + description: Do not insert records with unique fields or ID fields that already + exist. meta: $ref: '#/components/schemas/_Meta' FooFindUniqueArgs: diff --git a/packages/plugins/openapi/tests/baseline/rpc-type-coverage-3.1.0.baseline.yaml b/packages/plugins/openapi/tests/baseline/rpc-type-coverage-3.1.0.baseline.yaml index fe503bc4e..c9327b7f2 100644 --- a/packages/plugins/openapi/tests/baseline/rpc-type-coverage-3.1.0.baseline.yaml +++ b/packages/plugins/openapi/tests/baseline/rpc-type-coverage-3.1.0.baseline.yaml @@ -1964,7 +1964,15 @@ components: - data properties: data: - $ref: '#/components/schemas/FooCreateManyInput' + oneOf: + - $ref: '#/components/schemas/FooCreateManyInput' + - type: array + items: + $ref: '#/components/schemas/FooCreateManyInput' + skipDuplicates: + type: boolean + description: Do not insert records with unique fields or ID fields that already + exist. meta: $ref: '#/components/schemas/_Meta' FooFindUniqueArgs: diff --git a/packages/plugins/trpc/tests/projects/t3-trpc-v10/src/server/api/routers/generated/routers/Post.router.ts b/packages/plugins/trpc/tests/projects/t3-trpc-v10/src/server/api/routers/generated/routers/Post.router.ts index 4d2ade66f..fbc73cf06 100644 --- a/packages/plugins/trpc/tests/projects/t3-trpc-v10/src/server/api/routers/generated/routers/Post.router.ts +++ b/packages/plugins/trpc/tests/projects/t3-trpc-v10/src/server/api/routers/generated/routers/Post.router.ts @@ -12,8 +12,6 @@ export default function createRouter(router: RouterFa aggregate: procedure.input($Schema.PostInputSchema.aggregate).query(({ ctx, input }) => checkRead(db(ctx).post.aggregate(input as any))), - createMany: procedure.input($Schema.PostInputSchema.createMany).mutation(async ({ ctx, input }) => checkMutate(db(ctx).post.createMany(input as any))), - create: procedure.input($Schema.PostInputSchema.create).mutation(async ({ ctx, input }) => checkMutate(db(ctx).post.create(input as any))), deleteMany: procedure.input($Schema.PostInputSchema.deleteMany).mutation(async ({ ctx, input }) => checkMutate(db(ctx).post.deleteMany(input as any))), @@ -62,20 +60,6 @@ export interface ClientType >; - }; - createMany: { - - useMutation: (opts?: UseTRPCMutationOptions< - Prisma.PostCreateManyArgs, - TRPCClientErrorLike, - Prisma.BatchPayload, - Context - >,) => - Omit, Prisma.SelectSubset, Context>, 'mutateAsync'> & { - mutateAsync: - (variables: T, opts?: UseTRPCMutationOptions, Prisma.BatchPayload, Context>) => Promise - }; - }; create: { diff --git a/packages/plugins/trpc/tests/projects/t3-trpc-v10/src/server/api/routers/generated/routers/User.router.ts b/packages/plugins/trpc/tests/projects/t3-trpc-v10/src/server/api/routers/generated/routers/User.router.ts index 00a591ca1..c4bdb89de 100644 --- a/packages/plugins/trpc/tests/projects/t3-trpc-v10/src/server/api/routers/generated/routers/User.router.ts +++ b/packages/plugins/trpc/tests/projects/t3-trpc-v10/src/server/api/routers/generated/routers/User.router.ts @@ -12,8 +12,6 @@ export default function createRouter(router: RouterFa aggregate: procedure.input($Schema.UserInputSchema.aggregate).query(({ ctx, input }) => checkRead(db(ctx).user.aggregate(input as any))), - createMany: procedure.input($Schema.UserInputSchema.createMany).mutation(async ({ ctx, input }) => checkMutate(db(ctx).user.createMany(input as any))), - create: procedure.input($Schema.UserInputSchema.create).mutation(async ({ ctx, input }) => checkMutate(db(ctx).user.create(input as any))), deleteMany: procedure.input($Schema.UserInputSchema.deleteMany).mutation(async ({ ctx, input }) => checkMutate(db(ctx).user.deleteMany(input as any))), @@ -62,20 +60,6 @@ export interface ClientType >; - }; - createMany: { - - useMutation: (opts?: UseTRPCMutationOptions< - Prisma.UserCreateManyArgs, - TRPCClientErrorLike, - Prisma.BatchPayload, - Context - >,) => - Omit, Prisma.SelectSubset, Context>, 'mutateAsync'> & { - mutateAsync: - (variables: T, opts?: UseTRPCMutationOptions, Prisma.BatchPayload, Context>) => Promise - }; - }; create: { diff --git a/packages/schema/src/plugins/zod/transformer.ts b/packages/schema/src/plugins/zod/transformer.ts index 7ad736097..5b62687ff 100644 --- a/packages/schema/src/plugins/zod/transformer.ts +++ b/packages/schema/src/plugins/zod/transformer.ts @@ -490,7 +490,7 @@ export const ${this.name}ObjectSchema: SchemaType = ${schema} as SchemaType;`; imports.push( `import { ${modelName}CreateManyInputObjectSchema } from '../objects/${modelName}CreateManyInput.schema'` ); - codeBody += `createMany: z.object({ data: z.union([${modelName}CreateManyInputObjectSchema, z.array(${modelName}CreateManyInputObjectSchema)]) }),`; + codeBody += `createMany: z.object({ data: z.union([${modelName}CreateManyInputObjectSchema, z.array(${modelName}CreateManyInputObjectSchema)]), skipDuplicates: z.boolean().optional() }),`; operations.push(['createMany', origModelName]); } diff --git a/tests/integration/tests/regression/issue-1241.test.ts b/tests/integration/tests/regression/issue-1241.test.ts new file mode 100644 index 000000000..3a53f567c --- /dev/null +++ b/tests/integration/tests/regression/issue-1241.test.ts @@ -0,0 +1,88 @@ +import { loadSchema } from '@zenstackhq/testtools'; +import { randomBytes } from 'crypto'; + +describe('issue 1241', () => { + it('regression', async () => { + const { enhance, prisma } = await loadSchema( + ` + model User { + id String @id @default(uuid()) + todos Todo[] + + @@auth + @@allow('all', true) + } + + model Todo { + id String @id @default(uuid()) + + user_id String + user User @relation(fields: [user_id], references: [id]) + + images File[] @relation("todo_images") + documents File[] @relation("todo_documents") + + @@allow('all', true) + } + + model File { + id String @id @default(uuid()) + s3_key String @unique + label String + + todo_image_id String? + todo_image Todo? @relation("todo_images", fields: [todo_image_id], references: [id]) + + todo_document_id String? + todo_document Todo? @relation("todo_documents", fields: [todo_document_id], references: [id]) + + @@allow('all', true) + } + `, + { logPrismaQuery: true } + ); + + const user = await prisma.user.create({ + data: {}, + }); + await prisma.todo.create({ + data: { + user_id: user.id, + + images: { + create: new Array(3).fill(null).map((_, i) => ({ + s3_key: randomBytes(8).toString('hex'), + label: `img-label-${i + 1}`, + })), + }, + + documents: { + create: new Array(3).fill(null).map((_, i) => ({ + s3_key: randomBytes(8).toString('hex'), + label: `doc-label-${i + 1}`, + })), + }, + }, + }); + + const db = enhance(); + + const todo = await db.todo.findFirst({ where: {}, include: { documents: true } }); + await expect( + db.todo.update({ + where: { id: todo.id }, + data: { + documents: { + update: todo.documents.map((doc: any) => { + return { + where: { s3_key: doc.s3_key }, + data: { label: 'updated' }, + }; + }), + }, + }, + include: { documents: true }, + }) + ).toResolveTruthy(); + }); +});