Add subscription support to query parser (#430)

This commit is contained in:
nWacky 2019-09-30 03:43:56 +03:00 committed by Christian Legnitto
parent dcbb74155d
commit b61aa900b1
11 changed files with 64 additions and 7 deletions

View file

@ -1,6 +1,6 @@
# master
- No changes yet
- Add ability to parse 'subscription'
# [[0.13.1] 2019-07-29](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.13.1)

View file

@ -116,6 +116,7 @@ pub struct Directive<'a, S> {
pub enum OperationType {
Query,
Mutation,
Subscription,
}
#[derive(Clone, PartialEq, Debug)]

View file

@ -648,6 +648,10 @@ where
None => return Err(GraphQLError::UnknownOperationName),
};
if op.item.operation_type == OperationType::Subscription {
return Err(GraphQLError::IsSubscription);
}
let default_variable_values = op.item.variable_definitions.map(|defs| {
defs.item
.items
@ -683,6 +687,7 @@ where
.schema
.mutation_type()
.expect("No mutation type found"),
OperationType::Subscription => unreachable!(),
};
let executor = Executor {
@ -705,6 +710,7 @@ where
OperationType::Mutation => {
executor.resolve_into_value(&root_node.mutation_info, &root_node.mutation_type)
}
OperationType::Subscription => unreachable!(),
};
}

View file

@ -70,6 +70,10 @@ impl<'a> ser::Serialize for GraphQLError<'a> {
message: "Unknown operation",
}]
.serialize(serializer),
GraphQLError::IsSubscription => [SerializeHelper {
message: "Expected query, got subscription",
}]
.serialize(serializer),
}
}
}

View file

@ -185,6 +185,7 @@ pub enum GraphQLError<'a> {
NoOperationProvided,
MultipleOperationsProvided,
UnknownOperationName,
IsSubscription,
}
/// Execute a query in a provided schema

View file

@ -56,9 +56,12 @@ where
S: ScalarValue,
{
match parser.peek().item {
Token::CurlyOpen | Token::Name("query") | Token::Name("mutation") => Ok(
Definition::Operation(parse_operation_definition(parser, schema)?),
),
Token::CurlyOpen
| Token::Name("query")
| Token::Name("mutation")
| Token::Name("subscription") => Ok(Definition::Operation(parse_operation_definition(
parser, schema,
)?)),
Token::Name("fragment") => Ok(Definition::Fragment(parse_fragment_definition(
parser, schema,
)?)),
@ -95,6 +98,7 @@ where
let op = match operation_type.item {
OperationType::Query => Some(schema.concrete_query_type()),
OperationType::Mutation => schema.concrete_mutation_type(),
OperationType::Subscription => schema.concrete_subscription_type(),
};
let fields = op.and_then(|m| m.fields(schema));
let fields = fields.as_ref().map(|c| c as &[_]);
@ -394,6 +398,7 @@ fn parse_operation_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Operatio
match parser.peek().item {
Token::Name("query") => Ok(parser.next()?.map(|_| OperationType::Query)),
Token::Name("mutation") => Ok(parser.next()?.map(|_| OperationType::Mutation)),
Token::Name("subscription") => Ok(parser.next()?.map(|_| OperationType::Subscription)),
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
}
}

View file

@ -65,6 +65,7 @@ pub struct DirectiveType<'a, S> {
pub enum DirectiveLocation {
Query,
Mutation,
Subscription,
Field,
#[graphql(name = "FRAGMENT_DEFINITION")]
FragmentDefinition,
@ -243,6 +244,18 @@ impl<'a, S> SchemaType<'a, S> {
})
}
pub fn subscription_type(&self) -> Option<TypeType<S>> {
// subscription is not yet in `RootNode`,
// so return `None` for now
None
}
pub fn concrete_subscription_type(&self) -> Option<&MetaType<S>> {
// subscription is not yet in `RootNode`,
// so return `None` for now
None
}
pub fn type_list(&self) -> Vec<TypeType<S>> {
self.types.values().map(|t| TypeType::Concrete(t)).collect()
}
@ -452,6 +465,7 @@ impl fmt::Display for DirectiveLocation {
f.write_str(match *self {
DirectiveLocation::Query => "query",
DirectiveLocation::Mutation => "mutation",
DirectiveLocation::Subscription => "subscription",
DirectiveLocation::Field => "field",
DirectiveLocation::FragmentDefinition => "fragment definition",
DirectiveLocation::FragmentSpread => "fragment spread",

View file

@ -104,9 +104,8 @@ where
self.mutation_type()
}
// Included for compatibility with the introspection query in GraphQL.js
fn subscription_type(&self) -> Option<TypeType<S>> {
None
self.subscription_type()
}
fn directives(&self) -> Vec<&DirectiveType<S>> {

View file

@ -997,6 +997,12 @@ pub(crate) fn schema_introspection_result() -> value::Value {
"isDeprecated": false,
"deprecationReason": Null
},
{
"name": "SUBSCRIPTION",
"description": Null,
"isDeprecated": false,
"deprecationReason": Null
},
{
"name": "FIELD",
"description": Null,
@ -2204,6 +2210,11 @@ pub(crate) fn schema_introspection_result_without_descriptions() -> value::Value
"isDeprecated": false,
"deprecationReason": Null
},
{
"name": "SUBSCRIPTION",
"isDeprecated": false,
"deprecationReason": Null
},
{
"name": "FIELD",
"isDeprecated": false,

View file

@ -28,6 +28,7 @@ where
self.location_stack.push(match op.item.operation_type {
OperationType::Query => DirectiveLocation::Query,
OperationType::Mutation => DirectiveLocation::Mutation,
OperationType::Subscription => DirectiveLocation::Subscription,
});
}
@ -37,7 +38,11 @@ where
_: &'a Spanning<Operation<S>>,
) {
let top = self.location_stack.pop();
assert!(top == Some(DirectiveLocation::Query) || top == Some(DirectiveLocation::Mutation));
assert!(
top == Some(DirectiveLocation::Query)
|| top == Some(DirectiveLocation::Mutation)
|| top == Some(DirectiveLocation::Subscription)
);
}
fn enter_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Field<S>>) {

View file

@ -64,6 +64,17 @@ fn visit_definitions<'a, S, V>(
.schema
.concrete_mutation_type()
.map(|t| Type::NonNullNamed(Cow::Borrowed(t.name().unwrap()))),
Definition::Operation(Spanning {
item:
Operation {
operation_type: OperationType::Subscription,
..
},
..
}) => ctx
.schema
.concrete_subscription_type()
.map(|t| Type::NonNullNamed(Cow::Borrowed(t.name().unwrap()))),
};
ctx.with_pushed_type(def_type.as_ref(), |ctx| {