async-graphql/src/http/mod.rs

287 lines
8.0 KiB
Rust
Raw Normal View History

2020-03-05 00:39:56 +00:00
mod graphiql_source;
mod playground_source;
pub use graphiql_source::graphiql_source;
pub use playground_source::playground_source;
2020-03-08 12:35:36 +00:00
use crate::error::{RuleError, RuleErrors};
2020-03-17 09:26:59 +00:00
use crate::query::PreparedQuery;
2020-03-19 09:20:12 +00:00
use crate::{ObjectType, PositionError, Result, Schema, SubscriptionType, Variables};
2020-03-08 12:35:36 +00:00
use graphql_parser::Pos;
use serde::ser::{SerializeMap, SerializeSeq};
2020-03-05 00:39:56 +00:00
use serde::{Serialize, Serializer};
use std::ops::Deref;
2020-03-20 03:56:08 +00:00
/// GraphQL Request object
2020-03-05 00:39:56 +00:00
#[derive(Deserialize, Clone, PartialEq, Debug)]
pub struct GQLRequest {
2020-03-20 03:56:08 +00:00
/// Query source
2020-03-05 00:39:56 +00:00
pub query: String,
2020-03-20 03:56:08 +00:00
/// Operation name for this query
2020-03-05 00:39:56 +00:00
#[serde(rename = "operationName")]
pub operation_name: Option<String>,
2020-03-20 03:56:08 +00:00
/// Variables for this query
2020-03-05 00:39:56 +00:00
pub variables: Option<serde_json::Value>,
}
impl GQLRequest {
2020-03-20 03:56:08 +00:00
/// Execute the query and return the `GQLResponse`.
2020-03-17 09:26:59 +00:00
pub async fn execute<Query, Mutation, Subscription>(
mut self,
schema: &Schema<Query, Mutation, Subscription>,
) -> GQLResponse
2020-03-05 00:39:56 +00:00
where
2020-03-19 09:20:12 +00:00
Query: ObjectType + Send + Sync,
Mutation: ObjectType + Send + Sync,
Subscription: SubscriptionType + Send + Sync,
2020-03-05 00:39:56 +00:00
{
2020-03-14 03:46:20 +00:00
match self.prepare(schema) {
Ok(query) => GQLResponse(query.execute().await),
Err(err) => GQLResponse(Err(err)),
}
}
2020-03-20 03:56:08 +00:00
/// Prepare a query and return a `PreparedQuery` object that gets some information about the query.
2020-03-17 09:26:59 +00:00
pub fn prepare<'a, Query, Mutation, Subscription>(
2020-03-14 03:46:20 +00:00
&'a mut self,
2020-03-17 09:26:59 +00:00
schema: &'a Schema<Query, Mutation, Subscription>,
2020-03-14 03:46:20 +00:00
) -> Result<PreparedQuery<'a, Query, Mutation>>
where
2020-03-19 09:20:12 +00:00
Query: ObjectType + Send + Sync,
Mutation: ObjectType + Send + Sync,
Subscription: SubscriptionType + Send + Sync,
2020-03-14 03:46:20 +00:00
{
let vars = match self.variables.take() {
2020-03-05 00:39:56 +00:00
Some(value) => match Variables::parse_from_json(value) {
Ok(vars) => Some(vars),
2020-03-14 03:46:20 +00:00
Err(err) => return Err(err),
2020-03-05 00:39:56 +00:00
},
None => None,
};
let query = schema.query(&self.query);
2020-03-14 03:46:20 +00:00
let query = match vars {
2020-03-05 00:39:56 +00:00
Some(vars) => query.variables(vars),
None => query,
};
let query = match &self.operation_name {
2020-03-14 03:46:20 +00:00
Some(name) => query.operator_name(name),
2020-03-05 00:39:56 +00:00
None => query,
};
2020-03-14 03:46:20 +00:00
query.prepare()
2020-03-05 00:39:56 +00:00
}
}
2020-03-20 03:56:08 +00:00
/// Serializable query result type
2020-03-25 03:39:28 +00:00
pub struct GQLResponse(pub Result<serde_json::Value>);
2020-03-05 00:39:56 +00:00
impl Serialize for GQLResponse {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
match &self.0 {
Ok(res) => {
let mut map = serializer.serialize_map(None)?;
map.serialize_key("data")?;
2020-03-25 03:39:28 +00:00
map.serialize_value(&res)?;
2020-03-05 00:39:56 +00:00
map.end()
}
Err(err) => {
let mut map = serializer.serialize_map(None)?;
map.serialize_key("errors")?;
2020-03-08 12:35:36 +00:00
map.serialize_value(&GQLError(err))?;
2020-03-05 00:39:56 +00:00
map.end()
}
}
}
}
2020-03-20 03:56:08 +00:00
/// Serializable error type
2020-03-17 09:26:59 +00:00
pub struct GQLError<'a>(pub &'a anyhow::Error);
2020-03-05 00:39:56 +00:00
impl<'a> Deref for GQLError<'a> {
type Target = anyhow::Error;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a> Serialize for GQLError<'a> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
2020-03-08 12:35:36 +00:00
if let Some(err) = self.0.downcast_ref::<PositionError>() {
let mut seq = serializer.serialize_seq(Some(1))?;
seq.serialize_element(&GQLPositionError(err))?;
seq.end()
} else if let Some(err) = self.0.downcast_ref::<RuleErrors>() {
let mut seq = serializer.serialize_seq(Some(err.errors.len()))?;
for err in &err.errors {
seq.serialize_element(&GQLRuleError(err))?;
2020-03-05 00:39:56 +00:00
}
2020-03-08 12:35:36 +00:00
seq.end()
} else {
let mut seq = serializer.serialize_seq(None)?;
seq.serialize_element(&serde_json::json!({
"message": self.0.to_string(),
}))?;
seq.end()
2020-03-05 00:39:56 +00:00
}
2020-03-08 12:35:36 +00:00
}
}
struct GQLErrorPos<'a>(&'a Pos);
impl<'a> Serialize for GQLErrorPos<'a> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("line", &self.0.line)?;
map.serialize_entry("column", &self.0.column)?;
map.end()
}
}
struct GQLPositionError<'a>(&'a PositionError);
2020-03-05 00:39:56 +00:00
2020-03-08 12:35:36 +00:00
impl<'a> Serialize for GQLPositionError<'a> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("message", &self.0.inner.to_string())?;
map.serialize_entry(
"locations",
std::slice::from_ref(&GQLErrorPos(&self.0.position)),
)?;
map.end()
}
}
struct GQLRuleError<'a>(&'a RuleError);
impl<'a> Serialize for GQLRuleError<'a> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("message", &self.0.message)?;
map.serialize_entry(
"locations",
&self
.0
.locations
.iter()
.map(|pos| GQLErrorPos(pos))
.collect::<Vec<_>>(),
)?;
2020-03-05 00:39:56 +00:00
map.end()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ErrorWithPosition;
use graphql_parser::Pos;
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 }");
}
#[test]
fn test_response_data() {
2020-03-25 03:39:28 +00:00
let resp = GQLResponse(Ok(json!({"ok": true})));
2020-03-05 00:39:56 +00:00
assert_eq!(
serde_json::to_value(resp).unwrap(),
json! ({
"data": {
"ok": true,
}
})
);
}
#[test]
fn test_response_error() {
let resp = GQLResponse(Err(anyhow::anyhow!("error")));
assert_eq!(
serde_json::to_value(resp).unwrap(),
json!({
"errors": [{
"message":"error"
}]
})
);
}
#[test]
fn test_response_error_with_pos() {
let resp = GQLResponse(Err(anyhow::anyhow!("error")
.with_position(Pos {
line: 10,
column: 20,
})
.into()));
assert_eq!(
serde_json::to_value(resp).unwrap(),
json!({
"errors": [{
"message":"error",
"locations": [
{"line": 10, "column": 20}
]
}]
})
);
}
}