diff --git a/compiler/crates/common/src/feature_flags.rs b/compiler/crates/common/src/feature_flags.rs index c818ee3addd5c..47cbb2944aeba 100644 --- a/compiler/crates/common/src/feature_flags.rs +++ b/compiler/crates/common/src/feature_flags.rs @@ -49,6 +49,10 @@ pub struct FeatureFlags { #[serde(default)] pub enable_provided_variables: FeatureFlag, + + /// The name of the `id` field that exists on the `Node` interface. + #[serde(default)] + pub node_interface_id_field: Option, } #[derive(Debug, Deserialize, Clone, Serialize)] diff --git a/compiler/crates/relay-codegen/tests/client_edges/mod.rs b/compiler/crates/relay-codegen/tests/client_edges/mod.rs index bc6bb33f6057f..1a807bd849dd0 100644 --- a/compiler/crates/relay-codegen/tests/client_edges/mod.rs +++ b/compiler/crates/relay-codegen/tests/client_edges/mod.rs @@ -22,7 +22,7 @@ pub fn transform_fixture(fixture: &Fixture<'_>) -> Result { let ir = build(&schema, &ast.definitions).unwrap(); let program = Program::from_definitions(Arc::clone(&schema), ir); let next_program = sort_selections( - &client_edges(&program) + &client_edges(&program, None) .and_then(|program| relay_resolvers(&program, true)) .unwrap(), ); diff --git a/compiler/crates/relay-compiler-playground/src/lib.rs b/compiler/crates/relay-compiler-playground/src/lib.rs index f6f33561ce269..91f6238d9cb31 100644 --- a/compiler/crates/relay-compiler-playground/src/lib.rs +++ b/compiler/crates/relay-compiler-playground/src/lib.rs @@ -310,6 +310,7 @@ fn get_programs( base_fragment_names, &connection_interface, Arc::new(feature_flags), + None, &None, Arc::new(perf_logger), None, diff --git a/compiler/crates/relay-compiler/src/build_project/mod.rs b/compiler/crates/relay-compiler/src/build_project/mod.rs index 37575cb2a9296..e0a3e7278405b 100644 --- a/compiler/crates/relay-compiler/src/build_project/mod.rs +++ b/compiler/crates/relay-compiler/src/build_project/mod.rs @@ -156,9 +156,9 @@ pub fn build_programs( ) -> Result { let project_name = project_config.name; let is_incremental_build = compiler_state.has_processed_changes() - && !compiler_state.has_breaking_schema_change(project_name) + && !compiler_state.has_breaking_schema_change(project_name, project_config) && if let Some(base) = project_config.base { - !compiler_state.has_breaking_schema_change(base) + !compiler_state.has_breaking_schema_change(base, project_config) } else { true }; diff --git a/compiler/crates/relay-compiler/src/compiler_state.rs b/compiler/crates/relay-compiler/src/compiler_state.rs index d2876dccd5eee..b5f4904ea9c21 100644 --- a/compiler/crates/relay-compiler/src/compiler_state.rs +++ b/compiler/crates/relay-compiler/src/compiler_state.rs @@ -6,7 +6,7 @@ */ use crate::artifact_map::ArtifactMap; -use crate::config::Config; +use crate::config::{Config, ProjectConfig}; use crate::errors::{Error, Result}; use crate::file_source::{ categorize_files, extract_graphql_strings_from_file, read_file_to_string, Clock, File, @@ -341,7 +341,7 @@ impl CompilerState { .any(|sources| !sources.processed.is_empty()) } - fn is_change_safe(&self, sources: &SchemaSources) -> bool { + fn is_change_safe(&self, sources: &SchemaSources, project_config: &ProjectConfig) -> bool { let previous = sources .get_old_sources() .into_iter() @@ -363,7 +363,7 @@ impl CompilerState { ¤t, &Vec::<(&str, SourceLocationKey)>::new(), ) { - Ok(schema) => schema_change.is_safe(&schema), + Ok(schema) => schema_change.is_safe(&schema, project_config.feature_flags.node_interface_id_field), Err(_) => false, } } @@ -381,14 +381,14 @@ impl CompilerState { } /// This method is looking at the pending schema changes to see if they may be breaking (removed types, renamed field, etc) - pub fn has_breaking_schema_change(&self, project_name: StringKey) -> bool { + pub fn has_breaking_schema_change(&self, project_name: StringKey, project_config: &ProjectConfig) -> bool { if let Some(extension) = self.extensions.get(&project_name) { if !extension.pending.is_empty() { return true; } } if let Some(schema) = self.schemas.get(&project_name) { - if !(schema.pending.is_empty() || self.is_change_safe(schema)) { + if !(schema.pending.is_empty() || self.is_change_safe(schema, project_config)) { return true; } } diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/mod.rs b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/mod.rs index a03b4ab9b7236..b1a4b5f689b3e 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/mod.rs +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/mod.rs @@ -81,6 +81,7 @@ pub fn transform_fixture(fixture: &Fixture<'_>) -> Result { text_artifacts: FeatureFlag::Disabled, enable_client_edges: FeatureFlag::Enabled, enable_provided_variables: FeatureFlag::Enabled, + node_interface_id_field: None, }; // TODO pass base fragment names diff --git a/compiler/crates/relay-lsp/src/server/lsp_state_resources.rs b/compiler/crates/relay-lsp/src/server/lsp_state_resources.rs index a4a6c6bbd9d28..3ef18a36bbbc8 100644 --- a/compiler/crates/relay-lsp/src/server/lsp_state_resources.rs +++ b/compiler/crates/relay-lsp/src/server/lsp_state_resources.rs @@ -342,9 +342,9 @@ impl, + feature_flags: Arc, connection_interface: &ConnectionInterface, base_fragment_names: Arc, perf_logger: Arc, @@ -239,7 +240,7 @@ fn apply_operation_transforms( program = log_event.time("split_module_import", || { split_module_import(&program, &base_fragment_names) }); - program = log_event.time("generate_id_field", || generate_id_field(&program)); + program = log_event.time("generate_id_field", || generate_id_field(&program, feature_flags.node_interface_id_field)); program = log_event.time("declarative_connection", || { transform_declarative_connection(&program, connection_interface) })?; @@ -411,7 +412,7 @@ fn apply_typegen_transforms( transform_subscriptions(&program) })?; program = log_event.time("required_directive", || required_directive(&program))?; - program = log_event.time("client_edges", || client_edges(&program))?; + program = log_event.time("client_edges", || client_edges(&program, feature_flags.node_interface_id_field))?; program = log_event.time( "transform_assignable_fragment_spreads_in_regular_queries", || transform_assignable_fragment_spreads_in_regular_queries(&program), @@ -424,7 +425,7 @@ fn apply_typegen_transforms( })?; log_event.time("flatten", || flatten(&mut program, false, false))?; program = log_event.time("transform_refetchable_fragment", || { - transform_refetchable_fragment(&program, &base_fragment_names, true) + transform_refetchable_fragment(&program, feature_flags.node_interface_id_field, &base_fragment_names, true) })?; program = log_event.time("remove_base_fragments", || { remove_base_fragments(&program, base_fragment_names) diff --git a/compiler/crates/relay-transforms/src/client_edges.rs b/compiler/crates/relay-transforms/src/client_edges.rs index 461bde02a36b7..77d1b85a4cf96 100644 --- a/compiler/crates/relay-transforms/src/client_edges.rs +++ b/compiler/crates/relay-transforms/src/client_edges.rs @@ -79,8 +79,8 @@ impl<'a> ClientEdgeMetadata<'a> { }) } } -pub fn client_edges(program: &Program) -> DiagnosticsResult { - let mut transform = ClientEdgesTransform::new(program); +pub fn client_edges(program: &Program, node_interface_id_field: Option) -> DiagnosticsResult { + let mut transform = ClientEdgesTransform::new(program, node_interface_id_field); let mut next_program = transform .transform_program(program) .replace_or_else(|| program.clone()); @@ -106,12 +106,14 @@ struct ClientEdgesTransform<'program> { new_fragments: Vec>, new_operations: Vec, errors: Vec, + node_interface_id_field: Option, } impl<'program> ClientEdgesTransform<'program> { - fn new(program: &'program Program) -> Self { + fn new(program: &'program Program, node_interface_id_field: Option) -> Self { Self { program, + node_interface_id_field, path: Default::default(), query_names: Default::default(), document_name: Default::default(), @@ -169,7 +171,7 @@ impl<'program> ClientEdgesTransform<'program> { selections, }; - let mut transformer = RefetchableFragment::new(self.program, false); + let mut transformer = RefetchableFragment::new(self.program, false, self.node_interface_id_field); let refetchable_fragment = transformer .transform_refetch_fragment_with_refetchable_directive( diff --git a/compiler/crates/relay-transforms/src/generate_id_field.rs b/compiler/crates/relay-transforms/src/generate_id_field.rs index 52c2fe893011d..bc55768eac131 100644 --- a/compiler/crates/relay-transforms/src/generate_id_field.rs +++ b/compiler/crates/relay-transforms/src/generate_id_field.rs @@ -20,8 +20,8 @@ use std::{ /// A transform that adds an `id` field on any type that has an id field but /// where there is no unaliased `id` selection. -pub fn generate_id_field(program: &Program) -> Program { - let mut transform = GenerateIDFieldTransform::new(program); +pub fn generate_id_field(program: &Program, node_interface_id_field: Option) -> Program { + let mut transform = GenerateIDFieldTransform::new(program, node_interface_id_field); transform .transform_program(program) .replace_or_else(|| program.clone()) @@ -113,8 +113,8 @@ impl<'s> Transformer for GenerateIDFieldTransform<'s> { } impl<'s> GenerateIDFieldTransform<'s> { - fn new(program: &'s Program) -> Self { - let id_name = "id".intern(); + fn new(program: &'s Program, node_interface_id_field: Option) -> Self { + let id_name = node_interface_id_field.unwrap_or_else(|| "id".intern()); let schema = &program.schema; let node_interface = match schema.get_type("Node".intern()) { @@ -124,7 +124,7 @@ impl<'s> GenerateIDFieldTransform<'s> { .fields .iter() .find(|&&id| schema.field(id).name.item == id_name) - .expect("Expected `Node` to contain a field named `id`."); + .expect(&format!("Expected `Node` to contain a field named `{:}`.", id_name).to_string()); Some(NodeInterface { id: node_interface_id, diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment/fetchable_query_generator.rs b/compiler/crates/relay-transforms/src/refetchable_fragment/fetchable_query_generator.rs index a66c867377173..7037b6c170516 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment/fetchable_query_generator.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment/fetchable_query_generator.rs @@ -22,10 +22,13 @@ use std::sync::Arc; fn build_refetch_operation( schema: &SDLSchema, + node_interface_id_field: Option, fragment: &Arc, query_name: StringKey, variables_map: &VariableMap, ) -> DiagnosticsResult> { + let id_name = node_interface_id_field.unwrap_or_else(|| "id".intern()); + if let Some(identifier_field_name) = get_fetchable_field_name(fragment, schema)? { let identifier_field_id = get_identifier_field_id(fragment, schema, identifier_field_name)?; @@ -58,7 +61,7 @@ fn build_refetch_operation( ), }); let mut variable_definitions = build_operation_variable_definitions(&fragment); - if let Some(id_argument) = variable_definitions.named(CONSTANTS.id_name) { + if let Some(id_argument) = variable_definitions.named(id_name) { return Err(vec![Diagnostic::error( ValidationMessage::RefetchableFragmentOnNodeWithExistingID { fragment_name: fragment.name.item, @@ -67,7 +70,7 @@ fn build_refetch_operation( )]); } variable_definitions.push(VariableDefinition { - name: WithLocation::new(fragment.name.location, CONSTANTS.id_name), + name: WithLocation::new(fragment.name.location, id_name), type_: id_arg.type_.non_null(), default_value: None, directives: vec![], @@ -83,7 +86,7 @@ fn build_refetch_operation( value: WithLocation::new( fragment.name.location, Value::Variable(Variable { - name: WithLocation::new(fragment.name.location, CONSTANTS.id_name), + name: WithLocation::new(fragment.name.location, id_name), type_: id_arg.type_.non_null(), }), ), diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment/mod.rs b/compiler/crates/relay-transforms/src/refetchable_fragment/mod.rs index 60bc3f438759c..39a7704139b3c 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment/mod.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment/mod.rs @@ -54,13 +54,14 @@ pub use self::refetchable_directive::REFETCHABLE_NAME; /// Fragment to Root IR nodes. pub fn transform_refetchable_fragment( program: &Program, + node_interface_id_field: Option, base_fragment_names: &'_ StringKeySet, for_typegen: bool, ) -> DiagnosticsResult { let mut next_program = Program::new(Arc::clone(&program.schema)); let query_type = program.schema.query_type().unwrap(); - let mut transformer = RefetchableFragment::new(program, for_typegen); + let mut transformer = RefetchableFragment::new(program, for_typegen, node_interface_id_field); for operation in program.operations() { next_program.insert_operation(Arc::clone(operation)); @@ -102,15 +103,17 @@ pub struct RefetchableFragment<'program> { existing_refetch_operations: ExistingRefetchOperations, for_typegen: bool, program: &'program Program, + node_interface_id_field: Option, } impl<'program> RefetchableFragment<'program> { - pub fn new(program: &'program Program, for_typegen: bool) -> Self { + pub fn new(program: &'program Program, for_typegen: bool, node_interface_id_field: Option) -> Self { RefetchableFragment { connection_constants: Default::default(), existing_refetch_operations: Default::default(), for_typegen, program, + node_interface_id_field, } } @@ -145,6 +148,7 @@ impl<'program> RefetchableFragment<'program> { for generator in GENERATORS.iter() { if let Some(refetch_root) = (generator.build_refetch_operation)( &self.program.schema, + self.node_interface_id_field, fragment, refetchable_directive.query_name.item, &variables_map, @@ -294,6 +298,7 @@ impl<'program> RefetchableFragment<'program> { type BuildRefetchOperationFn = fn( schema: &SDLSchema, + node_interface_id_field: Option, fragment: &Arc, query_name: StringKey, variables_map: &VariableMap, diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment/node_query_generator.rs b/compiler/crates/relay-transforms/src/refetchable_fragment/node_query_generator.rs index f3194c2537d3c..4a76c508fbffa 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment/node_query_generator.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment/node_query_generator.rs @@ -16,16 +16,19 @@ use graphql_ir::{ Argument, Field, FragmentDefinition, InlineFragment, LinkedField, ScalarField, Selection, ValidationMessage, Value, Variable, VariableDefinition, }; -use intern::string_key::StringKey; +use intern::string_key::{Intern,StringKey}; use schema::{Argument as ArgumentDef, FieldID, InterfaceID, SDLSchema, Schema, Type}; use std::sync::Arc; fn build_refetch_operation( schema: &SDLSchema, + node_interface_id_field: Option, fragment: &Arc, query_name: StringKey, variables_map: &VariableMap, ) -> DiagnosticsResult> { + let id_name = node_interface_id_field.unwrap_or_else(|| "id".intern()); + let node_interface_id = schema.get_type(CONSTANTS.node_type_name).and_then(|type_| { if let Type::Interface(id) = type_ { Some(id) @@ -33,6 +36,7 @@ fn build_refetch_operation( None } }); + match node_interface_id { None => Ok(None), Some(node_interface_id) => { @@ -71,7 +75,7 @@ fn build_refetch_operation( // Check if the fragment type have an `id` field let should_generate_inline_fragment_on_node = schema - .named_field(fragment.type_condition, CONSTANTS.id_name) + .named_field(fragment.type_condition, id_name) .is_none(); let query_type = schema.query_type().unwrap(); @@ -81,8 +85,8 @@ fn build_refetch_operation( let id_field_id = *node_interface .fields .iter() - .find(|&&id| schema.field(id).name.item == CONSTANTS.id_name) - .expect("Expected `Node` to contain a field named `id`."); + .find(|&&id| schema.field(id).name.item == id_name) + .expect(&format!("Expected `Node` to contain a field named `{:}`.", id_name).to_string()); let fragment = Arc::new(FragmentDefinition { directives: build_fragment_metadata_as_directive( @@ -90,7 +94,7 @@ fn build_refetch_operation( RefetchableMetadata { operation_name: query_name, path: vec![CONSTANTS.node_field_name], - identifier_field: Some(CONSTANTS.id_name), + identifier_field: Some(id_name), }, ), used_global_variables: build_used_global_variables( @@ -99,6 +103,7 @@ fn build_refetch_operation( )?, variable_definitions: fragment.variable_definitions.clone(), selections: enforce_selections_with_id_field( + node_interface_id_field, fragment, schema, id_field_id, @@ -111,7 +116,7 @@ fn build_refetch_operation( ..fragment.as_ref().clone() }); let mut variable_definitions = build_operation_variable_definitions(&fragment); - if let Some(id_argument) = variable_definitions.named(CONSTANTS.id_name) { + if let Some(id_argument) = variable_definitions.named(id_name) { return Err(vec![Diagnostic::error( ValidationMessage::RefetchableFragmentOnNodeWithExistingID { fragment_name: fragment.name.item, @@ -121,7 +126,7 @@ fn build_refetch_operation( } variable_definitions.push(VariableDefinition { - name: WithLocation::new(fragment.name.location, CONSTANTS.id_name), + name: WithLocation::new(fragment.name.location, id_name), type_: id_arg.type_.non_null(), default_value: None, directives: vec![], @@ -136,7 +141,7 @@ fn build_refetch_operation( value: WithLocation::new( fragment.name.location, Value::Variable(Variable { - name: WithLocation::new(fragment.name.location, CONSTANTS.id_name), + name: WithLocation::new(fragment.name.location, id_name), type_: id_arg.type_.non_null(), }), ), @@ -174,15 +179,17 @@ fn get_node_field_id_and_id_arg<'s>( } fn enforce_selections_with_id_field( + node_interface_id_field: Option, fragment: &FragmentDefinition, schema: &SDLSchema, id_field_id: FieldID, node_interface_id: Option, ) -> Vec { + let id_name = node_interface_id_field.unwrap_or_else(|| "id".intern()); let mut next_selections = fragment.selections.clone(); let has_id_field = next_selections.iter().any(|sel| { if let Selection::ScalarField(field) = sel { - field.alias_or_name(schema) == CONSTANTS.id_name + field.alias_or_name(schema) == id_name && schema.field(field.definition.item).type_ == schema.field(id_field_id).type_ } else { false diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment/query_query_generator.rs b/compiler/crates/relay-transforms/src/refetchable_fragment/query_query_generator.rs index 54fdd40b1d507..2872e3e77cecd 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment/query_query_generator.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment/query_query_generator.rs @@ -19,6 +19,7 @@ use std::sync::Arc; fn build_refetch_operation( schema: &SDLSchema, + _node_interface_id_field: Option, fragment: &Arc, query_name: StringKey, variables_map: &VariableMap, diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment/utils.rs b/compiler/crates/relay-transforms/src/refetchable_fragment/utils.rs index 700c4ea717e1a..fc8760cc5c943 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment/utils.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment/utils.rs @@ -26,7 +26,6 @@ associated_data_impl!(RefetchableMetadata); pub struct Constants { pub fetchable: StringKey, pub field_name: StringKey, - pub id_name: StringKey, pub node_field_name: StringKey, pub node_type_name: StringKey, pub viewer_field_name: StringKey, @@ -37,7 +36,6 @@ lazy_static! { pub static ref CONSTANTS: Constants = Constants { fetchable: "fetchable".intern(), field_name: "field_name".intern(), - id_name: "id".intern(), node_field_name: "node".intern(), node_type_name: "Node".intern(), viewer_field_name: "viewer".intern(), diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment/viewer_query_generator.rs b/compiler/crates/relay-transforms/src/refetchable_fragment/viewer_query_generator.rs index 0ad99b3f25b9f..d1b1c12487768 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment/viewer_query_generator.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment/viewer_query_generator.rs @@ -19,6 +19,7 @@ use std::sync::Arc; fn build_refetch_operation( schema: &SDLSchema, + _node_interface_id_field: Option, fragment: &Arc, query_name: StringKey, variables_map: &VariableMap, diff --git a/compiler/crates/relay-transforms/src/relay_client_component.rs b/compiler/crates/relay-transforms/src/relay_client_component.rs index 9d7af8fa5a29e..e428d8b64e2ae 100644 --- a/compiler/crates/relay-transforms/src/relay_client_component.rs +++ b/compiler/crates/relay-transforms/src/relay_client_component.rs @@ -30,7 +30,6 @@ lazy_static! { pub static ref RELAY_CLIENT_COMPONENT_DIRECTIVE_NAME: StringKey = "relay_client_component".intern(); static ref STRING_TYPE: StringKey = "String".intern(); - static ref ID_FIELD_NAME: StringKey = "id".intern(); static ref NODE_TYPE_NAME: StringKey = "Node".intern(); static ref VIEWER_TYPE_NAME: StringKey = "Viewer".intern(); } diff --git a/compiler/crates/relay-transforms/tests/client_edges/mod.rs b/compiler/crates/relay-transforms/tests/client_edges/mod.rs index 1d26822375ad8..051a76dd9f6a8 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/mod.rs +++ b/compiler/crates/relay-transforms/tests/client_edges/mod.rs @@ -24,7 +24,7 @@ pub fn transform_fixture(fixture: &Fixture<'_>) -> Result { let ir = build(&schema, &ast.definitions).unwrap(); let program = Program::from_definitions(Arc::clone(&schema), ir); - let mut next_program = client_edges(&program) + let mut next_program = client_edges(&program, None) .map_err(|diagnostics| diagnostics_to_sorted_string(fixture.content, &diagnostics))?; next_program = relay_resolvers(&next_program, true) diff --git a/compiler/crates/relay-transforms/tests/generate_id_field/mod.rs b/compiler/crates/relay-transforms/tests/generate_id_field/mod.rs index a0e65aae6d05c..1ec7b34b86992 100644 --- a/compiler/crates/relay-transforms/tests/generate_id_field/mod.rs +++ b/compiler/crates/relay-transforms/tests/generate_id_field/mod.rs @@ -10,5 +10,5 @@ use graphql_test_helpers::apply_transform_for_test; use relay_transforms::generate_id_field; pub fn transform_fixture(fixture: &Fixture<'_>) -> Result { - apply_transform_for_test(fixture, |program| Ok(generate_id_field(program))) + apply_transform_for_test(fixture, |program| Ok(generate_id_field(program, None))) } diff --git a/compiler/crates/relay-transforms/tests/refetchable_fragment/mod.rs b/compiler/crates/relay-transforms/tests/refetchable_fragment/mod.rs index 69880ffc744d8..d9bd2cc217f5d 100644 --- a/compiler/crates/relay-transforms/tests/refetchable_fragment/mod.rs +++ b/compiler/crates/relay-transforms/tests/refetchable_fragment/mod.rs @@ -16,6 +16,6 @@ pub fn transform_fixture(fixture: &Fixture<'_>) -> Result { apply_transform_for_test(fixture, |program| { let program = transform_connections(program, &ConnectionInterface::default()); let base_fragments = Default::default(); - transform_refetchable_fragment(&program, &base_fragments, false) + transform_refetchable_fragment(&program, None, &base_fragments, false) }) } diff --git a/compiler/crates/schema-diff/src/check.rs b/compiler/crates/schema-diff/src/check.rs index 1e29ae638a923..5ea92129c658a 100644 --- a/compiler/crates/schema-diff/src/check.rs +++ b/compiler/crates/schema-diff/src/check.rs @@ -12,7 +12,7 @@ use schema::{SDLSchema, Schema}; /// Return if the changes are safe to skip full rebuild. impl SchemaChange { - pub fn is_safe(self: &SchemaChange, schema: &SDLSchema) -> bool { + pub fn is_safe(self: &SchemaChange, schema: &SDLSchema, node_interface_id_field: Option) -> bool { match self { SchemaChange::None => true, SchemaChange::GenericChange => false, @@ -30,7 +30,7 @@ impl SchemaChange { } => { if !interfaces_added.is_empty() || !interfaces_removed.is_empty() - || !is_field_changes_safe(added, removed, changed) + || !is_field_changes_safe(node_interface_id_field, added, removed, changed) { return false; } @@ -41,12 +41,12 @@ impl SchemaChange { removed, .. } => { - if !is_field_changes_safe(added, removed, changed) { + if !is_field_changes_safe(node_interface_id_field, added, removed, changed) { return false; } } DefinitionChange::ObjectAdded(name) => { - if !is_object_add_safe(*name, schema) { + if !is_object_add_safe(*name, schema, node_interface_id_field) { return false; } } @@ -70,7 +70,6 @@ impl SchemaChange { } lazy_static! { - static ref ID_FIELD_KEY: StringKey = "id".intern(); static ref JS_FIELD_KEY: StringKey = "js".intern(); static ref NODE_INTERFACE_KEY: StringKey = "Node".intern(); } @@ -84,13 +83,15 @@ lazy_static! { /// } /// But we have a special case for `Node`. The `id` field is automatically /// added to the selection for all types that implements `Node`. -fn is_object_add_safe(name: StringKey, schema: &SDLSchema) -> bool { +fn is_object_add_safe(name: StringKey, schema: &SDLSchema, node_interface_id_field: Option) -> bool { + let id_name = node_interface_id_field.unwrap_or_else(|| "id".intern()); + if let Some(schema::Type::Object(id)) = schema.get_type(name) { let object = schema.object(id); if object .fields .iter() - .any(|id| schema.field(*id).name.item == *ID_FIELD_KEY) + .any(|id| schema.field(*id).name.item == id_name) && object .interfaces .iter() @@ -108,10 +109,13 @@ fn is_object_add_safe(name: StringKey, schema: &SDLSchema) -> bool { } fn is_field_changes_safe( + node_interface_id_field: Option, added: &[TypeChange], removed: &[TypeChange], changed: &[ArgumentChange], ) -> bool { + let id_name = node_interface_id_field.unwrap_or_else(|| "id".intern()); + if !removed.is_empty() { return false; } @@ -121,7 +125,7 @@ fn is_field_changes_safe( // - `js` field added to a type might change 3D code generation if added .iter() - .any(|add| add.name == *ID_FIELD_KEY || add.name == *JS_FIELD_KEY) + .any(|add| add.name == id_name || add.name == *JS_FIELD_KEY) { return false; } diff --git a/compiler/crates/schema-diff/tests/diff_schema_tests.rs b/compiler/crates/schema-diff/tests/diff_schema_tests.rs index 55022f96e394a..6596c4a72a19a 100644 --- a/compiler/crates/schema-diff/tests/diff_schema_tests.rs +++ b/compiler/crates/schema-diff/tests/diff_schema_tests.rs @@ -20,7 +20,7 @@ fn diff(current: &str, previous: &str) -> SchemaChange { fn is_safe(current: &str, previous: &str) -> bool { let schema = build_schema(current).unwrap(); let change = detect_changes(&[current], &[previous]); - change.is_safe(&schema) + change.is_safe(&schema, None) } #[test]