Moved `Variables` from `async_graphql::context::Variables` to

`async_graphql::Variables`.
This commit is contained in:
Sunli 2021-02-27 21:52:43 +08:00
parent 10c41a7841
commit 4f2ea5ed50
9 changed files with 124 additions and 93 deletions

View File

@ -4,8 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Changed
- Moved `Variables` from `async_graphql::context::Variables` to `async_graphql::Variables`.
## [2.5.8] - 2021-02-27
### Added
- Allow the `deprecation` attribute to have no reason.
```rust
@ -27,10 +35,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [2.5.7] - 2021-02-23
### Fixed
- Fix the problem that the borrowing lifetime returned by the `Context::data` function is too small.
## [2.5.6] - 2021-02-23
### Changed
- When introspection is disabled, introspection related types are no longer registered.
## [2.5.5] - 2021-02-22

View File

@ -1,17 +1,15 @@
//! Query context.
use std::any::{Any, TypeId};
use std::collections::{BTreeMap, HashMap};
use std::convert::TryFrom;
use std::collections::HashMap;
use std::fmt::{self, Debug, Display, Formatter};
use std::ops::Deref;
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
use async_graphql_value::Value as InputValue;
use async_graphql_value::{Value as InputValue, Variables};
use fnv::FnvHashMap;
use http::header::{AsHeaderName, HeaderMap, IntoHeaderName};
use serde::de::{Deserialize, Deserializer};
use serde::ser::{SerializeSeq, Serializer};
use serde::Serialize;
@ -25,81 +23,6 @@ use crate::{
UploadValue, Value,
};
/// Variables of a query.
#[derive(Debug, Clone, Default, Serialize)]
#[serde(transparent)]
pub struct Variables(pub BTreeMap<Name, Value>);
impl Display for Variables {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("{")?;
for (i, (name, value)) in self.0.iter().enumerate() {
write!(f, "{}{}: {}", if i == 0 { "" } else { ", " }, name, value)?;
}
f.write_str("}")
}
}
impl<'de> Deserialize<'de> for Variables {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Ok(Self(
<Option<BTreeMap<Name, Value>>>::deserialize(deserializer)?.unwrap_or_default(),
))
}
}
impl Variables {
/// Get the variables from a GraphQL value.
///
/// If the value is not a map, then no variables will be returned.
#[must_use]
pub fn from_value(value: Value) -> Self {
match value {
Value::Object(obj) => Self(obj),
_ => Self::default(),
}
}
/// Get the values from a JSON value.
///
/// If the value is not a map or the keys of a map are not valid GraphQL names, then no
/// variables will be returned.
#[must_use]
pub fn from_json(value: serde_json::Value) -> Self {
Value::from_json(value)
.map(Self::from_value)
.unwrap_or_default()
}
/// Get the variables as a GraphQL value.
#[must_use]
pub fn into_value(self) -> Value {
Value::Object(self.0)
}
pub(crate) fn variable_path(&mut self, path: &str) -> Option<&mut Value> {
let mut parts = path.strip_prefix("variables.")?.split('.');
let initial = self.0.get_mut(parts.next().unwrap())?;
parts.try_fold(initial, |current, part| match current {
Value::List(list) => part
.parse::<u32>()
.ok()
.and_then(|idx| usize::try_from(idx).ok())
.and_then(move |idx| list.get_mut(idx)),
Value::Object(obj) => obj.get_mut(part),
_ => None,
})
}
}
impl From<Variables> for Value {
fn from(variables: Variables) -> Self {
variables.into_value()
}
}
/// Schema/Context data.
///
/// This is a type map, allowing you to store anything inside it.
@ -574,7 +497,6 @@ impl<'a, T> ContextBase<'a, T> {
.and_then(|def| {
self.query_env
.variables
.0
.get(&def.node.name.node)
.or_else(|| def.node.default_value())
})

View File

@ -208,7 +208,7 @@ pub use subscription::SubscriptionType;
pub use async_graphql_parser as parser;
pub use async_graphql_value::{
from_value, to_value, value, ConstValue as Value, DeserializerError, Name, Number,
SerializerError,
SerializerError, Variables,
};
pub use base::{
Description, InputObjectType, InputType, InterfaceType, ObjectType, OutputType, Type, UnionType,

View File

@ -1,5 +1,6 @@
use std::any::Any;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt::{self, Debug, Formatter};
use serde::{Deserialize, Deserializer, Serialize};
@ -79,7 +80,23 @@ impl Request {
/// `request.variables["files"][2]["content"]`. If no variable exists at the path this function
/// won't do anything.
pub fn set_upload(&mut self, var_path: &str, upload: UploadValue) {
let variable = match self.variables.variable_path(var_path) {
fn variable_path<'a>(variables: &'a mut Variables, path: &str) -> Option<&'a mut Value> {
let mut parts = path.strip_prefix("variables.")?.split('.');
let initial = variables.get_mut(parts.next().unwrap())?;
parts.try_fold(initial, |current, part| match current {
Value::List(list) => part
.parse::<u32>()
.ok()
.and_then(|idx| usize::try_from(idx).ok())
.and_then(move |idx| list.get_mut(idx)),
Value::Object(obj) => obj.get_mut(part),
_ => None,
})
}
let variable = match variable_path(&mut self.variables, var_path) {
Some(variable) => variable,
None => return,
};
@ -171,7 +188,7 @@ mod tests {
"query": "{ a b c }"
}))
.unwrap();
assert!(request.variables.0.is_empty());
assert!(request.variables.is_empty());
assert!(request.operation_name.is_none());
assert_eq!(request.query, "{ a b c }");
}
@ -183,7 +200,7 @@ mod tests {
"operationName": "a"
}))
.unwrap();
assert!(request.variables.0.is_empty());
assert!(request.variables.is_empty());
assert_eq!(request.operation_name.as_deref(), Some("a"));
assert_eq!(request.query, "{ a b c }");
}
@ -219,7 +236,7 @@ mod tests {
}))
.unwrap();
assert!(request.operation_name.is_none());
assert!(request.variables.0.is_empty());
assert!(request.variables.is_empty());
}
#[test]
@ -230,7 +247,7 @@ mod tests {
.unwrap();
if let BatchRequest::Single(request) = request {
assert!(request.variables.0.is_empty());
assert!(request.variables.is_empty());
assert!(request.operation_name.is_none());
assert_eq!(request.query, "{ a b c }");
} else {
@ -251,11 +268,11 @@ mod tests {
.unwrap();
if let BatchRequest::Batch(requests) = request {
assert!(requests[0].variables.0.is_empty());
assert!(requests[0].variables.is_empty());
assert!(requests[0].operation_name.is_none());
assert_eq!(requests[0].query, "{ a b c }");
assert!(requests[1].variables.0.is_empty());
assert!(requests[1].variables.is_empty());
assert!(requests[1].operation_name.is_none());
assert_eq!(requests[1].query, "{ d e }");
} else {

View File

@ -49,7 +49,7 @@ impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> {
.clone()
.into_const_with(|var_name| {
ctx.variables
.and_then(|variables| variables.0.get(&var_name))
.and_then(|variables| variables.get(&var_name))
.map(Clone::clone)
.ok_or(())
})

View File

@ -118,7 +118,6 @@ impl<'a> VisitorContext<'a> {
.and_then(|def| {
if let Some(variables) = self.variables {
variables
.0
.get(&def.node.name.node)
.or_else(|| def.node.default_value())
} else {

View File

@ -1688,9 +1688,7 @@ pub async fn test_input_validator_variable() {
let validator_length = 6;
for case in &test_cases {
let mut variables = Variables::default();
variables
.0
.insert(Name::new("id"), Value::String(case.to_string()));
variables.insert(Name::new("id"), Value::String(case.to_string()));
let field_query = "query($id: String!) {fieldParameter(id: $id)}";
let object_query = "query($id: String!) {inputObject(input: {id: $id})}";

View File

@ -6,6 +6,7 @@
mod de;
mod macros;
mod ser;
mod variables;
use std::borrow::{Borrow, Cow};
use std::collections::BTreeMap;
@ -22,6 +23,8 @@ pub use de::{from_value, DeserializerError};
pub use ser::{to_value, SerializerError};
pub use serde_json::Number;
pub use variables::Variables;
/// A GraphQL name.
///
/// [Reference](https://spec.graphql.org/June2018/#Name).

80
value/src/variables.rs Normal file
View File

@ -0,0 +1,80 @@
use std::collections::BTreeMap;
use std::fmt::{self, Display, Formatter};
use std::ops::{Deref, DerefMut};
use serde::{Deserialize, Deserializer, Serialize};
use crate::{ConstValue, Name};
/// Variables of a query.
#[derive(Debug, Clone, Default, Serialize)]
#[serde(transparent)]
pub struct Variables(BTreeMap<Name, ConstValue>);
impl Display for Variables {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("{")?;
for (i, (name, value)) in self.0.iter().enumerate() {
write!(f, "{}{}: {}", if i == 0 { "" } else { ", " }, name, value)?;
}
f.write_str("}")
}
}
impl<'de> Deserialize<'de> for Variables {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Ok(Self(
<Option<BTreeMap<Name, ConstValue>>>::deserialize(deserializer)?.unwrap_or_default(),
))
}
}
impl Variables {
/// Get the variables from a GraphQL value.
///
/// If the value is not a map, then no variables will be returned.
#[must_use]
pub fn from_value(value: ConstValue) -> Self {
match value {
ConstValue::Object(obj) => Self(obj),
_ => Self::default(),
}
}
/// Get the values from a JSON value.
///
/// If the value is not a map or the keys of a map are not valid GraphQL names, then no
/// variables will be returned.
#[must_use]
pub fn from_json(value: serde_json::Value) -> Self {
ConstValue::from_json(value)
.map(Self::from_value)
.unwrap_or_default()
}
/// Get the variables as a GraphQL value.
#[must_use]
pub fn into_value(self) -> ConstValue {
ConstValue::Object(self.0)
}
}
impl From<Variables> for ConstValue {
fn from(variables: Variables) -> Self {
variables.into_value()
}
}
impl Deref for Variables {
type Target = BTreeMap<Name, ConstValue>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Variables {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}