Skip to content

Instantly share code, notes, and snippets.

@oscartbeaumont
Created December 22, 2021 10:24
Show Gist options
  • Save oscartbeaumont/8e03041c021d9a22de2b15edee9308ea to your computer and use it in GitHub Desktop.
Save oscartbeaumont/8e03041c021d9a22de2b15edee9308ea to your computer and use it in GitHub Desktop.
Async GraphQL Generic Interface Bug
[package]
name = "async-graphql-relay-example"
version = "0.1.0"
authors = ["Oscar Beaumont <[email protected]>"]
edition = "2018"
[dependencies]
async-graphql = "3.0.17"
actix-web = "4.0.0-beta.14"
async-graphql-actix-web = "3.0.17"
query {
user {
id
name
}
tenant {
id
name
}
}
query {
node(id: "92ba0c2d4b4e4e2991dd8f96a078c3ffu") {
id
... on User {
name
role
}
}
}
Invalid schema Error:\nInterface field Node.id expects type NonGenericID! but Tenant.id is type GenericID!.\nInterface field Node.id expects type NonGenericID! but User.id is type GenericID!.
use crate::tenant::Tenant;
use crate::user::User;
use actix_web::guard;
use actix_web::web::Data;
use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use async_graphql::http::{playground_source, GraphQLPlaygroundConfig};
use async_graphql::{EmptyMutation, EmptySubscription, Interface, Object};
use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};
use test::{GenericID, NonGenericID};
mod tenant;
mod test;
mod user;
pub struct QueryRoot;
#[derive(Interface)]
#[graphql(field(name = "id", type = "NonGenericID"))]
pub enum Node {
User(User),
Tenant(Tenant),
}
#[Object]
impl QueryRoot {
async fn user(&self) -> User {
User {
id: GenericID::new("user_one".into()),
name: "Oscar".to_string(),
role: "Testing123".to_string(),
}
}
async fn tenant(&self) -> Tenant {
Tenant {
id: GenericID::new("tenant_one".into()),
name: "My Company".to_string(),
description: "Testing123".to_string(),
}
}
async fn node(&self, _id: String) -> Node {
// This only even returns a user which is useless but refer to the Relay server specification for how this query should work.
User {
id: GenericID::new("user_one".into()),
name: "Oscar".to_string(),
role: "Testing123".to_string(),
}
.into()
}
}
pub type Schema = async_graphql::Schema<QueryRoot, EmptyMutation, EmptySubscription>;
pub async fn handler(schema: web::Data<Schema>, req: GraphQLRequest) -> GraphQLResponse {
schema.execute(req.into_inner()).await.into()
}
pub async fn playground() -> impl Responder {
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(playground_source(GraphQLPlaygroundConfig::new("/")))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).finish();
println!("Listening http://localhost:8080/ ...");
HttpServer::new(move || {
App::new()
.app_data(Data::new(schema.clone()))
.service(web::resource("/").guard(guard::Post()).to(handler))
.service(web::resource("/").guard(guard::Get()).to(playground))
})
.bind(("0.0.0.0", 8080))?
.run()
.await
}
use async_graphql::SimpleObject;
use crate::test::GenericID;
#[derive(Debug, SimpleObject)]
pub struct Tenant {
pub id: GenericID<Self>,
pub name: String,
pub description: String,
}
impl ToString for Tenant {
fn to_string(&self) -> String {
format!("{}", self.name)
}
}
use std::marker::PhantomData;
use async_graphql::{InputValueResult, Scalar, ScalarType, Value};
use crate::{tenant::Tenant, user::User};
#[derive(Debug)]
pub struct GenericID<T: ToString>(String, PhantomData<T>);
impl<T: ToString> GenericID<T> {
pub fn new(val: String) -> Self {
return GenericID(val, PhantomData);
}
}
#[Scalar]
impl<T: ToString + Send + Sync> ScalarType for GenericID<T> {
fn parse(_value: Value) -> InputValueResult<Self> {
unimplemented!();
}
fn to_value(&self) -> Value {
Value::String(String::from(self.0.clone()))
}
}
#[derive(Debug)]
pub struct NonGenericID(String);
impl From<&GenericID<User>> for NonGenericID {
fn from(t: &GenericID<User>) -> Self {
NonGenericID(String::from(t.0.clone()))
}
}
impl From<&GenericID<Tenant>> for NonGenericID {
fn from(t: &GenericID<Tenant>) -> Self {
NonGenericID(String::from(t.0.clone()))
}
}
#[Scalar]
impl ScalarType for NonGenericID {
fn parse(_value: async_graphql::Value) -> async_graphql::InputValueResult<Self> {
panic!("not implemented");
}
fn to_value(&self) -> async_graphql::Value {
Value::String(self.0.clone())
}
}
use async_graphql::SimpleObject;
use crate::test::GenericID;
#[derive(Debug, SimpleObject)]
pub struct User {
pub id: GenericID<Self>,
pub name: String,
pub role: String,
}
impl ToString for User {
fn to_string(&self) -> String {
format!("{}", self.name)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment