Skip to content

Commit

Permalink
Allow Node interface id field to be configurable. (#3638)
Browse files Browse the repository at this point in the history
Summary:
This adds a new option to the Rust compiler, `nodeInterfaceIdField`, which allows customization of the `Node` interface's `id` field. I initially tried to determine this via inference, but it's entirely possible that the `Node` interface has multiple `id` fields, so I figured explicitly specifying it made more sense.

Pull Request resolved: #3638

Reviewed By: josephsavona

Differential Revision: D33131805

Pulled By: alunyov

fbshipit-source-id: ffbdfebf7bd958e0f81f281a291f1e579702d4ed
  • Loading branch information
kesne authored and facebook-github-bot committed Jan 5, 2022
1 parent 995bb87 commit c3f0292
Show file tree
Hide file tree
Showing 62 changed files with 5,464 additions and 66 deletions.
2 changes: 1 addition & 1 deletion compiler/crates/relay-codegen/tests/client_edges/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub fn transform_fixture(fixture: &Fixture<'_>) -> Result<String, String> {
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, &Default::default())
.and_then(|program| relay_resolvers(&program, true))
.unwrap(),
);
Expand Down
4 changes: 2 additions & 2 deletions compiler/crates/relay-compiler/src/build_project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ pub fn build_programs(
) -> Result<BuildProgramsOutput, BuildProjectFailure> {
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.schema_config)
&& if let Some(base) = project_config.base {
!compiler_state.has_breaking_schema_change(base)
!compiler_state.has_breaking_schema_change(base, &project_config.schema_config)
} else {
true
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub fn validate(
additional_validations: &Option<AdditionalValidations>,
) -> DiagnosticsResult<()> {
try_all(vec![
disallow_reserved_aliases(program),
disallow_reserved_aliases(program, &project_config.schema_config),
validate_no_double_underscore_alias(program),
validate_unused_variables(program),
validate_unused_fragment_variables(program),
Expand Down
13 changes: 9 additions & 4 deletions compiler/crates/relay-compiler/src/compiler_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use fnv::{FnvBuildHasher, FnvHashMap, FnvHashSet};
use graphql_syntax::GraphQLSource;
use intern::string_key::StringKey;
use rayon::prelude::*;
use relay_config::SchemaConfig;
use relay_transforms::DependencyMap;
use schema::SDLSchema;
use schema_diff::{definitions::SchemaChange, detect_changes};
Expand Down Expand Up @@ -341,7 +342,7 @@ impl CompilerState {
.any(|sources| !sources.processed.is_empty())
}

fn is_change_safe(&self, sources: &SchemaSources) -> bool {
fn is_change_safe(&self, sources: &SchemaSources, schema_config: &SchemaConfig) -> bool {
let previous = sources
.get_old_sources()
.into_iter()
Expand All @@ -363,7 +364,7 @@ impl CompilerState {
&current,
&Vec::<(&str, SourceLocationKey)>::new(),
) {
Ok(schema) => schema_change.is_safe(&schema),
Ok(schema) => schema_change.is_safe(&schema, schema_config),
Err(_) => false,
}
}
Expand All @@ -381,14 +382,18 @@ 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,
schema_config: &SchemaConfig,
) -> 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, schema_config)) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
==================================== INPUT ====================================
fragment fragmentOnNodeInterface_RefetchableFragment on Node
@refetchable(queryName: "RefetchableFragmentQuery") {
global_id
... on User {
name
...fragmentOnNodeInterface_ProfilePicture
}
}

fragment fragmentOnNodeInterface_ProfilePicture on User {
profilePicture(size: $size) {
uri
}
}
==================================== OUTPUT ===================================
{
"fragment": {
"argumentDefinitions": [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "global_id"
},
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "size"
}
],
"kind": "Fragment",
"metadata": null,
"name": "RefetchableFragmentQuery",
"selections": [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "global_id",
"variableName": "global_id"
}
],
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"args": null,
"kind": "FragmentSpread",
"name": "fragmentOnNodeInterface_RefetchableFragment"
}
],
"storageKey": null
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "size"
},
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "global_id"
}
],
"kind": "Operation",
"name": "RefetchableFragmentQuery",
"selections": [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "global_id",
"variableName": "global_id"
}
],
"concreteType": null,
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
{
"kind": "TypeDiscriminator",
"abstractKey": "__isNode"
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "global_id",
"storageKey": null
},
{
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "size",
"variableName": "size"
}
],
"concreteType": "Image",
"kind": "LinkedField",
"name": "profilePicture",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "uri",
"storageKey": null
}
],
"storageKey": null
}
],
"type": "User",
"abstractKey": null
}
],
"storageKey": null
}
]
},
"params": {
"id": null,
"metadata": {},
"name": "RefetchableFragmentQuery",
"operationKind": "query",
"text": null
}
}

QUERY:

query RefetchableFragmentQuery(
$size: [Int]
$global_id: ID!
) {
node(global_id: $global_id) {
__typename
...fragmentOnNodeInterface_RefetchableFragment
global_id
}
}

fragment fragmentOnNodeInterface_ProfilePicture on User {
profilePicture(size: $size) {
uri
}
}

fragment fragmentOnNodeInterface_RefetchableFragment on Node {
__isNode: __typename
global_id
... on User {
name
...fragmentOnNodeInterface_ProfilePicture
}
}


{
"argumentDefinitions": [
{
"kind": "RootArgument",
"name": "size"
}
],
"kind": "Fragment",
"metadata": null,
"name": "fragmentOnNodeInterface_ProfilePicture",
"selections": [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "size",
"variableName": "size"
}
],
"concreteType": "Image",
"kind": "LinkedField",
"name": "profilePicture",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "uri",
"storageKey": null
}
],
"storageKey": null
}
],
"type": "User",
"abstractKey": null
}

{
"argumentDefinitions": [
{
"kind": "RootArgument",
"name": "size"
}
],
"kind": "Fragment",
"metadata": {
"refetch": {
"connection": null,
"fragmentPathInResult": [
"node"
],
"operation": require('RefetchableFragmentQuery.graphql'),
"identifierField": "global_id"
}
},
"name": "fragmentOnNodeInterface_RefetchableFragment",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "global_id",
"storageKey": null
},
{
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
{
"args": null,
"kind": "FragmentSpread",
"name": "fragmentOnNodeInterface_ProfilePicture"
}
],
"type": "User",
"abstractKey": null
}
],
"type": "Node",
"abstractKey": "__isNode"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
fragment fragmentOnNodeInterface_RefetchableFragment on Node
@refetchable(queryName: "RefetchableFragmentQuery") {
global_id
... on User {
name
...fragmentOnNodeInterface_ProfilePicture
}
}

fragment fragmentOnNodeInterface_ProfilePicture on User {
profilePicture(size: $size) {
uri
}
}
Loading

0 comments on commit c3f0292

Please sign in to comment.