diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/required-bubbles-to-client-edge.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/required-bubbles-to-client-edge.expected new file mode 100644 index 0000000000000..c5e40cfa35ec9 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/required-bubbles-to-client-edge.expected @@ -0,0 +1,68 @@ +==================================== INPUT ==================================== +fragment requiredBubblesToClientEdge_user on User { + best_friend { + # This will bubble nullability to the parent which is a client edge. + # Under the hood that adds a metadata directive to that node, which + # this test is designed to assert is allowed. + name @required(action: NONE) + } +} + +# %extensions% + +type ClientUser { + name: String +} + +extend type User { + best_friend: ClientUser @relay_resolver(import_path: "BestFriendResolver") +} +==================================== OUTPUT =================================== +{ + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "requiredBubblesToClientEdge_user", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "concreteType": "ClientUser", + "backingField": { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "best_friend", + "resolverModule": require('BestFriendResolver'), + "path": "best_friend" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "ClientUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": [ + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + "action": "NONE", + "path": "best_friend.name" + } + ], + "storageKey": null + } + } + ], + "type": "User", + "abstractKey": null +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/required-bubbles-to-client-edge.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/required-bubbles-to-client-edge.graphql new file mode 100644 index 0000000000000..39f2e15373915 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/required-bubbles-to-client-edge.graphql @@ -0,0 +1,18 @@ +fragment requiredBubblesToClientEdge_user on User { + best_friend { + # This will bubble nullability to the parent which is a client edge. + # Under the hood that adds a metadata directive to that node, which + # this test is designed to assert is allowed. + name @required(action: NONE) + } +} + +# %extensions% + +type ClientUser { + name: String +} + +extend type User { + best_friend: ClientUser @relay_resolver(import_path: "BestFriendResolver") +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts_test.rs b/compiler/crates/relay-compiler/tests/compile_relay_artifacts_test.rs index c58dc2d637fbf..6669a2d726067 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts_test.rs +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2a17651ff486f00579270758059a000e>> + * @generated SignedSource<<4c83edb5a97709b48ab136e569a1bc3e>> */ mod compile_relay_artifacts; @@ -1244,6 +1244,13 @@ fn required_argument_not_passed_other_args_invalid() { test_fixture(transform_fixture, "required_argument_not_passed_other_args.invalid.graphql", "compile_relay_artifacts/fixtures/required_argument_not_passed_other_args.invalid.expected", input, expected); } +#[test] +fn required_bubbles_to_client_edge() { + let input = include_str!("compile_relay_artifacts/fixtures/required-bubbles-to-client-edge.graphql"); + let expected = include_str!("compile_relay_artifacts/fixtures/required-bubbles-to-client-edge.expected"); + test_fixture(transform_fixture, "required-bubbles-to-client-edge.graphql", "compile_relay_artifacts/fixtures/required-bubbles-to-client-edge.expected", input, expected); +} + #[test] fn required_directive() { let input = include_str!("compile_relay_artifacts/fixtures/required-directive.graphql"); diff --git a/compiler/crates/relay-transforms/src/client_edges.rs b/compiler/crates/relay-transforms/src/client_edges.rs index bb3086129651e..a38759dfc9be4 100644 --- a/compiler/crates/relay-transforms/src/client_edges.rs +++ b/compiler/crates/relay-transforms/src/client_edges.rs @@ -51,6 +51,7 @@ use crate::refetchable_fragment::REFETCHABLE_NAME; use crate::relay_resolvers::get_bool_argument_is_true; use crate::RequiredMetadataDirective; use crate::ValidationMessage; +use crate::CHILDREN_CAN_BUBBLE_METADATA_KEY; use crate::REQUIRED_DIRECTIVE_NAME; lazy_static! { @@ -319,6 +320,7 @@ impl<'program, 'sc> ClientEdgesTransform<'program, 'sc> { let allowed_directive_names = [ *CLIENT_EDGE_WATERFALL_DIRECTIVE_NAME, *REQUIRED_DIRECTIVE_NAME, + *CHILDREN_CAN_BUBBLE_METADATA_KEY, RequiredMetadataDirective::directive_name(), ]; diff --git a/compiler/crates/relay-transforms/src/relay_resolvers.rs b/compiler/crates/relay-transforms/src/relay_resolvers.rs index ff266716b4082..745f144065b2d 100644 --- a/compiler/crates/relay-transforms/src/relay_resolvers.rs +++ b/compiler/crates/relay-transforms/src/relay_resolvers.rs @@ -54,6 +54,7 @@ use crate::generate_relay_resolvers_operations_for_nested_objects::generate_name use crate::ClientEdgeMetadata; use crate::FragmentAliasMetadata; use crate::RequiredMetadataDirective; +use crate::CHILDREN_CAN_BUBBLE_METADATA_KEY; use crate::CLIENT_EDGE_WATERFALL_DIRECTIVE_NAME; use crate::REQUIRED_DIRECTIVE_NAME; @@ -408,6 +409,7 @@ impl<'program> RelayResolverFieldTransform<'program> { // For now, only @required and @waterfall are allowed on Resolver fields. directive.name.item != RequiredMetadataDirective::directive_name() && directive.name.item != *REQUIRED_DIRECTIVE_NAME + && directive.name.item != *CHILDREN_CAN_BUBBLE_METADATA_KEY && directive.name.item != *CLIENT_EDGE_WATERFALL_DIRECTIVE_NAME }); if let Some(directive) = non_required_directives.next() {