Skip to content

Commit

Permalink
fix: added type rename support for enum, union and interface. (#2793)
Browse files Browse the repository at this point in the history
  • Loading branch information
laststylebender14 authored Sep 21, 2024
1 parent 86b26b8 commit b6ce0cf
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 51 deletions.
35 changes: 3 additions & 32 deletions src/core/config/transformer/improve_type_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::{BTreeMap, HashSet};

use convert_case::{Case, Casing};

use super::RenameTypes;
use crate::core::config::Config;
use crate::core::transform::Transform;
use crate::core::valid::Valid;
Expand Down Expand Up @@ -113,42 +114,12 @@ impl<'a> CandidateGeneration<'a> {
#[derive(Default)]
pub struct ImproveTypeNames;

impl ImproveTypeNames {
/// Generates type names based on inferred candidates from the provided
/// configuration.
fn generate_type_names(&self, mut config: Config) -> Config {
let finalized_candidates = CandidateGeneration::new(&config).generate().converge();

for (old_type_name, new_type_name) in finalized_candidates {
if let Some(type_) = config.types.remove(old_type_name.as_str()) {
// Add newly generated type.
config.types.insert(new_type_name.to_owned(), type_);

// Replace all the instances of old name in config.
for actual_type in config.types.values_mut() {
for actual_field in actual_type.fields.values_mut() {
if actual_field.type_of.name() == &old_type_name {
// Update the field's type with the new name
actual_field.type_of = actual_field
.type_of
.clone()
.with_name(new_type_name.to_owned());
}
}
}
}
}
config
}
}

impl Transform for ImproveTypeNames {
type Value = Config;
type Error = String;
fn transform(&self, config: Config) -> Valid<Self::Value, Self::Error> {
let config = self.generate_type_names(config);

Valid::succeed(config)
let finalized_candidates = CandidateGeneration::new(&config).generate().converge();
RenameTypes::new(finalized_candidates.iter()).transform(config)
}
}

Expand Down
117 changes: 108 additions & 9 deletions src/core/config/transformer/rename_types.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashSet;

use indexmap::IndexMap;

use crate::core::config::Config;
Expand Down Expand Up @@ -28,12 +30,11 @@ impl Transform for RenameTypes {

// Ensure all types exist in the configuration
Valid::from_iter(self.0.iter(), |(existing_name, suggested_name)| {
if !config.types.contains_key(existing_name) {
Valid::fail(format!(
"Type '{}' not found in configuration.",
existing_name
))
} else {
if config.types.contains_key(existing_name)
|| config.enums.contains_key(existing_name)
|| config.unions.contains_key(existing_name)
{
// handle for the types.
if let Some(type_info) = config.types.remove(existing_name) {
config.types.insert(suggested_name.to_string(), type_info);
lookup.insert(existing_name.clone(), suggested_name.clone());
Expand All @@ -46,7 +47,24 @@ impl Transform for RenameTypes {
}
}

// handle for the enums.
if let Some(type_info) = config.enums.remove(existing_name) {
config.enums.insert(suggested_name.to_string(), type_info);
lookup.insert(existing_name.clone(), suggested_name.clone());
}

// handle for the union.
if let Some(type_info) = config.unions.remove(existing_name) {
config.unions.insert(suggested_name.to_string(), type_info);
lookup.insert(existing_name.clone(), suggested_name.clone());
}

Valid::succeed(())
} else {
Valid::fail(format!(
"Type '{}' not found in configuration.",
existing_name
))
}
})
.map(|_| {
Expand All @@ -65,6 +83,54 @@ impl Transform for RenameTypes {
}
}
}

// replace in interface.
type_.implements = type_
.implements
.iter()
.map(|interface_type_name| {
lookup
.get(interface_type_name)
.cloned()
.unwrap_or_else(|| interface_type_name.to_owned())
})
.collect();
}

// replace in the union as well.
for union_type_ in config.unions.values_mut() {
// Collect changes to be made
let mut types_to_remove = HashSet::new();
let mut types_to_add = HashSet::new();

for type_name in union_type_.types.iter() {
if let Some(new_type_name) = lookup.get(type_name) {
types_to_remove.insert(type_name.clone());
types_to_add.insert(new_type_name.clone());
}
}
// Apply changes
for type_name in types_to_remove {
union_type_.types.remove(&type_name);
}

for type_name in types_to_add {
union_type_.types.insert(type_name);
}
}

// replace in union as well.
for union_type_ in config.unions.values_mut() {
union_type_.types = union_type_
.types
.iter()
.map(|type_name| {
lookup
.get(type_name)
.cloned()
.unwrap_or_else(|| type_name.to_owned())
})
.collect();
}

config
Expand Down Expand Up @@ -92,14 +158,20 @@ mod test {
id: ID!
name: String
}
type B {
name: String
username: String
}
union FooBar = A | B
type Post {
id: ID!
title: String
body: String
}
type B {
name: String
username: String
enum Status {
PENDING
STARTED,
COMPLETED
}
type Query {
posts: [Post] @http(path: "/posts")
Expand All @@ -116,6 +188,7 @@ mod test {
"A" => "User",
"B" => "InputUser",
"Mutation" => "UserMutation",
"Status" => "TaskStatus"
}
.iter(),
)
Expand Down Expand Up @@ -184,4 +257,30 @@ mod test {
let expected = Err(b_err.combine(c_err));
assert_eq!(actual, expected);
}

#[test]
fn test_inferface_rename() {
let sdl = r#"
schema {
query: Query
}
interface Node {
id: ID
}
type Post implements Node {
id: ID
title: String
}
type Query {
posts: [Post] @http(path: "/posts")
}
"#;
let config = Config::from_sdl(sdl).to_result().unwrap();

let result = RenameTypes::new(hashmap! {"Node" => "NodeTest"}.iter())
.transform(config)
.to_result()
.unwrap();
insta::assert_snapshot!(result.to_sdl())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
source: src/core/config/transformer/rename_types.rs
expression: result.to_sdl()
---
schema @server @upstream {
query: Query
}

interface NodeTest {
id: ID
}

type Post implements NodeTest {
id: ID
title: String
}

type Query {
posts: [Post] @http(path: "/posts")
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ input InputUser {
username: String
}

union FooBar = InputUser | User

enum TaskStatus {
COMPLETED
PENDING
STARTED
}

type Post {
body: String
id: ID!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ input news__NewsInput {
body: String
id: Int
postImage: String
status: news__Status
status: Status
title: String
}

enum news__Status {
enum Status {
DELETED
DRAFT
PUBLISHED
Expand Down Expand Up @@ -52,7 +52,7 @@ type News {
body: String
id: Int
postImage: String
status: news__Status
status: Status
title: String
}

Expand Down Expand Up @@ -80,11 +80,11 @@ type Post {
type Query {
inCompatibleProperties: InCompatibleProperty @http(path: "/")
news__NewsService__AddNews(news: news__NewsInput!): News @grpc(body: "{{.args.news}}", method: "news.NewsService.AddNews")
news__NewsService__DeleteNews(newsId: news__NewsId!): Empty @grpc(body: "{{.args.newsId}}", method: "news.NewsService.DeleteNews")
news__NewsService__DeleteNews(newsId: Id!): Empty @grpc(body: "{{.args.newsId}}", method: "news.NewsService.DeleteNews")
news__NewsService__EditNews(news: news__NewsInput!): News @grpc(body: "{{.args.news}}", method: "news.NewsService.EditNews")
news__NewsService__GetAllNews: NewsNewsServiceGetMultipleNew @grpc(method: "news.NewsService.GetAllNews")
news__NewsService__GetMultipleNews(multipleNewsId: news__MultipleNewsId!): NewsNewsServiceGetMultipleNew @grpc(body: "{{.args.multipleNewsId}}", method: "news.NewsService.GetMultipleNews")
news__NewsService__GetNews(newsId: news__NewsId!): News @grpc(body: "{{.args.newsId}}", method: "news.NewsService.GetNews")
news__NewsService__GetNews(newsId: Id!): News @grpc(body: "{{.args.newsId}}", method: "news.NewsService.GetNews")
post(id: Int! = 1): Post @http(path: "/posts/{{.args.id}}")
posts: [Post] @http(path: "/posts?_limit=11")
user(id: Int!): User @http(path: "/users/{{.args.id}}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ input news__NewsInput {
body: String
id: Int
postImage: String
status: news__Status
status: Status
title: String
}

enum news__Status {
enum Status {
DELETED
DRAFT
PUBLISHED
Expand Down Expand Up @@ -51,7 +51,7 @@ type News {
body: String
id: Int
postImage: String
status: news__Status
status: Status
title: String
}

Expand All @@ -61,11 +61,11 @@ type NewsNewsServiceGetMultipleNew {

type Query {
news__NewsService__AddNews(news: news__NewsInput!): News @grpc(body: "{{.args.news}}", method: "news.NewsService.AddNews")
news__NewsService__DeleteNews(newsId: news__NewsId!): Empty @grpc(body: "{{.args.newsId}}", method: "news.NewsService.DeleteNews")
news__NewsService__DeleteNews(newsId: Id!): Empty @grpc(body: "{{.args.newsId}}", method: "news.NewsService.DeleteNews")
news__NewsService__EditNews(news: news__NewsInput!): News @grpc(body: "{{.args.news}}", method: "news.NewsService.EditNews")
news__NewsService__GetAllNews: NewsNewsServiceGetMultipleNew @grpc(method: "news.NewsService.GetAllNews")
news__NewsService__GetMultipleNews(multipleNewsId: news__MultipleNewsId!): NewsNewsServiceGetMultipleNew @grpc(body: "{{.args.multipleNewsId}}", method: "news.NewsService.GetMultipleNews")
news__NewsService__GetNews(newsId: news__NewsId!): News @grpc(body: "{{.args.newsId}}", method: "news.NewsService.GetNews")
news__NewsService__GetNews(newsId: Id!): News @grpc(body: "{{.args.newsId}}", method: "news.NewsService.GetNews")
users: [User] @http(path: "/users")
}

Expand Down

1 comment on commit b6ce0cf

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running 30s test @ http://localhost:8000/graphql

4 threads and 100 connections

Thread Stats Avg Stdev Max +/- Stdev
Latency 11.63ms 4.84ms 117.86ms 89.41%
Req/Sec 2.19k 299.47 3.06k 85.25%

261839 requests in 30.03s, 1.31GB read

Requests/sec: 8718.80

Transfer/sec: 44.75MB

Please sign in to comment.