async-graphql-warp 2.0
This commit is contained in:
parent
c2e32c2ea1
commit
a43e437a38
|
@ -60,6 +60,5 @@ members = [
|
||||||
"integrations/warp",
|
"integrations/warp",
|
||||||
"integrations/tide",
|
"integrations/tide",
|
||||||
"integrations/rocket",
|
"integrations/rocket",
|
||||||
# "integrations/lambda",
|
|
||||||
"benchmark",
|
"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>"]
|
authors = ["Daniel Wiesenberg <daniel@simplificAR.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "async-graphql for Rocket.rs"
|
description = "async-graphql for Rocket.rs"
|
||||||
|
# Waiting for Rocket 0.5 release.
|
||||||
#publish = true
|
#publish = true
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
documentation = "https://docs.rs/async-graphql/"
|
documentation = "https://docs.rs/async-graphql/"
|
||||||
|
|
|
@ -62,7 +62,7 @@ where
|
||||||
graphql_opts(req, schema, configuration, Default::default()).await
|
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>(
|
pub async fn graphql_opts<Query, Mutation, Subscription, TideState, F>(
|
||||||
req: Request<TideState>,
|
req: Request<TideState>,
|
||||||
schema: Schema<Query, Mutation, Subscription>,
|
schema: Schema<Query, Mutation, Subscription>,
|
||||||
|
@ -84,12 +84,12 @@ where
|
||||||
///
|
///
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait RequestExt<State: Clone + Send + Sync + 'static>: Sized {
|
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> {
|
async fn body_graphql(self) -> tide::Result<async_graphql::Request> {
|
||||||
self.body_graphql_opts(Default::default()).await
|
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(
|
async fn body_graphql_opts(
|
||||||
self,
|
self,
|
||||||
opts: MultipartOptions,
|
opts: MultipartOptions,
|
||||||
|
|
|
@ -5,11 +5,8 @@
|
||||||
#![allow(clippy::needless_doctest_main)]
|
#![allow(clippy::needless_doctest_main)]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
use async_graphql::http::{GQLRequest, StreamBody};
|
use async_graphql::http::{GQLRequest, MultipartOptions, StreamBody};
|
||||||
use async_graphql::{
|
use async_graphql::{Data, FieldResult, ObjectType, Schema, SubscriptionType};
|
||||||
Data, FieldResult, IntoQueryBuilder, ObjectType, QueryBuilder, ReceiveMultipartOptions,
|
|
||||||
Response, Schema, SubscriptionType, WebSocketTransport,
|
|
||||||
};
|
|
||||||
use futures::select;
|
use futures::select;
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
use hyper::Method;
|
use hyper::Method;
|
||||||
|
@ -35,9 +32,10 @@ impl Reject for BadRequest {}
|
||||||
|
|
||||||
/// GraphQL request filter
|
/// 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
|
/// # Examples
|
||||||
|
///
|
||||||
/// *[Full Example](<https://github.com/async-graphql/examples/blob/master/warp/starwars/src/main.rs>)*
|
/// *[Full Example](<https://github.com/async-graphql/examples/blob/master/warp/starwars/src/main.rs>)*
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
|
@ -57,18 +55,24 @@ impl Reject for BadRequest {}
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
|
/// type MySchema = Schema<QueryRoot, EmptyMutation, EmptySubscription>;
|
||||||
|
///
|
||||||
/// #[tokio::main]
|
/// #[tokio::main]
|
||||||
/// async fn main() {
|
/// async fn main() {
|
||||||
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
|
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
|
||||||
/// let filter = async_graphql_warp::graphql(schema).and_then(|(schema, builder): (_, QueryBuilder)| async move {
|
/// let filter = async_graphql_warp::graphql(schema).
|
||||||
/// Ok::<_, Infallible>(GQLResponse::from(builder.execute(&schema).await))
|
/// 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;
|
/// warp::serve(filter).run(([0, 0, 0, 0], 8000)).await;
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn graphql<Query, Mutation, Subscription>(
|
pub fn graphql<Query, Mutation, Subscription>(
|
||||||
schema: Schema<Query, Mutation, Subscription>,
|
schema: Schema<Query, Mutation, Subscription>,
|
||||||
) -> BoxedFilter<((Schema<Query, Mutation, Subscription>, QueryBuilder),)>
|
) -> BoxedFilter<((
|
||||||
|
Schema<Query, Mutation, Subscription>,
|
||||||
|
async_graphql::Request,
|
||||||
|
),)>
|
||||||
where
|
where
|
||||||
Query: ObjectType + Send + Sync + 'static,
|
Query: ObjectType + Send + Sync + 'static,
|
||||||
Mutation: ObjectType + Send + Sync + 'static,
|
Mutation: ObjectType + Send + Sync + 'static,
|
||||||
|
@ -77,11 +81,14 @@ where
|
||||||
graphql_opts(schema, Default::default())
|
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>(
|
pub fn graphql_opts<Query, Mutation, Subscription>(
|
||||||
schema: Schema<Query, Mutation, Subscription>,
|
schema: Schema<Query, Mutation, Subscription>,
|
||||||
opts: ReceiveMultipartOptions,
|
opts: MultipartOptions,
|
||||||
) -> BoxedFilter<((Schema<Query, Mutation, Subscription>, QueryBuilder),)>
|
) -> BoxedFilter<((
|
||||||
|
Schema<Query, Mutation, Subscription>,
|
||||||
|
async_graphql::Request,
|
||||||
|
),)>
|
||||||
where
|
where
|
||||||
Query: ObjectType + Send + Sync + 'static,
|
Query: ObjectType + Send + Sync + 'static,
|
||||||
Mutation: ObjectType + Send + Sync + 'static,
|
Mutation: ObjectType + Send + Sync + 'static,
|
||||||
|
@ -100,22 +107,19 @@ where
|
||||||
query: String,
|
query: String,
|
||||||
content_type,
|
content_type,
|
||||||
body,
|
body,
|
||||||
opts: Arc<ReceiveMultipartOptions>,
|
opts: Arc<MultipartOptions>,
|
||||||
schema| async move {
|
schema| async move {
|
||||||
if method == Method::GET {
|
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())))?;
|
.map_err(|err| warp::reject::custom(BadRequest(err.into())))?;
|
||||||
let builder = gql_request
|
Ok::<_, Rejection>((schema, async_graphql::Request::from(request)))
|
||||||
.into_query_builder_opts(&opts)
|
|
||||||
.await
|
|
||||||
.map_err(|err| warp::reject::custom(BadRequest(err.into())))?;
|
|
||||||
Ok::<_, Rejection>((schema, builder))
|
|
||||||
} else {
|
} else {
|
||||||
let builder = (content_type, StreamBody::new(body))
|
let request = async_graphql::http::receive_body(
|
||||||
.into_query_builder_opts(&opts)
|
content_type,
|
||||||
.await
|
StreamBody::new(body),
|
||||||
.map_err(|err| warp::reject::custom(BadRequest(err.into())))?;
|
MultipartOptions::clone(&opts)
|
||||||
Ok::<_, Rejection>((schema, builder))
|
).await.map_err(|err| warp::reject::custom(BadRequest(err.into())))?;
|
||||||
|
Ok::<_, Rejection>((schema, request))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -163,15 +167,15 @@ where
|
||||||
Mutation: ObjectType + Sync + Send + 'static,
|
Mutation: ObjectType + Sync + Send + 'static,
|
||||||
Subscription: SubscriptionType + Send + Sync + '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
|
/// GraphQL subscription filter
|
||||||
///
|
///
|
||||||
/// Specifies that a function converts the init payload to data.
|
/// 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>,
|
schema: Schema<Query, Mutation, Subscription>,
|
||||||
init_context_data: F,
|
initializer: F,
|
||||||
) -> BoxedFilter<(impl Reply,)>
|
) -> BoxedFilter<(impl Reply,)>
|
||||||
where
|
where
|
||||||
Query: ObjectType + Sync + Send + 'static,
|
Query: ObjectType + Sync + Send + 'static,
|
||||||
|
@ -182,20 +186,23 @@ where
|
||||||
warp::any()
|
warp::any()
|
||||||
.and(warp::ws())
|
.and(warp::ws())
|
||||||
.and(warp::any().map(move || schema.clone()))
|
.and(warp::any().map(move || schema.clone()))
|
||||||
.and(warp::any().map(move || init_context_data.clone()))
|
.and(warp::any().map(move || initializer.clone()))
|
||||||
.map(
|
.map(
|
||||||
|ws: warp::ws::Ws,
|
|ws: warp::ws::Ws, schema: Schema<Query, Mutation, Subscription>, initializer: F| {
|
||||||
schema: Schema<Query, Mutation, Subscription>,
|
|
||||||
init_context_data: F| {
|
|
||||||
ws.on_upgrade(move |websocket| {
|
ws.on_upgrade(move |websocket| {
|
||||||
let (mut tx, rx) = websocket.split();
|
let (mut tx, rx) = websocket.split();
|
||||||
let (mut stx, srx) =
|
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 rx = rx.fuse();
|
||||||
let mut srx = srx.fuse();
|
let srx = srx.fuse();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
|
futures::pin_mut!(srx);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
select! {
|
select! {
|
||||||
bytes = srx.next() => {
|
bytes = srx.next() => {
|
||||||
|
@ -231,17 +238,17 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GraphQL reply
|
/// GraphQL reply
|
||||||
pub struct GQLResponse(async_graphql::Result<Response>);
|
pub struct GQLResponse(async_graphql::Response);
|
||||||
|
|
||||||
impl From<async_graphql::Result<Response>> for GQLResponse {
|
impl From<async_graphql::Response> for GQLResponse {
|
||||||
fn from(resp: async_graphql::Result<Response>) -> Self {
|
fn from(resp: async_graphql::Response) -> Self {
|
||||||
GQLResponse(resp)
|
GQLResponse(resp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_cache_control(http_resp: &mut Response, resp: &async_graphql::Result<Response>) {
|
fn add_cache_control(http_resp: &mut Response, resp: &async_graphql::Response) {
|
||||||
if let Ok(Response { cache_control, .. }) = resp {
|
if resp.is_ok() {
|
||||||
if let Some(cache_control) = cache_control.value() {
|
if let Some(cache_control) = resp.cache_control.value() {
|
||||||
if let Ok(value) = cache_control.parse() {
|
if let Ok(value) = cache_control.parse() {
|
||||||
http_resp.headers_mut().insert("cache-control", value);
|
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 {
|
impl Reply for GQLResponse {
|
||||||
fn into_response(self) -> Response {
|
fn into_response(self) -> Response {
|
||||||
let gql_resp = async_graphql::http::GQLResponse(self.0);
|
|
||||||
let mut resp = warp::reply::with_header(
|
let mut resp = warp::reply::with_header(
|
||||||
warp::reply::json(&gql_resp),
|
warp::reply::json(&self.0),
|
||||||
"content-type",
|
"content-type",
|
||||||
"application/json",
|
"application/json",
|
||||||
)
|
)
|
||||||
.into_response();
|
.into_response();
|
||||||
add_cache_control(&mut resp, &gql_resp.0);
|
add_cache_control(&mut resp, &self.0);
|
||||||
resp
|
resp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user