From aa669c4e9932c6e502402322c4d46ff002bfb39f Mon Sep 17 00:00:00 2001 From: laststylebender <43403528+laststylebender14@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:19:29 +0530 Subject: [PATCH] feat(2560): merge unknows types (#2567) --- src/cli/generator/config.rs | 1 - .../transformer/merge_types/similarity.rs | 32 +++++++++- ...type_merger__test__merge_to_supertype.snap | 17 ++++++ .../transformer/merge_types/type_merger.rs | 59 ++++++++++++++++++- .../fixtures/generator/simple-json.json | 2 +- tests/cli/fixtures/generator/gen_deezer.md | 2 +- .../generator/gen_json_proto_mix_config.md | 2 +- .../fixtures/generator/gen_jsonplaceholder.md | 2 +- 8 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 src/core/config/transformer/merge_types/snapshots/tailcall__core__config__transformer__merge_types__type_merger__test__merge_to_supertype.snap diff --git a/src/cli/generator/config.rs b/src/cli/generator/config.rs index dab568cb42..a5f4859781 100644 --- a/src/cli/generator/config.rs +++ b/src/cli/generator/config.rs @@ -50,7 +50,6 @@ pub struct PresetConfig { pub tree_shake: Option, pub unwrap_single_field_types: Option, } - #[derive(Deserialize, Serialize, Debug, Default)] #[serde(transparent)] pub struct Location( diff --git a/src/core/config/transformer/merge_types/similarity.rs b/src/core/config/transformer/merge_types/similarity.rs index 7bc81368a0..396bdc8953 100644 --- a/src/core/config/transformer/merge_types/similarity.rs +++ b/src/core/config/transformer/merge_types/similarity.rs @@ -1,6 +1,7 @@ use super::pair_map::PairMap; use super::pair_set::PairSet; use crate::core::config::{Config, Type}; +use crate::core::scalar::Scalar; use crate::core::valid::{Valid, Validator}; /// Given Two types,it tells similarity between two types based on a specified @@ -63,7 +64,11 @@ impl<'a> Similarity<'a> { if config.is_scalar(&field_1_type_of) && config.is_scalar(&field_2_type_of) { // if field type_of is scalar and they don't match then we can't merge // types. - if field_1_type_of == field_2_type_of { + let json_scalar = Scalar::JSON.to_string(); + if field_1_type_of == field_2_type_of + || field_1_type_of == json_scalar + || field_2_type_of == json_scalar + { if field_1.list == field_2.list { same_field_count += 1; } else { @@ -513,4 +518,29 @@ mod test { // Assert that merging incompatible list and non-list fields fails assert!(result.is_err()) } + + #[test] + fn test_unknown_types_similarity() { + let sdl = r#" + type A { + primarySubcategoryId: String + } + type B { + primarySubcategoryId: JSON + } + "#; + let config = Config::from_sdl(sdl).to_result().unwrap(); + + let mut similarity = Similarity::new(&config); + + let result = similarity + .similarity( + ("B", config.types.get("B").unwrap()), + ("A", config.types.get("A").unwrap()), + 0.9, + ) + .to_result() + .unwrap(); + assert!(result); + } } diff --git a/src/core/config/transformer/merge_types/snapshots/tailcall__core__config__transformer__merge_types__type_merger__test__merge_to_supertype.snap b/src/core/config/transformer/merge_types/snapshots/tailcall__core__config__transformer__merge_types__type_merger__test__merge_to_supertype.snap new file mode 100644 index 0000000000..88ce824973 --- /dev/null +++ b/src/core/config/transformer/merge_types/snapshots/tailcall__core__config__transformer__merge_types__type_merger__test__merge_to_supertype.snap @@ -0,0 +1,17 @@ +--- +source: src/core/config/transformer/merge_types/type_merger.rs +expression: config.to_sdl() +--- +schema @server @upstream { + query: Query +} + +type M1 { + id: Int + name: JSON +} + +type Query { + bar: M1 + foo: M1 +} diff --git a/src/core/config/transformer/merge_types/type_merger.rs b/src/core/config/transformer/merge_types/type_merger.rs index e494cde350..f735af0b43 100644 --- a/src/core/config/transformer/merge_types/type_merger.rs +++ b/src/core/config/transformer/merge_types/type_merger.rs @@ -4,6 +4,7 @@ use super::mergeable_types::MergeableTypes; use super::similarity::Similarity; use crate::core::config::{Config, Type}; use crate::core::merge_right::MergeRight; +use crate::core::scalar::Scalar; use crate::core::transform::Transform; use crate::core::valid::{Valid, Validator}; @@ -57,7 +58,6 @@ impl TypeMerger { if let Some(type_info_2) = config.types.get(type_name_2) { let threshold = mergeable_types.get_threshold(type_name_1, type_name_2); - visited_types.insert(type_name_1.clone()); let is_similar = stat_gen .similarity( @@ -66,6 +66,7 @@ impl TypeMerger { threshold, ) .to_result(); + if let Ok(similar) = is_similar { if similar { visited_types.insert(type_name_2.clone()); @@ -187,8 +188,34 @@ impl TypeMerger { } } -fn merge_type(type_: &Type, merge_into: Type) -> Type { - merge_into.merge_right(type_.clone()) +fn merge_type(type_: &Type, mut merge_into: Type) -> Type { + // Merge the simple fields using `merge_right`. + merge_into.added_fields = merge_into + .added_fields + .merge_right(type_.added_fields.clone()); + merge_into.implements = merge_into.implements.merge_right(type_.implements.clone()); + merge_into.cache = merge_into.cache.merge_right(type_.cache.clone()); + merge_into.protected = merge_into.protected.merge_right(type_.protected.clone()); + merge_into.doc = merge_into.doc.merge_right(type_.doc.clone()); + + // Handle field output type merging correctly. + type_.fields.iter().for_each(|(key, new_field)| { + merge_into + .fields + .entry(key.to_owned()) + .and_modify(|existing_field| { + let mut merged_field = existing_field.clone().merge_right(new_field.clone()); + if existing_field.type_of == Scalar::JSON.to_string() + || new_field.type_of == Scalar::JSON.to_string() + { + merged_field.type_of = Scalar::JSON.to_string(); + } + *existing_field = merged_field; + }) + .or_insert_with(|| new_field.to_owned()); + }); + + merge_into } impl Transform for TypeMerger { @@ -379,4 +406,30 @@ mod test { let config = TypeMerger::default().transform(config).to_result().unwrap(); insta::assert_snapshot!(config.to_sdl()); } + + #[test] + fn test_merge_to_supertype() { + let sdl = r#" + schema { + query: Query + } + + type Bar { + id: Int + name: JSON + } + type Foo { + id: Int + name: String + } + type Query { + foo: Foo + bar: Bar + } + "#; + + let config = Config::from_sdl(sdl).to_result().unwrap(); + let config = TypeMerger::default().transform(config).to_result().unwrap(); + insta::assert_snapshot!(config.to_sdl()); + } } diff --git a/tailcall-fixtures/fixtures/generator/simple-json.json b/tailcall-fixtures/fixtures/generator/simple-json.json index 77995628c8..9e1d8f61cf 100644 --- a/tailcall-fixtures/fixtures/generator/simple-json.json +++ b/tailcall-fixtures/fixtures/generator/simple-json.json @@ -23,7 +23,7 @@ } ], "preset": { - "mergeType": 1, + "mergeType": 1.0, "consolidateURL": 0.5 }, "output": { diff --git a/tests/cli/fixtures/generator/gen_deezer.md b/tests/cli/fixtures/generator/gen_deezer.md index 6fb4f47ac9..74cfe25711 100644 --- a/tests/cli/fixtures/generator/gen_deezer.md +++ b/tests/cli/fixtures/generator/gen_deezer.md @@ -51,7 +51,7 @@ } ], "preset": { - "mergeType": 1, + "mergeType": 1.0, "consolidateURL": 0.5, "treeShake": true, "inferTypeNames": true diff --git a/tests/cli/fixtures/generator/gen_json_proto_mix_config.md b/tests/cli/fixtures/generator/gen_json_proto_mix_config.md index 1da85764b2..be7a4f639d 100644 --- a/tests/cli/fixtures/generator/gen_json_proto_mix_config.md +++ b/tests/cli/fixtures/generator/gen_json_proto_mix_config.md @@ -14,7 +14,7 @@ } ], "preset": { - "mergeType": 1, + "mergeType": 1.0, "consolidateURL": 0.5, "inferTypeNames": true, "treeShake": true diff --git a/tests/cli/fixtures/generator/gen_jsonplaceholder.md b/tests/cli/fixtures/generator/gen_jsonplaceholder.md index 2feead7b4f..d3d2d4202a 100644 --- a/tests/cli/fixtures/generator/gen_jsonplaceholder.md +++ b/tests/cli/fixtures/generator/gen_jsonplaceholder.md @@ -69,7 +69,7 @@ } ], "preset": { - "mergeType": 1, + "mergeType": 1.0, "consolidateURL": 0.5, "treeShake": true, "inferTypeNames": true