parent
d0fc062892
commit
e75cf26995
7 changed files with 127 additions and 55 deletions
|
@ -54,6 +54,7 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
|
|||
- Upgraded [GraphiQL] to 3.0.9 version (requires new [`graphql-transport-ws` GraphQL over WebSocket Protocol] integration on server, see `juniper_warp/examples/subscription.rs`). ([#1188], [#1193], [#1204])
|
||||
- Made `LookAheadMethods::children()` method to return slice instead of `Vec`. ([#1200])
|
||||
- Abstracted `Spanning::start` and `Spanning::end` fields into separate struct `Span`. ([#1207], [#1208])
|
||||
- Added `Span` to `Arguments` and `LookAheadArguments`. ([#1206], [#1209])
|
||||
|
||||
### Added
|
||||
|
||||
|
@ -132,8 +133,10 @@ All user visible changes to `juniper` crate will be documented in this file. Thi
|
|||
[#1199]: /../../pull/1199
|
||||
[#1200]: /../../pull/1200
|
||||
[#1204]: /../../pull/1204
|
||||
[#1206]: /../../pull/1206
|
||||
[#1207]: /../../pull/1207
|
||||
[#1208]: /../../pull/1208
|
||||
[#1209]: /../../pull/1209
|
||||
[ba1ed85b]: /../../commit/ba1ed85b3c3dd77fbae7baf6bc4e693321a94083
|
||||
[CVE-2022-31173]: /../../security/advisories/GHSA-4rx6-g5vg-5f3j
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||
|
||||
use crate::{
|
||||
ast::{Directive, Fragment, InputValue, Selection},
|
||||
parser::Spanning,
|
||||
parser::{Span, Spanning},
|
||||
value::ScalarValue,
|
||||
};
|
||||
|
||||
|
@ -18,48 +18,71 @@ pub enum Applies<'a> {
|
|||
OnlyType(&'a str),
|
||||
}
|
||||
|
||||
/// A JSON-like value that can is used as argument in the query execution
|
||||
/// Shortcut for a [`Spanning`] containing a borrowed [`Span`].
|
||||
type BorrowedSpanning<'a, T> = Spanning<T, &'a Span>;
|
||||
|
||||
/// JSON-like value that can be used as an argument in the query execution.
|
||||
///
|
||||
/// In contrast to `InputValue` these values do only contain constants,
|
||||
/// In contrast to an [`InputValue`], these values do only contain constants,
|
||||
/// meaning that variables are already resolved.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum LookAheadValue<'a, S: 'a> {
|
||||
Null,
|
||||
Scalar(&'a S),
|
||||
Enum(&'a str),
|
||||
List(Vec<LookAheadValue<'a, S>>),
|
||||
Object(Vec<(&'a str, LookAheadValue<'a, S>)>),
|
||||
List(Vec<BorrowedSpanning<'a, LookAheadValue<'a, S>>>),
|
||||
Object(
|
||||
Vec<(
|
||||
BorrowedSpanning<'a, &'a str>,
|
||||
BorrowedSpanning<'a, LookAheadValue<'a, S>>,
|
||||
)>,
|
||||
),
|
||||
}
|
||||
|
||||
impl<'a, S> LookAheadValue<'a, S>
|
||||
where
|
||||
S: ScalarValue,
|
||||
{
|
||||
fn from_input_value(input_value: &'a InputValue<S>, vars: &'a Variables<S>) -> Self {
|
||||
match *input_value {
|
||||
InputValue::Null => LookAheadValue::Null,
|
||||
InputValue::Scalar(ref s) => LookAheadValue::Scalar(s),
|
||||
InputValue::Enum(ref e) => LookAheadValue::Enum(e),
|
||||
InputValue::Variable(ref name) => vars
|
||||
.get(name)
|
||||
.map(|v| Self::from_input_value(v, vars))
|
||||
.unwrap_or(LookAheadValue::Null),
|
||||
InputValue::List(ref l) => LookAheadValue::List(
|
||||
l.iter()
|
||||
.map(|i| LookAheadValue::from_input_value(&i.item, vars))
|
||||
.collect(),
|
||||
),
|
||||
InputValue::Object(ref o) => LookAheadValue::Object(
|
||||
o.iter()
|
||||
.map(|(n, i)| {
|
||||
(
|
||||
&n.item as &str,
|
||||
LookAheadValue::from_input_value(&i.item, vars),
|
||||
)
|
||||
fn from_input_value(
|
||||
input_value: BorrowedSpanning<'a, &'a InputValue<S>>,
|
||||
vars: &'a Variables<S>,
|
||||
) -> BorrowedSpanning<'a, Self> {
|
||||
Spanning {
|
||||
span: input_value.span,
|
||||
item: match input_value.item {
|
||||
InputValue::Null => Self::Null,
|
||||
InputValue::Scalar(s) => Self::Scalar(s),
|
||||
InputValue::Enum(e) => Self::Enum(e),
|
||||
InputValue::Variable(name) => vars
|
||||
.get(name)
|
||||
.map(|item| {
|
||||
let input_value = Spanning {
|
||||
span: input_value.span,
|
||||
item,
|
||||
};
|
||||
Self::from_input_value(input_value, vars).item
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
.unwrap_or(Self::Null),
|
||||
InputValue::List(l) => Self::List(
|
||||
l.iter()
|
||||
.map(|i| Self::from_input_value(i.as_ref(), vars))
|
||||
.collect(),
|
||||
),
|
||||
InputValue::Object(o) => Self::Object(
|
||||
o.iter()
|
||||
.map(|(n, i)| {
|
||||
(
|
||||
Spanning {
|
||||
span: &n.span,
|
||||
item: n.item.as_str(),
|
||||
},
|
||||
Self::from_input_value(i.as_ref(), vars),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +91,7 @@ where
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct LookAheadArgument<'a, S: 'a> {
|
||||
name: &'a str,
|
||||
value: LookAheadValue<'a, S>,
|
||||
value: BorrowedSpanning<'a, LookAheadValue<'a, S>>,
|
||||
}
|
||||
|
||||
impl<'a, S> LookAheadArgument<'a, S>
|
||||
|
@ -81,7 +104,7 @@ where
|
|||
) -> Self {
|
||||
LookAheadArgument {
|
||||
name: name.item,
|
||||
value: LookAheadValue::from_input_value(&value.item, vars),
|
||||
value: LookAheadValue::from_input_value(value.as_ref(), vars),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +115,12 @@ where
|
|||
|
||||
/// The value of the argument
|
||||
pub fn value(&'a self) -> &LookAheadValue<'a, S> {
|
||||
&self.value
|
||||
&self.value.item
|
||||
}
|
||||
|
||||
/// The input source span of the argument
|
||||
pub fn span(&self) -> &Span {
|
||||
self.value.span
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +173,7 @@ where
|
|||
.find(|item| item.0.item == "if")
|
||||
.map(|(_, v)| {
|
||||
if let LookAheadValue::Scalar(s) =
|
||||
LookAheadValue::from_input_value(&v.item, vars)
|
||||
LookAheadValue::from_input_value(v.as_ref(), vars).item
|
||||
{
|
||||
s.as_bool().unwrap_or(false)
|
||||
} else {
|
||||
|
@ -160,7 +188,7 @@ where
|
|||
.find(|item| item.0.item == "if")
|
||||
.map(|(_, v)| {
|
||||
if let LookAheadValue::Scalar(b) =
|
||||
LookAheadValue::from_input_value(&v.item, vars)
|
||||
LookAheadValue::from_input_value(v.as_ref(), vars).item
|
||||
{
|
||||
b.as_bool().map(::std::ops::Not::not).unwrap_or(false)
|
||||
} else {
|
||||
|
@ -472,12 +500,12 @@ impl<'a, S> LookAheadMethods<'a, S> for LookAheadSelection<'a, S> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, ops::Range};
|
||||
|
||||
use crate::{
|
||||
ast::{Document, OwnedDocument},
|
||||
graphql_vars,
|
||||
parser::UnlocatedParseResult,
|
||||
parser::{SourcePosition, UnlocatedParseResult},
|
||||
schema::model::SchemaType,
|
||||
validation::test_harness::{MutationRoot, QueryRoot, SubscriptionRoot},
|
||||
value::{DefaultScalarValue, ScalarValue},
|
||||
|
@ -509,6 +537,13 @@ mod tests {
|
|||
fragments
|
||||
}
|
||||
|
||||
fn span(range: Range<(usize, usize, usize)>) -> Span {
|
||||
Span {
|
||||
start: SourcePosition::new(range.start.0, range.start.1, range.start.2),
|
||||
end: SourcePosition::new(range.end.0, range.end.1, range.end.2),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_simple_query() {
|
||||
let docs = parse_document_source::<DefaultScalarValue>(
|
||||
|
@ -711,12 +746,17 @@ query Hero {
|
|||
&fragments,
|
||||
)
|
||||
.unwrap();
|
||||
let span0 = span((32, 2, 18)..(38, 2, 24));
|
||||
let span1 = span((77, 4, 24)..(81, 4, 28));
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
arguments: vec![LookAheadArgument {
|
||||
name: "episode",
|
||||
value: LookAheadValue::Enum("EMPIRE"),
|
||||
value: Spanning {
|
||||
item: LookAheadValue::Enum("EMPIRE"),
|
||||
span: &span0,
|
||||
},
|
||||
}],
|
||||
applies_for: Applies::All,
|
||||
children: vec![
|
||||
|
@ -732,7 +772,10 @@ query Hero {
|
|||
alias: None,
|
||||
arguments: vec![LookAheadArgument {
|
||||
name: "uppercase",
|
||||
value: LookAheadValue::Scalar(&DefaultScalarValue::Boolean(true)),
|
||||
value: Spanning {
|
||||
item: LookAheadValue::Scalar(&DefaultScalarValue::Boolean(true)),
|
||||
span: &span1,
|
||||
},
|
||||
}],
|
||||
children: Vec::new(),
|
||||
applies_for: Applies::All,
|
||||
|
@ -768,12 +811,16 @@ query Hero($episode: Episode) {
|
|||
&fragments,
|
||||
)
|
||||
.unwrap();
|
||||
let span0 = span((51, 2, 18)..(59, 2, 26));
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
arguments: vec![LookAheadArgument {
|
||||
name: "episode",
|
||||
value: LookAheadValue::Enum("JEDI"),
|
||||
value: Spanning {
|
||||
item: LookAheadValue::Enum("JEDI"),
|
||||
span: &span0,
|
||||
},
|
||||
}],
|
||||
applies_for: Applies::All,
|
||||
children: vec![
|
||||
|
@ -821,12 +868,16 @@ query Hero($episode: Episode) {
|
|||
&fragments,
|
||||
)
|
||||
.unwrap();
|
||||
let span0 = span((51, 2, 18)..(59, 2, 26));
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
arguments: vec![LookAheadArgument {
|
||||
name: "episode",
|
||||
value: LookAheadValue::Null,
|
||||
value: Spanning {
|
||||
item: LookAheadValue::Null,
|
||||
span: &span0,
|
||||
},
|
||||
}],
|
||||
applies_for: Applies::All,
|
||||
children: vec![LookAheadSelection {
|
||||
|
@ -1121,12 +1172,16 @@ fragment comparisonFields on Character {
|
|||
&fragments,
|
||||
)
|
||||
.unwrap();
|
||||
let span0 = span((85, 2, 11)..(88, 2, 14));
|
||||
let expected = LookAheadSelection {
|
||||
name: "hero",
|
||||
alias: None,
|
||||
arguments: vec![LookAheadArgument {
|
||||
name: "id",
|
||||
value: LookAheadValue::Scalar(&DefaultScalarValue::Int(42)),
|
||||
value: Spanning {
|
||||
item: LookAheadValue::Scalar(&DefaultScalarValue::Int(42)),
|
||||
span: &span0,
|
||||
},
|
||||
}],
|
||||
applies_for: Applies::All,
|
||||
children: vec![
|
||||
|
|
|
@ -52,15 +52,15 @@ impl Span {
|
|||
|
||||
/// Data structure used to wrap items into a [`Span`].
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Spanning<T> {
|
||||
pub struct Spanning<T, Sp = Span> {
|
||||
/// Wrapped item.
|
||||
pub item: T,
|
||||
|
||||
/// [`Span`] of the wrapped item.
|
||||
pub span: Span,
|
||||
pub span: Sp,
|
||||
}
|
||||
|
||||
impl<T> Spanning<T> {
|
||||
impl<T> Spanning<T, Span> {
|
||||
#[doc(hidden)]
|
||||
pub fn new(span: Span, item: T) -> Self {
|
||||
Self { item, span }
|
||||
|
@ -126,6 +126,14 @@ impl<T> Spanning<T> {
|
|||
pub fn and_then<O, F: Fn(T) -> Option<O>>(self, f: F) -> Option<Spanning<O>> {
|
||||
f(self.item).map(|item| Spanning::new(self.span, item))
|
||||
}
|
||||
|
||||
/// Converts into a [`Spanning`] containing a borrowed item and a borrowed [`Span`].
|
||||
pub(crate) fn as_ref(&self) -> Spanning<&'_ T, &'_ Span> {
|
||||
Spanning {
|
||||
item: &self.item,
|
||||
span: &self.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for Spanning<T> {
|
||||
|
|
|
@ -244,7 +244,8 @@ where
|
|||
m.item
|
||||
.iter()
|
||||
.filter_map(|(k, v)| {
|
||||
v.item.clone().into_const(exec_vars).map(|v| (k.item, v))
|
||||
let val = v.item.clone().into_const(exec_vars)?;
|
||||
Some((k.item, Spanning::new(v.span, val)))
|
||||
})
|
||||
.collect()
|
||||
}),
|
||||
|
|
|
@ -68,13 +68,13 @@ pub enum TypeKind {
|
|||
/// Field argument container
|
||||
#[derive(Debug)]
|
||||
pub struct Arguments<'a, S = DefaultScalarValue> {
|
||||
args: Option<IndexMap<&'a str, InputValue<S>>>,
|
||||
args: Option<IndexMap<&'a str, Spanning<InputValue<S>>>>,
|
||||
}
|
||||
|
||||
impl<'a, S> Arguments<'a, S> {
|
||||
#[doc(hidden)]
|
||||
pub fn new(
|
||||
mut args: Option<IndexMap<&'a str, InputValue<S>>>,
|
||||
mut args: Option<IndexMap<&'a str, Spanning<InputValue<S>>>>,
|
||||
meta_args: &'a Option<Vec<Argument<S>>>,
|
||||
) -> Self
|
||||
where
|
||||
|
@ -89,7 +89,7 @@ impl<'a, S> Arguments<'a, S> {
|
|||
let arg_name = arg.name.as_str();
|
||||
if args.get(arg_name).is_none() {
|
||||
if let Some(val) = arg.default_value.as_ref() {
|
||||
args.insert(arg_name, val.clone());
|
||||
args.insert(arg_name, Spanning::unlocated(val.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,10 +117,16 @@ impl<'a, S> Arguments<'a, S> {
|
|||
self.args
|
||||
.as_ref()
|
||||
.and_then(|args| args.get(name))
|
||||
.map(|spanning| &spanning.item)
|
||||
.map(InputValue::convert)
|
||||
.transpose()
|
||||
.map_err(IntoFieldError::into_field_error)
|
||||
}
|
||||
|
||||
/// Gets a direct reference to the [`Spanning`] argument [`InputValue`].
|
||||
pub fn get_input_value(&self, name: &str) -> Option<&Spanning<InputValue<S>>> {
|
||||
self.args.as_ref().and_then(|args| args.get(name))
|
||||
}
|
||||
}
|
||||
|
||||
/// Primary trait used to resolve GraphQL values.
|
||||
|
@ -472,7 +478,8 @@ where
|
|||
m.item
|
||||
.iter()
|
||||
.filter_map(|(k, v)| {
|
||||
v.item.clone().into_const(exec_vars).map(|v| (k.item, v))
|
||||
let val = v.item.clone().into_const(exec_vars)?;
|
||||
Some((k.item, Spanning::new(v.span, val)))
|
||||
})
|
||||
.collect()
|
||||
}),
|
||||
|
|
|
@ -316,7 +316,8 @@ where
|
|||
m.item
|
||||
.iter()
|
||||
.filter_map(|(k, v)| {
|
||||
v.item.clone().into_const(exec_vars).map(|v| (k.item, v))
|
||||
let val = v.item.clone().into_const(exec_vars)?;
|
||||
Some((k.item, Spanning::new(v.span, val)))
|
||||
})
|
||||
.collect()
|
||||
}),
|
||||
|
|
|
@ -72,11 +72,8 @@ where
|
|||
let mut remaining_required_fields = input_fields
|
||||
.iter()
|
||||
.filter_map(|f| {
|
||||
if f.arg_type.is_non_null() && f.default_value.is_none() {
|
||||
Some(&f.name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
(f.arg_type.is_non_null() && f.default_value.is_none())
|
||||
.then_some(&f.name)
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
|
|
Loading…
Reference in a new issue