10000 Preserve underscore when renaming field by simonvandel · Pull Request #1706 · async-graphql/async-graphql · GitHub
[go: up one dir, main page]

Skip to content

Preserve underscore when renaming field #1706

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree 10000
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions derive/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ pub struct MergedSubscription {
pub extends: bool,
}

#[derive(Debug, Copy, Clone, FromMeta)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, FromMeta)]
pub enum RenameRule {
#[darling(rename = "lowercase")]
Lower,
Expand All @@ -842,13 +842,30 @@ pub enum RenameRule {

impl RenameRule {
fn rename(&self, name: impl AsRef<str>) -> String {
let name_str = name.as_ref();
let starts_with_underscore = name_str.starts_with('_');

// If the name starts with underscore and we're doing camelCase or PascalCase,
// we need special handling to preserve the underscore prefix
if starts_with_underscore && (*self == Self::Camel || *self == Self::Pascal) {
// Remove the underscore, apply the transformation, then add it back
let name_without_underscore = &name_str[1..];
let transformed = match self {
Self::Pascal => name_without_underscore.to_pascal_case(),
Self::Camel => name_without_underscore.to_camel_case(),
_ => unreachable!(),
};
return format!("_{}", transformed);
}

// Standard transformation for other cases
match self {
Self::Lower => name.as_ref().to_lowercase(),
Self::Upper => name.as_ref().to_uppercase(),
Self::Pascal => name.as_ref().to_pascal_case(),
Self::Camel => name.as_ref().to_camel_case(),
Self::Snake => name.as_ref().to_snake_case(),
Self::ScreamingSnake => name.as_ref().to_screaming_snake_case(),
Self::Lower => name_str.to_lowercase(),
Self::Upper => name_str.to_uppercase(),
Self::Pascal => name_str.to_pascal_case(),
Self::Camel => name_str.to_camel_case(),
Self::Snake => name_str.to_snake_case(),
Self::ScreamingSnake => name_str.to_screaming_snake_case(),
}
}
}
Expand Down
112 changes: 84 additions & 28 deletions tests/federation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ pub async fn test_entity_union() {
#[Object]
impl Query {
#[graphql(entity)]
async fn find_obj(&self, _id: i32) -> MyObj {
async fn find_obj(&self, #[graphql(name = "id")] _id: i32) -> MyObj {
todo!()
}
}
Expand Down Expand Up @@ -308,11 +308,14 @@ pub async fn test_entity_shareable() {
#[Object(extends)]
impl Query {
#[graphql(entity)]
async fn find_obj_field_shareable(&self, _id: i32) -> MyObjFieldShareable {
async fn find_obj_field_shareable(
&self,
#[graphql(name = "id")] _id: i32,
) -> MyObjFieldShareable {
todo!()
}
#[graphql(entity)]
async fn find_obj_shareable(&self, _id: i32) -> MyObjShareable {
async fn find_obj_shareable(&self, #[graphql(name = "id")] _id: i32) -> MyObjShareable {
todo!()
}
}
Expand All @@ -337,7 +340,10 @@ pub async fn test_field_override_directive() {
#[Object(extends)]
impl Query {
#[graphql(entity)]
async fn find_obj_field_override(&self, _id: i32) -> MyObjFieldOverride {
async fn find_obj_field_override(
&self,
#[graphql(name = "id")] _id: i32,
) -> MyObjFieldOverride {
todo!()
}
}
Expand Down Expand Up @@ -447,29 +453,41 @@ pub async fn test_entity_inaccessible() {
#[Object(extends)]
impl Query {
#[graphql(entity)]
async fn find_obj_field_inaccessible(&self, _id: i32) -> MyObjFieldInaccessible {
async fn find_obj_field_inaccessible(
&self,
#[graphql(name = "id")] _id: i32,
) -> MyObjFieldInaccessible {
todo!()
}

#[graphql(entity)]
async fn find_obj_inaccessible(&self, _id: i32) -> MyObjInaccessible {
async fn find_obj_inaccessible(
&self,
#[graphql(name = "id")] _id: i32,
) -> MyObjInaccessible {
todo!()
}

async fn enum_variant_inaccessible(&self, _id: i32) -> MyEnumVariantInaccessible {
async fn enum_variant_inaccessible(
&self,
#[graphql(name = "id")] _id: i32,
) -> MyEnumVariantInaccessible {
todo!()
}

async fn enum_inaccessible(&self, _id: i32) -> MyEnumInaccessible {
async fn enum_inaccessible(&self, #[graphql(name = "id")] _id: i32) -> MyEnumInaccessible {
todo!()
}

#[graphql(inaccessible)]
async fn inaccessible_field(&self, _id: i32) -> i32 {
async fn inaccessible_field(&self, #[graphql(name = "id")] _id: i32) -> i32 {
todo!()
}

async fn inaccessible_argument(&self, #[graphql(inaccessible)] _id: i32) -> i32 {
async fn inaccessible_argument(
&self,
#[graphql(inaccessible, name = "id")] _id: i32,
) -> i32 {
todo!()
}

Expand All @@ -485,11 +503,17 @@ pub async fn test_entity_inaccessible() {
todo!()
}

async fn inaccessible_input_field(&self, _value: MyInputObjFieldInaccessible) -> i32 {
async fn inaccessible_input_field(
&self,
#[graphql(name = "value")] _value: MyInputObjFieldInaccessible,
) -> i32 {
todo!()
}

async fn inaccessible_input(&self, _value: MyInputObjInaccessible) -> i32 {
async fn inaccessible_input(
&self,
#[graphql(name = "value")] _value: MyInputObjInaccessible,
) -> i32 {
todo!()
}

Expand Down Expand Up @@ -729,29 +753,38 @@ pub async fn test_entity_tag() {
#[Object(extends)]
impl Query {
#[graphql(entity)]
async fn find_obj_field_tagged(&self, _id: i32) -> MyObjFieldTagged {
async fn find_obj_field_tagged(
&self,
#[graphql(name = "id")] _id: i32,
) -> MyObjFieldTagged {
todo!()
}

#[graphql(entity)]
async fn find_obj_tagged(&self, _id: i32) -> MyObjTagged {
async fn find_obj_tagged(&self, #[graphql(name = "id")] _id: i32) -> MyObjTagged {
todo!()
}

async fn enum_variant_tagged(&self, _id: i32) -> MyEnumVariantTagged {
async fn enum_variant_tagged(
&self,
#[graphql(name = "id")] _id: i32,
) -> MyEnumVariantTagged {
todo!()
}

async fn enum_tagged(&self, _id: i32) -> MyEnumTagged {
async fn enum_tagged(&self, #[graphql(name = "id")] _id: i32) -> MyEnumTagged {
todo!()
}

#[graphql(tag = "tagged_\"field\"")]
async fn tagged_field(&self, _id: i32) -> i32 {
async fn tagged_field(&self, #[graphql(name = "id")] _id: i32) -> i32 {
todo!()
}

async fn tagged_argument(&self, #[graphql(tag = "tagged_argument")] _id: i32) -> i32 {
async fn tagged_argument(
&self,
#[graphql(tag = "tagged_argument", name = "id")] _id: i32,
) -> i32 {
todo!()
}

Expand All @@ -767,11 +800,14 @@ pub async fn test_entity_tag() {
todo!()
}

async fn tagged_input_field(&self, _value: MyInputObjFieldTagged) -> i32 {
async fn tagged_input_field(
&self,
#[graphql(name = "value")] _value: MyInputObjFieldTagged,
) -> i32 {
todo!()
}

async fn tagged_input(&self, _value: MyInputObjTagged) -> i32 {
async fn tagged_input(&self, #[graphql(name = "value")] _value: MyInputObjTagged) -> i32 {
todo!()
}

Expand Down Expand Up @@ -862,17 +898,23 @@ pub async fn test_interface_object() {
#[Object(extends)]
impl Query {
#[graphql(entity)]
async fn my_interface(&self, _id: u64) -> MyInterface {
async fn my_interface(&self, #[graphql(name = "id")] _id: u64) -> MyInterface {
todo!()
}

#[graphql(entity)]
async fn my_interface_object1(&self, _id: u64) -> MyInterfaceObject1 {
async fn my_interface_object1(
&self,
#[graphql(name = "id")] _id: u64,
) -> MyInterfaceObject1 {
todo!()
}

#[graphql(entity)]
async fn my_interface_object2(&self, _id: u64) -> MyInterfaceObject2 {
async fn my_interface_object2(
&self,
#[graphql(name = "id")] _id: u64,
) -> MyInterfaceObject2 {
todo!()
}
}
Expand Down Expand Up @@ -942,24 +984,38 @@ pub async fn test_unresolvable_entity() {

#[Object]
impl Query {
async fn simple_explicit_reference(&self, _id: u64) -> SimpleExplicitUnresolvable {
async fn simple_explicit_reference(
&self,
#[graphql(name = "id")] _id: u64,
) -> SimpleExplicitUnresolvable {
todo!()
}

async fn simple_implicit_reference(&self, _a: u64) -> SimpleImplicitUnresolvable {
async fn simple_implicit_reference(
&self,
#[graphql(name = "a")] _a: u64,
) -> SimpleImplicitUnresolvable {
todo!()
}

async fn explicit_reference(&self, _id1: u64, _id2: u64) -> ExplicitUnresolvable {
async fn explicit_reference(
&self,
#[graphql(name = "id1")] _id1: u64,
#[graphql(name = "id2")] _id2: u64,
) -> ExplicitUnresolvable {
todo!()
}

async fn implicit_unresolvable(&self, _a: String, _b: bool) -> ImplicitUnresolvable {
async fn implicit_unresolvable(
&self,
#[graphql(name = "a")] _a: String,
#[graphql(name = "b")] _b: bool,
) -> ImplicitUnresolvable {
todo!()
}

#[graphql(entity)]
async fn object_entity(&self, _id: u64) -> ResolvableObject {
async fn object_entity(&self, #[graphql(name = "id")] _id: u64) -> ResolvableObject {
todo!()
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/input_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub async fn test_input_value_custom_error() {

#[Object]
impl Query {
async fn parse_int(&self, _n: i8) -> bool {
async fn parse_int(&self, #[graphql(name = "n")] _n: i8) -> bool {
true
}
}
Expand Down
35 changes: 35 additions & 0 deletions tests/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,3 +548,38 @@ pub async fn test_interface_with_oneof_object() {
})
);
}

#[tokio::test]
pub async fn test_interface_dont_strip_starting_underscore() {
#[derive(SimpleObject)]
struct MyObjA {
#[graphql(name = "_id")]
_id: i32,
}

#[ 10000 derive(Interface)]
#[graphql(field(name = r"_id", ty = "&i32"))]
enum MyInterface {
MyObjA(MyObjA),
}

struct Query;

#[Object]
impl Query {
async fn node(&self) -> MyInterface {
todo!()
}
}

let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
let sdl = schema.sdl();

// Simply check that the field name _id appears in the SDL with underscore preserved
assert!(sdl.contains("_id: Int!"),
"SDL should contain '_id: Int!' but field name underscore was stripped: {}", sdl);

// Also verify it doesn't contain just "id" (without underscore)
assert!(!sdl.contains("\n\tid: Int!"),
"SDL should not contain 'id: Int!' without underscore: {}", sdl);
}
9 changes: 6 additions & 3 deletions tests/introspection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,10 @@ impl Mutation {
/// simple_mutation description
/// line2
/// line3
async fn simple_mutation(&self, _input: SimpleInput) -> SimpleObject {
async fn simple_mutation(
&self,
#[graphql(name = "input")] _input: SimpleInput,
) -> SimpleObject {
unimplemented!()
}
}
Expand Down Expand Up @@ -1539,15 +1542,15 @@ pub async fn test_introspection_directives() {
}
}
}

fragment InputValue on __InputValue {
name
type {
...TypeRef
}
defaultValue
}

fragment TypeRef on __Type {
kind
name
Expand Down
Loading
0