Skip to content

Commit

Permalink
Support for @required and client-edges
Browse files Browse the repository at this point in the history
Summary:
This diff adds runtime support for `required` directives on client-edge fields.

In the codegen:
 - client-edge is wrapped with required field

In typegen:
 - we are generating the correct flow-types for client-edge reader field.

In runtime:
- ReaderNode updated to reflect that `RequiredField` can have `ReaderClientEdgeToClientObject | ReaderClientEdgeToServerObject` as nodes.

Reviewed By: captbaritone

Differential Revision: D37187696

fbshipit-source-id: a11eae113dfbaff1a9e60053cd6a8337f5b3deac
  • Loading branch information
alunyov authored and facebook-github-bot committed Jul 8, 2022
1 parent d117221 commit cefab9e
Show file tree
Hide file tree
Showing 21 changed files with 1,822 additions and 30 deletions.
38 changes: 34 additions & 4 deletions compiler/crates/relay-codegen/src/build_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1072,9 +1072,10 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
&mut self,
context: &mut ContextualMetadata,
client_edge_metadata: ClientEdgeMetadata<'_>,
required_metadata: Option<RequiredMetadataDirective>,
) -> Primitive {
context.has_client_edges = true;
let backing_field = match client_edge_metadata.backing_field {
let backing_field = match &client_edge_metadata.backing_field {
Selection::FragmentSpread(fragment_spread) => {
self.build_fragment_spread(fragment_spread)
}
Expand All @@ -1095,11 +1096,32 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
};

let selections_item = match client_edge_metadata.selections {
Selection::LinkedField(linked_field) => self.build_linked_field(context, linked_field),
Selection::LinkedField(linked_field) => {
if required_metadata.is_none() {
self.build_linked_field(context, linked_field)
} else {
let next_directives = linked_field
.directives
.iter()
.filter(|directive| {
directive.name.item != RequiredMetadataDirective::directive_name()
})
.cloned()
.collect();

self.build_linked_field(
context,
&LinkedField {
directives: next_directives,
..linked_field.as_ref().clone()
},
)
}
}
_ => panic!("Expected Client Edge selections to be a LinkedField"),
};

match client_edge_metadata.metadata_directive {
let field = match client_edge_metadata.metadata_directive {
ClientEdgeMetadataDirective::ServerObject { query_name, .. } => {
Primitive::Key(self.object(object! {
kind: Primitive::String(CODEGEN_CONSTANTS.client_edge_to_server_object),
Expand All @@ -1116,6 +1138,12 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
client_edge_selections_key: selections_item,
}))
}
};

if let Some(required_metadata) = required_metadata {
self.build_required_field(&required_metadata, field)
} else {
field
}
}

Expand All @@ -1127,7 +1155,9 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
match inline_frag.type_condition {
None => {
if let Some(client_edge_metadata) = ClientEdgeMetadata::find(inline_frag) {
self.build_client_edge(context, client_edge_metadata)
let required_metadata =
RequiredMetadataDirective::find(&inline_frag.directives).cloned();
self.build_client_edge(context, client_edge_metadata, required_metadata)
} else if
// TODO(T63388023): Use typed custom directives
inline_frag.directives.len() == 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,11 @@ fragment RefetchableClientEdgeQuery_relayResolverWithRequiredClientEdgeQuery_me_
"plural": false,
"selections": [
{
"kind": "ClientEdgeToServerObject",
"operation": require('ClientEdgeQuery_relayResolverWithRequiredClientEdgeQuery_me__best_friend.graphql'),
"backingField": {
"kind": "RequiredField",
"field": {
"kind": "RequiredField",
"field": {
"kind": "ClientEdgeToServerObject",
"operation": require('ClientEdgeQuery_relayResolverWithRequiredClientEdgeQuery_me__best_friend.graphql'),
"backingField": {
"alias": null,
"args": null,
"fragment": {
Expand All @@ -182,12 +182,7 @@ fragment RefetchableClientEdgeQuery_relayResolverWithRequiredClientEdgeQuery_me_
"resolverModule": require('BestFriendResolver'),
"path": "me.best_friend"
},
"action": "THROW",
"path": "me.best_friend"
},
"linkedField": {
"kind": "RequiredField",
"field": {
"linkedField": {
"alias": null,
"args": null,
"concreteType": "User",
Expand All @@ -204,10 +199,10 @@ fragment RefetchableClientEdgeQuery_relayResolverWithRequiredClientEdgeQuery_me_
}
],
"storageKey": null
},
"action": "THROW",
"path": "me.best_friend"
}
}
},
"action": "THROW",
"path": "me.best_friend"
}
],
"storageKey": null
Expand Down
29 changes: 22 additions & 7 deletions compiler/crates/relay-transforms/src/client_edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub struct ClientEdgeGeneratedQueryMetadataDirective {
associated_data_impl!(ClientEdgeGeneratedQueryMetadataDirective);

pub struct ClientEdgeMetadata<'a> {
pub backing_field: &'a Selection,
pub backing_field: Selection,
pub selections: &'a Selection,
pub metadata_directive: ClientEdgeMetadataDirective,
}
Expand All @@ -108,12 +108,19 @@ impl<'a> ClientEdgeMetadata<'a> {
fragment.selections.len() == 2,
"Expected Client Edge inline fragment to have exactly two selections. This is a bug in the Relay compiler."
);
let mut backing_field = fragment
.selections
.get(0)
.expect("Client Edge inline fragments have exactly two selections").clone();

let backing_field_directives = backing_field.directives().iter().filter(|directive|
directive.name.item != RequiredMetadataDirective::directive_name()
).cloned().collect();
backing_field.set_directives(backing_field_directives);

ClientEdgeMetadata {
metadata_directive: metadata_directive.clone(),
backing_field: fragment
.selections
.get(0)
.expect("Client Edge inline fragments have exactly two selections"),
backing_field,
selections: fragment
.selections
.get(1)
Expand Down Expand Up @@ -390,6 +397,14 @@ impl<'program, 'sc> ClientEdgesTransform<'program, 'sc> {
unique_id: self.get_key(),
}
};
let mut inline_fragment_directives: Vec<Directive> = vec![metadata_directive.into()];
if let Some(required_directive_metadata) = field
.directives
.named(RequiredMetadataDirective::directive_name())
.cloned()
{
inline_fragment_directives.push(required_directive_metadata);
}

let transformed_field = Arc::new(LinkedField {
selections: new_selections,
Expand All @@ -398,7 +413,7 @@ impl<'program, 'sc> ClientEdgesTransform<'program, 'sc> {

let inline_fragment = InlineFragment {
type_condition: None,
directives: vec![metadata_directive.into()],
directives: inline_fragment_directives,
selections: vec![
Selection::LinkedField(transformed_field.clone()),
Selection::LinkedField(transformed_field),
Expand Down Expand Up @@ -520,7 +535,7 @@ impl Transformer for ClientEdgesCleanupTransform {
let new_selection = metadata.backing_field;

Transformed::Replace(
self.transform_selection(new_selection)
self.transform_selection(&new_selection)
.unwrap_or_else(|| new_selection.clone()),
)
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/crates/relay-transforms/src/relay_resolvers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ impl<'program> Transformer for RelayResolverSpreadTransform<'program> {
match ClientEdgeMetadata::find(fragment) {
Some(client_edge_metadata) => {
let backing_id_field = self
.transform_selection(client_edge_metadata.backing_field)
.transform_selection(&client_edge_metadata.backing_field)
.unwrap_or_else(|| client_edge_metadata.backing_field.clone());

let selections_field = match client_edge_metadata.selections {
Expand Down Expand Up @@ -380,7 +380,7 @@ impl Transformer for RelayResolverFieldTransform<'_> {
let transformed = match ClientEdgeMetadata::find(fragment) {
Some(client_edge_metadata) => {
let backing_id_field = self
.transform_selection(client_edge_metadata.backing_field)
.transform_selection(&client_edge_metadata.backing_field)
.unwrap_or_else(|| client_edge_metadata.backing_field.clone());

let selections_field = match client_edge_metadata.selections {
Expand Down
2 changes: 1 addition & 1 deletion compiler/crates/relay-typegen/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ fn visit_client_edge(
runtime_imports: &mut RuntimeImports,
enclosing_linked_field_concrete_type: Option<Type>,
) {
let (resolver_metadata, fragment_name) = match client_edge_metadata.backing_field {
let (resolver_metadata, fragment_name) = match &client_edge_metadata.backing_field {
Selection::FragmentSpread(fragment_spread) => (
RelayResolverMetadata::find(&fragment_spread.directives),
Some(fragment_spread.fragment.item),
Expand Down
Loading

0 comments on commit cefab9e

Please sign in to comment.