async-graphql-warp 2.0
This commit is contained in:
parent
c2e32c2ea1
commit
a43e437a38
|
@ -60,6 +60,5 @@ members = [
|
|||
"integrations/warp",
|
||||
"integrations/tide",
|
||||
"integrations/rocket",
|
||||
# "integrations/lambda",
|
||||
"benchmark",
|
||||
]
|
||||
|
|
|
@ -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"
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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/"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user