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}
|
|
|
|
]
|
|
|
|
}]
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|