@@ -308,6 +308,124 @@ impl<'program, 'sc, 'flag> ClientEdgesTransform<'program, 'sc, 'flag> {
308
308
} ;
309
309
}
310
310
311
+ fn verify_directives_or_push_errors ( & mut self , directives : & [ Directive ] ) {
312
+ let allowed_directive_names = [
313
+ * CLIENT_EDGE_WATERFALL_DIRECTIVE_NAME ,
314
+ * REQUIRED_DIRECTIVE_NAME ,
315
+ * CHILDREN_CAN_BUBBLE_METADATA_KEY ,
316
+ RequiredMetadataDirective :: directive_name ( ) ,
317
+ ] ;
318
+
319
+ let other_directives = directives
320
+ . iter ( )
321
+ . filter ( |directive| {
322
+ !allowed_directive_names
323
+ . iter ( )
324
+ . any ( |item| directive. name . item == * item)
325
+ } )
326
+ . collect :: < Vec < _ > > ( ) ;
327
+
328
+ for directive in other_directives {
329
+ self . errors . push ( Diagnostic :: error (
330
+ ValidationMessage :: ClientEdgeUnsupportedDirective {
331
+ directive_name : directive. name . item ,
332
+ } ,
333
+ directive. name . location ,
334
+ ) ) ;
335
+ }
336
+ }
337
+
338
+ fn get_edge_to_client_object_metadata_directive (
339
+ & mut self ,
340
+ field : & LinkedField ,
341
+ edge_to_type : Type ,
342
+ waterfall_directive : Option < & Directive > ,
343
+ resolver_directive : Option < & DirectiveValue > ,
344
+ ) -> Option < ClientEdgeMetadataDirective > {
345
+ // We assume edges to client objects will be resolved on the client
346
+ // and thus not incur a waterfall. This will change in the future
347
+ // for @live Resolvers that can trigger suspense.
348
+ if let Some ( directive) = waterfall_directive {
349
+ self . errors . push ( Diagnostic :: error_with_data (
350
+ ValidationMessageWithData :: RelayResolversUnexpectedWaterfall ,
351
+ directive. name . location ,
352
+ ) ) ;
353
+ }
354
+
355
+ match edge_to_type {
356
+ Type :: Interface ( interface_id) => {
357
+ let interface = self . program . schema . interface ( interface_id) ;
358
+ let implementing_objects =
359
+ interface. recursively_implementing_objects ( Arc :: as_ref ( & self . program . schema ) ) ;
360
+ if implementing_objects. is_empty ( ) {
361
+ self . errors . push ( Diagnostic :: error (
362
+ ValidationMessage :: RelayResolverClientInterfaceMustBeImplemented {
363
+ interface_name : interface. name . item ,
364
+ } ,
365
+ interface. name . location ,
366
+ ) ) ;
367
+ }
368
+ if !self
369
+ . relay_resolver_enable_interface_output_type
370
+ . is_fully_enabled ( )
371
+ && !has_output_type ( resolver_directive)
372
+ {
373
+ self . errors . push ( Diagnostic :: error (
374
+ ValidationMessage :: ClientEdgeToClientInterface ,
375
+ field. alias_or_name_location ( ) ,
376
+ ) ) ;
377
+ }
378
+ Some ( ClientEdgeMetadataDirective :: ClientObject {
379
+ type_name : None ,
380
+ unique_id : self . get_key ( ) ,
381
+ } )
382
+ }
383
+ Type :: Union ( _) => {
384
+ self . errors . push ( Diagnostic :: error (
385
+ ValidationMessage :: ClientEdgeToClientUnion ,
386
+ field. alias_or_name_location ( ) ,
387
+ ) ) ;
388
+ None
389
+ }
390
+ Type :: Object ( object_id) => Some ( ClientEdgeMetadataDirective :: ClientObject {
391
+ type_name : Some ( self . program . schema . object ( object_id) . name . item ) ,
392
+ unique_id : self . get_key ( ) ,
393
+ } ) ,
394
+ _ => {
395
+ panic ! ( "Expected a linked field to reference either an Object, Interface, or Union" )
396
+ }
397
+ }
398
+ }
399
+
400
+ fn get_edge_to_server_object_metadata_directive (
401
+ & mut self ,
402
+ field_type : & schema:: Field ,
403
+ field_location : Location ,
404
+ waterfall_directive : Option < & Directive > ,
405
+ selections : Vec < Selection > ,
406
+ ) -> ClientEdgeMetadataDirective {
407
+ // Client Edges to server objects must be annotated with @waterfall
408
+ if waterfall_directive. is_none ( ) {
409
+ self . errors . push ( Diagnostic :: error_with_data (
410
+ ValidationMessageWithData :: RelayResolversMissingWaterfall {
411
+ field_name : field_type. name . item ,
412
+ } ,
413
+ field_location,
414
+ ) ) ;
415
+ }
416
+ let client_edge_query_name = self . generate_query_name ( ) ;
417
+
418
+ self . generate_client_edge_query (
419
+ client_edge_query_name,
420
+ field_type. type_ . inner ( ) ,
421
+ selections,
422
+ ) ;
423
+ ClientEdgeMetadataDirective :: ServerObject {
424
+ query_name : client_edge_query_name,
425
+ unique_id : self . get_key ( ) ,
426
+ }
427
+ }
428
+
311
429
fn transform_linked_field_impl ( & mut self , field : & LinkedField ) -> Transformed < Selection > {
312
430
let schema = & self . program . schema ;
313
431
let field_type = schema. field ( field. definition . item ) ;
@@ -334,31 +452,7 @@ impl<'program, 'sc, 'flag> ClientEdgesTransform<'program, 'sc, 'flag> {
334
452
return self . default_transform_linked_field ( field) ;
335
453
}
336
454
337
- let allowed_directive_names = [
338
- * CLIENT_EDGE_WATERFALL_DIRECTIVE_NAME ,
339
- * REQUIRED_DIRECTIVE_NAME ,
340
- * CHILDREN_CAN_BUBBLE_METADATA_KEY ,
341
- RequiredMetadataDirective :: directive_name ( ) ,
342
- ] ;
343
-
344
- let other_directives = field
345
- . directives
346
- . iter ( )
347
- . filter ( |directive| {
348
- !allowed_directive_names
349
- . iter ( )
350
- . any ( |item| directive. name . item == * item)
351
- } )
352
- . collect :: < Vec < _ > > ( ) ;
353
-
354
- for directive in other_directives {
355
- self . errors . push ( Diagnostic :: error (
356
- ValidationMessage :: ClientEdgeUnsupportedDirective {
357
- directive_name : directive. name . item ,
358
- } ,
359
- directive. name . location ,
360
- ) ) ;
361
- }
455
+ self . verify_directives_or_push_errors ( & field. directives ) ;
362
456
363
457
let edge_to_type = field_type. type_ . inner ( ) ;
364
458
@@ -369,106 +463,26 @@ impl<'program, 'sc, 'flag> ClientEdgesTransform<'program, 'sc, 'flag> {
369
463
. replace_or_else ( || field. selections . clone ( ) ) ;
370
464
371
465
let metadata_directive = if is_edge_to_client_object {
372
- // We assume edges to client objects will be resolved on the client
373
- // and thus not incur a waterfall. This will change in the future
374
- // for @live Resolvers that can trigger suspense.
375
- if let Some ( directive) = waterfall_directive {
376
- self . errors . push ( Diagnostic :: error_with_data (
377
- ValidationMessageWithData :: RelayResolversUnexpectedWaterfall ,
378
- directive. name . location ,
379
- ) ) ;
380
- }
381
-
382
- match edge_to_type {
383
- Type :: Interface ( interface_id) => {
384
- let interface = schema. interface ( interface_id) ;
385
- let implementing_objects =
386
- interface. recursively_implementing_objects ( Arc :: as_ref ( schema) ) ;
387
- if implementing_objects. is_empty ( ) {
388
- self . errors . push ( Diagnostic :: error (
389
- ValidationMessage :: RelayResolverClientInterfaceMustBeImplemented {
390
- interface_name : interface. name . item ,
391
- } ,
392
- interface. name . location ,
393
- ) ) ;
394
- }
395
- if !self
396
- . relay_resolver_enable_interface_output_type
397
- . is_fully_enabled ( )
398
- && !has_output_type ( resolver_directive)
399
- {
400
- self . errors . push ( Diagnostic :: error (
401
- ValidationMessage :: ClientEdgeToClientInterface ,
402
- field. alias_or_name_location ( ) ,
403
- ) ) ;
404
- }
405
- ClientEdgeMetadataDirective :: ClientObject {
406
- type_name : None ,
407
- unique_id : self . get_key ( ) ,
408
- }
409
- }
410
- Type :: Union ( _) => {
411
- self . errors . push ( Diagnostic :: error (
412
- ValidationMessage :: ClientEdgeToClientUnion ,
413
- field. alias_or_name_location ( ) ,
414
- ) ) ;
415
- return Transformed :: Keep ;
416
- }
417
- Type :: Object ( object_id) => ClientEdgeMetadataDirective :: ClientObject {
418
- type_name : Some ( schema. object ( object_id) . name . item ) ,
419
- unique_id : self . get_key ( ) ,
420
- } ,
421
- _ => {
422
- panic ! (
423
- "Expected a linked field to reference either an Object, Interface, or Union"
424
- )
425
- }
466
+ match self . get_edge_to_client_object_metadata_directive (
467
+ field,
468
+ edge_to_type,
469
+ waterfall_directive,
470
+ resolver_directive,
471
+ ) {
472
+ Some ( directive) => directive,
473
+ None => return Transformed :: Keep ,
426
474
}
427
475
} else {
428
- // Client Edges to server objects must be annotated with @waterfall
429
- if waterfall_directive. is_none ( ) {
430
- self . errors . push ( Diagnostic :: error_with_data (
431
- ValidationMessageWithData :: RelayResolversMissingWaterfall {
432
- field_name : field_type. name . item ,
433
- } ,
434
- field. definition . location ,
435
- ) ) ;
436
- }
437
- let client_edge_query_name = self . generate_query_name ( ) ;
438
-
439
- self . generate_client_edge_query (
440
- client_edge_query_name,
441
- field_type. type_ . inner ( ) ,
476
+ self . get_edge_to_server_object_metadata_directive (
477
+ field_type,
478
+ field. definition . location ,
479
+ waterfall_directive,
442
480
new_selections. clone ( ) ,
443
- ) ;
444
- ClientEdgeMetadataDirective :: ServerObject {
445
- query_name : client_edge_query_name,
446
- unique_id : self . get_key ( ) ,
447
- }
481
+ )
448
482
} ;
449
- let mut inline_fragment_directives: Vec < Directive > = vec ! [ metadata_directive. into( ) ] ;
450
- if let Some ( required_directive_metadata) = field
451
- . directives
452
- . named ( RequiredMetadataDirective :: directive_name ( ) )
453
- . cloned ( )
454
- {
455
- inline_fragment_directives. push ( required_directive_metadata) ;
456
- }
457
483
458
- let transformed_field = Arc :: new ( LinkedField {
459
- selections : new_selections,
460
- ..field. clone ( )
461
- } ) ;
462
-
463
- let inline_fragment = InlineFragment {
464
- type_condition : None ,
465
- directives : inline_fragment_directives,
466
- selections : vec ! [
467
- Selection :: LinkedField ( transformed_field. clone( ) ) ,
468
- Selection :: LinkedField ( transformed_field) ,
469
- ] ,
470
- spread_location : Location :: generated ( ) ,
471
- } ;
484
+ let inline_fragment =
485
+ create_inline_fragment_for_client_edge ( field, new_selections, metadata_directive) ;
472
486
473
487
Transformed :: Replace ( Selection :: InlineFragment ( Arc :: new ( inline_fragment) ) )
474
488
}
@@ -480,6 +494,36 @@ impl<'program, 'sc, 'flag> ClientEdgesTransform<'program, 'sc, 'flag> {
480
494
}
481
495
}
482
496
497
+ fn create_inline_fragment_for_client_edge (
498
+ field : & LinkedField ,
499
+ selections : Vec < Selection > ,
500
+ metadata_directive : ClientEdgeMetadataDirective ,
501
+ ) -> InlineFragment {
502
+ let mut inline_fragment_directives: Vec < Directive > = vec ! [ metadata_directive. into( ) ] ;
503
+ if let Some ( required_directive_metadata) = field
504
+ . directives
505
+ . named ( RequiredMetadataDirective :: directive_name ( ) )
506
+ . cloned ( )
507
+ {
508
+ inline_fragment_directives. push ( required_directive_metadata) ;
509
+ }
510
+
511
+ let transformed_field = Arc :: new ( LinkedField {
512
+ selections,
513
+ ..field. clone ( )
514
+ } ) ;
515
+
516
+ InlineFragment {
517
+ type_condition : None ,
518
+ directives : inline_fragment_directives,
519
+ selections : vec ! [
520
+ Selection :: LinkedField ( transformed_field. clone( ) ) ,
521
+ Selection :: LinkedField ( transformed_field) ,
522
+ ] ,
523
+ spread_location : Location :: generated ( ) ,
524
+ }
525
+ }
526
+
483
527
impl Transformer for ClientEdgesTransform < ' _ , ' _ , ' _ > {
484
528
const NAME : & ' static str = "ClientEdgesTransform" ;
485
529
const VISIT_ARGUMENTS : bool = false ;
0 commit comments