Skip to content

Commit

Permalink
Add parser for terse resolver syntax
Browse files Browse the repository at this point in the history
Reviewed By: rbalicki2

Differential Revision: D40348744

fbshipit-source-id: 7b303e98e42f54b0e0bafb010000d9363aa50892
  • Loading branch information
captbaritone authored and facebook-github-bot committed Oct 19, 2022
1 parent 88d3f6a commit 8dc927f
Show file tree
Hide file tree
Showing 24 changed files with 862 additions and 54 deletions.
10 changes: 10 additions & 0 deletions compiler/crates/graphql-syntax/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ pub fn parse_schema_document(
parser.parse_schema_document()
}

pub fn parse_field_definition(
source: &str,
source_location: SourceLocationKey,
offset: u32,
) -> DiagnosticsResult<FieldDefinition> {
let features = ParserFeatures::default();
let parser = Parser::with_offset(source, source_location, features, offset);
parser.parse_field_definition()
}

pub fn parse_field_definition_stub(
source: &str,
source_location: SourceLocationKey,
Expand Down
14 changes: 12 additions & 2 deletions compiler/crates/graphql-syntax/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ impl<'a> Parser<'a> {
/// Parses a string containing a field name with optional arguments
pub fn parse_field_definition_stub(mut self) -> DiagnosticsResult<FieldDefinitionStub> {
let stub = self.parse_field_definition_stub_impl();
if self.errors.is_empty() {
Ok(stub.unwrap())
} else {
Err(self.errors)
}
}

/// Parses a string containing a field definition
pub fn parse_field_definition(mut self) -> DiagnosticsResult<FieldDefinition> {
let stub = self.parse_field_definition_impl();
if self.errors.is_empty() {
self.parse_eof()?;
Ok(stub.unwrap())
Expand Down Expand Up @@ -875,15 +885,15 @@ impl<'a> Parser<'a> {
self.parse_optional_delimited_nonempty_list(
TokenKind::OpenBrace,
TokenKind::CloseBrace,
Self::parse_field_definition,
Self::parse_field_definition_impl,
)
}

/**
* FieldDefinition :
* - Description? Name ArgumentsDefinition? : Type Directives?
*/
fn parse_field_definition(&mut self) -> ParseResult<FieldDefinition> {
fn parse_field_definition_impl(&mut self) -> ParseResult<FieldDefinition> {
let description = self.parse_optional_description();
let name = self.parse_identifier()?;
let arguments = self.parse_argument_defs()?;
Expand Down
23 changes: 23 additions & 0 deletions compiler/crates/relay-docblock/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ pub enum ErrorMessages {
type_name: StringKey,
},

#[error(
"The type specified in the fragment (`{fragment_type_condition}`) and the parent type (`{type_name}`) are different. Please make sure these are exactly the same."
)]
MismatchRootFragmentTypeConditionTerseSyntax {
fragment_type_condition: StringKey,
type_name: StringKey,
},

#[error(
"Unexpected plural server type in `@edgeTo` field. Currently Relay Resolvers only support plural `@edgeTo` if the type is defined via Client Schema Extensions."
)]
Expand All @@ -90,6 +98,21 @@ pub enum ErrorMessages {
field_name: StringKey,
interface_name: InterfaceName,
},

#[error(
"Unexpected character `{found}`. Expected @RelayResolver field to either be a GraphQL typename, or a field definition of the form `ParentType.field_name: ReturnType`."
)]
UnexpectedNonDot { found: char },

#[error(
"Unexpected character `{found}`. Terse @RelayResolver syntax, where a field is defined in a single line using the `ParentType.field_name: ReturnType` shorthand, is not enabled in your project's config."
)]
UnexpectedTerseSyntax { found: char },

#[error(
"Unexpected docblock field `{field_name}`. This field is not allowed in combination with terse @RelayResolver syntax, where a field is defined in a single line using the `ParentType.field_name: ReturnType` shorthand."
)]
UnexpectedFieldInTerseSyntax { field_name: StringKey },
}

#[derive(Clone, Debug, Error, Eq, PartialEq, Ord, PartialOrd, Hash)]
Expand Down
86 changes: 75 additions & 11 deletions compiler/crates/relay-docblock/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ lazy_static! {
#[derive(Debug, PartialEq)]
pub enum DocblockIr {
RelayResolver(RelayResolverIr),
TerseRelayResolver(TerseRelayResolverIr),
StrongObjectResolver(StrongObjectIr),
WeakObjectType(WeakObjectIr),
}
Expand All @@ -103,6 +104,9 @@ impl DocblockIr {
DocblockIr::RelayResolver(relay_resolver) => {
relay_resolver.to_graphql_schema_ast(schema)
}
DocblockIr::TerseRelayResolver(relay_resolver) => {
relay_resolver.to_graphql_schema_ast(schema)
}
DocblockIr::StrongObjectResolver(strong_object) => {
strong_object.to_graphql_schema_ast(schema)
}
Expand Down Expand Up @@ -240,11 +244,14 @@ trait ResolverIr {
arguments.push(true_argument(LIVE_ARGUMENT_NAME.0, live_field.key_location))
}

if let Some(OutputType::Output(type_)) = &self.output_type() {
arguments.push(true_argument(
HAS_OUTPUT_TYPE_ARGUMENT_NAME.0,
type_.location,
))
if let Some(output_type) = &self.output_type() {
match output_type {
OutputType::EdgeTo(_) => {}
OutputType::Output(type_) => arguments.push(true_argument(
HAS_OUTPUT_TYPE_ARGUMENT_NAME.0,
type_.location,
)),
}
}
if let Some(name) = self.named_import() {
arguments.push(string_argument(
Expand All @@ -262,6 +269,59 @@ trait ResolverIr {
}
}

#[derive(Debug, PartialEq)]
pub struct TerseRelayResolverIr {
pub field: FieldDefinition,
pub type_: WithLocation<StringKey>,
pub root_fragment: Option<WithLocation<FragmentDefinitionName>>,
pub deprecated: Option<IrField>,
pub output_type: Option<OutputType>,
pub live: Option<IrField>,
pub location: Location,
pub fragment_arguments: Option<Vec<Argument>>,
pub named_import: Option<StringKey>,
}

impl ResolverIr for TerseRelayResolverIr {
fn definitions(&self, _schema: &SDLSchema) -> DiagnosticsResult<Vec<TypeSystemDefinition>> {
Ok(vec![TypeSystemDefinition::ObjectTypeExtension(
ObjectTypeExtension {
name: as_identifier(self.type_),
interfaces: Vec::new(),
directives: self.directives(),
fields: Some(List::generated(vec![self.field.clone()])),
},
)])
}

fn location(&self) -> Location {
self.location
}

fn root_fragment(&self) -> Option<RootFragment> {
self.root_fragment.map(|fragment| RootFragment {
fragment,
inject_fragment_data: None,
})
}

fn output_type(&self) -> Option<&OutputType> {
self.output_type.as_ref()
}

fn deprecated(&self) -> Option<IrField> {
self.deprecated
}

fn live(&self) -> Option<IrField> {
self.live
}

fn named_import(&self) -> Option<StringKey> {
self.named_import
}
}

#[derive(Debug, PartialEq)]
pub struct RelayResolverIr {
pub field: FieldDefinitionStub,
Expand Down Expand Up @@ -303,7 +363,7 @@ impl ResolverIr for RelayResolverIr {
schema,
&schema.object(object_id).interfaces,
)?;
return Ok(self.object_definitions(value.map(ObjectName)));
return Ok(self.object_definitions(value.map(ObjectName), schema));
}
Type::Interface(_) => {
return Err(vec![Diagnostic::error_with_data(
Expand Down Expand Up @@ -426,10 +486,10 @@ impl RelayResolverIr {
for object_id in &schema.interface(interface_id).implementing_objects {
if !seen_objects.contains(object_id) {
seen_objects.insert(*object_id);
definitions.extend(self.object_definitions(WithLocation::new(
interface_name.location,
schema.object(*object_id).name.item,
)))
definitions.extend(self.object_definitions(
WithLocation::new(interface_name.location, schema.object(*object_id).name.item),
schema,
))
}
}

Expand Down Expand Up @@ -497,7 +557,11 @@ impl RelayResolverIr {
Ok(())
}

fn object_definitions(&self, on_type: WithLocation<ObjectName>) -> Vec<TypeSystemDefinition> {
fn object_definitions(
&self,
on_type: WithLocation<ObjectName>,
_schema: &SDLSchema,
) -> Vec<TypeSystemDefinition> {
vec![TypeSystemDefinition::ObjectTypeExtension(
ObjectTypeExtension {
name: obj_as_identifier(on_type),
Expand Down
Loading

0 comments on commit 8dc927f

Please sign in to comment.