Merge branch 'async-graphql-v4' of github.com:async-graphql/async-graphql into async-graphql-v4
This commit is contained in:
commit
2cec61aea2
|
@ -37,7 +37,7 @@ futures-util = { version = "0.3.0", default-features = false, features = ["io",
|
||||||
indexmap = "1.6.2"
|
indexmap = "1.6.2"
|
||||||
once_cell = "1.7.2"
|
once_cell = "1.7.2"
|
||||||
pin-project-lite = "0.2.6"
|
pin-project-lite = "0.2.6"
|
||||||
regex = "1.4.5"
|
regex = "1.5.5"
|
||||||
serde = { version = "1.0.125", features = ["derive"] }
|
serde = { version = "1.0.125", features = ["derive"] }
|
||||||
serde_json = "1.0.64"
|
serde_json = "1.0.64"
|
||||||
thiserror = "1.0.24"
|
thiserror = "1.0.24"
|
||||||
|
@ -53,7 +53,7 @@ fast_chemail = "0.9.6"
|
||||||
# Feature optional dependencies
|
# Feature optional dependencies
|
||||||
bson = { version = "2.0.0", optional = true, features = ["chrono-0_4"] }
|
bson = { version = "2.0.0", optional = true, features = ["chrono-0_4"] }
|
||||||
chrono = { version = "0.4.19", optional = true, default-features = false, features = ["clock", "std"] }
|
chrono = { version = "0.4.19", optional = true, default-features = false, features = ["clock", "std"] }
|
||||||
chrono-tz = { version = "0.5.3", optional = true }
|
chrono-tz = { version = "0.6.1", optional = true }
|
||||||
hashbrown = { version = "0.12.0", optional = true }
|
hashbrown = { version = "0.12.0", optional = true }
|
||||||
iso8601-duration = { version = "0.1.0", optional = true }
|
iso8601-duration = { version = "0.1.0", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
|
@ -20,6 +20,6 @@ bytes = "1.0.1"
|
||||||
http-body = "0.4.2"
|
http-body = "0.4.2"
|
||||||
serde_json = "1.0.66"
|
serde_json = "1.0.66"
|
||||||
serde_urlencoded = "0.7.0"
|
serde_urlencoded = "0.7.0"
|
||||||
tokio-util = { version = "0.6.7", features = ["io", "compat"] }
|
tokio-util = { version = "0.7.1", features = ["io", "compat"] }
|
||||||
futures-util = "0.3.0"
|
futures-util = "0.3.0"
|
||||||
tower-service = "0.3"
|
tower-service = "0.3"
|
||||||
|
|
|
@ -13,11 +13,11 @@ use http::HeaderValue;
|
||||||
use serde::ser::{SerializeSeq, Serializer};
|
use serde::ser::{SerializeSeq, Serializer};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::extensions::Extensions;
|
|
||||||
use crate::parser::types::{
|
use crate::parser::types::{
|
||||||
Directive, Field, FragmentDefinition, OperationDefinition, Selection, SelectionSet,
|
Directive, Field, FragmentDefinition, OperationDefinition, Selection, SelectionSet,
|
||||||
};
|
};
|
||||||
use crate::schema::SchemaEnv;
|
use crate::schema::SchemaEnv;
|
||||||
|
use crate::{extensions::Extensions, schema::IntrospectionMode};
|
||||||
use crate::{
|
use crate::{
|
||||||
Error, InputType, Lookahead, Name, OneofObjectType, PathSegment, Pos, Positioned, Result,
|
Error, InputType, Lookahead, Name, OneofObjectType, PathSegment, Pos, Positioned, Result,
|
||||||
ServerError, ServerResult, UploadValue, Value,
|
ServerError, ServerResult, UploadValue, Value,
|
||||||
|
@ -244,7 +244,7 @@ pub struct QueryEnvInner {
|
||||||
pub session_data: Arc<Data>,
|
pub session_data: Arc<Data>,
|
||||||
pub ctx_data: Arc<Data>,
|
pub ctx_data: Arc<Data>,
|
||||||
pub http_headers: Mutex<HeaderMap>,
|
pub http_headers: Mutex<HeaderMap>,
|
||||||
pub disable_introspection: bool,
|
pub introspection_mode: IntrospectionMode,
|
||||||
pub errors: Mutex<Vec<ServerError>>,
|
pub errors: Mutex<Vec<ServerError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,14 @@ use indexmap::map::IndexMap;
|
||||||
use indexmap::set::IndexSet;
|
use indexmap::set::IndexSet;
|
||||||
|
|
||||||
pub use crate::model::__DirectiveLocation;
|
pub use crate::model::__DirectiveLocation;
|
||||||
use crate::parser::types::{
|
|
||||||
BaseType as ParsedBaseType, Field, Type as ParsedType, VariableDefinition,
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
model, Any, Context, InputType, OutputType, Positioned, ServerResult, SubscriptionType, Value,
|
model, Any, Context, InputType, OutputType, Positioned, ServerResult, SubscriptionType, Value,
|
||||||
VisitorContext,
|
VisitorContext,
|
||||||
};
|
};
|
||||||
|
use crate::{
|
||||||
|
parser::types::{BaseType as ParsedBaseType, Field, Type as ParsedType, VariableDefinition},
|
||||||
|
schema::IntrospectionMode,
|
||||||
|
};
|
||||||
|
|
||||||
pub use cache_control::CacheControl;
|
pub use cache_control::CacheControl;
|
||||||
|
|
||||||
|
@ -401,7 +402,7 @@ pub struct Registry {
|
||||||
pub query_type: String,
|
pub query_type: String,
|
||||||
pub mutation_type: Option<String>,
|
pub mutation_type: Option<String>,
|
||||||
pub subscription_type: Option<String>,
|
pub subscription_type: Option<String>,
|
||||||
pub disable_introspection: bool,
|
pub introspection_mode: IntrospectionMode,
|
||||||
pub enable_federation: bool,
|
pub enable_federation: bool,
|
||||||
pub federation_subscription: bool,
|
pub federation_subscription: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
use crate::parser::parse_query;
|
use crate::parser::parse_query;
|
||||||
use crate::parser::types::ExecutableDocument;
|
use crate::parser::types::ExecutableDocument;
|
||||||
|
use crate::schema::IntrospectionMode;
|
||||||
use crate::{Data, ParseRequestError, ServerError, UploadValue, Value, Variables};
|
use crate::{Data, ParseRequestError, ServerError, UploadValue, Value, Variables};
|
||||||
|
|
||||||
/// GraphQL request.
|
/// GraphQL request.
|
||||||
|
@ -15,6 +16,7 @@ use crate::{Data, ParseRequestError, ServerError, UploadValue, Value, Variables}
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[non_exhaustive]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
/// The query source of the request.
|
/// The query source of the request.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -43,11 +45,17 @@ pub struct Request {
|
||||||
pub extensions: HashMap<String, Value>,
|
pub extensions: HashMap<String, Value>,
|
||||||
|
|
||||||
/// Disable introspection queries for this request.
|
/// Disable introspection queries for this request.
|
||||||
|
/// This option has priority over `introspection_mode` when set to true.
|
||||||
|
/// `introspection_mode` has priority when `disable_introspection` set to `false`.
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub disable_introspection: bool,
|
pub disable_introspection: bool,
|
||||||
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub(crate) parsed_query: Option<ExecutableDocument>,
|
pub(crate) parsed_query: Option<ExecutableDocument>,
|
||||||
|
|
||||||
|
/// Sets the introspection mode for this request (defaults to [IntrospectionMode::Enabled]).
|
||||||
|
#[serde(skip)]
|
||||||
|
pub introspection_mode: IntrospectionMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
|
@ -62,6 +70,7 @@ impl Request {
|
||||||
extensions: Default::default(),
|
extensions: Default::default(),
|
||||||
disable_introspection: false,
|
disable_introspection: false,
|
||||||
parsed_query: None,
|
parsed_query: None,
|
||||||
|
introspection_mode: IntrospectionMode::Enabled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +100,15 @@ impl Request {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn disable_introspection(mut self) -> Self {
|
pub fn disable_introspection(mut self) -> Self {
|
||||||
self.disable_introspection = true;
|
self.disable_introspection = true;
|
||||||
|
self.introspection_mode = IntrospectionMode::Disabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only allow introspection queries for this request.
|
||||||
|
#[must_use]
|
||||||
|
pub fn only_introspection(mut self) -> Self {
|
||||||
|
self.disable_introspection = false;
|
||||||
|
self.introspection_mode = IntrospectionMode::IntrospectionOnly;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,6 +251,17 @@ impl BatchRequest {
|
||||||
pub fn disable_introspection(mut self) -> Self {
|
pub fn disable_introspection(mut self) -> Self {
|
||||||
for request in self.iter_mut() {
|
for request in self.iter_mut() {
|
||||||
request.disable_introspection = true;
|
request.disable_introspection = true;
|
||||||
|
request.introspection_mode = IntrospectionMode::Disabled;
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only allow introspection queries for each request.
|
||||||
|
#[must_use]
|
||||||
|
pub fn introspection_only(mut self) -> Self {
|
||||||
|
for request in self.iter_mut() {
|
||||||
|
request.disable_introspection = false;
|
||||||
|
request.introspection_mode = IntrospectionMode::IntrospectionOnly;
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ use std::sync::Arc;
|
||||||
use futures_util::stream::{self, Stream, StreamExt};
|
use futures_util::stream::{self, Stream, StreamExt};
|
||||||
use indexmap::map::IndexMap;
|
use indexmap::map::IndexMap;
|
||||||
|
|
||||||
use crate::context::{Data, QueryEnvInner};
|
|
||||||
use crate::custom_directive::CustomDirectiveFactory;
|
use crate::custom_directive::CustomDirectiveFactory;
|
||||||
use crate::extensions::{ExtensionFactory, Extensions};
|
use crate::extensions::{ExtensionFactory, Extensions};
|
||||||
use crate::model::__DirectiveLocation;
|
use crate::model::__DirectiveLocation;
|
||||||
|
@ -17,11 +16,29 @@ use crate::resolver_utils::{resolve_container, resolve_container_serial};
|
||||||
use crate::subscription::collect_subscription_streams;
|
use crate::subscription::collect_subscription_streams;
|
||||||
use crate::types::QueryRoot;
|
use crate::types::QueryRoot;
|
||||||
use crate::validation::{check_rules, ValidationMode};
|
use crate::validation::{check_rules, ValidationMode};
|
||||||
|
use crate::{
|
||||||
|
context::{Data, QueryEnvInner},
|
||||||
|
EmptyMutation, EmptySubscription,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
BatchRequest, BatchResponse, CacheControl, ContextBase, InputType, ObjectType, OutputType,
|
BatchRequest, BatchResponse, CacheControl, ContextBase, InputType, ObjectType, OutputType,
|
||||||
QueryEnv, Request, Response, ServerError, SubscriptionType, Variables, ID,
|
QueryEnv, Request, Response, ServerError, SubscriptionType, Variables, ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Introspection mode
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum IntrospectionMode {
|
||||||
|
IntrospectionOnly,
|
||||||
|
Enabled,
|
||||||
|
Disabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for IntrospectionMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
IntrospectionMode::Enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Schema builder
|
/// Schema builder
|
||||||
pub struct SchemaBuilder<Query, Mutation, Subscription> {
|
pub struct SchemaBuilder<Query, Mutation, Subscription> {
|
||||||
validation_mode: ValidationMode,
|
validation_mode: ValidationMode,
|
||||||
|
@ -58,7 +75,14 @@ impl<Query, Mutation, Subscription> SchemaBuilder<Query, Mutation, Subscription>
|
||||||
/// Disable introspection queries.
|
/// Disable introspection queries.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn disable_introspection(mut self) -> Self {
|
pub fn disable_introspection(mut self) -> Self {
|
||||||
self.registry.disable_introspection = true;
|
self.registry.introspection_mode = IntrospectionMode::Disabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only process introspection queries, everything else is processed as an error.
|
||||||
|
#[must_use]
|
||||||
|
pub fn introspection_only(mut self) -> Self {
|
||||||
|
self.registry.introspection_mode = IntrospectionMode::IntrospectionOnly;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +327,7 @@ where
|
||||||
} else {
|
} else {
|
||||||
Some(Subscription::type_name().to_string())
|
Some(Subscription::type_name().to_string())
|
||||||
},
|
},
|
||||||
disable_introspection: false,
|
introspection_mode: IntrospectionMode::Enabled,
|
||||||
enable_federation: false,
|
enable_federation: false,
|
||||||
federation_subscription: false,
|
federation_subscription: false,
|
||||||
};
|
};
|
||||||
|
@ -515,7 +539,11 @@ where
|
||||||
session_data,
|
session_data,
|
||||||
ctx_data: query_data,
|
ctx_data: query_data,
|
||||||
http_headers: Default::default(),
|
http_headers: Default::default(),
|
||||||
disable_introspection: request.disable_introspection,
|
introspection_mode: if request.disable_introspection {
|
||||||
|
IntrospectionMode::Disabled
|
||||||
|
} else {
|
||||||
|
request.introspection_mode
|
||||||
|
},
|
||||||
errors: Default::default(),
|
errors: Default::default(),
|
||||||
};
|
};
|
||||||
Ok((QueryEnv::new(env), validation_result.cache_control))
|
Ok((QueryEnv::new(env), validation_result.cache_control))
|
||||||
|
@ -532,7 +560,15 @@ where
|
||||||
|
|
||||||
let res = match &env.operation.node.ty {
|
let res = match &env.operation.node.ty {
|
||||||
OperationType::Query => resolve_container(&ctx, &self.query).await,
|
OperationType::Query => resolve_container(&ctx, &self.query).await,
|
||||||
OperationType::Mutation => resolve_container_serial(&ctx, &self.mutation).await,
|
OperationType::Mutation => {
|
||||||
|
if self.env.registry.introspection_mode == IntrospectionMode::IntrospectionOnly
|
||||||
|
|| env.introspection_mode == IntrospectionMode::IntrospectionOnly
|
||||||
|
{
|
||||||
|
resolve_container_serial(&ctx, &EmptyMutation).await
|
||||||
|
} else {
|
||||||
|
resolve_container_serial(&ctx, &self.mutation).await
|
||||||
|
}
|
||||||
|
}
|
||||||
OperationType::Subscription => Err(ServerError::new(
|
OperationType::Subscription => Err(ServerError::new(
|
||||||
"Subscriptions are not supported on this transport.",
|
"Subscriptions are not supported on this transport.",
|
||||||
None,
|
None,
|
||||||
|
@ -627,7 +663,15 @@ where
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut streams = Vec::new();
|
let mut streams = Vec::new();
|
||||||
if let Err(err) = collect_subscription_streams(&ctx, &schema.subscription, &mut streams) {
|
let collect_result = if schema.env.registry.introspection_mode
|
||||||
|
== IntrospectionMode::IntrospectionOnly
|
||||||
|
|| env.introspection_mode == IntrospectionMode::IntrospectionOnly
|
||||||
|
{
|
||||||
|
collect_subscription_streams(&ctx, &EmptySubscription, &mut streams)
|
||||||
|
} else {
|
||||||
|
collect_subscription_streams(&ctx, &schema.subscription, &mut streams)
|
||||||
|
};
|
||||||
|
if let Err(err) = collect_result {
|
||||||
yield Response::from_errors(vec![err]);
|
yield Response::from_errors(vec![err]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
src/types/external/bson.rs
vendored
17
src/types/external/bson.rs
vendored
|
@ -2,6 +2,8 @@ use bson::{oid::ObjectId, Bson, Document};
|
||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
use bson::DateTime as UtcDateTime;
|
use bson::DateTime as UtcDateTime;
|
||||||
|
#[cfg(feature = "bson-uuid")]
|
||||||
|
use bson::Uuid;
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
|
@ -21,6 +23,21 @@ impl ScalarType for ObjectId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "bson-uuid")]
|
||||||
|
#[Scalar(internal, name = "UUID")]
|
||||||
|
impl ScalarType for Uuid {
|
||||||
|
fn parse(value: Value) -> InputValueResult<Self> {
|
||||||
|
match value {
|
||||||
|
Value::String(s) => Ok(Uuid::parse_str(&s)?),
|
||||||
|
_ => Err(InputValueError::expected_type(value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_value(&self) -> Value {
|
||||||
|
Value::String(self.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
#[Scalar(internal, name = "DateTime")]
|
#[Scalar(internal, name = "DateTime")]
|
||||||
impl ScalarType for UtcDateTime {
|
impl ScalarType for UtcDateTime {
|
||||||
|
|
|
@ -2,9 +2,12 @@ use std::borrow::Cow;
|
||||||
|
|
||||||
use indexmap::map::IndexMap;
|
use indexmap::map::IndexMap;
|
||||||
|
|
||||||
use crate::model::{__Schema, __Type};
|
|
||||||
use crate::parser::types::Field;
|
use crate::parser::types::Field;
|
||||||
use crate::resolver_utils::{resolve_container, ContainerType};
|
use crate::resolver_utils::{resolve_container, ContainerType};
|
||||||
|
use crate::{
|
||||||
|
model::{__Schema, __Type},
|
||||||
|
schema::IntrospectionMode,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
registry, Any, Context, ContextSelectionSet, ObjectType, OutputType, Positioned, ServerError,
|
registry, Any, Context, ContextSelectionSet, ObjectType, OutputType, Positioned, ServerError,
|
||||||
ServerResult, SimpleObject, Value,
|
ServerResult, SimpleObject, Value,
|
||||||
|
@ -24,7 +27,13 @@ pub(crate) struct QueryRoot<T> {
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<T: ObjectType> ContainerType for QueryRoot<T> {
|
impl<T: ObjectType> ContainerType for QueryRoot<T> {
|
||||||
async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
|
async fn resolve_field(&self, ctx: &Context<'_>) -> ServerResult<Option<Value>> {
|
||||||
if !ctx.schema_env.registry.disable_introspection && !ctx.query_env.disable_introspection {
|
if matches!(
|
||||||
|
ctx.schema_env.registry.introspection_mode,
|
||||||
|
IntrospectionMode::Enabled | IntrospectionMode::IntrospectionOnly
|
||||||
|
) && matches!(
|
||||||
|
ctx.query_env.introspection_mode,
|
||||||
|
IntrospectionMode::Enabled | IntrospectionMode::IntrospectionOnly,
|
||||||
|
) {
|
||||||
if ctx.item.node.name.node == "__schema" {
|
if ctx.item.node.name.node == "__schema" {
|
||||||
let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
|
let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
|
||||||
let visible_types = ctx.schema_env.registry.find_visible_types(ctx);
|
let visible_types = ctx.schema_env.registry.find_visible_types(ctx);
|
||||||
|
@ -54,6 +63,12 @@ impl<T: ObjectType> ContainerType for QueryRoot<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.schema_env.registry.introspection_mode == IntrospectionMode::IntrospectionOnly
|
||||||
|
|| ctx.query_env.introspection_mode == IntrospectionMode::IntrospectionOnly
|
||||||
|
{
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
if ctx.schema_env.registry.enable_federation || ctx.schema_env.registry.has_entities() {
|
if ctx.schema_env.registry.enable_federation || ctx.schema_env.registry.has_entities() {
|
||||||
if ctx.item.node.name.node == "_entities" {
|
if ctx.item.node.name.node == "_entities" {
|
||||||
let (_, representations) = ctx.param_value::<Vec<Any>>("representations", None)?;
|
let (_, representations) = ctx.param_value::<Vec<Any>>("representations", None)?;
|
||||||
|
@ -93,7 +108,10 @@ impl<T: ObjectType> OutputType for QueryRoot<T> {
|
||||||
fn create_type_info(registry: &mut registry::Registry) -> String {
|
fn create_type_info(registry: &mut registry::Registry) -> String {
|
||||||
let root = T::create_type_info(registry);
|
let root = T::create_type_info(registry);
|
||||||
|
|
||||||
if !registry.disable_introspection {
|
if matches!(
|
||||||
|
registry.introspection_mode,
|
||||||
|
IntrospectionMode::Enabled | IntrospectionMode::IntrospectionOnly
|
||||||
|
) {
|
||||||
let schema_type = __Schema::create_type_info(registry);
|
let schema_type = __Schema::create_type_info(registry);
|
||||||
if let Some(registry::MetaType::Object { fields, .. }) =
|
if let Some(registry::MetaType::Object { fields, .. }) =
|
||||||
registry.types.get_mut(T::type_name().as_ref())
|
registry.types.get_mut(T::type_name().as_ref())
|
||||||
|
|
|
@ -1272,3 +1272,109 @@ pub async fn test_disable_introspection() {
|
||||||
value!({ "__type": null })
|
value!({ "__type": null })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
pub async fn test_introspection_only() {
|
||||||
|
let schema = Schema::build(Query, Mutation, EmptySubscription)
|
||||||
|
.introspection_only()
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
// Test whether introspection works.
|
||||||
|
let query = r#"
|
||||||
|
{
|
||||||
|
__type(name: "Mutation") {
|
||||||
|
name
|
||||||
|
kind
|
||||||
|
description
|
||||||
|
fields {
|
||||||
|
description
|
||||||
|
name
|
||||||
|
type { kind name }
|
||||||
|
args { name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let res_json = value!({
|
||||||
|
"__type": {
|
||||||
|
"name": "Mutation",
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"description": "Global mutation",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"description": "simple_mutation description\nline2\nline3",
|
||||||
|
"name": "simpleMutation",
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "input"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let res = schema.execute(query).await.into_result().unwrap().data;
|
||||||
|
assert_eq!(res, res_json);
|
||||||
|
|
||||||
|
// Test whether introspection works.
|
||||||
|
let query = r#"
|
||||||
|
{
|
||||||
|
__type(name: "Query") {
|
||||||
|
name
|
||||||
|
kind
|
||||||
|
description
|
||||||
|
fields {
|
||||||
|
description
|
||||||
|
name
|
||||||
|
type { kind name }
|
||||||
|
args { name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let res_json = value!({
|
||||||
|
"__type": {
|
||||||
|
"name": "Query",
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"description": "Global query",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"description": "Get a simple object",
|
||||||
|
"name": "simpleObject",
|
||||||
|
"type": { "kind": "NON_NULL", "name": null },
|
||||||
|
"args": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let res = schema.execute(query).await.into_result().unwrap().data;
|
||||||
|
assert_eq!(res, res_json);
|
||||||
|
|
||||||
|
// Queries shouldn't work in introspection only mode.
|
||||||
|
let query = r#"
|
||||||
|
{
|
||||||
|
simpleObject {
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let res_json = value!({ "simpleObject": null });
|
||||||
|
let res = schema.execute(query).await.into_result().unwrap().data;
|
||||||
|
assert_eq!(res, res_json);
|
||||||
|
|
||||||
|
// Mutations shouldn't work in introspection only mode.
|
||||||
|
let query = r#"
|
||||||
|
mutation {
|
||||||
|
simpleMutation(input: { a: "" }) {
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let res_json = value!({ "simpleMutation": null });
|
||||||
|
let res = schema.execute(query).await.into_result().unwrap().data;
|
||||||
|
assert_eq!(res, res_json);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user