@@ -12,6 +12,7 @@ import {
12
12
NestedWriteVisitorContext ,
13
13
enumerate ,
14
14
getIdFields ,
15
+ getModelInfo ,
15
16
requireField ,
16
17
resolveField ,
17
18
type FieldInfo ,
@@ -435,17 +436,16 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
435
436
436
437
args = this . policyUtils . safeClone ( args ) ;
437
438
438
- // go through create items, statically check input to determine if post-create
439
- // check is needed, and also validate zod schema
440
- const needPostCreateCheck = this . validateCreateInput ( args ) ;
439
+ // `createManyAndReturn` may need to be converted to regular `create`s
440
+ const shouldConvertToCreate = this . preprocessCreateManyPayload ( args ) ;
441
441
442
- if ( ! needPostCreateCheck ) {
443
- // direct create
442
+ if ( ! shouldConvertToCreate ) {
443
+ // direct `createMany`
444
444
return this . modelClient . createMany ( args ) ;
445
445
} else {
446
446
// create entities in a transaction with post-create checks
447
447
return this . queryUtils . transaction ( this . prisma , async ( tx ) => {
448
- const { result, postWriteChecks } = await this . doCreateMany ( this . model , args , tx ) ;
448
+ const { result, postWriteChecks } = await this . doCreateMany ( this . model , args , tx , 'createMany' ) ;
449
449
// post-create check
450
450
await this . runPostWriteChecks ( postWriteChecks , tx ) ;
451
451
return { count : result . length } ;
@@ -472,14 +472,13 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
472
472
const origArgs = args ;
473
473
args = this . policyUtils . safeClone ( args ) ;
474
474
475
- // go through create items, statically check input to determine if post-create
476
- // check is needed, and also validate zod schema
477
- const needPostCreateCheck = this . validateCreateInput ( args ) ;
475
+ // `createManyAndReturn` may need to be converted to regular `create`s
476
+ const shouldConvertToCreate = this . preprocessCreateManyPayload ( args ) ;
478
477
479
478
let result : { result : unknown ; error ?: Error } [ ] ;
480
479
481
- if ( ! needPostCreateCheck ) {
482
- // direct create
480
+ if ( ! shouldConvertToCreate ) {
481
+ // direct `createManyAndReturn`
483
482
const created = await this . modelClient . createManyAndReturn ( args ) ;
484
483
485
484
// process read-back
@@ -489,7 +488,13 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
489
488
} else {
490
489
// create entities in a transaction with post-create checks
491
490
result = await this . queryUtils . transaction ( this . prisma , async ( tx ) => {
492
- const { result : created , postWriteChecks } = await this . doCreateMany ( this . model , args , tx ) ;
491
+ const { result : created , postWriteChecks } = await this . doCreateMany (
492
+ this . model ,
493
+ args ,
494
+ tx ,
495
+ 'createManyAndReturn'
496
+ ) ;
497
+
493
498
// post-create check
494
499
await this . runPostWriteChecks ( postWriteChecks , tx ) ;
495
500
@@ -510,6 +515,46 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
510
515
} ) ;
511
516
}
512
517
518
+ /**
519
+ * Preprocess the payload of `createMany` and `createManyAndReturn` and update in place if needed.
520
+ * @returns `true` if the operation should be converted to regular `create`s; false otherwise.
521
+ */
522
+ private preprocessCreateManyPayload ( args : { data : any ; select ?: any ; skipDuplicates ?: boolean } ) {
523
+ if ( ! args ) {
524
+ return false ;
525
+ }
526
+
527
+ // if post-create check is needed
528
+ const needPostCreateCheck = this . validateCreateInput ( args ) ;
529
+
530
+ // if the payload has any relation fields. Note that other enhancements (`withDefaultInAuth` for now)
531
+ // can introduce relation fields into the payload
532
+ let hasRelationFields = false ;
533
+ if ( args . data ) {
534
+ hasRelationFields = this . hasRelationFieldsInPayload ( this . model , args . data ) ;
535
+ }
536
+
537
+ return needPostCreateCheck || hasRelationFields ;
538
+ }
539
+
540
+ private hasRelationFieldsInPayload ( model : string , payload : any ) {
541
+ const modelInfo = getModelInfo ( this . modelMeta , model ) ;
542
+ if ( ! modelInfo ) {
543
+ return false ;
544
+ }
545
+
546
+ for ( const item of enumerate ( payload ) ) {
547
+ for ( const field of Object . keys ( item ) ) {
548
+ const fieldInfo = resolveField ( this . modelMeta , model , field ) ;
549
+ if ( fieldInfo ?. isDataModel ) {
550
+ return true ;
551
+ }
552
+ }
553
+ }
554
+
555
+ return false ;
556
+ }
557
+
513
558
private validateCreateInput ( args : { data : any ; skipDuplicates ?: boolean | undefined } ) {
514
559
let needPostCreateCheck = false ;
515
560
for ( const item of enumerate ( args . data ) ) {
@@ -537,7 +582,12 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
537
582
return needPostCreateCheck ;
538
583
}
539
584
540
- private async doCreateMany ( model : string , args : { data : any ; skipDuplicates ?: boolean } , db : CrudContract ) {
585
+ private async doCreateMany (
586
+ model : string ,
587
+ args : { data : any ; skipDuplicates ?: boolean } ,
588
+ db : CrudContract ,
589
+ action : 'createMany' | 'createManyAndReturn'
590
+ ) {
541
591
// We can't call the native "createMany" because we can't get back what was created
542
592
// for post-create checks. Instead, do a "create" for each item and collect the results.
543
593
@@ -553,7 +603,7 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
553
603
}
554
604
555
605
if ( this . shouldLogQuery ) {
556
- this . logger . info ( `[policy] \`create\` for \`createMany \` ${ model } : ${ formatObject ( item ) } ` ) ;
606
+ this . logger . info ( `[policy] \`create\` for \`${ action } \` ${ model } : ${ formatObject ( item ) } ` ) ;
557
607
}
558
608
return await db [ model ] . create ( { select : this . policyUtils . makeIdSelection ( model ) , data : item } ) ;
559
609
} )
0 commit comments