use crate::{ domain::handler::BackendHandler, infra::{ auth_service::{check_if_token_is_valid, ValidationResults}, cli::ExportGraphQLSchemaOpts, tcp_server::AppState, }, }; use actix_web::{web, Error, HttpResponse}; use actix_web_httpauth::extractors::bearer::BearerAuth; use juniper::{EmptySubscription, RootNode}; use juniper_actix::{graphiql_handler, graphql_handler, playground_handler}; use super::{mutation::Mutation, query::Query}; pub struct Context { pub handler: Box, pub validation_result: ValidationResults, } impl juniper::Context for Context {} type Schema = RootNode<'static, Query, Mutation, EmptySubscription>>; fn schema() -> Schema { Schema::new( Query::::new(), Mutation::::new(), EmptySubscription::>::new(), ) } pub fn export_schema(opts: ExportGraphQLSchemaOpts) -> anyhow::Result<()> { use crate::domain::sql_backend_handler::SqlBackendHandler; use anyhow::Context; let output = schema::().as_schema_language(); match opts.output_file { None => println!("{}", output), Some(path) => { use std::fs::File; use std::io::prelude::*; use std::path::Path; let path = Path::new(&path); let mut file = File::create(&path).context(format!("unable to open '{}'", path.display()))?; file.write_all(output.as_bytes()) .context(format!("unable to write in '{}'", path.display()))?; } } Ok(()) } async fn graphiql_route() -> Result { graphiql_handler("/api/graphql", None).await } async fn playground_route() -> Result { playground_handler("/api/graphql", None).await } async fn graphql_route( req: actix_web::HttpRequest, mut payload: actix_web::web::Payload, data: web::Data>, ) -> Result { use actix_web::FromRequest; let bearer = BearerAuth::from_request(&req, &mut payload.0).await?; let validation_result = check_if_token_is_valid(&data, bearer.token())?; let context = Context:: { handler: Box::new(data.backend_handler.clone()), validation_result, }; graphql_handler(&schema(), &context, req, payload).await } pub fn configure_endpoint(cfg: &mut web::ServiceConfig) where Backend: BackendHandler + Sync + 'static, { let json_config = web::JsonConfig::default() .limit(4096) .error_handler(|err, _req| { // create custom error response log::error!("API error: {}", err); let msg = err.to_string(); actix_web::error::InternalError::from_response( err, HttpResponse::BadRequest().body(msg), ) .into() }); cfg.app_data(json_config); cfg.service( web::resource("/graphql") .route(web::post().to(graphql_route::)) .route(web::get().to(graphql_route::)), ); cfg.service(web::resource("/graphql/playground").route(web::get().to(playground_route))); cfg.service(web::resource("/graphql/graphiql").route(web::get().to(graphiql_route))); }