Skip to content

Commit

Permalink
Update normalization artifacts for exec time resolver strong ids
Browse files Browse the repository at this point in the history
Summary: This diff updates artifact generation for exec time resolvers to include the resolver module in the normalization AST. It's fairly similar to how the reader AST is built but we need far less information at normalization time. This is the most basic version of the artifact and we may need to add additional fields as the feature continues to be built.

Reviewed By: captbaritone

Differential Revision: D64635272

fbshipit-source-id: 3627fbb51570e4559bf98a0334b136d11d0f0de1
  • Loading branch information
evanyeung authored and facebook-github-bot committed Oct 22, 2024
1 parent 356327c commit d036d49
Show file tree
Hide file tree
Showing 17 changed files with 513 additions and 143 deletions.
101 changes: 90 additions & 11 deletions compiler/crates/relay-codegen/src/build_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1343,17 +1343,51 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
let type_name = model_resolver.type_name.item.0;
ObjectEntry {
key: type_name,
value: self.build_client_edge_model_resolver(
model_resolver.type_name,
model_resolver.is_live,
relay_resolver_metadata,
),
value: match self.variant {
CodegenVariant::Reader => self.build_reader_client_edge_model_resolver(
model_resolver.type_name,
model_resolver.is_live,
relay_resolver_metadata,
),
CodegenVariant::Normalization => self
.build_normalization_client_edge_model_resolver(
model_resolver.type_name,
relay_resolver_metadata,
),
},
}
})
.collect()
}

fn build_client_edge_model_resolver(
fn build_normalization_client_edge_model_resolver(
&mut self,
type_name: WithLocation<ObjectName>,
relay_resolver_metadata: &RelayResolverMetadata,
) -> Primitive {
let import_path = self.project_config.js_module_import_identifier(
&self
.project_config
.artifact_path_for_definition(self.definition_source_location),
&PathBuf::from(type_name.location.source_location().path()),
);
let variable_name = relay_resolver_metadata.generate_local_resolver_name(self.schema);
let resolver_module = JSModuleDependency {
path: import_path,
import_name: ModuleImportName::Named {
name: type_name.item.0,
import_as: Some(variable_name),
},
};

let object_props = object! {
resolver_module: Primitive::JSModuleDependency(resolver_module),
};

Primitive::Key(self.object(object_props))
}

fn build_reader_client_edge_model_resolver(
&mut self,
type_name: WithLocation<ObjectName>,
is_live: bool,
Expand Down Expand Up @@ -1705,12 +1739,57 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
}
let backing_field = backing_field_primitives.into_iter().next().unwrap();

let client_edge_model_resolvers = match &client_edge_metadata.metadata_directive {
ClientEdgeMetadataDirective::ClientObject {
model_resolvers, ..
} => {
let field_directives = match &client_edge_metadata.backing_field {
Selection::ScalarField(field) => Some(&field.directives),
// Although the reader checks for FragmentSpreads on the backing field, the normalization
// transforms inline the fragment spread so we match an InlineFragment here
Selection::InlineFragment(inline_frag) => Some(&inline_frag.directives),
_ => panic!(
"Expected Client Edge backing field to be a Relay Resolver. {:?}",
client_edge_metadata.backing_field
),
};
field_directives.and_then(|field_directives| {
let resolver_metadata = RelayResolverMetadata::find(field_directives).unwrap();
let is_weak_resolver = matches!(
resolver_metadata.output_type_info,
ResolverOutputTypeInfo::Composite(_)
);
let model_resolver_primitives = if !is_weak_resolver {
self.build_client_edge_model_resolvers(model_resolvers, resolver_metadata)
} else {
vec![]
};
if model_resolver_primitives.is_empty() {
None
} else {
Some(Primitive::Key(self.object(model_resolver_primitives)))
}
})
}
ClientEdgeMetadataDirective::ServerObject { .. } => None,
};

let selections_item = self.build_linked_field(context, client_edge_metadata.linked_field);
Primitive::Key(self.object(object! {
kind: Primitive::String(CODEGEN_CONSTANTS.client_edge_to_client_object),
client_edge_backing_field_key: backing_field,
client_edge_selections_key: selections_item,
}))

let obj = match client_edge_model_resolvers {
Some(model_resolvers) => object! {
kind: Primitive::String(CODEGEN_CONSTANTS.client_edge_to_client_object),
client_edge_model_resolvers: model_resolvers,
client_edge_backing_field_key: backing_field,
client_edge_selections_key: selections_item,
},
None => object! {
kind: Primitive::String(CODEGEN_CONSTANTS.client_edge_to_client_object),
client_edge_backing_field_key: backing_field,
client_edge_selections_key: selections_item,
},
};
Primitive::Key(self.object(obj))
}

fn build_normalization_client_edge(
Expand Down
18 changes: 12 additions & 6 deletions compiler/crates/relay-codegen/tests/client_edges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use relay_config::ProjectName;
use relay_test_schema::get_test_schema_with_extensions;
use relay_transforms::client_edges;
use relay_transforms::relay_resolvers;
use relay_transforms::sort_selections;

pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result<String, String> {
let parts: Vec<_> = fixture.content.split("%extensions%").collect();
Expand All @@ -41,17 +40,16 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result<String, String>
};
let feature_flags = Arc::new(FeatureFlags {
relay_resolver_enable_interface_output_type,
enable_exec_time_resolvers_directive: true,
..Default::default()
});
let project_config: ProjectConfig = ProjectConfig {
feature_flags,
..Default::default()
};
let next_program = sort_selections(
&client_edges(&program, &project_config, &Default::default())
.and_then(|program| relay_resolvers(ProjectName::default(), &program))
.unwrap(),
);
let next_program = &client_edges(&program, &project_config, &Default::default())
.and_then(|program| relay_resolvers(ProjectName::default(), &program))
.unwrap();
let mut result = next_program
.fragments()
.map(|def| {
Expand All @@ -61,6 +59,10 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result<String, String>
def,
&ProjectConfig {
js_module_format: JsModuleFormat::Haste,
feature_flags: Arc::new(FeatureFlags {
enable_exec_time_resolvers_directive: true,
..Default::default()
}),
..Default::default()
},
&mut import_statements,
Expand All @@ -74,6 +76,10 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result<String, String>
def,
&ProjectConfig {
js_module_format: JsModuleFormat::Haste,
feature_flags: Arc::new(FeatureFlags {
enable_exec_time_resolvers_directive: true,
..Default::default()
}),
..Default::default()
},
&mut import_statements,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,14 @@ extend type User {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"name": "name",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"name": "id",
"storageKey": null
}
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
==================================== INPUT ====================================
query Foo @exec_time_resolvers {
me {
pet {
name
}
}
}

# %extensions%

directive @exec_time_resolvers on QUERY

type Cat @__RelayResolverModel {
name: String
@relay_resolver(import_name: "name", import_path: "CatNameResolver")
__relay_model_instance: RelayResolverValue!
@relay_resolver(import_name: "Cat")
@unselectable(
reason: "This field is intended only for Relay's internal use"
)
}

extend type User {
pet: Cat @relay_resolver(import_name: "Pet", import_path: "PetResolver")
}
==================================== OUTPUT ===================================
{
"argumentDefinitions": [],
"kind": "Operation",
"name": "Foo",
"selections": [
{
"alias": null,
"args": null,
"concreteType": "User",
"kind": "LinkedField",
"name": "me",
"plural": false,
"selections": [
{
"kind": "ClientEdgeToClientObject",
"modelResolvers": {
"Cat": {
"resolverModule": require('<generated>').Cat
}
},
"backingField": {
"name": "pet",
"args": null,
"kind": "RelayResolver",
"storageKey": null,
"isOutputType": false,
"resolverInfo": {
"resolverFunction": require('PetResolver').Pet,
"rootFragment": null
}
},
"linkedField": {
"alias": null,
"args": null,
"concreteType": "Cat",
"kind": "LinkedField",
"name": "pet",
"plural": false,
"selections": [
{
"name": "name",
"args": null,
"kind": "RelayResolver",
"storageKey": null,
"isOutputType": false,
"resolverInfo": {
"resolverFunction": require('CatNameResolver').name,
"rootFragment": null
}
}
],
"storageKey": null
}
}
],
"storageKey": null
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
query Foo @exec_time_resolvers {
me {
pet {
name
}
}
}

# %extensions%

directive @exec_time_resolvers on QUERY

type Cat @__RelayResolverModel {
name: String
@relay_resolver(import_name: "name", import_path: "CatNameResolver")
__relay_model_instance: RelayResolverValue!
@relay_resolver(import_name: "Cat")
@unselectable(
reason: "This field is intended only for Relay's internal use"
)
}

extend type User {
pet: Cat @relay_resolver(import_name: "Pet", import_path: "PetResolver")
}
9 changes: 8 additions & 1 deletion compiler/crates/relay-codegen/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<<47f77ebc1507bbe5e60146b25f042c50>>
* @generated SignedSource<<5873fe295d3d76246e0125a0b8a37f15>>
*/

mod client_edges;
Expand All @@ -19,6 +19,13 @@ async fn client_edge_backed_by_resolver() {
test_fixture(transform_fixture, file!(), "client-edge-backed-by-resolver.graphql", "client_edges/fixtures/client-edge-backed-by-resolver.expected", input, expected).await;
}

#[tokio::test]
async fn client_edge_exec_time_resolver() {
let input = include_str!("client_edges/fixtures/client-edge-exec-time-resolver.graphql");
let expected = include_str!("client_edges/fixtures/client-edge-exec-time-resolver.expected");
test_fixture(transform_fixture, file!(), "client-edge-exec-time-resolver.graphql", "client_edges/fixtures/client-edge-exec-time-resolver.expected", input, expected).await;
}

#[tokio::test]
async fn client_edge_to_client_object() {
let input = include_str!("client_edges/fixtures/client-edge-to-client-object.graphql");
Expand Down
Loading

0 comments on commit d036d49

Please sign in to comment.