async-graphql-warp 2.0

This commit is contained in:
Sunli 2020-09-11 22:37:48 +08:00
parent c2e32c2ea1
commit a43e437a38
6 changed files with 52 additions and 105 deletions

View File

@ -60,6 +60,5 @@ members = [
"integrations/warp",
"integrations/tide",
"integrations/rocket",
# "integrations/lambda",
"benchmark",
]

View File

@ -1,19 +0,0 @@
[package]
name = "async-graphql-lambda"
version = "1.18.0"
authors = ["Sunli <scott_s829@163.com>"]
edition = "2018"
description = "async-graphql for AWS Lambda"
publish = true
license = "MIT/Apache-2.0"
documentation = "https://docs.rs/async-graphql/"
homepage = "https://github.com/async-graphql/async-graphql"
repository = "https://github.com/async-graphql/async-graphql"
keywords = ["futures", "async", "graphql"]
categories = ["network-programming", "asynchronous"]
[dependencies]
async-graphql = { path = "../..", version = "1.18.0" }
lambda_http = { git = "https://github.com/awslabs/aws-lambda-rust-runtime" }
futures = "0.3.0"
async-trait = "0.1.30"

View File

@ -1,40 +0,0 @@
//! Async-graphql integration with AWS lambda
//!
#![forbid(unsafe_code)]
use async_graphql::{IntoQueryBuilder, IntoQueryBuilderOpts, ParseRequestError, QueryBuilder};
use futures::io::AllowStdIo;
use lambda_http::Request;
use std::io::Cursor;
/// Lambda request extension
///
#[async_trait::async_trait]
pub trait GQLRequestExt {
/// Convert a query to `async_graphql::QueryBuilder`.
async fn graphql(&self) -> Result<QueryBuilder, ParseRequestError> {
self.graphql_opts(Default::default()).await
}
/// Similar to graphql, but you can set the options `IntoQueryBuilderOpts`.
async fn graphql_opts(
&self,
opts: IntoQueryBuilderOpts,
) -> Result<QueryBuilder, ParseRequestError>;
}
#[async_trait::async_trait]
impl GQLRequestExt for Request {
async fn graphql_opts(
&self,
opts: IntoQueryBuilderOpts,
) -> Result<QueryBuilder, ParseRequestError> {
let ct = self
.headers()
.get("content-type")
.and_then(|value| value.to_str().ok());
(ct, AllowStdIo::new(Cursor::new(self.body().to_vec())))
.into_query_builder_opts(&opts)
.await
}
}

View File

@ -4,6 +4,7 @@ version = "1.18.0"
authors = ["Daniel Wiesenberg <daniel@simplificAR.io>"]
edition = "2018"
description = "async-graphql for Rocket.rs"
# Waiting for Rocket 0.5 release.
#publish = true
license = "MIT/Apache-2.0"
documentation = "https://docs.rs/async-graphql/"

View File

@ -62,7 +62,7 @@ where
graphql_opts(req, schema, configuration, Default::default()).await
}
/// Similar to graphql, but you can set the options `MultipartOptions`.
/// Similar to graphql, but you can set the options `async_graphql::MultipartOptions`.
pub async fn graphql_opts<Query, Mutation, Subscription, TideState, F>(
req: Request<TideState>,
schema: Schema<Query, Mutation, Subscription>,
@ -84,12 +84,12 @@ where
///
#[async_trait]
pub trait RequestExt<State: Clone + Send + Sync + 'static>: Sized {
/// Convert a query to `async_graphql::QueryBuilder`.
/// Convert a query to `async_graphql::Request`.
async fn body_graphql(self) -> tide::Result<async_graphql::Request> {
self.body_graphql_opts(Default::default()).await
}
/// Similar to graphql, but you can set the options `IntoQueryBuilderOpts`.
/// Similar to `RequestExt::body_graphql`, but you can set the options `async_graphql::MultipartOptions`.
async fn body_graphql_opts(
self,
opts: MultipartOptions,

View File

@ -5,11 +5,8 @@
#![allow(clippy::needless_doctest_main)]
#![forbid(unsafe_code)]
use async_graphql::http::{GQLRequest, StreamBody};
use async_graphql::{
Data, FieldResult, IntoQueryBuilder, ObjectType, QueryBuilder, ReceiveMultipartOptions,
Response, Schema, SubscriptionType, WebSocketTransport,
};
use async_graphql::http::{GQLRequest, MultipartOptions, StreamBody};
use async_graphql::{Data, FieldResult, ObjectType, Schema, SubscriptionType};
use futures::select;
use futures::{SinkExt, StreamExt};
use hyper::Method;
@ -35,9 +32,10 @@ impl Reject for BadRequest {}
/// GraphQL request filter
///
/// It outputs a tuple containing the `Schema` and `QuertBuilder`.
/// It outputs a tuple containing the `async_graphql::Schema` and `async_graphql::Request`.
///
/// # Examples
///
/// *[Full Example](<https://github.com/async-graphql/examples/blob/master/warp/starwars/src/main.rs>)*
///
/// ```no_run
@ -57,18 +55,24 @@ impl Reject for BadRequest {}
/// }
/// }
///
/// type MySchema = Schema<QueryRoot, EmptyMutation, EmptySubscription>;
///
/// #[tokio::main]
/// async fn main() {
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
/// let filter = async_graphql_warp::graphql(schema).and_then(|(schema, builder): (_, QueryBuilder)| async move {
/// Ok::<_, Infallible>(GQLResponse::from(builder.execute(&schema).await))
/// let filter = async_graphql_warp::graphql(schema).
/// and_then(|(schema, request): (MySchema, async_graphql::Request)| async move {
/// Ok::<_, Infallible>(GQLResponse::from(schema.execute(request).await))
/// });
/// warp::serve(filter).run(([0, 0, 0, 0], 8000)).await;
/// }
/// ```
pub fn graphql<Query, Mutation, Subscription>(
schema: Schema<Query, Mutation, Subscription>,
) -> BoxedFilter<((Schema<Query, Mutation, Subscription>, QueryBuilder),)>
) -> BoxedFilter<((
Schema<Query, Mutation, Subscription>,
async_graphql::Request,
),)>
where
Query: ObjectType + Send + Sync + 'static,
Mutation: ObjectType + Send + Sync + 'static,
@ -77,11 +81,14 @@ where
graphql_opts(schema, Default::default())
}
/// Similar to graphql, but you can set the options `IntoQueryBuilderOpts`.
/// Similar to graphql, but you can set the options `async_graphql::MultipartOptions`.
pub fn graphql_opts<Query, Mutation, Subscription>(
schema: Schema<Query, Mutation, Subscription>,
opts: ReceiveMultipartOptions,
) -> BoxedFilter<((Schema<Query, Mutation, Subscription>, QueryBuilder),)>
opts: MultipartOptions,
) -> BoxedFilter<((
Schema<Query, Mutation, Subscription>,
async_graphql::Request,
),)>
where
Query: ObjectType + Send + Sync + 'static,
Mutation: ObjectType + Send + Sync + 'static,
@ -100,22 +107,19 @@ where
query: String,
content_type,
body,
opts: Arc<ReceiveMultipartOptions>,
opts: Arc<MultipartOptions>,
schema| async move {
if method == Method::GET {
let gql_request: GQLRequest = serde_urlencoded::from_str(&query)
let request: GQLRequest = serde_urlencoded::from_str(&query)
.map_err(|err| warp::reject::custom(BadRequest(err.into())))?;
let builder = gql_request
.into_query_builder_opts(&opts)
.await
.map_err(|err| warp::reject::custom(BadRequest(err.into())))?;
Ok::<_, Rejection>((schema, builder))
Ok::<_, Rejection>((schema, async_graphql::Request::from(request)))
} else {
let builder = (content_type, StreamBody::new(body))
.into_query_builder_opts(&opts)
.await
.map_err(|err| warp::reject::custom(BadRequest(err.into())))?;
Ok::<_, Rejection>((schema, builder))
let request = async_graphql::http::receive_body(
content_type,
StreamBody::new(body),
MultipartOptions::clone(&opts)
).await.map_err(|err| warp::reject::custom(BadRequest(err.into())))?;
Ok::<_, Rejection>((schema, request))
}
},
)
@ -163,15 +167,15 @@ where
Mutation: ObjectType + Sync + Send + 'static,
Subscription: SubscriptionType + Send + Sync + 'static,
{
graphql_subscription_with_data(schema, |_| Ok(Default::default()))
graphql_subscription_with_initializer(schema, |_| Ok(Default::default()))
}
/// GraphQL subscription filter
///
/// Specifies that a function converts the init payload to data.
pub fn graphql_subscription_with_data<Query, Mutation, Subscription, F>(
pub fn graphql_subscription_with_initializer<Query, Mutation, Subscription, F>(
schema: Schema<Query, Mutation, Subscription>,
init_context_data: F,
initializer: F,
) -> BoxedFilter<(impl Reply,)>
where
Query: ObjectType + Sync + Send + 'static,
@ -182,20 +186,23 @@ where
warp::any()
.and(warp::ws())
.and(warp::any().map(move || schema.clone()))
.and(warp::any().map(move || init_context_data.clone()))
.and(warp::any().map(move || initializer.clone()))
.map(
|ws: warp::ws::Ws,
schema: Schema<Query, Mutation, Subscription>,
init_context_data: F| {
|ws: warp::ws::Ws, schema: Schema<Query, Mutation, Subscription>, initializer: F| {
ws.on_upgrade(move |websocket| {
let (mut tx, rx) = websocket.split();
let (mut stx, srx) =
schema.subscription_connection(WebSocketTransport::new(init_context_data));
async_graphql::transports::websocket::create_with_initializer(
&schema,
initializer,
);
let mut rx = rx.fuse();
let mut srx = srx.fuse();
let srx = srx.fuse();
async move {
futures::pin_mut!(srx);
loop {
select! {
bytes = srx.next() => {
@ -231,17 +238,17 @@ where
}
/// GraphQL reply
pub struct GQLResponse(async_graphql::Result<Response>);
pub struct GQLResponse(async_graphql::Response);
impl From<async_graphql::Result<Response>> for GQLResponse {
fn from(resp: async_graphql::Result<Response>) -> Self {
impl From<async_graphql::Response> for GQLResponse {
fn from(resp: async_graphql::Response) -> Self {
GQLResponse(resp)
}
}
fn add_cache_control(http_resp: &mut Response, resp: &async_graphql::Result<Response>) {
if let Ok(Response { cache_control, .. }) = resp {
if let Some(cache_control) = cache_control.value() {
fn add_cache_control(http_resp: &mut Response, resp: &async_graphql::Response) {
if resp.is_ok() {
if let Some(cache_control) = resp.cache_control.value() {
if let Ok(value) = cache_control.parse() {
http_resp.headers_mut().insert("cache-control", value);
}
@ -251,14 +258,13 @@ fn add_cache_control(http_resp: &mut Response, resp: &async_graphql::Result<Resp
impl Reply for GQLResponse {
fn into_response(self) -> Response {
let gql_resp = async_graphql::http::GQLResponse(self.0);
let mut resp = warp::reply::with_header(
warp::reply::json(&gql_resp),
warp::reply::json(&self.0),
"content-type",
"application/json",
)
.into_response();
add_cache_control(&mut resp, &gql_resp.0);
add_cache_control(&mut resp, &self.0);
resp
}
}