Respect query object field order. #612
This commit is contained in:
parent
33ccd67073
commit
9b5149ee23
|
@ -4,6 +4,11 @@ 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
|
||||
|
||||
- Add derived for object & simple object & complex object. [#667](https://github.com/async-graphql/async-graphql/pull/667) [#670](https://github.com/async-graphql/async-graphql/pull/670)
|
||||
- Respect query object field order. [#612](https://github.com/async-graphql/async-graphql/issues/612)
|
||||
|
||||
## [2.10.5] 2021-10-22
|
||||
|
||||
- Bump poem from `0.6.6` to `1.0.7`.
|
||||
|
|
|
@ -85,8 +85,7 @@ pub fn generate(
|
|||
_ => Some(Err(Error::new_spanned(
|
||||
&pat,
|
||||
"Must be a simple argument",
|
||||
)
|
||||
.into())),
|
||||
))),
|
||||
},
|
||||
FnArg::Receiver(_) => None,
|
||||
})
|
||||
|
|
|
@ -231,7 +231,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
|
|||
}
|
||||
|
||||
fn to_value(&self) -> #crate_name::Value {
|
||||
let mut map = ::std::collections::BTreeMap::new();
|
||||
let mut map = #crate_name::indexmap::IndexMap::new();
|
||||
#(#put_fields)*
|
||||
#crate_name::Value::Object(map)
|
||||
}
|
||||
|
@ -272,7 +272,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
|
|||
}
|
||||
|
||||
fn __internal_to_value(&self) -> #crate_name::Value where Self: #crate_name::InputType {
|
||||
let mut map = ::std::collections::BTreeMap::new();
|
||||
let mut map = #crate_name::indexmap::IndexMap::new();
|
||||
#(#put_fields)*
|
||||
#crate_name::Value::Object(map)
|
||||
}
|
||||
|
|
|
@ -100,8 +100,7 @@ pub fn generate(
|
|||
_ => Some(Err(Error::new_spanned(
|
||||
&pat,
|
||||
"Must be a simple argument",
|
||||
)
|
||||
.into())),
|
||||
))),
|
||||
},
|
||||
FnArg::Receiver(_) => None,
|
||||
})
|
||||
|
|
|
@ -53,7 +53,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
|||
// Before processing the fields, we generate the derivated fields
|
||||
for field in &s.fields {
|
||||
processed_fields.push(SimpleObjectFieldGenerator {
|
||||
field: &field,
|
||||
field,
|
||||
derived: None,
|
||||
});
|
||||
|
||||
|
@ -66,7 +66,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
|
|||
let derived = DerivedFieldMetadata { ident: name, into };
|
||||
|
||||
processed_fields.push(SimpleObjectFieldGenerator {
|
||||
field: &field,
|
||||
field,
|
||||
derived: Some(derived),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -375,7 +375,7 @@ pub fn generate(
|
|||
};
|
||||
#crate_name::futures_util::pin_mut!(resolve_fut);
|
||||
let mut resp = query_env.extensions.resolve(ri, &mut resolve_fut).await.map(|value| {
|
||||
let mut map = ::std::collections::BTreeMap::new();
|
||||
let mut map = #crate_name::indexmap::IndexMap::new();
|
||||
map.insert(::std::clone::Clone::clone(&field_name), value.unwrap_or_default());
|
||||
#crate_name::Response::new(#crate_name::Value::Object(map))
|
||||
})
|
||||
|
|
|
@ -624,7 +624,7 @@ impl<'a> GraphQLPlaygroundConfig<'a> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::BTreeMap;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
#[test]
|
||||
fn test_with_setting_can_use_any_json_value() {
|
||||
|
@ -634,7 +634,7 @@ mod tests {
|
|||
.with_setting("number", 10)
|
||||
.with_setting("null", Value::Null)
|
||||
.with_setting("array", Vec::from([1, 2, 3]))
|
||||
.with_setting("object", BTreeMap::new());
|
||||
.with_setting("object", IndexMap::new());
|
||||
|
||||
let json = serde_json::to_value(settings).unwrap();
|
||||
let settings = json["settings"].as_object().unwrap();
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use crate::extensions::ResolveInfo;
|
||||
use crate::parser::types::Selection;
|
||||
use crate::registry::MetaType;
|
||||
|
@ -74,7 +75,7 @@ pub async fn resolve_container_serial<'a, T: ContainerType + ?Sized>(
|
|||
resolve_container_inner(ctx, root, false).await
|
||||
}
|
||||
|
||||
fn insert_value(target: &mut BTreeMap<Name, Value>, name: Name, value: Value) {
|
||||
fn insert_value(target: &mut IndexMap<Name, Value>, name: Name, value: Value) {
|
||||
if let Some(prev_value) = target.get_mut(&name) {
|
||||
if let Value::Object(target_map) = prev_value {
|
||||
if let Value::Object(obj) = value {
|
||||
|
@ -118,7 +119,7 @@ async fn resolve_container_inner<'a, T: ContainerType + ?Sized>(
|
|||
results
|
||||
};
|
||||
|
||||
let mut map = BTreeMap::new();
|
||||
let mut map = IndexMap::new();
|
||||
for (name, value) in res {
|
||||
insert_value(&mut map, name, value);
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@ impl Response {
|
|||
}
|
||||
|
||||
/// Response for batchable queries
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum BatchResponse {
|
||||
|
|
|
@ -2,6 +2,8 @@ use std::collections::BTreeMap;
|
|||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use crate::{
|
||||
InputType, InputValueError, InputValueResult, Name, OutputType, Scalar, ScalarType, Value,
|
||||
};
|
||||
|
@ -33,7 +35,7 @@ where
|
|||
}
|
||||
|
||||
fn to_value(&self) -> Value {
|
||||
let mut map = BTreeMap::new();
|
||||
let mut map = IndexMap::new();
|
||||
for (name, value) in self {
|
||||
map.insert(Name::new(name.to_string()), value.to_value());
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::hash::Hash;
|
||||
use std::str::FromStr;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use crate::{
|
||||
InputType, InputValueError, InputValueResult, Name, OutputType, Scalar, ScalarType, Value,
|
||||
};
|
||||
|
@ -34,7 +36,7 @@ where
|
|||
}
|
||||
|
||||
fn to_value(&self) -> Value {
|
||||
let mut map = BTreeMap::new();
|
||||
let mut map = IndexMap::new();
|
||||
for (name, value) in self {
|
||||
map.insert(Name::new(name.to_string()), value.to_value());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
use async_graphql::*;
|
||||
|
||||
#[tokio::test]
|
||||
pub async fn test_preserve_order() {
|
||||
#[derive(SimpleObject)]
|
||||
struct Root {
|
||||
a: i32,
|
||||
b: i32,
|
||||
c: i32,
|
||||
}
|
||||
|
||||
let schema = Schema::new(Root { a: 1, b: 2, c: 3 }, EmptyMutation, EmptySubscription);
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute("{ a c b }")
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap()
|
||||
.data,
|
||||
value!({
|
||||
"a": 1, "c": 3, "b": 2
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(
|
||||
&schema
|
||||
.execute("{ a c b }")
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap()
|
||||
.data
|
||||
)
|
||||
.unwrap(),
|
||||
r#"{"a":1,"c":3,"b":2}"#
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
schema
|
||||
.execute("{ c b a }")
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap()
|
||||
.data,
|
||||
value!({
|
||||
"c": 3, "b": 2, "a": 1
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(
|
||||
&schema
|
||||
.execute("{ c b a }")
|
||||
.await
|
||||
.into_result()
|
||||
.unwrap()
|
||||
.data
|
||||
)
|
||||
.unwrap(),
|
||||
r#"{"c":3,"b":2,"a":1}"#
|
||||
);
|
||||
}
|
|
@ -15,3 +15,4 @@ categories = ["network-programming", "asynchronous"]
|
|||
serde_json = "1.0.64"
|
||||
serde = { version = "1.0.125", features = ["derive"] }
|
||||
bytes = { version = "1.0.1", features = ["serde"] }
|
||||
indexmap = { version = "1.7.0", features = ["serde"] }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::{fmt, vec};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use crate::{ConstValue, Name};
|
||||
|
||||
use serde::de::{
|
||||
|
@ -78,7 +79,7 @@ where
|
|||
}
|
||||
|
||||
fn visit_object<'de, V>(
|
||||
object: BTreeMap<Name, ConstValue>,
|
||||
object: IndexMap<Name, ConstValue>,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, DeserializerError>
|
||||
where
|
||||
|
@ -365,13 +366,13 @@ impl<'de> SeqAccess<'de> for SeqDeserializer {
|
|||
}
|
||||
|
||||
struct MapDeserializer {
|
||||
iter: <BTreeMap<Name, ConstValue> as IntoIterator>::IntoIter,
|
||||
iter: <IndexMap<Name, ConstValue> as IntoIterator>::IntoIter,
|
||||
value: Option<ConstValue>,
|
||||
}
|
||||
|
||||
impl MapDeserializer {
|
||||
#[inline]
|
||||
fn new(map: BTreeMap<Name, ConstValue>) -> Self {
|
||||
fn new(map: IndexMap<Name, ConstValue>) -> Self {
|
||||
MapDeserializer {
|
||||
iter: map.into_iter(),
|
||||
value: None,
|
||||
|
|
|
@ -10,7 +10,6 @@ mod value_serde;
|
|||
mod variables;
|
||||
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fmt::{self, Display, Formatter, Write};
|
||||
use std::iter::FromIterator;
|
||||
|
@ -18,9 +17,12 @@ use std::ops::Deref;
|
|||
use std::sync::Arc;
|
||||
|
||||
use bytes::Bytes;
|
||||
use indexmap::IndexMap;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
pub use deserializer::{from_value, DeserializerError};
|
||||
#[doc(hidden)]
|
||||
pub use indexmap;
|
||||
pub use serde_json::Number;
|
||||
pub use serializer::{to_value, SerializerError};
|
||||
|
||||
|
@ -139,7 +141,7 @@ pub enum ConstValue {
|
|||
/// A list of values.
|
||||
List(Vec<ConstValue>),
|
||||
/// An object. This is a map of keys to values.
|
||||
Object(BTreeMap<Name, ConstValue>),
|
||||
Object(IndexMap<Name, ConstValue>),
|
||||
}
|
||||
|
||||
impl PartialEq for ConstValue {
|
||||
|
@ -254,8 +256,8 @@ impl<T: Into<ConstValue>> From<Vec<T>> for ConstValue {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<BTreeMap<Name, ConstValue>> for ConstValue {
|
||||
fn from(f: BTreeMap<Name, ConstValue>) -> Self {
|
||||
impl From<IndexMap<Name, ConstValue>> for ConstValue {
|
||||
fn from(f: IndexMap<Name, ConstValue>) -> Self {
|
||||
ConstValue::Object(f)
|
||||
}
|
||||
}
|
||||
|
@ -364,7 +366,7 @@ pub enum Value {
|
|||
/// A list of values.
|
||||
List(Vec<Value>),
|
||||
/// An object. This is a map of keys to values.
|
||||
Object(BTreeMap<Name, Value>),
|
||||
Object(IndexMap<Name, Value>),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
|
|
|
@ -191,7 +191,7 @@ macro_rules! value_internal {
|
|||
|
||||
({ $($tt:tt)+ }) => {
|
||||
$crate::ConstValue::Object({
|
||||
let mut object = std::collections::BTreeMap::new();
|
||||
let mut object = $crate::indexmap::IndexMap::new();
|
||||
$crate::value_internal!(@object object () ($($tt)+) ($($tt)+));
|
||||
object
|
||||
})
|
||||
|
@ -227,7 +227,7 @@ macro_rules! value_expect_expr_comma {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{ConstValue, Name};
|
||||
use std::collections::BTreeMap;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
#[test]
|
||||
fn test_macro() {
|
||||
|
@ -254,7 +254,7 @@ mod tests {
|
|||
)
|
||||
);
|
||||
assert_eq!(value!({"a": 10, "b": true}), {
|
||||
let mut map = BTreeMap::new();
|
||||
let mut map = IndexMap::new();
|
||||
map.insert(Name::new("a"), ConstValue::Number(10.into()));
|
||||
map.insert(Name::new("b"), ConstValue::Boolean(true));
|
||||
ConstValue::Object(map)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use serde::ser::{self, Impossible};
|
||||
use serde::Serialize;
|
||||
|
||||
|
@ -180,7 +180,7 @@ impl ser::Serializer for Serializer {
|
|||
T: ser::Serialize,
|
||||
{
|
||||
value.serialize(self).map(|v| {
|
||||
let mut map = BTreeMap::new();
|
||||
let mut map = IndexMap::new();
|
||||
map.insert(Name::new(variant), v);
|
||||
ConstValue::Object(map)
|
||||
})
|
||||
|
@ -222,7 +222,7 @@ impl ser::Serializer for Serializer {
|
|||
#[inline]
|
||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
||||
Ok(SerializeMap {
|
||||
map: BTreeMap::new(),
|
||||
map: IndexMap::new(),
|
||||
key: None,
|
||||
})
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ impl ser::Serializer for Serializer {
|
|||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStruct, Self::Error> {
|
||||
Ok(SerializeStruct(BTreeMap::new()))
|
||||
Ok(SerializeStruct(IndexMap::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -244,7 +244,7 @@ impl ser::Serializer for Serializer {
|
|||
variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStructVariant, Self::Error> {
|
||||
Ok(SerializeStructVariant(Name::new(variant), BTreeMap::new()))
|
||||
Ok(SerializeStructVariant(Name::new(variant), IndexMap::new()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -337,14 +337,14 @@ impl ser::SerializeTupleVariant for SerializeTupleVariant {
|
|||
|
||||
#[inline]
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
let mut map = BTreeMap::new();
|
||||
let mut map = IndexMap::new();
|
||||
map.insert(self.0, ConstValue::List(self.1));
|
||||
Ok(ConstValue::Object(map))
|
||||
}
|
||||
}
|
||||
|
||||
struct SerializeMap {
|
||||
map: BTreeMap<Name, ConstValue>,
|
||||
map: IndexMap<Name, ConstValue>,
|
||||
key: Option<Name>,
|
||||
}
|
||||
|
||||
|
@ -378,7 +378,7 @@ impl ser::SerializeMap for SerializeMap {
|
|||
}
|
||||
}
|
||||
|
||||
struct SerializeStruct(BTreeMap<Name, ConstValue>);
|
||||
struct SerializeStruct(IndexMap<Name, ConstValue>);
|
||||
|
||||
impl ser::SerializeStruct for SerializeStruct {
|
||||
type Ok = ConstValue;
|
||||
|
@ -405,7 +405,7 @@ impl ser::SerializeStruct for SerializeStruct {
|
|||
}
|
||||
}
|
||||
|
||||
struct SerializeStructVariant(Name, BTreeMap<Name, ConstValue>);
|
||||
struct SerializeStructVariant(Name, IndexMap<Name, ConstValue>);
|
||||
|
||||
impl ser::SerializeStructVariant for SerializeStructVariant {
|
||||
type Ok = ConstValue;
|
||||
|
@ -428,7 +428,7 @@ impl ser::SerializeStructVariant for SerializeStructVariant {
|
|||
|
||||
#[inline]
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
let mut map = BTreeMap::new();
|
||||
let mut map = IndexMap::new();
|
||||
map.insert(self.0, ConstValue::Object(self.1));
|
||||
Ok(ConstValue::Object(map))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::fmt::{self, Formatter};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use serde::de::{Error as DeError, MapAccess, SeqAccess, Visitor};
|
||||
use serde::ser::Error as SerError;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
@ -137,7 +137,7 @@ impl<'de> Deserialize<'de> for ConstValue {
|
|||
where
|
||||
A: MapAccess<'de>,
|
||||
{
|
||||
let mut map = BTreeMap::new();
|
||||
let mut map = IndexMap::new();
|
||||
while let Some((name, value)) = visitor.next_entry()? {
|
||||
map.insert(name, value);
|
||||
}
|
||||
|
@ -280,7 +280,7 @@ impl<'de> Deserialize<'de> for Value {
|
|||
where
|
||||
A: MapAccess<'de>,
|
||||
{
|
||||
let mut map = BTreeMap::new();
|
||||
let mut map = IndexMap::new();
|
||||
while let Some((name, value)) = visitor.next_entry()? {
|
||||
map.insert(name, value);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ impl Variables {
|
|||
#[must_use]
|
||||
pub fn from_value(value: ConstValue) -> Self {
|
||||
match value {
|
||||
ConstValue::Object(obj) => Self(obj),
|
||||
ConstValue::Object(obj) => Self(obj.into_iter().collect()),
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ impl Variables {
|
|||
/// Get the variables as a GraphQL value.
|
||||
#[must_use]
|
||||
pub fn into_value(self) -> ConstValue {
|
||||
ConstValue::Object(self.0)
|
||||
ConstValue::Object(self.0.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue