Merge branch 'async-await' into remove-async-closure-feature
# Conflicts: # examples/warp_async/src/main.rs # integration_tests/async_await/src/main.rs # juniper/src/lib.rs # juniper_benchmarks/src/lib.rs # juniper_rocket/src/lib.rs # juniper_warp/src/lib.rs
This commit is contained in:
commit
635bf1ff34
26 changed files with 344 additions and 45 deletions
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
- No changes yet
|
- No changes yet
|
||||||
|
|
||||||
|
# [[0.14.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.14.0)
|
||||||
|
|
||||||
|
- Require `url` 2.x if `url` feature is enabled.
|
||||||
|
- Improve lookahead visitability.
|
||||||
|
- Add ability to parse 'subscription'.
|
||||||
|
|
||||||
# [[0.13.1] 2019-07-29](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.13.1)
|
# [[0.13.1] 2019-07-29](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.13.1)
|
||||||
|
|
||||||
- Fix a regression when using lookaheads with fragments containing nested types [#404](https://github.com/graphql-rust/juniper/pull/404)
|
- Fix a regression when using lookaheads with fragments containing nested types [#404](https://github.com/graphql-rust/juniper/pull/404)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "juniper"
|
name = "juniper"
|
||||||
version = "0.13.1"
|
version = "0.14.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Magnus Hallin <mhallin@fastmail.com>",
|
"Magnus Hallin <mhallin@fastmail.com>",
|
||||||
"Christoph Herzog <chris@theduke.at>",
|
"Christoph Herzog <chris@theduke.at>",
|
||||||
|
@ -33,7 +33,7 @@ default = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
juniper_codegen = { version = "0.13.2", path = "../juniper_codegen" }
|
juniper_codegen = { version = "0.14.0", path = "../juniper_codegen" }
|
||||||
|
|
||||||
fnv = "1.0.3"
|
fnv = "1.0.3"
|
||||||
indexmap = { version = "1.0.0", features = ["serde-1"] }
|
indexmap = { version = "1.0.0", features = ["serde-1"] }
|
||||||
|
@ -42,7 +42,7 @@ serde_derive = { version = "1.0.2" }
|
||||||
|
|
||||||
chrono = { version = "0.4.0", optional = true }
|
chrono = { version = "0.4.0", optional = true }
|
||||||
serde_json = { version="1.0.2", optional = true }
|
serde_json = { version="1.0.2", optional = true }
|
||||||
url = { version = "1.5.1", optional = true }
|
url = { version = "2", optional = true }
|
||||||
uuid = { version = "0.7", optional = true }
|
uuid = { version = "0.7", optional = true }
|
||||||
|
|
||||||
futures-preview = { version = "=0.3.0-alpha.19", optional = true }
|
futures-preview = { version = "=0.3.0-alpha.19", optional = true }
|
||||||
|
|
|
@ -116,6 +116,7 @@ pub struct Directive<'a, S> {
|
||||||
pub enum OperationType {
|
pub enum OperationType {
|
||||||
Query,
|
Query,
|
||||||
Mutation,
|
Mutation,
|
||||||
|
Subscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
|
|
|
@ -88,6 +88,11 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The argument's name
|
||||||
|
pub fn name(&'a self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
/// The value of the argument
|
/// The value of the argument
|
||||||
pub fn value(&'a self) -> &LookAheadValue<'a, S> {
|
pub fn value(&'a self) -> &LookAheadValue<'a, S> {
|
||||||
&self.value
|
&self.value
|
||||||
|
@ -347,6 +352,12 @@ pub trait LookAheadMethods<S> {
|
||||||
self.select_child(name).is_some()
|
self.select_child(name).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Does the current node have any arguments?
|
||||||
|
fn has_arguments(&self) -> bool;
|
||||||
|
|
||||||
|
/// Does the current node have any children?
|
||||||
|
fn has_children(&self) -> bool;
|
||||||
|
|
||||||
/// Get the top level arguments for the current selection
|
/// Get the top level arguments for the current selection
|
||||||
fn arguments(&self) -> &[LookAheadArgument<S>];
|
fn arguments(&self) -> &[LookAheadArgument<S>];
|
||||||
|
|
||||||
|
@ -354,6 +365,9 @@ pub trait LookAheadMethods<S> {
|
||||||
fn argument(&self, name: &str) -> Option<&LookAheadArgument<S>> {
|
fn argument(&self, name: &str) -> Option<&LookAheadArgument<S>> {
|
||||||
self.arguments().iter().find(|a| a.name == name)
|
self.arguments().iter().find(|a| a.name == name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the top level children for the current selection
|
||||||
|
fn child_names(&self) -> Vec<&str>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S> LookAheadMethods<S> for ConcreteLookAheadSelection<'a, S> {
|
impl<'a, S> LookAheadMethods<S> for ConcreteLookAheadSelection<'a, S> {
|
||||||
|
@ -368,6 +382,21 @@ impl<'a, S> LookAheadMethods<S> for ConcreteLookAheadSelection<'a, S> {
|
||||||
fn arguments(&self) -> &[LookAheadArgument<S>] {
|
fn arguments(&self) -> &[LookAheadArgument<S>] {
|
||||||
&self.arguments
|
&self.arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn child_names(&self) -> Vec<&str> {
|
||||||
|
self.children
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.alias.unwrap_or(c.name))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_arguments(&self) -> bool {
|
||||||
|
!self.arguments.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_children(&self) -> bool {
|
||||||
|
!self.children.is_empty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S> LookAheadMethods<S> for LookAheadSelection<'a, S> {
|
impl<'a, S> LookAheadMethods<S> for LookAheadSelection<'a, S> {
|
||||||
|
@ -385,6 +414,21 @@ impl<'a, S> LookAheadMethods<S> for LookAheadSelection<'a, S> {
|
||||||
fn arguments(&self) -> &[LookAheadArgument<S>] {
|
fn arguments(&self) -> &[LookAheadArgument<S>] {
|
||||||
&self.arguments
|
&self.arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn child_names(&self) -> Vec<&str> {
|
||||||
|
self.children
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.inner.alias.unwrap_or(c.inner.name))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_arguments(&self) -> bool {
|
||||||
|
!self.arguments.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_children(&self) -> bool {
|
||||||
|
!self.children.is_empty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1399,4 +1443,59 @@ fragment heroFriendNames on Hero {
|
||||||
panic!("No Operation found");
|
panic!("No Operation found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_visitability() {
|
||||||
|
let docs = parse_document_source::<DefaultScalarValue>(
|
||||||
|
"
|
||||||
|
query Hero {
|
||||||
|
hero(episode: EMPIRE) {
|
||||||
|
name
|
||||||
|
friends {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let fragments = extract_fragments(&docs);
|
||||||
|
|
||||||
|
if let crate::ast::Definition::Operation(ref op) = docs[0] {
|
||||||
|
let vars = Variables::default();
|
||||||
|
let look_ahead = LookAheadSelection::build_from_selection(
|
||||||
|
&op.item.selection_set[0],
|
||||||
|
&vars,
|
||||||
|
&fragments,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(look_ahead.field_name(), "hero");
|
||||||
|
|
||||||
|
assert!(look_ahead.has_arguments());
|
||||||
|
let args = look_ahead.arguments();
|
||||||
|
assert_eq!(args[0].name(), "episode");
|
||||||
|
assert_eq!(args[0].value(), &LookAheadValue::Enum("EMPIRE"));
|
||||||
|
|
||||||
|
assert!(look_ahead.has_children());
|
||||||
|
assert_eq!(look_ahead.child_names(), vec!["name", "friends"]);
|
||||||
|
|
||||||
|
let child0 = look_ahead.select_child("name").unwrap();
|
||||||
|
assert_eq!(child0.field_name(), "name");
|
||||||
|
assert!(!child0.has_arguments());
|
||||||
|
assert!(!child0.has_children());
|
||||||
|
|
||||||
|
let child1 = look_ahead.select_child("friends").unwrap();
|
||||||
|
assert_eq!(child1.field_name(), "friends");
|
||||||
|
assert!(!child1.has_arguments());
|
||||||
|
assert!(child1.has_children());
|
||||||
|
assert_eq!(child1.child_names(), vec!["name"]);
|
||||||
|
|
||||||
|
let child2 = child1.select_child("name").unwrap();
|
||||||
|
assert!(!child2.has_arguments());
|
||||||
|
assert!(!child2.has_children());
|
||||||
|
} else {
|
||||||
|
panic!("No Operation found");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -713,6 +713,121 @@ where
|
||||||
None => return Err(GraphQLError::UnknownOperationName),
|
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
|
||||||
|
.iter()
|
||||||
|
.filter_map(|&(ref name, ref def)| {
|
||||||
|
def.default_value
|
||||||
|
.as_ref()
|
||||||
|
.map(|i| (name.item.to_owned(), i.item.clone()))
|
||||||
|
})
|
||||||
|
.collect::<HashMap<String, InputValue<S>>>()
|
||||||
|
});
|
||||||
|
|
||||||
|
let errors = RwLock::new(Vec::new());
|
||||||
|
let value;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut all_vars;
|
||||||
|
let mut final_vars = variables;
|
||||||
|
|
||||||
|
if let Some(defaults) = default_variable_values {
|
||||||
|
all_vars = variables.clone();
|
||||||
|
|
||||||
|
for (name, value) in defaults {
|
||||||
|
all_vars.entry(name).or_insert(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
final_vars = &all_vars;
|
||||||
|
}
|
||||||
|
|
||||||
|
let root_type = match op.item.operation_type {
|
||||||
|
OperationType::Query => root_node.schema.query_type(),
|
||||||
|
OperationType::Mutation => root_node
|
||||||
|
.schema
|
||||||
|
.mutation_type()
|
||||||
|
.expect("No mutation type found"),
|
||||||
|
OperationType::Subscription => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let executor = Executor {
|
||||||
|
fragments: &fragments
|
||||||
|
.iter()
|
||||||
|
.map(|f| (f.item.name.item, &f.item))
|
||||||
|
.collect(),
|
||||||
|
variables: final_vars,
|
||||||
|
current_selection_set: Some(&op.item.selection_set[..]),
|
||||||
|
parent_selection_set: None,
|
||||||
|
current_type: root_type,
|
||||||
|
schema: &root_node.schema,
|
||||||
|
context,
|
||||||
|
errors: &errors,
|
||||||
|
field_path: FieldPath::Root(op.start),
|
||||||
|
};
|
||||||
|
|
||||||
|
value = match op.item.operation_type {
|
||||||
|
OperationType::Query => executor.resolve_into_value(&root_node.query_info, &root_node),
|
||||||
|
OperationType::Mutation => {
|
||||||
|
executor.resolve_into_value(&root_node.mutation_info, &root_node.mutation_type)
|
||||||
|
}
|
||||||
|
OperationType::Subscription => unreachable!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut errors = errors.into_inner().unwrap();
|
||||||
|
errors.sort();
|
||||||
|
|
||||||
|
Ok((value, errors))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub async fn execute_validated_query_async<'a, QueryT, MutationT, CtxT, S>(
|
||||||
|
document: Document<'a, S>,
|
||||||
|
operation_name: Option<&str>,
|
||||||
|
root_node: &RootNode<'a, QueryT, MutationT, S>,
|
||||||
|
variables: &Variables<S>,
|
||||||
|
context: &CtxT,
|
||||||
|
) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError<'a>>
|
||||||
|
where
|
||||||
|
S: ScalarValue + Send + Sync,
|
||||||
|
QueryT: crate::GraphQLTypeAsync<S, Context = CtxT> + Send + Sync,
|
||||||
|
QueryT::TypeInfo: Send + Sync,
|
||||||
|
MutationT: crate::GraphQLTypeAsync<S, Context = CtxT> + Send + Sync,
|
||||||
|
MutationT::TypeInfo: Send + Sync,
|
||||||
|
CtxT: Send + Sync,
|
||||||
|
for<'b> &'b S: ScalarRefValue<'b>,
|
||||||
|
{
|
||||||
|
let mut fragments = vec![];
|
||||||
|
let mut operation = None;
|
||||||
|
|
||||||
|
for def in document {
|
||||||
|
match def {
|
||||||
|
Definition::Operation(op) => {
|
||||||
|
if operation_name.is_none() && operation.is_some() {
|
||||||
|
return Err(GraphQLError::MultipleOperationsProvided);
|
||||||
|
}
|
||||||
|
|
||||||
|
let move_op = operation_name.is_none()
|
||||||
|
|| op.item.name.as_ref().map(|s| s.item) == operation_name;
|
||||||
|
|
||||||
|
if move_op {
|
||||||
|
operation = Some(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Definition::Fragment(f) => fragments.push(f),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let op = match operation {
|
||||||
|
Some(op) => op,
|
||||||
|
None => return Err(GraphQLError::UnknownOperationName),
|
||||||
|
};
|
||||||
|
|
||||||
let default_variable_values = op.item.variable_definitions.map(|defs| {
|
let default_variable_values = op.item.variable_definitions.map(|defs| {
|
||||||
defs.item
|
defs.item
|
||||||
.items
|
.items
|
||||||
|
@ -766,9 +881,15 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
value = match op.item.operation_type {
|
value = match op.item.operation_type {
|
||||||
OperationType::Query => executor.resolve_into_value(&root_node.query_info, &root_node),
|
OperationType::Query => {
|
||||||
|
executor
|
||||||
|
.resolve_into_value_async(&root_node.query_info, &root_node)
|
||||||
|
.await
|
||||||
|
}
|
||||||
OperationType::Mutation => {
|
OperationType::Mutation => {
|
||||||
executor.resolve_into_value(&root_node.mutation_info, &root_node.mutation_type)
|
executor
|
||||||
|
.resolve_into_value_async(&root_node.mutation_info, &root_node.mutation_type)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,10 @@ impl<'a> ser::Serialize for GraphQLError<'a> {
|
||||||
message: "Unknown operation",
|
message: "Unknown operation",
|
||||||
}]
|
}]
|
||||||
.serialize(serializer),
|
.serialize(serializer),
|
||||||
|
GraphQLError::IsSubscription => [SerializeHelper {
|
||||||
|
message: "Expected query, got subscription",
|
||||||
|
}]
|
||||||
|
.serialize(serializer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,8 +88,7 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected.
|
||||||
[chrono]: https://crates.io/crates/chrono
|
[chrono]: https://crates.io/crates/chrono
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
#![doc(html_root_url = "https://docs.rs/juniper/0.14.0")]
|
||||||
#![doc(html_root_url = "https://docs.rs/juniper/0.13.1")]
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -191,6 +190,7 @@ pub enum GraphQLError<'a> {
|
||||||
NoOperationProvided,
|
NoOperationProvided,
|
||||||
MultipleOperationsProvided,
|
MultipleOperationsProvided,
|
||||||
UnknownOperationName,
|
UnknownOperationName,
|
||||||
|
IsSubscription,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a query in a provided schema
|
/// Execute a query in a provided schema
|
||||||
|
|
|
@ -56,9 +56,12 @@ where
|
||||||
S: ScalarValue,
|
S: ScalarValue,
|
||||||
{
|
{
|
||||||
match parser.peek().item {
|
match parser.peek().item {
|
||||||
Token::CurlyOpen | Token::Name("query") | Token::Name("mutation") => Ok(
|
Token::CurlyOpen
|
||||||
Definition::Operation(parse_operation_definition(parser, schema)?),
|
| 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(
|
Token::Name("fragment") => Ok(Definition::Fragment(parse_fragment_definition(
|
||||||
parser, schema,
|
parser, schema,
|
||||||
)?)),
|
)?)),
|
||||||
|
@ -95,6 +98,7 @@ where
|
||||||
let op = match operation_type.item {
|
let op = match operation_type.item {
|
||||||
OperationType::Query => Some(schema.concrete_query_type()),
|
OperationType::Query => Some(schema.concrete_query_type()),
|
||||||
OperationType::Mutation => schema.concrete_mutation_type(),
|
OperationType::Mutation => schema.concrete_mutation_type(),
|
||||||
|
OperationType::Subscription => schema.concrete_subscription_type(),
|
||||||
};
|
};
|
||||||
let fields = op.and_then(|m| m.fields(schema));
|
let fields = op.and_then(|m| m.fields(schema));
|
||||||
let fields = fields.as_ref().map(|c| c as &[_]);
|
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 {
|
match parser.peek().item {
|
||||||
Token::Name("query") => Ok(parser.next()?.map(|_| OperationType::Query)),
|
Token::Name("query") => Ok(parser.next()?.map(|_| OperationType::Query)),
|
||||||
Token::Name("mutation") => Ok(parser.next()?.map(|_| OperationType::Mutation)),
|
Token::Name("mutation") => Ok(parser.next()?.map(|_| OperationType::Mutation)),
|
||||||
|
Token::Name("subscription") => Ok(parser.next()?.map(|_| OperationType::Subscription)),
|
||||||
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
|
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ pub struct DirectiveType<'a, S> {
|
||||||
pub enum DirectiveLocation {
|
pub enum DirectiveLocation {
|
||||||
Query,
|
Query,
|
||||||
Mutation,
|
Mutation,
|
||||||
|
Subscription,
|
||||||
Field,
|
Field,
|
||||||
#[graphql(name = "FRAGMENT_DEFINITION")]
|
#[graphql(name = "FRAGMENT_DEFINITION")]
|
||||||
FragmentDefinition,
|
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>> {
|
pub fn type_list(&self) -> Vec<TypeType<S>> {
|
||||||
self.types.values().map(|t| TypeType::Concrete(t)).collect()
|
self.types.values().map(|t| TypeType::Concrete(t)).collect()
|
||||||
}
|
}
|
||||||
|
@ -452,6 +465,7 @@ impl fmt::Display for DirectiveLocation {
|
||||||
f.write_str(match *self {
|
f.write_str(match *self {
|
||||||
DirectiveLocation::Query => "query",
|
DirectiveLocation::Query => "query",
|
||||||
DirectiveLocation::Mutation => "mutation",
|
DirectiveLocation::Mutation => "mutation",
|
||||||
|
DirectiveLocation::Subscription => "subscription",
|
||||||
DirectiveLocation::Field => "field",
|
DirectiveLocation::Field => "field",
|
||||||
DirectiveLocation::FragmentDefinition => "fragment definition",
|
DirectiveLocation::FragmentDefinition => "fragment definition",
|
||||||
DirectiveLocation::FragmentSpread => "fragment spread",
|
DirectiveLocation::FragmentSpread => "fragment spread",
|
||||||
|
|
|
@ -138,9 +138,8 @@ where
|
||||||
self.mutation_type()
|
self.mutation_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Included for compatibility with the introspection query in GraphQL.js
|
|
||||||
fn subscription_type(&self) -> Option<TypeType<S>> {
|
fn subscription_type(&self) -> Option<TypeType<S>> {
|
||||||
None
|
self.subscription_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn directives(&self) -> Vec<&DirectiveType<S>> {
|
fn directives(&self) -> Vec<&DirectiveType<S>> {
|
||||||
|
|
|
@ -997,6 +997,12 @@ pub(crate) fn schema_introspection_result() -> value::Value {
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": Null
|
"deprecationReason": Null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "SUBSCRIPTION",
|
||||||
|
"description": Null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": Null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "FIELD",
|
"name": "FIELD",
|
||||||
"description": Null,
|
"description": Null,
|
||||||
|
@ -2204,6 +2210,11 @@ pub(crate) fn schema_introspection_result_without_descriptions() -> value::Value
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": Null
|
"deprecationReason": Null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "SUBSCRIPTION",
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": Null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "FIELD",
|
"name": "FIELD",
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
|
|
|
@ -28,6 +28,7 @@ where
|
||||||
self.location_stack.push(match op.item.operation_type {
|
self.location_stack.push(match op.item.operation_type {
|
||||||
OperationType::Query => DirectiveLocation::Query,
|
OperationType::Query => DirectiveLocation::Query,
|
||||||
OperationType::Mutation => DirectiveLocation::Mutation,
|
OperationType::Mutation => DirectiveLocation::Mutation,
|
||||||
|
OperationType::Subscription => DirectiveLocation::Subscription,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +38,11 @@ where
|
||||||
_: &'a Spanning<Operation<S>>,
|
_: &'a Spanning<Operation<S>>,
|
||||||
) {
|
) {
|
||||||
let top = self.location_stack.pop();
|
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>>) {
|
fn enter_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Field<S>>) {
|
||||||
|
|
|
@ -64,6 +64,17 @@ fn visit_definitions<'a, S, V>(
|
||||||
.schema
|
.schema
|
||||||
.concrete_mutation_type()
|
.concrete_mutation_type()
|
||||||
.map(|t| Type::NonNullNamed(Cow::Borrowed(t.name().unwrap()))),
|
.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| {
|
ctx.with_pushed_type(def_type.as_ref(), |ctx| {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "juniper_codegen"
|
name = "juniper_codegen"
|
||||||
version = "0.13.2"
|
version = "0.14.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Magnus Hallin <mhallin@fastmail.com>",
|
"Magnus Hallin <mhallin@fastmail.com>",
|
||||||
"Christoph Herzog <chris@theduke.at>",
|
"Christoph Herzog <chris@theduke.at>",
|
||||||
|
@ -21,11 +21,9 @@ async = []
|
||||||
proc-macro2 = "1.0.1"
|
proc-macro2 = "1.0.1"
|
||||||
syn = { version = "1.0.3", features = ["full", "extra-traits", "parsing"] }
|
syn = { version = "1.0.3", features = ["full", "extra-traits", "parsing"] }
|
||||||
quote = "1.0.2"
|
quote = "1.0.2"
|
||||||
regex = "1"
|
|
||||||
lazy_static = "1.0.0"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
juniper = { version = "0.13.1", path = "../juniper" }
|
juniper = { version = "0.14.0", path = "../juniper" }
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
travis-ci = { repository = "graphql-rust/juniper" }
|
travis-ci = { repository = "graphql-rust/juniper" }
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
//! You should not depend on juniper_codegen directly.
|
//! You should not depend on juniper_codegen directly.
|
||||||
//! You only need the `juniper` crate.
|
//! You only need the `juniper` crate.
|
||||||
|
|
||||||
#![doc(html_root_url = "https://docs.rs/juniper_codegen/0.13.2")]
|
#![doc(html_root_url = "https://docs.rs/juniper_codegen/0.14.0")]
|
||||||
#![recursion_limit = "1024"]
|
#![recursion_limit = "1024"]
|
||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use regex::Regex;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse, parse_quote, punctuated::Punctuated, Attribute, Lit, Meta, MetaList, MetaNameValue,
|
parse, parse_quote, punctuated::Punctuated, Attribute, Lit, Meta, MetaList, MetaNameValue,
|
||||||
|
@ -279,10 +278,16 @@ pub(crate) fn to_upper_snake_case(s: &str) -> String {
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn is_valid_name(field_name: &str) -> bool {
|
pub fn is_valid_name(field_name: &str) -> bool {
|
||||||
lazy_static::lazy_static! {
|
let mut chars = field_name.chars();
|
||||||
static ref GRAPHQL_NAME_SPEC: Regex = Regex::new("^[_A-Za-z][_0-9A-Za-z]*$").unwrap();
|
|
||||||
}
|
match chars.next() {
|
||||||
GRAPHQL_NAME_SPEC.is_match(field_name)
|
// first char can't be a digit
|
||||||
|
Some(c) if c.is_ascii_alphabetic() || c == '_' => (),
|
||||||
|
// can't be an empty string or any other character
|
||||||
|
_ => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
- Compatibility with the latest `juniper`.
|
- Compatibility with the latest `juniper`.
|
||||||
|
|
||||||
|
# [[0.5.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_hyper-0.5.0)
|
||||||
|
|
||||||
|
- Compatibility with the latest `juniper`.
|
||||||
|
|
||||||
# [[0.4.1] 2019-07-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_hyper-0.4.1)
|
# [[0.4.1] 2019-07-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_hyper-0.4.1)
|
||||||
|
|
||||||
- Compatibility with the latest `juniper`.
|
- Compatibility with the latest `juniper`.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "juniper_hyper"
|
name = "juniper_hyper"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
authors = ["Damir Vandic <info@dvic.io>"]
|
authors = ["Damir Vandic <info@dvic.io>"]
|
||||||
description = "Juniper GraphQL integration with Hyper"
|
description = "Juniper GraphQL integration with Hyper"
|
||||||
license = "BSD-2-Clause"
|
license = "BSD-2-Clause"
|
||||||
|
@ -12,8 +12,8 @@ edition = "2018"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
url = "1.7"
|
url = "2"
|
||||||
juniper = { version = "0.13.1" , default-features = false, path = "../juniper"}
|
juniper = { version = "0.14.0", default-features = false, path = "../juniper"}
|
||||||
|
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
tokio = "0.1.8"
|
tokio = "0.1.8"
|
||||||
|
@ -25,6 +25,6 @@ pretty_env_logger = "0.2"
|
||||||
reqwest = "0.9"
|
reqwest = "0.9"
|
||||||
|
|
||||||
[dev-dependencies.juniper]
|
[dev-dependencies.juniper]
|
||||||
version = "0.13.1"
|
version = "0.14.0"
|
||||||
features = ["expose-test-schema", "serde_json"]
|
features = ["expose-test-schema", "serde_json"]
|
||||||
path = "../juniper"
|
path = "../juniper"
|
||||||
|
|
|
@ -27,7 +27,7 @@ fn main() {
|
||||||
let new_service = move || {
|
let new_service = move || {
|
||||||
let root_node = root_node.clone();
|
let root_node = root_node.clone();
|
||||||
let ctx = db.clone();
|
let ctx = db.clone();
|
||||||
service_fn(move |req| -> Box<Future<Item = _, Error = _> + Send> {
|
service_fn(move |req| -> Box<dyn Future<Item = _, Error = _> + Send> {
|
||||||
let root_node = root_node.clone();
|
let root_node = root_node.clone();
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
match (req.method(), req.uri().path()) {
|
match (req.method(), req.uri().path()) {
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
- Compatibility with the latest `juniper`.
|
- Compatibility with the latest `juniper`.
|
||||||
|
|
||||||
|
# [[0.6.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_iron-0.6.0)
|
||||||
|
|
||||||
|
- Compatibility with the latest `juniper`.
|
||||||
|
|
||||||
# [[0.5.1] 2019-07-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_iron-0.5.1)
|
# [[0.5.1] 2019-07-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_iron-0.5.1)
|
||||||
|
|
||||||
- Compatibility with the latest `juniper`.
|
- Compatibility with the latest `juniper`.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "juniper_iron"
|
name = "juniper_iron"
|
||||||
version = "0.5.1"
|
version = "0.6.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Magnus Hallin <mhallin@fastmail.com>",
|
"Magnus Hallin <mhallin@fastmail.com>",
|
||||||
"Christoph Herzog <chris@theduke.at>",
|
"Christoph Herzog <chris@theduke.at>",
|
||||||
|
@ -15,7 +15,7 @@ edition = "2018"
|
||||||
serde = { version = "1.0.2" }
|
serde = { version = "1.0.2" }
|
||||||
serde_json = { version = "1.0.2" }
|
serde_json = { version = "1.0.2" }
|
||||||
serde_derive = { version = "1.0.2" }
|
serde_derive = { version = "1.0.2" }
|
||||||
juniper = { version = "0.13.1", path = "../juniper" }
|
juniper = { version = "0.14.0", path = "../juniper" }
|
||||||
|
|
||||||
urlencoded = { version = ">= 0.5, < 0.7" }
|
urlencoded = { version = ">= 0.5, < 0.7" }
|
||||||
iron = ">= 0.5, < 0.7"
|
iron = ">= 0.5, < 0.7"
|
||||||
|
@ -25,9 +25,10 @@ iron-test = "0.6"
|
||||||
router = "0.6"
|
router = "0.6"
|
||||||
mount = "0.4"
|
mount = "0.4"
|
||||||
logger = "0.4"
|
logger = "0.4"
|
||||||
url = "1.7.1"
|
url = "2"
|
||||||
|
percent-encoding = "2"
|
||||||
|
|
||||||
[dev-dependencies.juniper]
|
[dev-dependencies.juniper]
|
||||||
version = "0.13.1"
|
version = "0.14.0"
|
||||||
features = ["expose-test-schema", "serde_json"]
|
features = ["expose-test-schema", "serde_json"]
|
||||||
path = "../juniper"
|
path = "../juniper"
|
||||||
|
|
|
@ -431,7 +431,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use iron::{Handler, Headers, Url};
|
use iron::{Handler, Headers, Url};
|
||||||
use iron_test::{request, response};
|
use iron_test::{request, response};
|
||||||
use url::percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
|
use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS};
|
||||||
|
|
||||||
use juniper::{
|
use juniper::{
|
||||||
http::tests as http_tests,
|
http::tests as http_tests,
|
||||||
|
@ -441,6 +441,9 @@ mod tests {
|
||||||
|
|
||||||
use super::GraphQLHandler;
|
use super::GraphQLHandler;
|
||||||
|
|
||||||
|
/// https://url.spec.whatwg.org/#query-state
|
||||||
|
const QUERY_ENCODE_SET: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'#').add(b'<').add(b'>');
|
||||||
|
|
||||||
// This is ugly but it works. `iron_test` just dumps the path/url in headers
|
// This is ugly but it works. `iron_test` just dumps the path/url in headers
|
||||||
// and newer `hyper` doesn't allow unescaped "{" or "}".
|
// and newer `hyper` doesn't allow unescaped "{" or "}".
|
||||||
fn fixup_url(url: &str) -> String {
|
fn fixup_url(url: &str) -> String {
|
||||||
|
@ -454,7 +457,7 @@ mod tests {
|
||||||
format!(
|
format!(
|
||||||
"http://localhost:3000{}?{}",
|
"http://localhost:3000{}?{}",
|
||||||
path,
|
path,
|
||||||
utf8_percent_encode(url.query().unwrap_or(""), DEFAULT_ENCODE_SET)
|
utf8_percent_encode(url.query().unwrap_or(""), QUERY_ENCODE_SET)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,7 +524,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_handler() -> Box<Handler> {
|
fn make_handler() -> Box<dyn Handler> {
|
||||||
Box::new(GraphQLHandler::new(
|
Box::new(GraphQLHandler::new(
|
||||||
context_factory,
|
context_factory,
|
||||||
Query,
|
Query,
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
- Compatibility with the latest `juniper`.
|
- Compatibility with the latest `juniper`.
|
||||||
|
|
||||||
|
# [[0.5.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_rocket-0.5.0)
|
||||||
|
|
||||||
|
- Compatibility with the latest `juniper`.
|
||||||
|
|
||||||
# [[0.4.1] 2019-07-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_rocket-0.4.1)
|
# [[0.4.1] 2019-07-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_rocket-0.4.1)
|
||||||
|
|
||||||
- Compatibility with the latest `juniper`.
|
- Compatibility with the latest `juniper`.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "juniper_rocket"
|
name = "juniper_rocket"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Magnus Hallin <mhallin@fastmail.com>",
|
"Magnus Hallin <mhallin@fastmail.com>",
|
||||||
"Christoph Herzog <chris@theduke.at>",
|
"Christoph Herzog <chris@theduke.at>",
|
||||||
|
@ -18,13 +18,13 @@ async = [ "juniper/async" ]
|
||||||
serde = { version = "1.0.2" }
|
serde = { version = "1.0.2" }
|
||||||
serde_json = { version = "1.0.2" }
|
serde_json = { version = "1.0.2" }
|
||||||
serde_derive = { version = "1.0.2" }
|
serde_derive = { version = "1.0.2" }
|
||||||
juniper = { version = "0.13.1" , default-features = false, path = "../juniper"}
|
juniper = { version = "0.14.0", default-features = false, path = "../juniper"}
|
||||||
|
|
||||||
futures03 = { version = "=0.3.0-alpha.19", package = "futures-preview", features = ["compat"] }
|
futures03 = { version = "=0.3.0-alpha.19", package = "futures-preview", features = ["compat"] }
|
||||||
rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "async" }
|
rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "async" }
|
||||||
tokio = "=0.2.0-alpha.6"
|
tokio = "=0.2.0-alpha.6"
|
||||||
|
|
||||||
[dev-dependencies.juniper]
|
[dev-dependencies.juniper]
|
||||||
version = "0.13.1"
|
version = "0.14.0"
|
||||||
features = ["expose-test-schema", "serde_json"]
|
features = ["expose-test-schema", "serde_json"]
|
||||||
path = "../juniper"
|
path = "../juniper"
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
- Compatibility with the latest `juniper`.
|
- Compatibility with the latest `juniper`.
|
||||||
|
|
||||||
|
# [[0.5.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_warp-0.5.0)
|
||||||
|
|
||||||
|
- Compatibility with the latest `juniper`.
|
||||||
|
|
||||||
# [[0.4.1] 2019-07-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_warp-0.4.1)
|
# [[0.4.1] 2019-07-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_warp-0.4.1)
|
||||||
|
|
||||||
- Compatibility with the latest `juniper`.
|
- Compatibility with the latest `juniper`.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "juniper_warp"
|
name = "juniper_warp"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
authors = ["Tom Houlé <tom@tomhoule.com>"]
|
authors = ["Tom Houlé <tom@tomhoule.com>"]
|
||||||
description = "Juniper GraphQL integration with Warp"
|
description = "Juniper GraphQL integration with Warp"
|
||||||
license = "BSD-2-Clause"
|
license = "BSD-2-Clause"
|
||||||
|
@ -13,7 +13,7 @@ async = [ "juniper/async", "futures03" ]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
warp = "0.1.8"
|
warp = "0.1.8"
|
||||||
juniper = { version = "0.13.1", path = "../juniper" }
|
juniper = { version = "0.14.0", path = "../juniper", default-features = false }
|
||||||
serde_json = "1.0.24"
|
serde_json = "1.0.24"
|
||||||
serde_derive = "1.0.75"
|
serde_derive = "1.0.75"
|
||||||
failure = "0.1.2"
|
failure = "0.1.2"
|
||||||
|
@ -24,7 +24,7 @@ tokio-threadpool = "0.1.7"
|
||||||
futures03 = { version = "=0.3.0-alpha.19", optional = true, package = "futures-preview", features = ["compat"] }
|
futures03 = { version = "=0.3.0-alpha.19", optional = true, package = "futures-preview", features = ["compat"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
juniper = { version = "0.13.1", path = "../juniper", features = ["expose-test-schema", "serde_json"] }
|
juniper = { version = "0.14.0", path = "../juniper", features = ["expose-test-schema", "serde_json"] }
|
||||||
env_logger = "0.5.11"
|
env_logger = "0.5.11"
|
||||||
log = "0.4.3"
|
log = "0.4.3"
|
||||||
percent-encoding = "1.0"
|
percent-encoding = "1.0"
|
||||||
|
|
Loading…
Reference in a new issue