diff --git a/Cargo.lock b/Cargo.lock index 6a68576724..05b2aa402d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1015,6 +1015,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cooked-waker" version = "5.0.0" @@ -4941,6 +4950,7 @@ dependencies = [ "chrono", "clap", "colored", + "convert_case", "criterion", "deno_core", "derive_setters", diff --git a/Cargo.toml b/Cargo.toml index 4d4c03cc0f..f966c350a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,6 +121,7 @@ async-graphql = { version = "7.0.3", features = [ "apollo_tracing", "opentelemetry", ]} +convert_case = "0.6.0" [dev-dependencies] criterion = "0.5.1" diff --git a/src/grpc/protobuf.rs b/src/grpc/protobuf.rs index 5393305fc0..e8d067d0e3 100644 --- a/src/grpc/protobuf.rs +++ b/src/grpc/protobuf.rs @@ -201,7 +201,7 @@ impl ProtobufOperation { } #[cfg(test)] -mod tests { +pub mod tests { // TODO: Rewrite protobuf tests use std::path::PathBuf; @@ -232,7 +232,7 @@ mod tests { test_file } - async fn get_proto_file(name: &str) -> Result { + pub async fn get_proto_file(name: &str) -> Result { let runtime = crate::runtime::test::init(None); let reader = ConfigReader::init(runtime); diff --git a/src/grpc/tests/proto/news.proto b/src/grpc/tests/proto/news.proto index ddd35e23dc..1c5860df3d 100644 --- a/src/grpc/tests/proto/news.proto +++ b/src/grpc/tests/proto/news.proto @@ -8,7 +8,7 @@ message News { int32 id = 1; string title = 2; string body = 3; - string postImage = 4; + string post_image = 4; } service NewsService { diff --git a/src/grpc/tests/proto/news_no_pkg.proto b/src/grpc/tests/proto/news_no_pkg.proto index c549a88729..bdcf98a9ec 100644 --- a/src/grpc/tests/proto/news_no_pkg.proto +++ b/src/grpc/tests/proto/news_no_pkg.proto @@ -6,7 +6,7 @@ message News { int32 id = 1; string title = 2; string body = 3; - string postImage = 4; + string post_image = 4; } service NewsService { diff --git a/src/json/json_schema.rs b/src/json/json_schema.rs index c4c0cde138..357869a4fc 100644 --- a/src/json/json_schema.rs +++ b/src/json/json_schema.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use convert_case::{Case, Casing}; use prost_reflect::{FieldDescriptor, Kind, MessageDescriptor}; use serde::{Deserialize, Serialize}; @@ -158,7 +159,10 @@ impl TryFrom<&MessageDescriptor> for JsonSchema { for field in fields { let field_schema = JsonSchema::try_from(&field)?; - map.insert(field.name().to_string(), field_schema); + // the snake_case for field names is automatically converted to camelCase + // by prost on serde serialize/deserealize and in graphql type name should be in + // camelCase as well, so convert field.name to camelCase here + map.insert(field.name().to_case(Case::Camel), field_schema); } Ok(JsonSchema::Obj(map)) @@ -210,9 +214,14 @@ impl TryFrom<&FieldDescriptor> for JsonSchema { #[cfg(test)] mod tests { + use std::collections::HashMap; + use async_graphql::Name; use indexmap::IndexMap; + use crate::blueprint::GrpcMethod; + use crate::grpc::protobuf::tests::get_proto_file; + use crate::grpc::protobuf::ProtobufSet; use crate::json::JsonSchema; use crate::valid::{Valid, Validator}; @@ -274,4 +283,30 @@ mod tests { let result = schema.validate(&value); assert_eq!(result, Valid::succeed(())); } + + #[tokio::test] + async fn test_from_protobuf_conversion() -> anyhow::Result<()> { + let grpc_method = GrpcMethod::try_from("news.NewsService.GetNews").unwrap(); + + let file = ProtobufSet::from_proto_file(&get_proto_file("news.proto").await?)?; + let service = file.find_service(&grpc_method)?; + let operation = service.find_operation(&grpc_method)?; + + let schema = JsonSchema::try_from(&operation.output_type)?; + + assert_eq!( + schema, + JsonSchema::Obj(HashMap::from_iter([ + ( + "postImage".to_owned(), + JsonSchema::Opt(JsonSchema::Str.into()) + ), + ("title".to_owned(), JsonSchema::Opt(JsonSchema::Str.into())), + ("id".to_owned(), JsonSchema::Opt(JsonSchema::Num.into())), + ("body".to_owned(), JsonSchema::Opt(JsonSchema::Str.into())), + ])) + ); + + Ok(()) + } }