From 018af6da244e1242833e1bd89e8762c1409bee6d Mon Sep 17 00:00:00 2001 From: Inanc Sevinc Date: Mon, 9 Nov 2020 11:28:17 -0800 Subject: [PATCH] Add __token metafield to schema definition Reviewed By: kassens Differential Revision: D24509450 fbshipit-source-id: fc5c173e784690e7b753a4f811cc44a0108da06f --- compiler/crates/graphql-ir/src/build.rs | 29 +++++++++++++++++ compiler/crates/graphql-ir/src/errors.rs | 3 ++ ...etch_token_with_arguments.invalid.expected | 13 ++++++++ ...fetch_token_with_arguments.invalid.graphql | 4 +++ .../crates/graphql-ir/tests/parse_test.rs | 9 +++++- compiler/crates/schema/src/definitions.rs | 31 +++++++++++++++++++ .../directives-for-external-types.expected | 12 +++++++ .../interface-implements-interface.expected | 12 +++++++ .../fixtures/kitchen-sink.expected | 12 +++++++ 9 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 compiler/crates/graphql-ir/tests/parse/fixtures/fetch_token_with_arguments.invalid.expected create mode 100644 compiler/crates/graphql-ir/tests/parse/fixtures/fetch_token_with_arguments.invalid.graphql diff --git a/compiler/crates/graphql-ir/src/build.rs b/compiler/crates/graphql-ir/src/build.rs index 98cd77f22fa7f..470aa9af618ab 100644 --- a/compiler/crates/graphql-ir/src/build.rs +++ b/compiler/crates/graphql-ir/src/build.rs @@ -24,6 +24,7 @@ use schema::{ lazy_static! { static ref TYPENAME_FIELD_NAME: StringKey = "__typename".intern(); + static ref FETCH_TOKEN_FIELD_NAME: StringKey = "__token".intern(); /// Relay extension field that's available on all types. static ref CLIENT_ID_FIELD_NAME: StringKey = "__id".intern(); @@ -813,6 +814,8 @@ impl<'schema, 'signatures> Builder<'schema, 'signatures> { return self.build_typename_field(field); } else if self.options.relay_mode && field_name == *CLIENT_ID_FIELD_NAME { return self.build_clientid_field(field); + } else if field_name == *FETCH_TOKEN_FIELD_NAME { + return self.build_fetch_token_field(field); }; let span = field.name.span; let field_id = match self.lookup_field( @@ -911,6 +914,32 @@ impl<'schema, 'signatures> Builder<'schema, 'signatures> { }) } + fn build_fetch_token_field( + &mut self, + field: &graphql_syntax::ScalarField, + ) -> DiagnosticsResult { + let field_id = self.schema.fetch_token_field(); + let alias = self.build_alias(&field.alias); + if let Some(arguments) = &field.arguments { + return Err(Diagnostic::error( + ValidationMessage::InvalidArgumentsOnFetchTokenField(), + self.location.with_span(arguments.span), + ) + .into()); + } + let directives = self.build_directives(&field.directives, DirectiveLocation::Field)?; + Ok(ScalarField { + alias, + definition: WithLocation::from_span( + self.location.source_location(), + field.name.span, + field_id, + ), + arguments: Default::default(), + directives, + }) + } + fn build_alias( &mut self, alias: &Option, diff --git a/compiler/crates/graphql-ir/src/errors.rs b/compiler/crates/graphql-ir/src/errors.rs index 1c28fc2941715..30e085859d855 100644 --- a/compiler/crates/graphql-ir/src/errors.rs +++ b/compiler/crates/graphql-ir/src/errors.rs @@ -145,6 +145,9 @@ pub enum ValidationMessage { #[error("Unexpected arguments on `__typename` field")] InvalidArgumentsOnTypenameField(), + #[error("Unexpected arguments on '__token' field")] + InvalidArgumentsOnFetchTokenField(), + #[error( "Relay does not allow aliasing fields to `id`. This name is reserved for the globally unique `id` field on `Node`." )] diff --git a/compiler/crates/graphql-ir/tests/parse/fixtures/fetch_token_with_arguments.invalid.expected b/compiler/crates/graphql-ir/tests/parse/fixtures/fetch_token_with_arguments.invalid.expected new file mode 100644 index 0000000000000..8502323c5c490 --- /dev/null +++ b/compiler/crates/graphql-ir/tests/parse/fixtures/fetch_token_with_arguments.invalid.expected @@ -0,0 +1,13 @@ +==================================== INPUT ==================================== +# expected-to-throw +fragment Foo on User { + __token(arg: 1) +} +==================================== ERROR ==================================== +✖︎ Unexpected arguments on '__token' field + + fetch_token_with_arguments.invalid.graphql:3:10 + 2 │ fragment Foo on User { + 3 │ __token(arg: 1) + │ ^^^^^^^^ + 4 │ } diff --git a/compiler/crates/graphql-ir/tests/parse/fixtures/fetch_token_with_arguments.invalid.graphql b/compiler/crates/graphql-ir/tests/parse/fixtures/fetch_token_with_arguments.invalid.graphql new file mode 100644 index 0000000000000..bff17cb5532dc --- /dev/null +++ b/compiler/crates/graphql-ir/tests/parse/fixtures/fetch_token_with_arguments.invalid.graphql @@ -0,0 +1,4 @@ +# expected-to-throw +fragment Foo on User { + __token(arg: 1) +} diff --git a/compiler/crates/graphql-ir/tests/parse_test.rs b/compiler/crates/graphql-ir/tests/parse_test.rs index 17082283ce084..a89f83814ab33 100644 --- a/compiler/crates/graphql-ir/tests/parse_test.rs +++ b/compiler/crates/graphql-ir/tests/parse_test.rs @@ -1,4 +1,4 @@ -// @generated SignedSource<> +// @generated SignedSource<> // Generated by $ cargo run -p fixture-tests -- oss/crates/graphql-ir/tests/parse mod parse; @@ -97,6 +97,13 @@ fn enum_values_invalid() { test_fixture(transform_fixture, "enum-values.invalid.graphql", "parse/fixtures/enum-values.invalid.expected", input, expected); } +#[test] +fn fetch_token_with_arguments_invalid() { + let input = include_str!("parse/fixtures/fetch_token_with_arguments.invalid.graphql"); + let expected = include_str!("parse/fixtures/fetch_token_with_arguments.invalid.expected"); + test_fixture(transform_fixture, "fetch_token_with_arguments.invalid.graphql", "parse/fixtures/fetch_token_with_arguments.invalid.expected", input, expected); +} + #[test] fn field_argument_missing_required_invalid() { let input = include_str!("parse/fixtures/field_argument_missing_required.invalid.graphql"); diff --git a/compiler/crates/schema/src/definitions.rs b/compiler/crates/schema/src/definitions.rs index efa76a9adf01e..8d2c262f322a3 100644 --- a/compiler/crates/schema/src/definitions.rs +++ b/compiler/crates/schema/src/definitions.rs @@ -31,9 +31,11 @@ pub struct Schema { clientid_field: FieldID, typename_field: FieldID, + fetch_token_field: FieldID, clientid_field_name: StringKey, typename_field_name: StringKey, + fetch_token_field_name: StringKey, string_type: Option, id_type: Option, @@ -72,6 +74,10 @@ impl Schema { self.typename_field } + pub fn fetch_token_field(&self) -> FieldID { + self.fetch_token_field + } + pub fn get_type(&self, type_name: StringKey) -> Option { self.type_map.get(&type_name).cloned() } @@ -259,6 +265,10 @@ impl Schema { if name == self.typename_field_name { return Some(self.typename_field); } + // TODO(inanc): Also check if the parent type is fetchable? + if name == self.fetch_token_field_name { + return Some(self.fetch_token_field); + } if name == self.clientid_field_name { return Some(self.clientid_field); } @@ -631,8 +641,10 @@ impl Schema { type_map: HashMap::new(), clientid_field: FieldID(0), typename_field: FieldID(0), + fetch_token_field: FieldID(0), clientid_field_name: "__id".intern(), typename_field_name: "__typename".intern(), + fetch_token_field_name: "__token".intern(), string_type: None, id_type: None, unchecked_argument_type_sentinel: None, @@ -737,8 +749,10 @@ impl Schema { type_map, clientid_field: FieldID(0), // dummy value, overwritten later typename_field: FieldID(0), // dummy value, overwritten later + fetch_token_field: FieldID(0), // dummy value, overwritten later clientid_field_name: "__id".intern(), typename_field_name: "__typename".intern(), + fetch_token_field_name: "__token".intern(), string_type: Some(string_type), id_type: Some(id_type), unchecked_argument_type_sentinel, @@ -792,6 +806,7 @@ impl Schema { pub fn load_defaults(&mut self) { self.load_default_root_types(); self.load_default_typename_field(); + self.load_default_fetch_token_field(); self.load_default_clientid_field(); } @@ -832,6 +847,20 @@ impl Schema { }); } + fn load_default_fetch_token_field(&mut self) { + let id_type = *self.type_map.get(&"ID".intern()).unwrap(); + let fetch_token_field_id = self.fields.len(); + self.fetch_token_field = FieldID(fetch_token_field_id.try_into().unwrap()); + self.fields.push(Field { + name: self.fetch_token_field_name, + is_extension: false, + arguments: ArgumentDefinitions::new(Default::default()), + type_: TypeReference::NonNull(Box::new(TypeReference::Named(id_type))), + directives: Vec::new(), + parent_type: None, + }); + } + fn load_default_clientid_field(&mut self) { let id_type = *self.type_map.get(&"ID".intern()).unwrap(); let clientid_field_id = self.fields.len(); @@ -1296,8 +1325,10 @@ impl Schema { directives, clientid_field: _clientid_field, typename_field: _typename_field, + fetch_token_field: _fetch_token_field, clientid_field_name: _clientid_field_name, typename_field_name: _typename_field_name, + fetch_token_field_name: _fetch_token_field_name, string_type: _string_type, id_type: _id_type, unchecked_argument_type_sentinel: _unchecked_argument_type_sentinel, diff --git a/compiler/crates/schema/tests/build_schema/fixtures/directives-for-external-types.expected b/compiler/crates/schema/tests/build_schema/fixtures/directives-for-external-types.expected index d285a09d5afe2..8fbe3bbe3a031 100644 --- a/compiler/crates/schema/tests/build_schema/fixtures/directives-for-external-types.expected +++ b/compiler/crates/schema/tests/build_schema/fixtures/directives-for-external-types.expected @@ -400,6 +400,18 @@ Schema { directives: [], parent_type: None, }, + Field { + name: "__token", + is_extension: false, + arguments: [], + type_: NonNull( + Named( + Scalar(5), + ), + ), + directives: [], + parent_type: None, + }, Field { name: "__id", is_extension: true, diff --git a/compiler/crates/schema/tests/build_schema/fixtures/interface-implements-interface.expected b/compiler/crates/schema/tests/build_schema/fixtures/interface-implements-interface.expected index 2c1ff7dd9c710..bcc778ddc3309 100644 --- a/compiler/crates/schema/tests/build_schema/fixtures/interface-implements-interface.expected +++ b/compiler/crates/schema/tests/build_schema/fixtures/interface-implements-interface.expected @@ -198,6 +198,18 @@ Schema { directives: [], parent_type: None, }, + Field { + name: "__token", + is_extension: false, + arguments: [], + type_: NonNull( + Named( + Scalar(4), + ), + ), + directives: [], + parent_type: None, + }, Field { name: "__id", is_extension: true, diff --git a/compiler/crates/schema/tests/build_schema/fixtures/kitchen-sink.expected b/compiler/crates/schema/tests/build_schema/fixtures/kitchen-sink.expected index 2c1874acd91d6..222e5ea10e140 100644 --- a/compiler/crates/schema/tests/build_schema/fixtures/kitchen-sink.expected +++ b/compiler/crates/schema/tests/build_schema/fixtures/kitchen-sink.expected @@ -312,6 +312,18 @@ Schema { directives: [], parent_type: None, }, + Field { + name: "__token", + is_extension: false, + arguments: [], + type_: NonNull( + Named( + Scalar(4), + ), + ), + directives: [], + parent_type: None, + }, Field { name: "__id", is_extension: true,