Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: added type rename support for enum, union and interface. #2793

Merged
merged 8 commits into from
Sep 21, 2024
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 @@

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

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

Check warning on line 59 in src/core/config/transformer/rename_types.rs

View check run for this annotation

Codecov / codecov/patch

src/core/config/transformer/rename_types.rs#L58-L59

Added lines #L58 - L59 were not covered by tests
}

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

// 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 @@
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 @@
"A" => "User",
"B" => "InputUser",
"Mutation" => "UserMutation",
"Status" => "TaskStatus"
}
.iter(),
)
Expand Down Expand Up @@ -184,4 +257,30 @@
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
Loading