From f00ef6100afb90cbd38b3a117a4a2fc062273a7f Mon Sep 17 00:00:00 2001 From: laststylebender14 Date: Wed, 11 Sep 2024 21:53:10 +0530 Subject: [PATCH 1/4] - basic setup --- src/graphql_tests.rs | 38 +++++++++++- src/introspection.rs | 94 ++++++++++++++++++++++++++++ src/introspection_query.graphql | 106 ++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/project.rs | 4 +- 5 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 src/introspection.rs create mode 100644 src/introspection_query.graphql diff --git a/src/graphql_tests.rs b/src/graphql_tests.rs index 6729022..d2e54f4 100644 --- a/src/graphql_tests.rs +++ b/src/graphql_tests.rs @@ -5,7 +5,10 @@ use diff_logger::DiffLogger; use reqwest::Method; use tracing::{error, info}; -use crate::request::{MOCK_API_CLIENT, REFERENCE_GRAPHQL_CLIENT, TESTED_GRAPHQL_CLIENT}; +use crate::{ + introspection::Schema, + request::{MOCK_API_CLIENT, REFERENCE_GRAPHQL_CLIENT, TESTED_GRAPHQL_CLIENT}, +}; use super::ROOT_DIR; @@ -65,3 +68,36 @@ pub async fn run_graphql_tests() -> Result<()> { Ok(()) } + +pub async fn run_introspection_query() -> Result<()> { + info!("Run graphql introspection tests"); + let test = include_str!("./introspection_query.graphql"); + + let actual: Schema = serde_json::from_value(TESTED_GRAPHQL_CLIENT.request(&test).await?)?; + let expected: Schema = serde_json::from_value(REFERENCE_GRAPHQL_CLIENT.request(&test).await?)?; + + println!("----------------------------------------------------"); + println!("expected {:#?}", expected); + println!("actual {:#?}", actual); + println!("----------------------------------------------------"); + + let differ = DiffLogger::new(); + let difference = differ.diff( + &serde_json::to_value(expected)?, + &serde_json::to_value(actual)?, + ); + + if !difference.is_empty() { + error!( + "Actual response is not equal to expected + Note: left is expected response -> right is actual response" + ); + println!("{}", difference); + + return Err(anyhow!("Actual response is not equal to expected")); + } + + info!("Execution of graphql tests finished"); + + Ok(()) +} diff --git a/src/introspection.rs b/src/introspection.rs new file mode 100644 index 0000000..38cc856 --- /dev/null +++ b/src/introspection.rs @@ -0,0 +1,94 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub struct Schema { + data: SchemaData, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub struct SchemaData { + #[serde(rename = "__schema")] + schema: SchemaDetails, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "camelCase")] +pub struct SchemaDetails { + query_type: QueryType, + mutation_type: Option, + subscription_type: Option, + #[serde(serialize_with = "serialize_sorted_vec")] + types: Option>, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub struct QueryType { + name: String, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[serde(rename_all = "camelCase")] +pub struct TypeDetails { + kind: String, + name: Option, + #[serde(serialize_with = "serialize_sorted_vec")] + fields: Option>, + #[serde(serialize_with = "serialize_sorted_vec")] + input_fields: Option>, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Field { + name: String, + #[serde(serialize_with = "serialize_sorted_vec")] + args: Option>, + #[serde(rename = "type")] + type_: TypeRef, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[serde(rename_all = "camelCase")] +pub struct InputValue { + name: String, + #[serde(rename = "type")] + type_: TypeRef, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct TypeRef { + kind: String, + name: Option, + of_type: Option>, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "camelCase")] +pub struct EnumValue { + name: String, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub struct Directive { + name: String, + #[serde(serialize_with = "serialize_sorted_vec")] + locations: Option>, + #[serde(serialize_with = "serialize_sorted_vec")] + args: Option>, +} + +// Add this function at the end of the file +fn serialize_sorted_vec(vec: &Option>, serializer: S) -> Result +where + S: serde::Serializer, + T: Serialize + Ord + Clone, +{ + match vec { + Some(v) => { + let mut sorted = v.clone(); + sorted.sort(); + sorted.serialize(serializer) + } + None => serializer.serialize_none(), + } +} \ No newline at end of file diff --git a/src/introspection_query.graphql b/src/introspection_query.graphql new file mode 100644 index 0000000..3365224 --- /dev/null +++ b/src/introspection_query.graphql @@ -0,0 +1,106 @@ + +query IntrospectionQuery { + __schema { + + queryType { name } + mutationType { name } + subscriptionType { name } + types { + ...FullType + } + directives { + name + description + + locations + args { + ...InputValue + } + } + } +} + +fragment FullType on __Type { + kind + name + description + + + fields(includeDeprecated: true) { + name + description + args { + ...InputValue + } + type { + ...TypeRef + } + isDeprecated + deprecationReason + } + inputFields { + ...InputValue + } + interfaces { + ...TypeRef + } + enumValues(includeDeprecated: true) { + name + description + isDeprecated + deprecationReason + } + possibleTypes { + ...TypeRef + } +} + +fragment InputValue on __InputValue { + name + description + type { ...TypeRef } + defaultValue + + +} + +fragment TypeRef on __Type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index f6d859e..f81acde 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,5 +4,6 @@ mod graphql_tests; pub mod project; mod request; mod utils; +mod introspection; pub const ROOT_DIR: &str = env!("CARGO_MANIFEST_DIR"); diff --git a/src/project.rs b/src/project.rs index ce91a7d..5edf288 100644 --- a/src/project.rs +++ b/src/project.rs @@ -9,7 +9,7 @@ use tracing::{error, info, instrument}; use crate::{ benchmarks::run_benchmarks, command::{Command, CommandInstance}, - graphql_tests::run_graphql_tests, + graphql_tests::{run_graphql_tests, run_introspection_query}, request::{REFERENCE_GRAPHQL_CLIENT, TESTED_GRAPHQL_CLIENT}, utils::env_default, ROOT_DIR, @@ -53,6 +53,8 @@ impl Project { let reference_server = self.run_reference_server().await?; let server = self.run_server().await?; + run_introspection_query().await?; + run_graphql_tests().await?; run_benchmarks(&Path::new(ROOT_DIR).join(format!("results/{}", self.name()))).await?; run_graphql_tests().await?; From aff22f54c7a7aeac0664d93e8afe8be8316be31f Mon Sep 17 00:00:00 2001 From: laststylebender Date: Thu, 12 Sep 2024 19:13:08 +0530 Subject: [PATCH 2/4] - validate the gql schema. --- src/graphql_tests.rs | 94 +++++++++++++++++++++++------- src/lib.rs | 2 + src/query_info.graphql | 41 +++++++++++++ src/query_info.rs | 127 +++++++++++++++++++++++++++++++++++++++++ src/type_info.rs | 105 ++++++++++++++++++++++++++++++++++ 5 files changed, 349 insertions(+), 20 deletions(-) create mode 100644 src/query_info.graphql create mode 100644 src/query_info.rs create mode 100644 src/type_info.rs diff --git a/src/graphql_tests.rs b/src/graphql_tests.rs index d2e54f4..98c3149 100644 --- a/src/graphql_tests.rs +++ b/src/graphql_tests.rs @@ -6,8 +6,9 @@ use reqwest::Method; use tracing::{error, info}; use crate::{ - introspection::Schema, + query_info::Schema, request::{MOCK_API_CLIENT, REFERENCE_GRAPHQL_CLIENT, TESTED_GRAPHQL_CLIENT}, + type_info::Root, }; use super::ROOT_DIR; @@ -69,35 +70,88 @@ pub async fn run_graphql_tests() -> Result<()> { Ok(()) } +fn query_builder(type_name: &str) -> String { + format!( + r#" + {{ + __type(name: "{}") {{ + name + kind + fields {{ + name + args {{ + name + }} + }} + }} + }} + "#, + type_name + ) +} + pub async fn run_introspection_query() -> Result<()> { info!("Run graphql introspection tests"); - let test = include_str!("./introspection_query.graphql"); + let query_info = include_str!("./query_info.graphql"); - let actual: Schema = serde_json::from_value(TESTED_GRAPHQL_CLIENT.request(&test).await?)?; - let expected: Schema = serde_json::from_value(REFERENCE_GRAPHQL_CLIENT.request(&test).await?)?; - - println!("----------------------------------------------------"); - println!("expected {:#?}", expected); - println!("actual {:#?}", actual); - println!("----------------------------------------------------"); + // check the root query is same or not. + let actual_value = TESTED_GRAPHQL_CLIENT.request(&query_info).await?; + let expected_value = REFERENCE_GRAPHQL_CLIENT.request(&query_info).await?; + let actual: Schema = serde_json::from_value(actual_value.clone())?; + let expected: Schema = serde_json::from_value(expected_value.clone())?; let differ = DiffLogger::new(); - let difference = differ.diff( - &serde_json::to_value(expected)?, - &serde_json::to_value(actual)?, - ); - - if !difference.is_empty() { - error!( - "Actual response is not equal to expected - Note: left is expected response -> right is actual response" + if actual != expected { + let difference = differ.diff( + &serde_json::to_value(actual.clone())?, + &serde_json::to_value(expected.clone())?, ); + error!("Query Operation type mismatch"); println!("{}", difference); + return Err(anyhow!("Query Operation type mismatch")); + } - return Err(anyhow!("Actual response is not equal to expected")); + for (actual, expected) in actual + .data + .schema + .query_type + .fields + .iter() + .zip(expected.data.schema.query_type.fields.iter()) + { + let actual_op_type = match actual.field_type.get_name() { + Some(name) => name, + None => continue, + }; + + let expected_op_type = match expected.field_type.get_name() { + Some(name) => name, + None => continue, + }; + + let actual_op_type_query = query_builder(&actual_op_type); + let expected_op_type_query = query_builder(&expected_op_type); + + let actual: Root = + serde_json::from_value(TESTED_GRAPHQL_CLIENT.request(&actual_op_type_query).await?)?; + let expected: Root = serde_json::from_value( + REFERENCE_GRAPHQL_CLIENT + .request(&expected_op_type_query) + .await?, + )?; + + if actual != expected { + let difference = differ.diff( + &serde_json::to_value(actual)?, + &serde_json::to_value(expected)?, + ); + error!("Type Defination mismatch"); + println!("{}", difference); + return Err(anyhow!("Type Defination mismatch")); + } } - info!("Execution of graphql tests finished"); + info!("Execution of graphql schema validation finished"); Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index f81acde..77a5797 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,5 +5,7 @@ pub mod project; mod request; mod utils; mod introspection; +mod query_info; +mod type_info; pub const ROOT_DIR: &str = env!("CARGO_MANIFEST_DIR"); diff --git a/src/query_info.graphql b/src/query_info.graphql new file mode 100644 index 0000000..3c760f1 --- /dev/null +++ b/src/query_info.graphql @@ -0,0 +1,41 @@ +query { + __schema { + queryType { + fields { + name + type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + args { + name + type { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + defaultValue + } + } + } + } +} diff --git a/src/query_info.rs b/src/query_info.rs new file mode 100644 index 0000000..27a7181 --- /dev/null +++ b/src/query_info.rs @@ -0,0 +1,127 @@ +use serde::{Deserialize, Serialize, Serializer, Deserializer}; +use serde::ser::SerializeStruct; + +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Schema { + pub data: SchemaData, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +pub struct SchemaData { + #[serde(rename = "__schema")] + pub schema: SchemaType, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +#[serde(rename_all = "camelCase")] +pub struct SchemaType { + pub query_type: QueryType, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct QueryType { + pub fields: Vec, +} + +impl Serialize for QueryType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut fields = self.fields.clone(); + fields.sort_by(|a, b| a.name.cmp(&b.name)); + let mut state = serializer.serialize_struct("queryType", 1)?; + state.serialize_field("fields", &fields)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for QueryType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct QueryTypeHelper { + fields: Vec, + } + + let helper = QueryTypeHelper::deserialize(deserializer)?; + let mut fields = helper.fields; + fields.sort_by(|a, b| a.name.cmp(&b.name)); + Ok(QueryType { fields }) + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct Field { + pub name: String, + pub field_type: FieldType, + pub args: Vec, +} + +impl Serialize for Field { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut args = self.args.clone(); + args.sort_by(|a, b| a.name.cmp(&b.name)); + let mut state = serializer.serialize_struct("field", 3)?; + state.serialize_field("name", &self.name)?; + state.serialize_field("type", &self.field_type)?; + state.serialize_field("args", &args)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct FieldHelper { + name: String, + #[serde(rename = "type")] + field_type: FieldType, + args: Vec, + } + + let helper = FieldHelper::deserialize(deserializer)?; + let mut args = helper.args; + args.sort_by(|a, b| a.name.cmp(&b.name)); + Ok(Field { + name: helper.name, + field_type: helper.field_type, + args, + }) + } +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +pub struct Argument { + pub name: String, + #[serde(rename = "type")] + pub arg_type: FieldType, + pub default_value: Option, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +#[serde(rename_all = "camelCase")] +pub struct FieldType { + pub kind: String, + pub name: Option, + pub of_type: Option>, +} + + +impl FieldType { + pub fn get_name(&self) -> Option { + match &self.name { + Some(name) if !name.is_empty() => Some(name.clone()), + _ => self.of_type.as_ref().and_then(|t| t.get_name()), + } + } +} \ No newline at end of file diff --git a/src/type_info.rs b/src/type_info.rs new file mode 100644 index 0000000..71db580 --- /dev/null +++ b/src/type_info.rs @@ -0,0 +1,105 @@ +use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq)] +pub struct Field { + name: String, + args: Vec, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Type { + name: String, + kind: String, + fields: Vec, +} + + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct Data { + #[serde(rename = "__type")] + type_info: Type, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct Root { + data: Data, +} + +// Custom serialization for Field +impl Serialize for Field { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Field", 2)?; + state.serialize_field("name", &self.name)?; + let mut sorted_args = self.args.clone(); + sorted_args.sort(); + state.serialize_field("args", &sorted_args)?; + state.end() + } +} + +// Custom deserialization for Field +impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct FieldHelper { + name: String, + args: Vec, + } + + let helper = FieldHelper::deserialize(deserializer)?; + let mut args = helper.args; + args.sort(); + + Ok(Field { + name: helper.name, + args, + }) + } +} + +// Custom serialization for Type +impl Serialize for Type { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Type", 3)?; + state.serialize_field("name", &self.name)?; + state.serialize_field("kind", &self.kind)?; + let mut sorted_fields = self.fields.clone(); + sorted_fields.sort_by(|a, b| a.name.cmp(&b.name)); + state.serialize_field("fields", &sorted_fields)?; + state.end() + } +} + +// Custom deserialization for Type +impl<'de> Deserialize<'de> for Type { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + struct TypeHelper { + name: String, + kind: String, + fields: Vec, + } + + let helper = TypeHelper::deserialize(deserializer)?; + let mut fields = helper.fields; + fields.sort_by(|a, b| a.name.cmp(&b.name)); + + Ok(Type { + name: helper.name, + kind: helper.kind, + fields, + }) + } +} From a58d4b498591d75cdf9034dae4588c37ff42e57e Mon Sep 17 00:00:00 2001 From: laststylebender Date: Thu, 12 Sep 2024 20:34:07 +0530 Subject: [PATCH 3/4] - clean up of code. --- src/graphql_tests.rs | 84 +++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/src/graphql_tests.rs b/src/graphql_tests.rs index 98c3149..68aab2e 100644 --- a/src/graphql_tests.rs +++ b/src/graphql_tests.rs @@ -3,6 +3,8 @@ use std::{fs, sync::LazyLock}; use anyhow::{anyhow, Result}; use diff_logger::DiffLogger; use reqwest::Method; +use serde::{de::DeserializeOwned, Serialize}; +use serde_json::Value; use tracing::{error, info}; use crate::{ @@ -90,6 +92,31 @@ fn query_builder(type_name: &str) -> String { ) } +fn compare( + actual: Value, + expected: Value, + error_message: &str, +) -> Result<()> { + // in order to have sorting. + let actual: T = serde_json::from_value(actual)?; + let expected: T = serde_json::from_value(expected)?; + + // with value we can compare with diff logger + let actual_value = serde_json::to_value(actual)?; + let expected_value = serde_json::to_value(expected)?; + + let differ = DiffLogger::new(); + + let difference = differ.diff(&actual_value, &expected_value); + if !difference.is_empty() { + error!(error_message); + println!("{}", difference); + return Err(anyhow!(error_message.to_owned())); + } + + Ok(()) +} + pub async fn run_introspection_query() -> Result<()> { info!("Run graphql introspection tests"); let query_info = include_str!("./query_info.graphql"); @@ -97,19 +124,14 @@ pub async fn run_introspection_query() -> Result<()> { // check the root query is same or not. let actual_value = TESTED_GRAPHQL_CLIENT.request(&query_info).await?; let expected_value = REFERENCE_GRAPHQL_CLIENT.request(&query_info).await?; - let actual: Schema = serde_json::from_value(actual_value.clone())?; let expected: Schema = serde_json::from_value(expected_value.clone())?; - let differ = DiffLogger::new(); - if actual != expected { - let difference = differ.diff( - &serde_json::to_value(actual.clone())?, - &serde_json::to_value(expected.clone())?, - ); - error!("Query Operation type mismatch"); - println!("{}", difference); - return Err(anyhow!("Query Operation type mismatch")); - } + + let _ = compare::( + actual_value, + expected_value, + "Query Operation type mismatch", + )?; for (actual, expected) in actual .data @@ -119,36 +141,26 @@ pub async fn run_introspection_query() -> Result<()> { .iter() .zip(expected.data.schema.query_type.fields.iter()) { - let actual_op_type = match actual.field_type.get_name() { - Some(name) => name, - None => continue, - }; + let actual_op_type = actual.field_type.get_name(); + let expected_op_type = expected.field_type.get_name(); - let expected_op_type = match expected.field_type.get_name() { - Some(name) => name, - None => continue, - }; + if actual_op_type.is_none() != expected_op_type.is_none() { + error!("Output type mismatch for field: {:?}", actual.name); + return Err(anyhow!("Output type mismatch for field: {:?}", actual.name)); + } + + let actual_op_type = actual_op_type.unwrap(); + let expected_op_type = expected_op_type.unwrap(); let actual_op_type_query = query_builder(&actual_op_type); let expected_op_type_query = query_builder(&expected_op_type); - let actual: Root = - serde_json::from_value(TESTED_GRAPHQL_CLIENT.request(&actual_op_type_query).await?)?; - let expected: Root = serde_json::from_value( - REFERENCE_GRAPHQL_CLIENT - .request(&expected_op_type_query) - .await?, - )?; - - if actual != expected { - let difference = differ.diff( - &serde_json::to_value(actual)?, - &serde_json::to_value(expected)?, - ); - error!("Type Defination mismatch"); - println!("{}", difference); - return Err(anyhow!("Type Defination mismatch")); - } + let actual = TESTED_GRAPHQL_CLIENT.request(&actual_op_type_query).await?; + let expected = REFERENCE_GRAPHQL_CLIENT + .request(&expected_op_type_query) + .await?; + + let _ = compare::(actual, expected, "Type Defination mismatch")?; } info!("Execution of graphql schema validation finished"); From c225c72769ecf0a225134caeeaf880edbf4a3646 Mon Sep 17 00:00:00 2001 From: laststylebender Date: Thu, 12 Sep 2024 20:37:39 +0530 Subject: [PATCH 4/4] - removing this as it's not required. --- src/introspection.rs | 94 ---------------------------- src/introspection_query.graphql | 106 -------------------------------- 2 files changed, 200 deletions(-) delete mode 100644 src/introspection.rs delete mode 100644 src/introspection_query.graphql diff --git a/src/introspection.rs b/src/introspection.rs deleted file mode 100644 index 38cc856..0000000 --- a/src/introspection.rs +++ /dev/null @@ -1,94 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -pub struct Schema { - data: SchemaData, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -pub struct SchemaData { - #[serde(rename = "__schema")] - schema: SchemaDetails, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -#[serde(rename_all = "camelCase")] -pub struct SchemaDetails { - query_type: QueryType, - mutation_type: Option, - subscription_type: Option, - #[serde(serialize_with = "serialize_sorted_vec")] - types: Option>, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -pub struct QueryType { - name: String, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)] -#[serde(rename_all = "camelCase")] -pub struct TypeDetails { - kind: String, - name: Option, - #[serde(serialize_with = "serialize_sorted_vec")] - fields: Option>, - #[serde(serialize_with = "serialize_sorted_vec")] - input_fields: Option>, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)] -#[serde(rename_all = "camelCase")] -pub struct Field { - name: String, - #[serde(serialize_with = "serialize_sorted_vec")] - args: Option>, - #[serde(rename = "type")] - type_: TypeRef, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)] -#[serde(rename_all = "camelCase")] -pub struct InputValue { - name: String, - #[serde(rename = "type")] - type_: TypeRef, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone)] -pub struct TypeRef { - kind: String, - name: Option, - of_type: Option>, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -#[serde(rename_all = "camelCase")] -pub struct EnumValue { - name: String, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -pub struct Directive { - name: String, - #[serde(serialize_with = "serialize_sorted_vec")] - locations: Option>, - #[serde(serialize_with = "serialize_sorted_vec")] - args: Option>, -} - -// Add this function at the end of the file -fn serialize_sorted_vec(vec: &Option>, serializer: S) -> Result -where - S: serde::Serializer, - T: Serialize + Ord + Clone, -{ - match vec { - Some(v) => { - let mut sorted = v.clone(); - sorted.sort(); - sorted.serialize(serializer) - } - None => serializer.serialize_none(), - } -} \ No newline at end of file diff --git a/src/introspection_query.graphql b/src/introspection_query.graphql deleted file mode 100644 index 3365224..0000000 --- a/src/introspection_query.graphql +++ /dev/null @@ -1,106 +0,0 @@ - -query IntrospectionQuery { - __schema { - - queryType { name } - mutationType { name } - subscriptionType { name } - types { - ...FullType - } - directives { - name - description - - locations - args { - ...InputValue - } - } - } -} - -fragment FullType on __Type { - kind - name - description - - - fields(includeDeprecated: true) { - name - description - args { - ...InputValue - } - type { - ...TypeRef - } - isDeprecated - deprecationReason - } - inputFields { - ...InputValue - } - interfaces { - ...TypeRef - } - enumValues(includeDeprecated: true) { - name - description - isDeprecated - deprecationReason - } - possibleTypes { - ...TypeRef - } -} - -fragment InputValue on __InputValue { - name - description - type { ...TypeRef } - defaultValue - - -} - -fragment TypeRef on __Type { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - ofType { - kind - name - } - } - } - } - } - } - } - } - } -}