From d0bb37f419ccc6d1edeeee5b607241e9db76b7e5 Mon Sep 17 00:00:00 2001 From: Sunli Date: Tue, 24 May 2022 20:08:35 +0800 Subject: [PATCH] Add `Schema::build_with_ignore_name_conflicts` method to specifies a list to ignore type conflict detection. --- CHANGELOG.md | 4 +++ src/registry/mod.rs | 4 ++- src/schema.rs | 32 +++++++++++++++-- src/validation/visitors/complexity.rs | 3 +- src/validation/visitors/depth.rs | 3 +- tests/object.rs | 52 +++++++++++++++++++++++++++ 6 files changed, 92 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4bcbd8d..fb145626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ 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). +# [4.0.1] 2022-5-24 + +- Add `Schema::build_with_ignore_name_conflicts` method to specifies a list to ignore type conflict detection. + # [4.0.0] 2022-5-17 - Implement the `ConnectionNameType` and `EdgeNameType` traits to specify GraphQL type names for `Connection` and `Edge`, which can be automatically generated using `DefaultConnectionName` and `DefaultEdgeName`. diff --git a/src/registry/mod.rs b/src/registry/mod.rs index 3afbc362..5cbc542c 100644 --- a/src/registry/mod.rs +++ b/src/registry/mod.rs @@ -403,6 +403,7 @@ pub struct Registry { pub introspection_mode: IntrospectionMode, pub enable_federation: bool, pub federation_subscription: bool, + pub ignore_name_conflicts: HashSet, } impl Registry { @@ -462,7 +463,8 @@ impl Registry { return; } - if rust_typename != prev_typename { + if rust_typename != prev_typename && !self.ignore_name_conflicts.contains(name) + { panic!( "`{}` and `{}` have the same GraphQL name `{}`", prev_typename, rust_typename, name, diff --git a/src/schema.rs b/src/schema.rs index c5a8b8f2..13c609c6 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,4 +1,9 @@ -use std::{any::Any, collections::HashMap, ops::Deref, sync::Arc}; +use std::{ + any::Any, + collections::{HashMap, HashSet}, + ops::Deref, + sync::Arc, +}; use futures_util::stream::{self, Stream, StreamExt}; use indexmap::map::IndexMap; @@ -302,12 +307,32 @@ where mutation: Mutation, subscription: Subscription, ) -> SchemaBuilder { + Self::build_with_ignore_name_conflicts(query, mutation, subscription, [] as [&str; 0]) + } + + /// Create a schema builder and specifies a list to ignore type conflict + /// detection. + /// + /// NOTE: It is not recommended to use it unless you know what it does. + #[must_use] + pub fn build_with_ignore_name_conflicts( + query: Query, + mutation: Mutation, + subscription: Subscription, + ignore_name_conflicts: I, + ) -> SchemaBuilder + where + I: IntoIterator, + T: Into, + { SchemaBuilder { validation_mode: ValidationMode::Strict, query: QueryRoot { inner: query }, mutation, subscription, - registry: Self::create_registry(), + registry: Self::create_registry( + ignore_name_conflicts.into_iter().map(Into::into).collect(), + ), data: Default::default(), complexity: None, depth: None, @@ -316,7 +341,7 @@ where } } - pub(crate) fn create_registry() -> Registry { + pub(crate) fn create_registry(ignore_name_conflicts: HashSet) -> Registry { let mut registry = Registry { types: Default::default(), directives: Default::default(), @@ -335,6 +360,7 @@ where introspection_mode: IntrospectionMode::Enabled, enable_federation: false, federation_subscription: false, + ignore_name_conflicts, }; registry.add_directive(MetaDirective { diff --git a/src/validation/visitors/complexity.rs b/src/validation/visitors/complexity.rs index b8fb4406..c21315c2 100644 --- a/src/validation/visitors/complexity.rs +++ b/src/validation/visitors/complexity.rs @@ -171,7 +171,8 @@ mod tests { } fn check_complex(query: &str, expect_complex: usize) { - let registry = Schema::::create_registry(); + let registry = + Schema::::create_registry(Default::default()); let doc = parse_query(query).unwrap(); let mut ctx = VisitorContext::new(®istry, &doc, None); let mut complex = 0; diff --git a/src/validation/visitors/depth.rs b/src/validation/visitors/depth.rs index 21cc9bdb..555cdd78 100644 --- a/src/validation/visitors/depth.rs +++ b/src/validation/visitors/depth.rs @@ -76,7 +76,8 @@ mod tests { } fn check_depth(query: &str, expect_depth: usize) { - let registry = Schema::::create_registry(); + let registry = + Schema::::create_registry(Default::default()); let doc = parse_query(query).unwrap(); let mut ctx = VisitorContext::new(®istry, &doc, None); let mut depth = 0; diff --git a/tests/object.rs b/tests/object.rs index 1c3d5189..fdd2a2dd 100644 --- a/tests/object.rs +++ b/tests/object.rs @@ -141,3 +141,55 @@ async fn test_object_process_with_field() { }) ); } + +#[tokio::test] +async fn ignore_name_conflicts() { + #[derive(SimpleObject)] + #[graphql(name = "MyObj")] + struct MyObj { + name: String, + } + + #[derive(SimpleObject)] + #[graphql(name = "MyObj")] + struct MyObjRef<'a> { + name: &'a str, + } + + struct Query; + + #[Object] + impl Query { + async fn obj_owned(&self) -> MyObj { + MyObj { + name: "a".to_string(), + } + } + + async fn obj_ref(&self) -> MyObjRef<'_> { + MyObjRef { name: "b" } + } + } + + let schema = Schema::build_with_ignore_name_conflicts( + Query, + EmptyMutation, + EmptySubscription, + ["MyObj"], + ) + .finish(); + + let query = r#" + { + objOwned { name } + objRef { name } + } + "#; + assert_eq!( + schema.execute(query).await.into_result().unwrap().data, + value!({ + "objOwned": { "name": "a" }, + "objRef": { "name": "b" }, + }) + ); +}