Description
@smmoosavi Thanks. I have checked first scenario but I wasn't able to fit it into my case. The main problem is that I have part of GQL application that is defined as in above example with MyObj
(not MySimpleObj
). Could you provide example scenario for that?
As far as I understand I need to rewrite all Object defined with async_graphql
to be defined with dynamic_graphql
? Then it's hard to go from one library to second and still keep compatible. For example below will not compile:
#[derive(Debug, async_graphql::SimpleObject, dynamic_graphql::SimpleObject)]
#[graphql(rename_fields = "camelCase")]
#[graphql(shareable)] // this will cause compiler error
pub struct Version {
version: String,
name: String,
}
Also in my scenario I don't know how to use states correctly with dynamic-graphql
crate ExtendedObject
don't accept self as parameter.
Below I share code that compile but panic at runtime with
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SchemaError("Type \"MapperFromDynamicGraphqlToAsyncGraphql\" not found")', src/bin/gql-dynamic.rs:137:54
I would be glad if you could help me understand how can I fix it.
use async_graphql::{
dynamic::ResolverContext,
http::{playground_source, GraphQLPlaygroundConfig},
Context, Object,
};
use async_graphql_axum::{GraphQLRequest, GraphQLResponse};
use axum::{
extract::Extension,
response::{self, IntoResponse},
routing::get,
Router, Server,
};
pub type GqlResult<T> = async_graphql::Result<T>;
use dynamic_graphql::{
dynamic, experimental::GetSchemaData, internal::Registry, ExpandObject, ExpandObjectFields,
SimpleObject,
};
#[derive(Debug, async_graphql::SimpleObject, dynamic_graphql::SimpleObject)]
#[graphql(rename_fields = "camelCase")]
// FIXME this not works because of `dynamic_graphql::SimpleObject` #[graphql(shareable)]
pub struct Version {
/// aplication name
pub name: String,
/// schematic version (semver.org)
pub version: String,
/// date of compilation
pub compilation_date: Option<String>,
/// unique hash that represent source code that was used to build
pub compilation_hash: Option<String>,
}
pub struct AsyncGraphGqlObj {
// inner_state will be connection to DB or other service
inner_state: String,
}
#[Object]
impl AsyncGraphGqlObj {
async fn name<'a>(&self, _ctx: &Context<'a>) -> &str {
&self.inner_state
}
pub async fn version<'a>(&self, ctx: &Context<'a>) -> GqlResult<Vec<Version>> {
let name = format!(
"{}+{}",
env!("CARGO_PKG_NAME"),
self.name(ctx).await.unwrap().to_lowercase(),
);
let version = env!("CARGO_PKG_VERSION").to_string();
let compilation_date = option_env!("VERGEN_BUILD_TIMESTAMP").map(String::from);
let compilation_hash = option_env!("VERGEN_GIT_SHA").map(String::from);
let gql_api_ver = Version {
name,
version,
compilation_date,
compilation_hash,
};
// TODO: query sth from DB where connection is keep in with &self (so inner state).
Ok(vec![gql_api_ver])
}
}
// this is just to make compiler happy
#[derive(SimpleObject)]
pub struct UselessButAllowCompile {}
#[derive(ExpandObject)]
struct MapperFromDynamicGraphqlToAsyncGraphql(UselessButAllowCompile);
#[ExpandObjectFields]
impl MapperFromDynamicGraphqlToAsyncGraphql {
// just map query from dynamic-graphql to async-graphql implementation
//
// input parameters are here to test how they are handled by dynamic-graphql
// more complicated requests will require parameters
async fn version<'a>(address: String, ctx: &ResolverContext<'a>) -> GqlResult<Vec<Version>> {
let d = ctx.get_schema_data();
let o = d.get::<AsyncGraphGqlObj>().unwrap();
// call async-graphql version
o.version(ctx.ctx).await?
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenvy::dotenv().ok();
let registry = Registry::new()
.register::<UselessButAllowCompile>()
.register::<MapperFromDynamicGraphqlToAsyncGraphql>();
let schema = dynamic::Schema::build("Query", None, None);
let schema = registry.apply_into_schema_builder(schema);
let query = dynamic::Object::new("Query");
let query = query.field(dynamic::Field::new(
"anyname",
dynamic::TypeRef::named("MapperFromDynamicGraphqlToAsyncGraphql"),
|_ctx| {
/* What I am expecting here is to return Object that was defined with async_graphql*/
// below is one of my tries to achieve it without success
let o = MapperFromDynamicGraphqlToAsyncGraphql(UselessButAllowCompile {});
dynamic::FieldFuture::new(async move {
Ok(Some(
// depending on request this should return
/*
{
name: "my_inner_name_state"
}
or
{
version: {
name: "my_inner_name_state"
}
}
*/
dynamic::FieldValue::owned_any(o).with_type("FooExpanded"),
))
})
},
));
let o = AsyncGraphGqlObj {
inner_state: String::from("my_inner_name_state"),
};
let schema = schema.data(o).register(query);
let schema = schema.enable_federation().finish().unwrap();
let app = Router::new()
.route("/", get(graphql_playground).post(graphql_handler))
.layer(Extension(schema));
println!("Playground: http://localhost:8000");
Server::bind(&"0.0.0.0:8000".parse()?)
.serve(app.into_make_service())
.await
.unwrap();
Ok(())
}
async fn graphql_handler(
schema: Extension<async_graphql::dynamic::Schema>,
req: GraphQLRequest,
) -> GraphQLResponse {
let inner = req.into_inner();
println!("req: {:?}", inner);
schema.execute(inner).await.into()
}
async fn graphql_playground() -> impl IntoResponse {
response::Html(playground_source(GraphQLPlaygroundConfig::new("/")))
}
Originally posted by @xoac in async-graphql/async-graphql#1248 (comment)