Replace http::GQLRequest with Request
This commit is contained in:
parent
79bcf5f710
commit
dcc7d1be14
|
@ -41,10 +41,10 @@ impl FromRequest for GQLRequest {
|
|||
let config = req.app_data::<Self::Config>().cloned().unwrap_or_default();
|
||||
|
||||
if req.method() == Method::GET {
|
||||
let res = web::Query::<async_graphql::http::GQLRequest>::from_query(req.query_string());
|
||||
let res = web::Query::<async_graphql::Request>::from_query(req.query_string());
|
||||
Box::pin(async move {
|
||||
let gql_request = res?;
|
||||
Ok(GQLRequest(gql_request.into_inner().into()))
|
||||
Ok(GQLRequest(gql_request.into_inner()))
|
||||
})
|
||||
} else {
|
||||
let content_type = req
|
||||
|
|
|
@ -199,7 +199,7 @@ impl<'q> FromQuery<'q> for GQLRequest {
|
|||
let decoded = value.url_decode().map_err(|e| e.to_string())?;
|
||||
let json_value = serde_json::from_str::<serde_json::Value>(&decoded)
|
||||
.map_err(|e| e.to_string())?;
|
||||
variables = Variables::parse_from_json(json_value).into();
|
||||
variables = Variables::from_json(json_value).into();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#![allow(clippy::needless_doctest_main)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use async_graphql::http::{GQLRequest, MultipartOptions};
|
||||
use async_graphql::http::MultipartOptions;
|
||||
use async_graphql::{resolver_utils::ObjectType, Schema, SubscriptionType};
|
||||
use async_trait::async_trait;
|
||||
use std::str::FromStr;
|
||||
|
@ -103,7 +103,7 @@ impl<State: Clone + Send + Sync + 'static> RequestExt<State> for Request<State>
|
|||
opts: MultipartOptions,
|
||||
) -> tide::Result<async_graphql::Request> {
|
||||
if self.method() == Method::Get {
|
||||
Ok(self.query::<GQLRequest>()?.into())
|
||||
Ok(self.query::<async_graphql::Request>()?)
|
||||
} else {
|
||||
let content_type = self
|
||||
.header(&headers::CONTENT_TYPE)
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
#![allow(clippy::needless_doctest_main)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use async_graphql::http::{GQLRequest, MultipartOptions};
|
||||
use async_graphql::{resolver_utils::ObjectType, Data, FieldResult, Schema, SubscriptionType};
|
||||
use async_graphql::http::MultipartOptions;
|
||||
use async_graphql::{
|
||||
resolver_utils::ObjectType, Data, FieldResult, Request, Schema, SubscriptionType,
|
||||
};
|
||||
use futures::io::ErrorKind;
|
||||
use futures::{select, TryStreamExt};
|
||||
use futures::{SinkExt, StreamExt};
|
||||
|
@ -112,9 +114,9 @@ where
|
|||
opts: Arc<MultipartOptions>,
|
||||
schema| async move {
|
||||
if method == Method::GET {
|
||||
let request: GQLRequest = serde_urlencoded::from_str(&query)
|
||||
let request: Request = serde_urlencoded::from_str(&query)
|
||||
.map_err(|err| warp::reject::custom(BadRequest(err.into())))?;
|
||||
Ok::<_, Rejection>((schema, async_graphql::Request::from(request)))
|
||||
Ok::<_, Rejection>((schema, request))
|
||||
} else {
|
||||
let request = async_graphql::http::receive_body(
|
||||
content_type,
|
||||
|
|
|
@ -6,18 +6,19 @@ use crate::parser::types::{
|
|||
use crate::schema::SchemaEnv;
|
||||
use crate::{FieldResult, InputValueType, Lookahead, Pos, Positioned, QueryError, Result, Value};
|
||||
use fnv::FnvHashMap;
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::{Serialize, Serializer};
|
||||
use serde::ser::{SerializeSeq, Serializer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::any::{Any, TypeId};
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Variables of a query.
|
||||
#[derive(Debug, Clone, Default, Serialize)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct Variables(pub BTreeMap<Name, Value>);
|
||||
|
||||
impl Display for Variables {
|
||||
|
@ -31,18 +32,34 @@ impl Display for Variables {
|
|||
}
|
||||
|
||||
impl Variables {
|
||||
/// Parse variables from JSON object.
|
||||
/// Get the variables from a GraphQL value.
|
||||
///
|
||||
/// If the value is not a map, or the keys of map are not valid GraphQL names, then an empty
|
||||
/// `Variables` instance will be returned.
|
||||
pub fn parse_from_json(value: serde_json::Value) -> Self {
|
||||
if let Ok(Value::Object(obj)) = Value::from_json(value) {
|
||||
Self(obj)
|
||||
} else {
|
||||
Default::default()
|
||||
/// If the value is not a map, then no variables will be returned.
|
||||
#[must_use]
|
||||
pub fn from_value(value: Value) -> Self {
|
||||
match value {
|
||||
Value::Object(obj) => Self(obj),
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the values from a JSON value.
|
||||
///
|
||||
/// If the value is not a map or the keys of a map are not valid GraphQL names, then no
|
||||
/// variables will be returned.
|
||||
#[must_use]
|
||||
pub fn from_json(value: serde_json::Value) -> Self {
|
||||
Value::from_json(value)
|
||||
.map(Self::from_value)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Get the variables as a GraphQL value.
|
||||
#[must_use]
|
||||
pub fn into_value(self) -> Value {
|
||||
Value::Object(self.0)
|
||||
}
|
||||
|
||||
pub(crate) fn variable_path(&mut self, path: &str) -> Option<&mut Value> {
|
||||
let mut parts = path.strip_prefix("variables.")?.split('.');
|
||||
|
||||
|
@ -60,17 +77,31 @@ impl Variables {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Variables> for Value {
|
||||
fn from(variables: Variables) -> Self {
|
||||
variables.into_value()
|
||||
}
|
||||
}
|
||||
|
||||
/// Schema/Context data.
|
||||
///
|
||||
/// This is a type map, allowing you to store anything inside it.
|
||||
#[derive(Default)]
|
||||
pub struct Data(FnvHashMap<TypeId, Box<dyn Any + Sync + Send>>);
|
||||
|
||||
impl Data {
|
||||
#[allow(missing_docs)]
|
||||
/// Insert data.
|
||||
pub fn insert<D: Any + Send + Sync>(&mut self, data: D) {
|
||||
self.0.insert(TypeId::of::<D>(), Box::new(data));
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Data {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.debug_tuple("Data").finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Context for `SelectionSet`
|
||||
pub type ContextSelectionSet<'a> = ContextBase<'a, &'a Positioned<SelectionSet>>;
|
||||
|
||||
|
|
|
@ -8,38 +8,9 @@ pub use graphiql_source::graphiql_source;
|
|||
pub use multipart::{receive_multipart, MultipartOptions};
|
||||
pub use playground_source::{playground_source, GraphQLPlaygroundConfig};
|
||||
|
||||
use crate::{Data, ParseRequestError, Request, Variables};
|
||||
use crate::{ParseRequestError, Request};
|
||||
use futures::io::AsyncRead;
|
||||
use futures::AsyncReadExt;
|
||||
use serde::Deserialize;
|
||||
|
||||
/// Deserializable GraphQL Request object
|
||||
#[derive(Deserialize, Clone, PartialEq, Debug)]
|
||||
pub struct GQLRequest {
|
||||
/// Query source
|
||||
pub query: String,
|
||||
|
||||
/// Operation name for this query
|
||||
#[serde(rename = "operationName")]
|
||||
pub operation_name: Option<String>,
|
||||
|
||||
/// Variables for this query
|
||||
pub variables: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
impl From<GQLRequest> for Request {
|
||||
fn from(request: GQLRequest) -> Self {
|
||||
Self {
|
||||
query: request.query,
|
||||
operation_name: request.operation_name,
|
||||
variables: request
|
||||
.variables
|
||||
.map(Variables::parse_from_json)
|
||||
.unwrap_or_default(),
|
||||
ctx_data: Data::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive a GraphQL request from a content type and body.
|
||||
pub async fn receive_body(
|
||||
|
@ -54,60 +25,6 @@ pub async fn receive_body(
|
|||
body.read_to_end(&mut data)
|
||||
.await
|
||||
.map_err(ParseRequestError::Io)?;
|
||||
Ok(serde_json::from_slice::<GQLRequest>(&data)
|
||||
.map_err(ParseRequestError::InvalidRequest)?
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn test_request() {
|
||||
let request: GQLRequest = serde_json::from_value(json! ({
|
||||
"query": "{ a b c }"
|
||||
}))
|
||||
.unwrap();
|
||||
assert!(request.variables.is_none());
|
||||
assert!(request.operation_name.is_none());
|
||||
assert_eq!(request.query, "{ a b c }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_request_with_operation_name() {
|
||||
let request: GQLRequest = serde_json::from_value(json! ({
|
||||
"query": "{ a b c }",
|
||||
"operationName": "a"
|
||||
}))
|
||||
.unwrap();
|
||||
assert!(request.variables.is_none());
|
||||
assert_eq!(request.operation_name.as_deref(), Some("a"));
|
||||
assert_eq!(request.query, "{ a b c }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_request_with_variables() {
|
||||
let request: GQLRequest = serde_json::from_value(json! ({
|
||||
"query": "{ a b c }",
|
||||
"variables": {
|
||||
"v1": 100,
|
||||
"v2": [1, 2, 3],
|
||||
"v3": "str",
|
||||
}
|
||||
}))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
request.variables,
|
||||
Some(json!({
|
||||
"v1": 100,
|
||||
"v2": [1, 2, 3],
|
||||
"v3": "str",
|
||||
}))
|
||||
);
|
||||
assert!(request.operation_name.is_none());
|
||||
assert_eq!(request.query, "{ a b c }");
|
||||
Ok(serde_json::from_slice::<Request>(&data).map_err(ParseRequestError::InvalidRequest)?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use crate::http::GQLRequest;
|
||||
use crate::{ParseRequestError, Request};
|
||||
use bytes::Bytes;
|
||||
use futures::io::AsyncRead;
|
||||
|
@ -66,9 +65,8 @@ pub async fn receive_multipart(
|
|||
Some("operations") => {
|
||||
let request_str = field.text().await?;
|
||||
request = Some(
|
||||
serde_json::from_str::<GQLRequest>(&request_str)
|
||||
.map_err(ParseRequestError::InvalidRequest)?
|
||||
.into(),
|
||||
serde_json::from_str::<Request>(&request_str)
|
||||
.map_err(ParseRequestError::InvalidRequest)?,
|
||||
);
|
||||
}
|
||||
Some("map") => {
|
||||
|
|
|
@ -1,14 +1,29 @@
|
|||
use crate::parser::types::UploadValue;
|
||||
use crate::{Data, Value, Variables};
|
||||
use serde::Deserialize;
|
||||
use std::any::Any;
|
||||
use std::fs::File;
|
||||
|
||||
/// GraphQL query request
|
||||
/// GraphQL request.
|
||||
///
|
||||
/// This can be deserialized from a structure of the query string, the operation name and the
|
||||
/// variables. The names are all in `camelCase` (e.g. `operationName`).
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Request {
|
||||
pub(crate) query: String,
|
||||
pub(crate) operation_name: Option<String>,
|
||||
pub(crate) variables: Variables,
|
||||
pub(crate) ctx_data: Data,
|
||||
/// The query source of the request.
|
||||
pub query: String,
|
||||
/// The operation name of the request.
|
||||
#[serde(default, rename = "operationName")]
|
||||
pub operation_name: Option<String>,
|
||||
/// The variables of the request.
|
||||
#[serde(default)]
|
||||
pub variables: Variables,
|
||||
/// The data of the request that can be accessed through `Context::data`.
|
||||
///
|
||||
/// **This data is only valid for this request**
|
||||
#[serde(skip)]
|
||||
pub data: Data,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
|
@ -18,11 +33,11 @@ impl Request {
|
|||
query: query.into(),
|
||||
operation_name: None,
|
||||
variables: Variables::default(),
|
||||
ctx_data: Data::default(),
|
||||
data: Data::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Specify the operation name.
|
||||
/// Specify the operation name of the request.
|
||||
pub fn operation_name<T: Into<String>>(self, name: T) -> Self {
|
||||
Self {
|
||||
operation_name: Some(name.into()),
|
||||
|
@ -35,15 +50,18 @@ impl Request {
|
|||
Self { variables, ..self }
|
||||
}
|
||||
|
||||
/// Add a context data that can be accessed in the `Context`, you access it with `Context::data`.
|
||||
///
|
||||
/// **This data is only valid for this query**
|
||||
/// Insert some data for this request.
|
||||
pub fn data<D: Any + Send + Sync>(mut self, data: D) -> Self {
|
||||
self.ctx_data.insert(data);
|
||||
self.data.insert(data);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set uploaded file path
|
||||
/// Set a variable to an upload value.
|
||||
///
|
||||
/// `var_path` is a dot-separated path to the item that begins with `variables`, for example
|
||||
/// `variables.files.2.content` is equivalent to the Rust code
|
||||
/// `request.variables["files"][2]["content"]`. If no variable exists at the path this function
|
||||
/// won't do anything.
|
||||
pub fn set_upload(
|
||||
&mut self,
|
||||
var_path: &str,
|
||||
|
@ -68,3 +86,55 @@ impl<T: Into<String>> From<T> for Request {
|
|||
Self::new(query)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn test_request() {
|
||||
let request: Request = serde_json::from_value(json! ({
|
||||
"query": "{ a b c }"
|
||||
}))
|
||||
.unwrap();
|
||||
assert!(request.variables.0.is_empty());
|
||||
assert!(request.operation_name.is_none());
|
||||
assert_eq!(request.query, "{ a b c }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_request_with_operation_name() {
|
||||
let request: Request = serde_json::from_value(json! ({
|
||||
"query": "{ a b c }",
|
||||
"operationName": "a"
|
||||
}))
|
||||
.unwrap();
|
||||
assert!(request.variables.0.is_empty());
|
||||
assert_eq!(request.operation_name.as_deref(), Some("a"));
|
||||
assert_eq!(request.query, "{ a b c }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_request_with_variables() {
|
||||
let request: Request = serde_json::from_value(json! ({
|
||||
"query": "{ a b c }",
|
||||
"variables": {
|
||||
"v1": 100,
|
||||
"v2": [1, 2, 3],
|
||||
"v3": "str",
|
||||
}
|
||||
}))
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
request.variables.into_value().into_json().unwrap(),
|
||||
json!({
|
||||
"v1": 100,
|
||||
"v2": [1, 2, 3],
|
||||
"v3": "str",
|
||||
})
|
||||
);
|
||||
assert!(request.operation_name.is_none());
|
||||
assert_eq!(request.query, "{ a b c }");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -251,7 +251,7 @@ impl<'a> Fields<'a> {
|
|||
|
||||
let introspection_type_name = root.introspection_type_name();
|
||||
|
||||
if type_condition.map_or(false, |condition| {
|
||||
let applies_concrete_object = type_condition.map_or(false, |condition| {
|
||||
introspection_type_name == condition
|
||||
|| ctx
|
||||
.schema_env
|
||||
|
@ -259,7 +259,8 @@ impl<'a> Fields<'a> {
|
|||
.implements
|
||||
.get(&*introspection_type_name)
|
||||
.map_or(false, |interfaces| interfaces.contains(condition))
|
||||
}) {
|
||||
});
|
||||
if applies_concrete_object {
|
||||
// The fragment applies to the concrete object type.
|
||||
|
||||
// TODO: This solution isn't ideal. If there are two interfaces InterfaceA
|
||||
|
|
|
@ -442,7 +442,7 @@ where
|
|||
let (document, cache_control, extensions) =
|
||||
try_query_result!(self.prepare_request(&request));
|
||||
let mut resp = self
|
||||
.execute_once(document, extensions, request.variables, request.ctx_data)
|
||||
.execute_once(document, extensions, request.variables, request.data)
|
||||
.await;
|
||||
resp.cache_control = cache_control;
|
||||
resp
|
||||
|
@ -466,7 +466,7 @@ where
|
|||
|
||||
if document.operation.node.ty != OperationType::Subscription {
|
||||
let mut resp = schema
|
||||
.execute_once(document, extensions, request.variables, request.ctx_data)
|
||||
.execute_once(document, extensions, request.variables, request.data)
|
||||
.await;
|
||||
resp.cache_control = cache_control;
|
||||
yield resp;
|
||||
|
@ -509,7 +509,7 @@ where
|
|||
/// Execute an GraphQL subscription.
|
||||
pub fn execute_stream(&self, request: impl Into<Request>) -> impl Stream<Item = Response> {
|
||||
let mut request = request.into();
|
||||
let ctx_data = std::mem::take(&mut request.ctx_data);
|
||||
let ctx_data = std::mem::take(&mut request.data);
|
||||
self.execute_stream_with_ctx_data(request, Arc::new(ctx_data))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! WebSocket transport for subscription
|
||||
|
||||
use crate::resolver_utils::ObjectType;
|
||||
use crate::{http, Data, FieldResult, Request, Response, Schema, SubscriptionType};
|
||||
use crate::{Data, FieldResult, Request, Response, Schema, SubscriptionType};
|
||||
use futures::channel::mpsc;
|
||||
use futures::task::{Context, Poll};
|
||||
use futures::{Future, Stream, StreamExt};
|
||||
|
@ -229,8 +229,7 @@ where
|
|||
}
|
||||
"start" => {
|
||||
if let (Some(id), Some(payload)) = (msg.id, msg.payload) {
|
||||
if let Ok(request) = serde_json::from_value::<http::GQLRequest>(payload) {
|
||||
let request = Request::from(request);
|
||||
if let Ok(request) = serde_json::from_value::<Request>(payload) {
|
||||
let stream = schema
|
||||
.execute_stream_with_ctx_data(request, ctx.ctx_data.clone())
|
||||
.boxed();
|
||||
|
|
|
@ -24,7 +24,7 @@ pub async fn test_variables() {
|
|||
}
|
||||
"#,
|
||||
)
|
||||
.variables(Variables::parse_from_json(serde_json::json!({
|
||||
.variables(Variables::from_json(serde_json::json!({
|
||||
"intVal": 10,
|
||||
"intListVal": [1, 2, 3, 4, 5],
|
||||
})));
|
||||
|
@ -117,7 +117,7 @@ pub async fn test_variable_null() {
|
|||
}
|
||||
"#,
|
||||
)
|
||||
.variables(Variables::parse_from_json(serde_json::json!({
|
||||
.variables(Variables::from_json(serde_json::json!({
|
||||
"intVal": null,
|
||||
})));
|
||||
let resp = schema.execute(query).await;
|
||||
|
@ -168,7 +168,7 @@ pub async fn test_variable_in_input_object() {
|
|||
}"#;
|
||||
let resp = schema
|
||||
.execute(
|
||||
Request::new(query).variables(Variables::parse_from_json(serde_json::json!({
|
||||
Request::new(query).variables(Variables::from_json(serde_json::json!({
|
||||
"value": 10,
|
||||
}))),
|
||||
)
|
||||
|
@ -189,7 +189,7 @@ pub async fn test_variable_in_input_object() {
|
|||
}"#;
|
||||
let resp = schema
|
||||
.execute(
|
||||
Request::new(query).variables(Variables::parse_from_json(serde_json::json!({
|
||||
Request::new(query).variables(Variables::from_json(serde_json::json!({
|
||||
"value": 3,
|
||||
}))),
|
||||
)
|
||||
|
@ -210,7 +210,7 @@ pub async fn test_variable_in_input_object() {
|
|||
}"#;
|
||||
let resp = schema
|
||||
.execute(
|
||||
Request::new(query).variables(Variables::parse_from_json(serde_json::json!({
|
||||
Request::new(query).variables(Variables::from_json(serde_json::json!({
|
||||
"value": 10,
|
||||
}))),
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user