add actix-web example
This commit is contained in:
parent
4735636a0b
commit
cc96dba8d8
|
@ -31,6 +31,8 @@ uuid = { version = "0.8.1", optional = true }
|
|||
|
||||
[dev-dependencies]
|
||||
async-std = { version = "1.5.0", features = ["attributes"] }
|
||||
actix-web = "2.0.0"
|
||||
actix-rt = "1.0.0"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
|
|
|
@ -86,7 +86,7 @@
|
|||
- [X] Schema
|
||||
- [ ] Validation rules
|
||||
- [ ]
|
||||
- [ ] Actix-web
|
||||
- [X] Actix-web (https://crates.io/crates/async-graphql-actix-web)
|
||||
- [ ] Hyper
|
||||
- [ ] Tide
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
mod schema;
|
||||
|
||||
use crate::schema::MyObj;
|
||||
use actix_web::{guard, web, App, HttpResponse, HttpServer};
|
||||
use async_graphql::http::{graphiql_source, playground_source, GQLRequest, GQLResponse};
|
||||
use async_graphql::{GQLEmptyMutation, Schema};
|
||||
|
||||
type MySchema = Schema<MyObj, GQLEmptyMutation>;
|
||||
|
||||
async fn index(s: web::Data<MySchema>, req: web::Json<GQLRequest>) -> web::Json<GQLResponse> {
|
||||
web::Json(req.into_inner().execute(&s).await)
|
||||
}
|
||||
|
||||
async fn gql_playgound() -> HttpResponse {
|
||||
HttpResponse::Ok()
|
||||
.content_type("text/html; charset=utf-8")
|
||||
.body(playground_source("/"))
|
||||
}
|
||||
|
||||
async fn gql_graphiql() -> HttpResponse {
|
||||
HttpResponse::Ok()
|
||||
.content_type("text/html; charset=utf-8")
|
||||
.body(graphiql_source("/"))
|
||||
}
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
HttpServer::new(|| {
|
||||
App::new()
|
||||
.data(Schema::new(MyObj { value: 10 }, GQLEmptyMutation))
|
||||
.service(web::resource("/").guard(guard::Post()).to(index))
|
||||
.service(web::resource("/").guard(guard::Get()).to(gql_playgound))
|
||||
.service(web::resource("/graphiql").guard(guard::Get()).to(gql_graphiql))
|
||||
})
|
||||
.bind("127.0.0.1:8000")?
|
||||
.run()
|
||||
.await
|
||||
}
|
|
@ -42,38 +42,38 @@ impl MyObjFields for MyObj {
|
|||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
let schema = async_graphql::Schema::<MyObj, async_graphql::GQLEmptyMutation>::new();
|
||||
let vars = async_graphql::Variables::parse_from_json(b"{\"myvar1\": 999}").unwrap();
|
||||
let res = schema
|
||||
.query(
|
||||
MyObj { value: 100 },
|
||||
async_graphql::GQLEmptyMutation,
|
||||
r#"
|
||||
{
|
||||
__schema {
|
||||
directives @skip(if: true) {
|
||||
name
|
||||
description
|
||||
locations
|
||||
args {
|
||||
name description type {
|
||||
name
|
||||
description
|
||||
kind
|
||||
ofType {
|
||||
name
|
||||
description
|
||||
}
|
||||
} defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.variables(&vars)
|
||||
.execute()
|
||||
.await
|
||||
.unwrap();
|
||||
serde_json::to_writer_pretty(std::io::stdout(), &res).unwrap();
|
||||
// let schema = async_graphql::Schema::<MyObj, async_graphql::GQLEmptyMutation>::new();
|
||||
// let vars = async_graphql::Variables::parse_from_json(b"{\"myvar1\": 999}").unwrap();
|
||||
// let res = schema
|
||||
// .query(
|
||||
// MyObj { value: 100 },
|
||||
// async_graphql::GQLEmptyMutation,
|
||||
// r#"
|
||||
// {
|
||||
// __schema {
|
||||
// directives @skip(if: true) {
|
||||
// name
|
||||
// description
|
||||
// locations
|
||||
// args {
|
||||
// name description type {
|
||||
// name
|
||||
// description
|
||||
// kind
|
||||
// ofType {
|
||||
// name
|
||||
// description
|
||||
// }
|
||||
// } defaultValue
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// "#,
|
||||
// )
|
||||
// .variables(&vars)
|
||||
// .execute()
|
||||
// .await
|
||||
// .unwrap();
|
||||
// serde_json::to_writer_pretty(std::io::stdout(), &res).unwrap();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
#[async_graphql::Enum]
|
||||
pub enum MyEnum {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
#[async_graphql::InputObject]
|
||||
pub struct MyInputObj {
|
||||
#[field(default = "\"hehe\"")]
|
||||
a: i32,
|
||||
b: i32,
|
||||
}
|
||||
|
||||
#[async_graphql::Object(
|
||||
field(name = "a", type = "i32"),
|
||||
field(
|
||||
owned,
|
||||
name = "b",
|
||||
type = "i32",
|
||||
arg(name = "v", type = "i32", default = "123")
|
||||
),
|
||||
field(owned, name = "c", type = "Option<String>")
|
||||
)]
|
||||
pub struct MyObj {
|
||||
pub value: i32,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MyObjFields for MyObj {
|
||||
async fn a<'a>(&'a self, ctx: &async_graphql::Context<'_>) -> async_graphql::Result<&'a i32> {
|
||||
Ok(&self.value)
|
||||
}
|
||||
|
||||
async fn b(&self, ctx: &async_graphql::Context<'_>, v: i32) -> async_graphql::Result<i32> {
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
async fn c(&self, ctx: &async_graphql::Context<'_>) -> async_graphql::Result<Option<String>> {
|
||||
Ok(Some(format!("**{}**", self.value)))
|
||||
}
|
||||
}
|
|
@ -18,9 +18,14 @@ impl Deref for Variables {
|
|||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Variables {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Variables {
|
||||
pub fn parse_from_json(data: &[u8]) -> Result<Self> {
|
||||
let value = serde_json::from_slice(data)?;
|
||||
pub(crate) fn parse_from_json(value: serde_json::Value) -> Result<Self> {
|
||||
let gql_value = json_value_to_gql_value(value);
|
||||
if let Value::Object(obj) = gql_value {
|
||||
Ok(Variables(obj))
|
||||
|
@ -50,12 +55,6 @@ fn json_value_to_gql_value(value: serde_json::Value) -> Value {
|
|||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Variables {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Data(HashMap<TypeId, Box<dyn Any + Sync + Send>, BuildHasherDefault<FnvHasher>>);
|
||||
|
||||
|
@ -63,10 +62,6 @@ impl Data {
|
|||
pub fn insert<D: Any + Send + Sync>(&mut self, data: D) {
|
||||
self.0.insert(TypeId::of::<D>(), Box::new(data));
|
||||
}
|
||||
|
||||
pub fn remove<D: Any + Send + Sync>(&mut self) {
|
||||
self.0.remove(&TypeId::of::<D>());
|
||||
}
|
||||
}
|
||||
|
||||
pub type ContextSelectionSet<'a> = ContextBase<'a, &'a SelectionSet>;
|
||||
|
@ -74,10 +69,10 @@ pub type Context<'a> = ContextBase<'a, &'a Field>;
|
|||
|
||||
pub struct ContextBase<'a, T> {
|
||||
pub(crate) item: T,
|
||||
pub(crate) data: Option<&'a Data>,
|
||||
pub(crate) variables: Option<&'a Variables>,
|
||||
pub(crate) variable_definitions: Option<&'a [VariableDefinition]>,
|
||||
pub(crate) registry: &'a Registry,
|
||||
pub(crate) data: &'a Data,
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for ContextBase<'a, T> {
|
||||
|
@ -93,19 +88,18 @@ impl<'a, T> ContextBase<'a, T> {
|
|||
pub fn with_item<R>(&self, item: R) -> ContextBase<'a, R> {
|
||||
ContextBase {
|
||||
item,
|
||||
data: self.data,
|
||||
variables: self.variables,
|
||||
variable_definitions: self.variable_definitions,
|
||||
registry: self.registry.clone(),
|
||||
data: self.data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data<D: Any + Send + Sync>(&self) -> Option<&D> {
|
||||
self.data.and_then(|data| {
|
||||
data.0
|
||||
.get(&TypeId::of::<D>())
|
||||
.and_then(|d| d.downcast_ref::<D>())
|
||||
})
|
||||
self.data
|
||||
.0
|
||||
.get(&TypeId::of::<D>())
|
||||
.and_then(|d| d.downcast_ref::<D>())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,10 +95,6 @@ impl PositionError {
|
|||
|
||||
impl Display for PositionError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}:{}: {}",
|
||||
self.position.line, self.position.column, self.inner
|
||||
)
|
||||
write!(f, "{}", self.inner)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
pub fn graphiql_source(graphql_endpoint_url: &str) -> String {
|
||||
let stylesheet_source = r#"
|
||||
<style>
|
||||
html, body, #app {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
"#;
|
||||
let fetcher_source = r#"
|
||||
<script>
|
||||
function graphQLFetcher(params) {
|
||||
return fetch(GRAPHQL_URL, {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
body: JSON.stringify(params)
|
||||
}).then(function (response) {
|
||||
return response.text();
|
||||
}).then(function (body) {
|
||||
try {
|
||||
return JSON.parse(body);
|
||||
} catch (error) {
|
||||
return body;
|
||||
}
|
||||
});
|
||||
}
|
||||
ReactDOM.render(
|
||||
React.createElement(GraphiQL, {
|
||||
fetcher: graphQLFetcher,
|
||||
}),
|
||||
document.querySelector('#app'));
|
||||
</script>
|
||||
"#;
|
||||
|
||||
format!(
|
||||
r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>GraphQL</title>
|
||||
{stylesheet_source}
|
||||
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/graphiql@0.17.2/graphiql.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/graphiql@0.17.2/graphiql.min.js"></script>
|
||||
<script>var GRAPHQL_URL = '{graphql_url}';</script>
|
||||
{fetcher_source}
|
||||
</body>
|
||||
</html>
|
||||
"#,
|
||||
graphql_url = graphql_endpoint_url,
|
||||
stylesheet_source = stylesheet_source,
|
||||
fetcher_source = fetcher_source
|
||||
)
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
mod graphiql_source;
|
||||
mod playground_source;
|
||||
|
||||
pub use graphiql_source::graphiql_source;
|
||||
pub use playground_source::playground_source;
|
||||
|
||||
use crate::{GQLObject, PositionError, Result, Schema, Variables};
|
||||
use serde::ser::SerializeMap;
|
||||
use serde::{Serialize, Serializer};
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Deserialize, Clone, PartialEq, Debug)]
|
||||
pub struct GQLRequest {
|
||||
pub query: String,
|
||||
#[serde(rename = "operationName")]
|
||||
pub operation_name: Option<String>,
|
||||
pub variables: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
impl GQLRequest {
|
||||
pub async fn execute<Query, Mutation>(self, schema: &Schema<Query, Mutation>) -> GQLResponse
|
||||
where
|
||||
Query: GQLObject + Send + Sync,
|
||||
Mutation: GQLObject + Send + Sync,
|
||||
{
|
||||
let vars = match self.variables {
|
||||
Some(value) => match Variables::parse_from_json(value) {
|
||||
Ok(vars) => Some(vars),
|
||||
Err(err) => return GQLResponse(Err(err)),
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
let query = schema.query(&self.query);
|
||||
let query = match &vars {
|
||||
Some(vars) => query.variables(vars),
|
||||
None => query,
|
||||
};
|
||||
let query = match &self.operation_name {
|
||||
Some(operation_name) => query.operator_name(operation_name),
|
||||
None => query,
|
||||
};
|
||||
GQLResponse(query.execute().await)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GQLResponse(Result<serde_json::Value>);
|
||||
|
||||
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")?;
|
||||
map.serialize_value(&res)?;
|
||||
map.end()
|
||||
}
|
||||
Err(err) => {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
map.serialize_key("errors")?;
|
||||
map.serialize_value(&[GQLError(err)])?;
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GQLError<'a>(&'a anyhow::Error);
|
||||
|
||||
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,
|
||||
{
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
|
||||
match self.0.downcast_ref::<PositionError>() {
|
||||
Some(err) => {
|
||||
map.serialize_key("message")?;
|
||||
map.serialize_value(&err.to_string())?;
|
||||
|
||||
map.serialize_key("locations")?;
|
||||
map.serialize_value(&[serde_json::json! ({
|
||||
"line": err.position.line,
|
||||
"column": err.position.column,
|
||||
})])?;
|
||||
}
|
||||
None => {
|
||||
map.serialize_key("message")?;
|
||||
map.serialize_value(&self.0.to_string())?;
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
let resp = GQLResponse(Ok(json!({"ok": true})));
|
||||
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}
|
||||
]
|
||||
}]
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,542 @@
|
|||
pub fn playground_source(graphql_endpoint_url: &str) -> String {
|
||||
r##"
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset=utf-8 />
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
|
||||
<title>GraphQL Playground</title>
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/graphql-playground-react/build/static/css/index.css" />
|
||||
<link rel="shortcut icon" href="//cdn.jsdelivr.net/npm/graphql-playground-react/build/favicon.png" />
|
||||
<script src="//cdn.jsdelivr.net/npm/graphql-playground-react/build/static/js/middleware.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<style type="text/css">
|
||||
html {
|
||||
font-family: "Open Sans", sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #172a3a;
|
||||
}
|
||||
|
||||
.playgroundIn {
|
||||
-webkit-animation: playgroundIn 0.5s ease-out forwards;
|
||||
animation: playgroundIn 0.5s ease-out forwards;
|
||||
}
|
||||
|
||||
@-webkit-keyframes playgroundIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(10px);
|
||||
-ms-transform: translateY(10px);
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes playgroundIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(10px);
|
||||
-ms-transform: translateY(10px);
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style type="text/css">
|
||||
.fadeOut {
|
||||
-webkit-animation: fadeOut 0.5s ease-out forwards;
|
||||
animation: fadeOut 0.5s ease-out forwards;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(-10px);
|
||||
-ms-transform: translateY(-10px);
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(-10px);
|
||||
-ms-transform: translateY(-10px);
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(-10px);
|
||||
-ms-transform: translateY(-10px);
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(-10px);
|
||||
-ms-transform: translateY(-10px);
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes appearIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
transform: translateY(0px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes appearIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
-webkit-transform: translateY(0px);
|
||||
-ms-transform: translateY(0px);
|
||||
transform: translateY(0px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
-webkit-transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes scaleIn {
|
||||
from {
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
}
|
||||
to {
|
||||
-webkit-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scaleIn {
|
||||
from {
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
}
|
||||
to {
|
||||
-webkit-transform: scale(1);
|
||||
-ms-transform: scale(1);
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes innerDrawIn {
|
||||
0% {
|
||||
stroke-dashoffset: 70;
|
||||
}
|
||||
50% {
|
||||
stroke-dashoffset: 140;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 210;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes innerDrawIn {
|
||||
0% {
|
||||
stroke-dashoffset: 70;
|
||||
}
|
||||
50% {
|
||||
stroke-dashoffset: 140;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 210;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes outerDrawIn {
|
||||
0% {
|
||||
stroke-dashoffset: 76;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 152;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes outerDrawIn {
|
||||
0% {
|
||||
stroke-dashoffset: 76;
|
||||
}
|
||||
100% {
|
||||
stroke-dashoffset: 152;
|
||||
}
|
||||
}
|
||||
|
||||
.hHWjkv {
|
||||
-webkit-transform-origin: 0px 0px;
|
||||
-ms-transform-origin: 0px 0px;
|
||||
transform-origin: 0px 0px;
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
-webkit-animation: scaleIn 0.25s linear forwards 0.2222222222222222s;
|
||||
animation: scaleIn 0.25s linear forwards 0.2222222222222222s;
|
||||
}
|
||||
|
||||
.gCDOzd {
|
||||
-webkit-transform-origin: 0px 0px;
|
||||
-ms-transform-origin: 0px 0px;
|
||||
transform-origin: 0px 0px;
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
-webkit-animation: scaleIn 0.25s linear forwards 0.4222222222222222s;
|
||||
animation: scaleIn 0.25s linear forwards 0.4222222222222222s;
|
||||
}
|
||||
|
||||
.hmCcxi {
|
||||
-webkit-transform-origin: 0px 0px;
|
||||
-ms-transform-origin: 0px 0px;
|
||||
transform-origin: 0px 0px;
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
-webkit-animation: scaleIn 0.25s linear forwards 0.6222222222222222s;
|
||||
animation: scaleIn 0.25s linear forwards 0.6222222222222222s;
|
||||
}
|
||||
|
||||
.eHamQi {
|
||||
-webkit-transform-origin: 0px 0px;
|
||||
-ms-transform-origin: 0px 0px;
|
||||
transform-origin: 0px 0px;
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
-webkit-animation: scaleIn 0.25s linear forwards 0.8222222222222223s;
|
||||
animation: scaleIn 0.25s linear forwards 0.8222222222222223s;
|
||||
}
|
||||
|
||||
.byhgGu {
|
||||
-webkit-transform-origin: 0px 0px;
|
||||
-ms-transform-origin: 0px 0px;
|
||||
transform-origin: 0px 0px;
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
-webkit-animation: scaleIn 0.25s linear forwards 1.0222222222222221s;
|
||||
animation: scaleIn 0.25s linear forwards 1.0222222222222221s;
|
||||
}
|
||||
|
||||
.llAKP {
|
||||
-webkit-transform-origin: 0px 0px;
|
||||
-ms-transform-origin: 0px 0px;
|
||||
transform-origin: 0px 0px;
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
-webkit-animation: scaleIn 0.25s linear forwards 1.2222222222222223s;
|
||||
animation: scaleIn 0.25s linear forwards 1.2222222222222223s;
|
||||
}
|
||||
|
||||
.bglIGM {
|
||||
-webkit-transform-origin: 64px 28px;
|
||||
-ms-transform-origin: 64px 28px;
|
||||
transform-origin: 64px 28px;
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
-webkit-animation: scaleIn 0.25s linear forwards 0.2222222222222222s;
|
||||
animation: scaleIn 0.25s linear forwards 0.2222222222222222s;
|
||||
}
|
||||
|
||||
.ksxRII {
|
||||
-webkit-transform-origin: 95.98500061035156px 46.510000228881836px;
|
||||
-ms-transform-origin: 95.98500061035156px 46.510000228881836px;
|
||||
transform-origin: 95.98500061035156px 46.510000228881836px;
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
-webkit-animation: scaleIn 0.25s linear forwards 0.4222222222222222s;
|
||||
animation: scaleIn 0.25s linear forwards 0.4222222222222222s;
|
||||
}
|
||||
|
||||
.cWrBmb {
|
||||
-webkit-transform-origin: 95.97162628173828px 83.4900016784668px;
|
||||
-ms-transform-origin: 95.97162628173828px 83.4900016784668px;
|
||||
transform-origin: 95.97162628173828px 83.4900016784668px;
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
-webkit-animation: scaleIn 0.25s linear forwards 0.6222222222222222s;
|
||||
animation: scaleIn 0.25s linear forwards 0.6222222222222222s;
|
||||
}
|
||||
|
||||
.Wnusb {
|
||||
-webkit-transform-origin: 64px 101.97999572753906px;
|
||||
-ms-transform-origin: 64px 101.97999572753906px;
|
||||
transform-origin: 64px 101.97999572753906px;
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
-webkit-animation: scaleIn 0.25s linear forwards 0.8222222222222223s;
|
||||
animation: scaleIn 0.25s linear forwards 0.8222222222222223s;
|
||||
}
|
||||
|
||||
.bfPqf {
|
||||
-webkit-transform-origin: 32.03982162475586px 83.4900016784668px;
|
||||
-ms-transform-origin: 32.03982162475586px 83.4900016784668px;
|
||||
transform-origin: 32.03982162475586px 83.4900016784668px;
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
-webkit-animation: scaleIn 0.25s linear forwards 1.0222222222222221s;
|
||||
animation: scaleIn 0.25s linear forwards 1.0222222222222221s;
|
||||
}
|
||||
|
||||
.edRCTN {
|
||||
-webkit-transform-origin: 32.033552169799805px 46.510000228881836px;
|
||||
-ms-transform-origin: 32.033552169799805px 46.510000228881836px;
|
||||
transform-origin: 32.033552169799805px 46.510000228881836px;
|
||||
-webkit-transform: scale(0);
|
||||
-ms-transform: scale(0);
|
||||
transform: scale(0);
|
||||
-webkit-animation: scaleIn 0.25s linear forwards 1.2222222222222223s;
|
||||
animation: scaleIn 0.25s linear forwards 1.2222222222222223s;
|
||||
}
|
||||
|
||||
.iEGVWn {
|
||||
opacity: 0;
|
||||
stroke-dasharray: 76;
|
||||
-webkit-animation: outerDrawIn 0.5s ease-out forwards 0.3333333333333333s, appearIn 0.1s ease-out forwards 0.3333333333333333s;
|
||||
animation: outerDrawIn 0.5s ease-out forwards 0.3333333333333333s, appearIn 0.1s ease-out forwards 0.3333333333333333s;
|
||||
-webkit-animation-iteration-count: 1, 1;
|
||||
animation-iteration-count: 1, 1;
|
||||
}
|
||||
|
||||
.bsocdx {
|
||||
opacity: 0;
|
||||
stroke-dasharray: 76;
|
||||
-webkit-animation: outerDrawIn 0.5s ease-out forwards 0.5333333333333333s, appearIn 0.1s ease-out forwards 0.5333333333333333s;
|
||||
animation: outerDrawIn 0.5s ease-out forwards 0.5333333333333333s, appearIn 0.1s ease-out forwards 0.5333333333333333s;
|
||||
-webkit-animation-iteration-count: 1, 1;
|
||||
animation-iteration-count: 1, 1;
|
||||
}
|
||||
|
||||
.jAZXmP {
|
||||
opacity: 0;
|
||||
stroke-dasharray: 76;
|
||||
-webkit-animation: outerDrawIn 0.5s ease-out forwards 0.7333333333333334s, appearIn 0.1s ease-out forwards 0.7333333333333334s;
|
||||
animation: outerDrawIn 0.5s ease-out forwards 0.7333333333333334s, appearIn 0.1s ease-out forwards 0.7333333333333334s;
|
||||
-webkit-animation-iteration-count: 1, 1;
|
||||
animation-iteration-count: 1, 1;
|
||||
}
|
||||
|
||||
.hSeArx {
|
||||
opacity: 0;
|
||||
stroke-dasharray: 76;
|
||||
-webkit-animation: outerDrawIn 0.5s ease-out forwards 0.9333333333333333s, appearIn 0.1s ease-out forwards 0.9333333333333333s;
|
||||
animation: outerDrawIn 0.5s ease-out forwards 0.9333333333333333s, appearIn 0.1s ease-out forwards 0.9333333333333333s;
|
||||
-webkit-animation-iteration-count: 1, 1;
|
||||
animation-iteration-count: 1, 1;
|
||||
}
|
||||
|
||||
.bVgqGk {
|
||||
opacity: 0;
|
||||
stroke-dasharray: 76;
|
||||
-webkit-animation: outerDrawIn 0.5s ease-out forwards 1.1333333333333333s, appearIn 0.1s ease-out forwards 1.1333333333333333s;
|
||||
animation: outerDrawIn 0.5s ease-out forwards 1.1333333333333333s, appearIn 0.1s ease-out forwards 1.1333333333333333s;
|
||||
-webkit-animation-iteration-count: 1, 1;
|
||||
animation-iteration-count: 1, 1;
|
||||
}
|
||||
|
||||
.hEFqBt {
|
||||
opacity: 0;
|
||||
stroke-dasharray: 76;
|
||||
-webkit-animation: outerDrawIn 0.5s ease-out forwards 1.3333333333333333s, appearIn 0.1s ease-out forwards 1.3333333333333333s;
|
||||
animation: outerDrawIn 0.5s ease-out forwards 1.3333333333333333s, appearIn 0.1s ease-out forwards 1.3333333333333333s;
|
||||
-webkit-animation-iteration-count: 1, 1;
|
||||
animation-iteration-count: 1, 1;
|
||||
}
|
||||
|
||||
.dzEKCM {
|
||||
opacity: 0;
|
||||
stroke-dasharray: 70;
|
||||
-webkit-animation: innerDrawIn 1s ease-in-out forwards 1.3666666666666667s, appearIn 0.1s linear forwards 1.3666666666666667s;
|
||||
animation: innerDrawIn 1s ease-in-out forwards 1.3666666666666667s, appearIn 0.1s linear forwards 1.3666666666666667s;
|
||||
-webkit-animation-iteration-count: infinite, 1;
|
||||
animation-iteration-count: infinite, 1;
|
||||
}
|
||||
|
||||
.DYnPx {
|
||||
opacity: 0;
|
||||
stroke-dasharray: 70;
|
||||
-webkit-animation: innerDrawIn 1s ease-in-out forwards 1.5333333333333332s, appearIn 0.1s linear forwards 1.5333333333333332s;
|
||||
animation: innerDrawIn 1s ease-in-out forwards 1.5333333333333332s, appearIn 0.1s linear forwards 1.5333333333333332s;
|
||||
-webkit-animation-iteration-count: infinite, 1;
|
||||
animation-iteration-count: infinite, 1;
|
||||
}
|
||||
|
||||
.hjPEAQ {
|
||||
opacity: 0;
|
||||
stroke-dasharray: 70;
|
||||
-webkit-animation: innerDrawIn 1s ease-in-out forwards 1.7000000000000002s, appearIn 0.1s linear forwards 1.7000000000000002s;
|
||||
animation: innerDrawIn 1s ease-in-out forwards 1.7000000000000002s, appearIn 0.1s linear forwards 1.7000000000000002s;
|
||||
-webkit-animation-iteration-count: infinite, 1;
|
||||
animation-iteration-count: infinite, 1;
|
||||
}
|
||||
|
||||
#loading-wrapper {
|
||||
position: absolute;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
margin-bottom: 20px;
|
||||
opacity: 0;
|
||||
-webkit-animation: fadeIn 0.5s ease-out forwards;
|
||||
animation: fadeIn 0.5s ease-out forwards;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 32px;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
opacity: 0;
|
||||
-webkit-animation: fadeIn 0.5s ease-out forwards;
|
||||
animation: fadeIn 0.5s ease-out forwards;
|
||||
}
|
||||
|
||||
.dGfHfc {
|
||||
font-weight: 400;
|
||||
}
|
||||
</style>
|
||||
<div id="loading-wrapper">
|
||||
<svg class="logo" viewBox="0 0 128 128" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>GraphQL Playground Logo</title>
|
||||
<defs>
|
||||
<linearGradient id="linearGradient-1" x1="4.86%" x2="96.21%" y1="0%" y2="99.66%">
|
||||
<stop stop-color="#E00082" stop-opacity=".8" offset="0%"></stop>
|
||||
<stop stop-color="#E00082" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<rect id="Gradient" width="127.96" height="127.96" y="1" fill="url(#linearGradient-1)" rx="4"></rect>
|
||||
<path id="Border" fill="#E00082" fill-rule="nonzero" d="M4.7 2.84c-1.58 0-2.86 1.28-2.86 2.85v116.57c0 1.57 1.28 2.84 2.85 2.84h116.57c1.57 0 2.84-1.26 2.84-2.83V5.67c0-1.55-1.26-2.83-2.83-2.83H4.67zM4.7 0h116.58c3.14 0 5.68 2.55 5.68 5.7v116.58c0 3.14-2.54 5.68-5.68 5.68H4.68c-3.13 0-5.68-2.54-5.68-5.68V5.68C-1 2.56 1.55 0 4.7 0z"></path>
|
||||
<path class="bglIGM" x="64" y="28" fill="#fff" d="M64 36c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8" style="transform: translate(100px, 100px);"></path>
|
||||
<path class="ksxRII" x="95.98500061035156" y="46.510000228881836" fill="#fff" d="M89.04 50.52c-2.2-3.84-.9-8.73 2.94-10.96 3.83-2.2 8.72-.9 10.95 2.94 2.2 3.84.9 8.73-2.94 10.96-3.85 2.2-8.76.9-10.97-2.94"
|
||||
style="transform: translate(100px, 100px);"></path>
|
||||
<path class="cWrBmb" x="95.97162628173828" y="83.4900016784668" fill="#fff" d="M102.9 87.5c-2.2 3.84-7.1 5.15-10.94 2.94-3.84-2.2-5.14-7.12-2.94-10.96 2.2-3.84 7.12-5.15 10.95-2.94 3.86 2.23 5.16 7.12 2.94 10.96"
|
||||
style="transform: translate(100px, 100px);"></path>
|
||||
<path class="Wnusb" x="64" y="101.97999572753906" fill="#fff" d="M64 110c-4.43 0-8-3.6-8-8.02 0-4.44 3.57-8.02 8-8.02s8 3.58 8 8.02c0 4.4-3.57 8.02-8 8.02"
|
||||
style="transform: translate(100px, 100px);"></path>
|
||||
<path class="bfPqf" x="32.03982162475586" y="83.4900016784668" fill="#fff" d="M25.1 87.5c-2.2-3.84-.9-8.73 2.93-10.96 3.83-2.2 8.72-.9 10.95 2.94 2.2 3.84.9 8.73-2.94 10.96-3.85 2.2-8.74.9-10.95-2.94"
|
||||
style="transform: translate(100px, 100px);"></path>
|
||||
<path class="edRCTN" x="32.033552169799805" y="46.510000228881836" fill="#fff" d="M38.96 50.52c-2.2 3.84-7.12 5.15-10.95 2.94-3.82-2.2-5.12-7.12-2.92-10.96 2.2-3.84 7.12-5.15 10.95-2.94 3.83 2.23 5.14 7.12 2.94 10.96"
|
||||
style="transform: translate(100px, 100px);"></path>
|
||||
<path class="iEGVWn" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M63.55 27.5l32.9 19-32.9-19z"></path>
|
||||
<path class="bsocdx" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M96 46v38-38z"></path>
|
||||
<path class="jAZXmP" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M96.45 84.5l-32.9 19 32.9-19z"></path>
|
||||
<path class="hSeArx" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M64.45 103.5l-32.9-19 32.9 19z"></path>
|
||||
<path class="bVgqGk" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M32 84V46v38z"></path>
|
||||
<path class="hEFqBt" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M31.55 46.5l32.9-19-32.9 19z"></path>
|
||||
<path class="dzEKCM" id="Triangle-Bottom" stroke="#fff" stroke-width="4" d="M30 84h70" stroke-linecap="round"></path>
|
||||
<path class="DYnPx" id="Triangle-Left" stroke="#fff" stroke-width="4" d="M65 26L30 87" stroke-linecap="round"></path>
|
||||
<path class="hjPEAQ" id="Triangle-Right" stroke="#fff" stroke-width="4" d="M98 87L63 26" stroke-linecap="round"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="text">Loading
|
||||
<span class="dGfHfc">GraphQL Playground</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="root" />
|
||||
<script type="text/javascript">
|
||||
window.addEventListener('load', function (event) {
|
||||
|
||||
const loadingWrapper = document.getElementById('loading-wrapper');
|
||||
loadingWrapper.classList.add('fadeOut');
|
||||
|
||||
|
||||
const root = document.getElementById('root');
|
||||
root.classList.add('playgroundIn');
|
||||
|
||||
GraphQLPlayground.init(root, { endpoint: 'GRAPHQL_URL' })
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"##.replace("GRAPHQL_URL", graphql_endpoint_url)
|
||||
}
|
12
src/lib.rs
12
src/lib.rs
|
@ -50,6 +50,8 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate thiserror;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
mod base;
|
||||
mod context;
|
||||
|
@ -68,24 +70,24 @@ pub use graphql_parser;
|
|||
#[doc(hidden)]
|
||||
pub use serde_json;
|
||||
|
||||
pub mod http;
|
||||
|
||||
pub use async_graphql_derive::{Enum, InputObject, Object};
|
||||
pub use base::Scalar;
|
||||
pub use context::{Context, ContextBase, Data, Variables};
|
||||
pub use base::{GQLInputObject, GQLInputValue, GQLObject, GQLOutputValue, GQLType};
|
||||
pub use context::{Context, ContextBase, Variables};
|
||||
pub use error::{ErrorWithPosition, PositionError, QueryError, QueryParseError};
|
||||
pub use graphql_parser::query::Value;
|
||||
pub use scalars::ID;
|
||||
pub use schema::{QueryBuilder, Schema};
|
||||
pub use types::GQLEmptyMutation;
|
||||
pub use types::{GQLEnum, GQLEnumItem};
|
||||
|
||||
pub type Result<T> = anyhow::Result<T>;
|
||||
pub type Error = anyhow::Error;
|
||||
|
||||
// internal types
|
||||
#[doc(hidden)]
|
||||
pub use base::{GQLInputObject, GQLInputValue, GQLObject, GQLOutputValue, GQLType};
|
||||
#[doc(hidden)]
|
||||
pub use context::ContextSelectionSet;
|
||||
#[doc(hidden)]
|
||||
pub mod registry;
|
||||
#[doc(hidden)]
|
||||
pub use types::{GQLEnum, GQLEnumItem};
|
||||
|
|
|
@ -21,7 +21,13 @@ use async_graphql_derive::Object;
|
|||
field(
|
||||
name = "mutationType",
|
||||
desc = "If this server supports mutation, the type that mutation operations will be rooted at.",
|
||||
type = "__Type",
|
||||
type = "Option<__Type>",
|
||||
owned
|
||||
),
|
||||
field(
|
||||
name = "subscriptionType",
|
||||
desc = "If this server support subscription, the type that subscription operations will be rooted at.",
|
||||
type = "Option<__Type>",
|
||||
owned
|
||||
),
|
||||
field(
|
||||
|
@ -55,11 +61,15 @@ impl<'a> __SchemaFields for __Schema<'a> {
|
|||
))
|
||||
}
|
||||
|
||||
async fn mutation_type<'b>(&'b self, _: &Context<'_>) -> Result<__Type<'b>> {
|
||||
Ok(__Type::new_simple(
|
||||
async fn mutation_type<'b>(&'b self, _: &Context<'_>) -> Result<Option<__Type<'b>>> {
|
||||
Ok(Some(__Type::new_simple(
|
||||
self.registry,
|
||||
&self.registry.types[self.mutation_type],
|
||||
))
|
||||
)))
|
||||
}
|
||||
|
||||
async fn subscription_type<'b>(&'b self, _: &Context<'_>) -> Result<Option<__Type<'b>>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn directives<'b>(&'b self, _: &Context<'_>) -> Result<Vec<__Directive<'b>>> {
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
use crate::context::Data;
|
||||
use crate::model::__DirectiveLocation;
|
||||
use crate::registry::{Directive, InputValue, Registry};
|
||||
use crate::types::QueryRoot;
|
||||
use crate::{
|
||||
ContextBase, Data, ErrorWithPosition, GQLObject, GQLOutputValue, GQLType, QueryError,
|
||||
ContextBase, ErrorWithPosition, GQLObject, GQLOutputValue, GQLType, QueryError,
|
||||
QueryParseError, Result, Variables,
|
||||
};
|
||||
use graphql_parser::parse_query;
|
||||
use graphql_parser::query::{Definition, OperationDefinition};
|
||||
use serde::export::PhantomData;
|
||||
use std::any::Any;
|
||||
|
||||
pub struct Schema<Query, Mutation> {
|
||||
mark_query: PhantomData<Query>,
|
||||
mark_mutation: PhantomData<Mutation>,
|
||||
query: QueryRoot<Query>,
|
||||
mutation: Mutation,
|
||||
registry: Registry,
|
||||
data: Data,
|
||||
}
|
||||
|
||||
impl<Query: GQLObject, Mutation: GQLObject> Schema<Query, Mutation> {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(query: Query, mutation: Mutation) -> Self {
|
||||
let mut registry = Registry::default();
|
||||
|
||||
registry.add_directive(Directive {
|
||||
|
@ -53,42 +55,43 @@ impl<Query: GQLObject, Mutation: GQLObject> Schema<Query, Mutation> {
|
|||
Mutation::create_type_info(&mut registry);
|
||||
|
||||
Self {
|
||||
mark_query: PhantomData,
|
||||
mark_mutation: PhantomData,
|
||||
registry,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query<'a>(
|
||||
&'a self,
|
||||
query: Query,
|
||||
mutation: Mutation,
|
||||
query_source: &'a str,
|
||||
) -> QueryBuilder<'a, Query, Mutation> {
|
||||
QueryBuilder {
|
||||
query: QueryRoot {
|
||||
inner: query,
|
||||
query_type: Query::type_name().to_string(),
|
||||
mutation_type: Mutation::type_name().to_string(),
|
||||
},
|
||||
mutation,
|
||||
registry,
|
||||
data: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data<D: Any + Send + Sync>(mut self, data: D) -> Self {
|
||||
self.data.insert(Box::new(data));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn query<'a>(&'a self, query_source: &'a str) -> QueryBuilder<'a, Query, Mutation> {
|
||||
QueryBuilder {
|
||||
query: &self.query,
|
||||
mutation: &self.mutation,
|
||||
registry: &self.registry,
|
||||
query_source,
|
||||
operation_name: None,
|
||||
variables: None,
|
||||
data: None,
|
||||
data: &self.data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QueryBuilder<'a, Query, Mutation> {
|
||||
query: QueryRoot<Query>,
|
||||
mutation: Mutation,
|
||||
query: &'a QueryRoot<Query>,
|
||||
mutation: &'a Mutation,
|
||||
registry: &'a Registry,
|
||||
query_source: &'a str,
|
||||
operation_name: Option<&'a str>,
|
||||
variables: Option<&'a Variables>,
|
||||
data: Option<&'a Data>,
|
||||
data: &'a Data,
|
||||
}
|
||||
|
||||
impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
|
||||
|
@ -106,13 +109,6 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn data(self, data: &'a Data) -> Self {
|
||||
QueryBuilder {
|
||||
data: Some(data),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(self) -> Result<serde_json::Value>
|
||||
where
|
||||
Query: GQLObject + Send + Sync,
|
||||
|
@ -127,10 +123,10 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
|
|||
if self.operation_name.is_none() {
|
||||
let ctx = ContextBase {
|
||||
item: selection_set,
|
||||
data: self.data.as_deref(),
|
||||
variables: self.variables.as_deref(),
|
||||
variable_definitions: None,
|
||||
registry: &self.registry,
|
||||
data: self.data,
|
||||
};
|
||||
return self.query.resolve(&ctx).await;
|
||||
}
|
||||
|
@ -141,10 +137,10 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
|
|||
{
|
||||
let ctx = ContextBase {
|
||||
item: &query.selection_set,
|
||||
data: self.data.as_deref(),
|
||||
variables: self.variables.as_deref(),
|
||||
variable_definitions: Some(&query.variable_definitions),
|
||||
registry: self.registry.clone(),
|
||||
data: self.data,
|
||||
};
|
||||
return self.query.resolve(&ctx).await;
|
||||
}
|
||||
|
@ -155,10 +151,10 @@ impl<'a, Query, Mutation> QueryBuilder<'a, Query, Mutation> {
|
|||
{
|
||||
let ctx = ContextBase {
|
||||
item: &mutation.selection_set,
|
||||
data: self.data.as_deref(),
|
||||
variables: self.variables.as_deref(),
|
||||
variable_definitions: Some(&mutation.variable_definitions),
|
||||
registry: self.registry.clone(),
|
||||
data: self.data,
|
||||
};
|
||||
return self.mutation.resolve(&ctx).await;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue