Skip to content

Commit

Permalink
add model resolvers for unions
Browse files Browse the repository at this point in the history
Reviewed By: captbaritone

Differential Revision: D58219919

fbshipit-source-id: 4ae4b4abfb2abec46759a265b01d18d7e6a816bb
  • Loading branch information
monicatang authored and facebook-github-bot committed Jun 7, 2024
1 parent 7b65101 commit 98823ea
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 94 deletions.
87 changes: 52 additions & 35 deletions compiler/crates/relay-transforms/src/client_edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,41 +379,14 @@ impl<'program, 'pc> ClientEdgesTransform<'program, 'pc> {
field.alias_or_name_location(),
));
}
let mut model_resolvers: Vec<ClientEdgeModelResolver> = implementing_objects
.iter()
.filter_map(|object_id| {
let model_resolver = self.get_client_edge_model_resolver_for_object(*object_id);
model_resolver.or_else(|| {
let object = Type::Object(*object_id);
let schema = self.program.schema.as_ref();
if !object.is_weak_resolver_object(schema) && object.is_resolver_object(schema) {
let model_name = self.program.schema.object(*object_id).name;
self.errors.push(Diagnostic::error(
ValidationMessage::ClientEdgeToClientInterfaceImplementingObjectMissingModelResolver {
interface_name: interface.name.item,
type_name: model_name.item,
},
model_name.location,
));
}
None
})
})
.collect();
model_resolvers.sort();
Some(ClientEdgeMetadataDirective::ClientObject {
type_name: None,
model_resolvers,
unique_id: self.get_key(),
})
self.get_client_object_for_abstract_type(
implementing_objects.iter(),
interface.name.item.0,
)
}
Type::Union(_) => {
self.errors.push(Diagnostic::error(
ValidationMessage::ClientEdgeToClientUnion,
field.alias_or_name_location(),
));
// TODO model resolvers for ClientEdgeToClientUnion
None
Type::Union(union) => {
let union = self.program.schema.union(union);
self.get_client_object_for_abstract_type(union.members.iter(), union.name.item.0)
}
Type::Object(object_id) => {
let type_name = self.program.schema.object(object_id).name.item;
Expand All @@ -432,6 +405,50 @@ impl<'program, 'pc> ClientEdgesTransform<'program, 'pc> {
}
}

fn get_client_object_for_abstract_type<'a>(
&mut self,
members: impl Iterator<Item = &'a ObjectID>,
abstract_type_name: StringKey,
) -> Option<ClientEdgeMetadataDirective> {
let mut model_resolvers: Vec<ClientEdgeModelResolver> = members
.filter_map(|object_id| {
let model_resolver = self.get_client_edge_model_resolver_for_object(*object_id);
model_resolver.or_else(|| {
self.maybe_report_error_for_missing_model_resolver(
object_id,
abstract_type_name,
);
None
})
})
.collect();
model_resolvers.sort();
Some(ClientEdgeMetadataDirective::ClientObject {
type_name: None,
model_resolvers,
unique_id: self.get_key(),
})
}

fn maybe_report_error_for_missing_model_resolver(
&mut self,
object_id: &ObjectID,
abstract_type_name: StringKey,
) {
let object = Type::Object(*object_id);
let schema = self.program.schema.as_ref();
if !object.is_weak_resolver_object(schema) && object.is_resolver_object(schema) {
let model_name = self.program.schema.object(*object_id).name;
self.errors.push(Diagnostic::error(
ValidationMessage::ClientEdgeImplementingObjectMissingModelResolver {
name: abstract_type_name,
type_name: model_name.item,
},
model_name.location,
));
}
}

fn get_client_edge_model_resolver_for_object(
&mut self,
object_id: ObjectID,
Expand Down Expand Up @@ -596,7 +613,7 @@ fn create_inline_fragment_for_client_edge(
type_condition: None,
directives: inline_fragment_directives,
selections: vec![
Selection::LinkedField(transformed_field.clone()),
Selection::LinkedField(Arc::clone(&transformed_field)),
Selection::LinkedField(transformed_field),
],
spread_location: Location::generated(),
Expand Down
12 changes: 3 additions & 9 deletions compiler/crates/relay-transforms/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,18 +130,12 @@ pub enum ValidationMessage {
ClientEdgeToClientInterface,

#[error(
"The client edge pointing to the interface `{interface_name}` with implementing object, `{type_name}`, is missing its corresponding model resolver. The concrete type `{type_name}` and its resolver fields should be defined with the newer dot notation resolver syntax. See https://relay.dev/docs/guides/relay-resolvers/."
"The client edge pointing to `{name}` with implementing object, `{type_name}`, is missing its corresponding model resolver. The concrete type `{type_name}` and its resolver fields should be defined with the newer dot notation resolver syntax. See https://relay.dev/docs/guides/relay-resolvers/."
)]
ClientEdgeToClientInterfaceImplementingObjectMissingModelResolver {
interface_name: InterfaceName,
ClientEdgeImplementingObjectMissingModelResolver {
name: StringKey,
type_name: ObjectName,
},

#[error(
"Client Edges that reference client-defined union types are not currently supported in Relay."
)]
ClientEdgeToClientUnion,

#[error("Invalid directive combination. @alias may not be combined with other directives.")]
FragmentAliasIncompatibleDirective,

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
==================================== INPUT ====================================
fragment FeedbackFragmentType on User {
feedback_as_union {
... on Like {
__typename
}
}
}

# %extensions%

type Like @__RelayResolverModel {
id: ID!
__relay_model_instance: RelayResolverValue @relay_resolver(import_path: "LikeResolver", fragment_name: "Like__id", inject_fragment_data: "id")
}

type Heart @__RelayResolverModel {
id: ID!
__relay_model_instance: RelayResolverValue @relay_resolver(import_path: "HeartResolver", fragment_name: "Heart__id", inject_fragment_data: "id")
}

union ClientOnlyUnion = Comment | Like | Heart

extend type User {
feedback_as_union: ClientOnlyUnion @relay_resolver(import_path: "FeedbackResolver")
}
==================================== OUTPUT ===================================
fragment FeedbackFragmentType on User {
... @__ClientEdgeMetadataDirective
# ClientObject {
# type_name: None,
# unique_id: 0,
# model_resolvers: [
# ClientEdgeModelResolver {
# type_name: WithLocation {
# location: <generated>:199:204,
# item: ObjectName(
# "Heart",
# ),
# },
# is_live: false,
# },
# ClientEdgeModelResolver {
# type_name: WithLocation {
# location: <generated>:7:11,
# item: ObjectName(
# "Like",
# ),
# },
# is_live: false,
# },
# ],
# }
{
__id @__RelayResolverMetadata
# RelayResolverMetadata {
# field_id: FieldID(530),
# import_path: "FeedbackResolver",
# import_name: None,
# field_alias: None,
# field_path: "feedback_as_union",
# field_arguments: [],
# live: false,
# output_type_info: EdgeTo,
# fragment_data_injection_mode: None,
# }

feedback_as_union {
... on Like {
__typename
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
fragment FeedbackFragmentType on User {
feedback_as_union {
... on Like {
__typename
}
}
}

# %extensions%

type Like @__RelayResolverModel {
id: ID!
__relay_model_instance: RelayResolverValue @relay_resolver(import_path: "LikeResolver", fragment_name: "Like__id", inject_fragment_data: "id")
}

type Heart @__RelayResolverModel {
id: ID!
__relay_model_instance: RelayResolverValue @relay_resolver(import_path: "HeartResolver", fragment_name: "Heart__id", inject_fragment_data: "id")
}

union ClientOnlyUnion = Comment | Like | Heart

extend type User {
feedback_as_union: ClientOnlyUnion @relay_resolver(import_path: "FeedbackResolver")
}

This file was deleted.

This file was deleted.

10 changes: 5 additions & 5 deletions compiler/crates/relay-transforms/tests/client_edges_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<<b333fb4f27ac97378f98d6f40e32504f>>
* @generated SignedSource<<0fcfe34726846687001fb9cf0431f725>>
*/

mod client_edges;
Expand Down Expand Up @@ -55,10 +55,10 @@ async fn client_edge_to_client_object() {
}

#[tokio::test]
async fn client_edge_to_client_union_invalid() {
let input = include_str!("client_edges/fixtures/client-edge-to-client-union.invalid.graphql");
let expected = include_str!("client_edges/fixtures/client-edge-to-client-union.invalid.expected");
test_fixture(transform_fixture, file!(), "client-edge-to-client-union.invalid.graphql", "client_edges/fixtures/client-edge-to-client-union.invalid.expected", input, expected).await;
async fn client_edge_to_client_union() {
let input = include_str!("client_edges/fixtures/client-edge-to-client-union.graphql");
let expected = include_str!("client_edges/fixtures/client-edge-to-client-union.expected");
test_fixture(transform_fixture, file!(), "client-edge-to-client-union.graphql", "client_edges/fixtures/client-edge-to-client-union.expected", input, expected).await;
}

#[tokio::test]
Expand Down

0 comments on commit 98823ea

Please sign in to comment.