Respect query object field order. #612

This commit is contained in:
Sunli 2021-10-26 18:57:39 +08:00
parent 33ccd67073
commit 9b5149ee23
19 changed files with 117 additions and 44 deletions

View File

@ -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`.

View File

@ -85,8 +85,7 @@ pub fn generate(
_ => Some(Err(Error::new_spanned(
&pat,
"Must be a simple argument",
)
.into())),
))),
},
FnArg::Receiver(_) => None,
})

View File

@ -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)
}

View File

@ -100,8 +100,7 @@ pub fn generate(
_ => Some(Err(Error::new_spanned(
&pat,
"Must be a simple argument",
)
.into())),
))),
},
FnArg::Receiver(_) => None,
})

View File

@ -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),
})
}

View File

@ -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))
})

View File

@ -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();

View File

@ -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);
}

View File

@ -98,6 +98,7 @@ impl Response {
}
/// Response for batchable queries
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum BatchResponse {

View File

@ -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());
}

View File

@ -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());
}

60
tests/preserve_order.rs Normal file
View File

@ -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}"#
);
}

View File

@ -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"] }

View File

@ -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,

View File

@ -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 {

View File

@ -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)

View File

@ -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))
}

View File

@ -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);
}

View File

@ -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())
}
}