Skip to content

Commit

Permalink
COMPILER changes add enabled argument inside @exec_time_resolvers dir…
Browse files Browse the repository at this point in the history
…ective

Reviewed By: tyao1

Differential Revision: D69808900

fbshipit-source-id: 94b7e6614e31c7de2103e8a981c5568bc2f37cbf
  • Loading branch information
lynnshaoyu authored and facebook-github-bot committed Feb 19, 2025
1 parent 4906015 commit 8c32ec4
Show file tree
Hide file tree
Showing 23 changed files with 449 additions and 122 deletions.
144 changes: 99 additions & 45 deletions compiler/crates/relay-codegen/src/build_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use ::intern::intern;
use ::intern::string_key::Intern;
use ::intern::string_key::StringKey;
use ::intern::Lookup;
use common::ArgumentName;
use common::DirectiveName;
use common::NamedItem;
use common::ObjectName;
Expand Down Expand Up @@ -104,6 +105,8 @@ lazy_static! {
DirectiveName("throwOnFieldError".intern());
pub static ref EXEC_TIME_RESOLVERS: DirectiveName =
DirectiveName("exec_time_resolvers".intern());
static ref EXEC_TIME_RESOLVERS_ENABLED_ARGUMENT: ArgumentName =
ArgumentName("enabled".intern());
}

pub fn build_request_params_ast_key(
Expand Down Expand Up @@ -373,17 +376,54 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
}

fn build_operation(&mut self, operation: &OperationDefinition) -> AstKey {
let exec_time_resolvers_enabled_provider = operation
.directives
.named(*EXEC_TIME_RESOLVERS)
.and_then(|directive| {
directive
.arguments
.named(*EXEC_TIME_RESOLVERS_ENABLED_ARGUMENT)
.map(|arg| match &arg.value.item {
Value::Constant(ConstantValue::String(cons)) => WithLocation {
item: *cons,
location: arg.value.location,
},
_ => panic!(
"The enabled argument in exec_time_resolvers directive should be the string name of your provider file."
),
})
});
let artifact_path = self
.project_config
.artifact_path_for_definition(self.definition_source_location);
let exec_time_resolvers_field = if let Some(provider) = exec_time_resolvers_enabled_provider
{
let mut provider_path = PathBuf::from(provider.location.source_location().path());
provider_path.pop();
provider_path.push(PathBuf::from(provider.item.lookup()));

ObjectEntry {
key: "exec_time_resolvers_enabled_provider".intern(),
value: Primitive::JSModuleDependency(JSModuleDependency {
path: self
.project_config
.js_module_import_identifier(&artifact_path, &provider_path),
import_name: ModuleImportName::Default(provider.item),
}),
}
} else {
ObjectEntry {
key: "use_exec_time_resolvers".intern(),
value: Primitive::Bool(true),
}
};
let mut context = ContextualMetadata {
has_client_edges: false,
has_exec_time_resolvers_directive: operation
.directives
.named(*EXEC_TIME_RESOLVERS)
.is_some(),
};
let exec_time_resolvers_field = ObjectEntry {
key: "use_exec_time_resolvers".intern(),
value: Primitive::Bool(context.has_exec_time_resolvers_directive),
};
match operation.directives.named(*DIRECTIVE_SPLIT_OPERATION) {
Some(_split_directive) => {
let metadata = Primitive::Key(self.object(vec![]));
Expand Down Expand Up @@ -844,16 +884,23 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
{
self.build_normalization_relay_resolver_execution_time_for_worker(resolver_metadata)
} else if self.use_exec_time_resolvers(context) {
self.build_normalization_relay_resolver_execution_time(resolver_metadata)
// We must handle both read time resolvers case and exec time resolvers case
// since the mode it is in (read time resolvers vs exec time resolvers)
// is now determined at runtime.
self.build_normalization_relay_resolver_exec_and_read_time(
resolver_metadata,
inline_fragment,
)
} else {
self.build_normalization_relay_resolver_read_time(resolver_metadata, inline_fragment)
}
}

// For read time execution time Relay Resolvers in the normalization AST,
// we do not need to include resolver modules since those modules will be
// evaluated at read time.
fn build_normalization_relay_resolver_read_time(
// This function generates a Normalization AST node that is the UNION of the node that would be
// generated for the exec time resolvers case and the node that would be generated for the read
// time resolvers case. This is because we need information to fulfill requests for both cases
// in the runtime now, since the query's mode is determined dynamically at runtime.
fn build_normalization_relay_resolver_exec_and_read_time(
&mut self,
resolver_metadata: &RelayResolverMetadata,
inline_fragment: Option<Primitive>,
Expand All @@ -864,6 +911,29 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
let is_output_type = resolver_metadata
.output_type_info
.normalization_ast_should_have_is_output_type_true();
let kind = if resolver_metadata.live {
CODEGEN_CONSTANTS.relay_live_resolver
} else {
CODEGEN_CONSTANTS.relay_resolver
};
let variable_name = resolver_metadata.generate_local_resolver_name(self.schema);
let artifact_path = &self
.project_config
.artifact_path_for_definition(self.definition_source_location);
let resolver_info = build_resolver_info(
self.ast_builder,
self.project_config,
artifact_path,
self.schema.field(resolver_metadata.field_id),
resolver_metadata.import_path,
match resolver_metadata.import_name {
Some(name) => ModuleImportName::Named {
name,
import_as: Some(variable_name),
},
None => ModuleImportName::Default(variable_name),
},
);
Primitive::Key(self.object(object! {
name: Primitive::String(field_name),
args: match args {
Expand All @@ -874,7 +944,7 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
None => Primitive::SkippableNull,
Some(fragment) => fragment,
},
kind: Primitive::String(CODEGEN_CONSTANTS.relay_resolver),
kind: Primitive::String(kind),
storage_key: match args {
None => Primitive::SkippableNull,
Some(key) => {
Expand All @@ -886,56 +956,35 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
}
},
is_output_type: Primitive::Bool(is_output_type),
resolver_info: Primitive::Key(resolver_info),
}))
}

// For execution time Relay Resolvers in the normalization AST, we need to
// also include enough information for resolver function backing each field,
// so that normalization AST have full information on how to resolve client
// edges and fields. That means we need to include the resolver module. Note
// that we don't support inline fragment as we did for read time resolvers
fn build_normalization_relay_resolver_execution_time(
// For read time Relay Resolvers in the normalization AST, we do not
// need to include resolver modules since those modules will be
// evaluated at read time.
fn build_normalization_relay_resolver_read_time(
&mut self,
resolver_metadata: &RelayResolverMetadata,
inline_fragment: Option<Primitive>,
) -> Primitive {
let field_name = resolver_metadata.field_name(self.schema);
let field_arguments = &resolver_metadata.field_arguments;
let args = self.build_arguments(field_arguments);
let is_output_type = resolver_metadata
.output_type_info
.normalization_ast_should_have_is_output_type_true();

let variable_name = resolver_metadata.generate_local_resolver_name(self.schema);
let artifact_path = &self
.project_config
.artifact_path_for_definition(self.definition_source_location);
let kind = if resolver_metadata.live {
CODEGEN_CONSTANTS.relay_live_resolver
} else {
CODEGEN_CONSTANTS.relay_resolver
};
let resolver_info = build_resolver_info(
self.ast_builder,
self.project_config,
artifact_path,
self.schema.field(resolver_metadata.field_id),
resolver_metadata.import_path,
match resolver_metadata.import_name {
Some(name) => ModuleImportName::Named {
name,
import_as: Some(variable_name),
},
None => ModuleImportName::Default(variable_name),
},
);

Primitive::Key(self.object(object! {
name: Primitive::String(field_name),
args: match args {
None => Primitive::SkippableNull,
Some(key) => Primitive::Key(key),
},
kind: Primitive::String(kind),
fragment: match inline_fragment {
None => Primitive::SkippableNull,
Some(fragment) => fragment,
},
kind: Primitive::String(CODEGEN_CONSTANTS.relay_resolver),
storage_key: match args {
None => Primitive::SkippableNull,
Some(key) => {
Expand All @@ -947,7 +996,6 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
}
},
is_output_type: Primitive::Bool(is_output_type),
resolver_info: Primitive::Key(resolver_info),
}))
}

Expand Down Expand Up @@ -1762,7 +1810,11 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
})
}

fn build_client_edge_with_enabled_resolver_normalization_ast(
// This function creates a node that is the UNION of the nodes that would be created for read time resolvers
// and for exec time resolvers (so runtime has ALL the information it needs to run for both resolver modes.)
// Surprisingly, this function can stay exactly the same as build_client_edge_with_enabled_resolver_normalization_ast
// (the function for exec time resolver nodes).
fn build_client_edge_exec_and_read_time(
&mut self,
context: &mut ContextualMetadata,
client_edge_metadata: ClientEdgeMetadata<'_>,
Expand Down Expand Up @@ -2006,7 +2058,9 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> {
}
CodegenVariant::Normalization => {
if self.use_exec_time_resolvers(context) {
self.build_client_edge_with_enabled_resolver_normalization_ast(
// We must handle both read time resolvers case and exec time resolvers case
// since the mode it is in is now determined in runtime.
self.build_client_edge_exec_and_read_time(
context,
client_edge_metadata,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
==================================== INPUT ====================================
# enable-exec-time-resolvers-directive

query Foo @exec_time_resolvers {
query Foo @exec_time_resolvers(enabled: "execTimeResolversFlagProvider") {
me {
pet {
name
Expand Down Expand Up @@ -48,6 +48,7 @@ extend type User {
"backingField": {
"name": "pet",
"args": null,
"fragment": null,
"kind": "RelayResolver",
"storageKey": null,
"isOutputType": false,
Expand All @@ -67,6 +68,7 @@ extend type User {
{
"name": "name",
"args": null,
"fragment": null,
"kind": "RelayResolver",
"storageKey": null,
"isOutputType": false,
Expand All @@ -83,5 +85,5 @@ extend type User {
"storageKey": null
}
],
"use_exec_time_resolvers": true
"exec_time_resolvers_enabled_provider": require('execTimeResolversFlagProvider')
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# enable-exec-time-resolvers-directive

query Foo @exec_time_resolvers {
query Foo @exec_time_resolvers(enabled: "execTimeResolversFlagProvider") {
me {
pet {
name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ extend type Query {
"backingField": {
"name": "client_type",
"args": null,
"fragment": null,
"kind": "RelayResolver",
"storageKey": null,
"isOutputType": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ module.exports = ((node/*: any*/)/*: Fragment<

//- __generated__/PersonComponentQuery.graphql.js
/**
* <auto-generated> SignedSource<<dbeb4ee17a1c947989956e4d5cdee9eb>>
* <auto-generated> SignedSource<<96ff3019324330e6420cc620576fbee9>>
* @flow
* @lightSyntaxTransform
* @nogrep
Expand Down Expand Up @@ -244,6 +244,57 @@ v3 = {
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v4 = [
(v3/*: any*/)
],
v5 = {
"kind": "InlineFragment",
"selections": [
{
"name": "__relay_model_instance",
"args": null,
"fragment": {
"kind": "InlineFragment",
"selections": (v4/*: any*/),
"type": "Admin",
"abstractKey": null
},
"kind": "RelayResolver",
"storageKey": null,
"isOutputType": false,
"resolverInfo": {
"resolverFunction": require('AdminTypeResolvers').Admin,
"rootFragment": null
}
}
],
"type": "Admin",
"abstractKey": null
},
v6 = {
"kind": "InlineFragment",
"selections": [
{
"name": "__relay_model_instance",
"args": null,
"fragment": {
"kind": "InlineFragment",
"selections": (v4/*: any*/),
"type": "User",
"abstractKey": null
},
"kind": "RelayResolver",
"storageKey": null,
"isOutputType": false,
"resolverInfo": {
"resolverFunction": require('UserTypeResolvers').User,
"rootFragment": null
}
}
],
"type": "User",
"abstractKey": null
};
return {
"fragment": {
Expand Down Expand Up @@ -376,6 +427,7 @@ return {
{
"name": "name",
"args": null,
"fragment": (v5/*: any*/),
"kind": "RelayResolver",
"storageKey": null,
"isOutputType": true,
Expand All @@ -394,6 +446,7 @@ return {
{
"name": "name",
"args": null,
"fragment": (v6/*: any*/),
"kind": "RelayResolver",
"storageKey": null,
"isOutputType": true,
Expand Down Expand Up @@ -430,6 +483,7 @@ return {
{
"name": "description",
"args": null,
"fragment": (v5/*: any*/),
"kind": "RelayResolver",
"storageKey": null,
"isOutputType": true,
Expand All @@ -448,6 +502,7 @@ return {
{
"name": "description",
"args": null,
"fragment": (v6/*: any*/),
"kind": "RelayResolver",
"storageKey": null,
"isOutputType": true,
Expand Down
Loading

0 comments on commit 8c32ec4

Please sign in to comment.