1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![cfg_attr(test, recursion_limit = "256")]
5#![warn(missing_docs)]
6
7extern crate core;
10extern crate self as juniper;
11
12use std::fmt;
13
14#[doc(hidden)]
16pub use {async_trait::async_trait, futures, serde, static_assertions as sa};
17
18#[doc(inline)]
19pub use futures::future::{BoxFuture, LocalBoxFuture};
20
21pub use juniper_codegen::{
25 graphql_interface, graphql_object, graphql_scalar, graphql_subscription, graphql_union,
26 GraphQLEnum, GraphQLInputObject, GraphQLInterface, GraphQLObject, GraphQLScalar, GraphQLUnion,
27};
28
29#[doc(hidden)]
30#[macro_use]
31pub mod macros;
32mod ast;
33pub mod executor;
34mod introspection;
35pub mod parser;
36pub(crate) mod schema;
37mod types;
38mod util;
39pub mod validation;
40mod value;
41pub mod http;
44pub mod integrations;
45
46#[cfg(all(test, not(feature = "expose-test-schema")))]
47mod tests;
48#[cfg(feature = "expose-test-schema")]
49pub mod tests;
50
51#[cfg(test)]
52mod executor_tests;
53
54pub use crate::util::to_camel_case;
56
57use crate::{
58 executor::{execute_validated_query, get_operation},
59 introspection::{INTROSPECTION_QUERY, INTROSPECTION_QUERY_WITHOUT_DESCRIPTIONS},
60 parser::parse_document_source,
61 validation::{
62 rules, validate_input_values, visit as visit_rule, visit_all_rules, MultiVisitorNil,
63 ValidatorContext,
64 },
65};
66
67pub use crate::{
68 ast::{
69 Definition, Document, FromInputValue, InputValue, Operation, OperationType, Selection,
70 ToInputValue, Type,
71 },
72 executor::{
73 Applies, Context, ExecutionError, ExecutionResult, Executor, FieldError, FieldResult,
74 FromContext, IntoFieldError, IntoResolvable, LookAheadArgument, LookAheadChildren,
75 LookAheadList, LookAheadObject, LookAheadSelection, LookAheadValue, OwnedExecutor,
76 Registry, ValuesStream, Variables,
77 },
78 introspection::IntrospectionFormat,
79 macros::helper::subscription::{ExtractTypeFromStream, IntoFieldResult},
80 parser::{ParseError, ScalarToken, Span, Spanning},
81 schema::{
82 meta,
83 model::{RootNode, SchemaType},
84 },
85 types::{
86 async_await::{GraphQLTypeAsync, GraphQLValueAsync},
87 base::{Arguments, GraphQLType, GraphQLValue, TypeKind},
88 marker::{self, GraphQLInterface, GraphQLObject, GraphQLUnion},
89 nullable::Nullable,
90 scalars::{EmptyMutation, EmptySubscription, ID},
91 subscriptions::{
92 ExecutionOutput, GraphQLSubscriptionType, GraphQLSubscriptionValue,
93 SubscriptionConnection, SubscriptionCoordinator,
94 },
95 },
96 validation::RuleError,
97 value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarValue, Value},
98};
99
100#[allow(missing_docs)]
102#[derive(Clone, Debug, Eq, PartialEq)]
103pub enum GraphQLError {
104 ParseError(Spanning<ParseError>),
105 ValidationError(Vec<RuleError>),
106 NoOperationProvided,
107 MultipleOperationsProvided,
108 UnknownOperationName,
109 IsSubscription,
110 NotSubscription,
111}
112
113impl fmt::Display for GraphQLError {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 match self {
116 Self::ParseError(e) => write!(f, "{e}"),
117 Self::ValidationError(errs) => {
118 for e in errs {
119 writeln!(f, "{e}")?;
120 }
121 Ok(())
122 }
123 Self::NoOperationProvided => write!(f, "No operation provided"),
124 Self::MultipleOperationsProvided => write!(f, "Multiple operations provided"),
125 Self::UnknownOperationName => write!(f, "Unknown operation name"),
126 Self::IsSubscription => write!(f, "Operation is a subscription"),
127 Self::NotSubscription => write!(f, "Operation is not a subscription"),
128 }
129 }
130}
131
132impl std::error::Error for GraphQLError {
133 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
134 match self {
135 Self::ParseError(e) => Some(e),
136 Self::ValidationError(errs) => Some(errs.first()?),
137 Self::NoOperationProvided
138 | Self::MultipleOperationsProvided
139 | Self::UnknownOperationName
140 | Self::IsSubscription
141 | Self::NotSubscription => None,
142 }
143 }
144}
145
146pub fn execute_sync<'a, S, QueryT, MutationT, SubscriptionT>(
148 document_source: &'a str,
149 operation_name: Option<&str>,
150 root_node: &'a RootNode<QueryT, MutationT, SubscriptionT, S>,
151 variables: &Variables<S>,
152 context: &QueryT::Context,
153) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError>
154where
155 S: ScalarValue,
156 QueryT: GraphQLType<S>,
157 MutationT: GraphQLType<S, Context = QueryT::Context>,
158 SubscriptionT: GraphQLType<S, Context = QueryT::Context>,
159{
160 let document = parse_document_source(document_source, &root_node.schema)?;
161
162 {
163 let mut ctx = ValidatorContext::new(&root_node.schema, &document);
164 visit_all_rules(&mut ctx, &document);
165 if root_node.introspection_disabled {
166 visit_rule(
167 &mut MultiVisitorNil.with(rules::disable_introspection::factory()),
168 &mut ctx,
169 &document,
170 );
171 }
172
173 let errors = ctx.into_errors();
174 if !errors.is_empty() {
175 return Err(GraphQLError::ValidationError(errors));
176 }
177 }
178
179 let operation = get_operation(&document, operation_name)?;
180
181 {
182 let errors = validate_input_values(variables, operation, &root_node.schema);
183
184 if !errors.is_empty() {
185 return Err(GraphQLError::ValidationError(errors));
186 }
187 }
188
189 execute_validated_query(&document, operation, root_node, variables, context)
190}
191
192pub async fn execute<'a, S, QueryT, MutationT, SubscriptionT>(
194 document_source: &'a str,
195 operation_name: Option<&str>,
196 root_node: &'a RootNode<'a, QueryT, MutationT, SubscriptionT, S>,
197 variables: &Variables<S>,
198 context: &QueryT::Context,
199) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError>
200where
201 QueryT: GraphQLTypeAsync<S>,
202 QueryT::TypeInfo: Sync,
203 QueryT::Context: Sync,
204 MutationT: GraphQLTypeAsync<S, Context = QueryT::Context>,
205 MutationT::TypeInfo: Sync,
206 SubscriptionT: GraphQLType<S, Context = QueryT::Context> + Sync,
207 SubscriptionT::TypeInfo: Sync,
208 S: ScalarValue + Send + Sync,
209{
210 let document = parse_document_source(document_source, &root_node.schema)?;
211
212 {
213 let mut ctx = ValidatorContext::new(&root_node.schema, &document);
214 visit_all_rules(&mut ctx, &document);
215 if root_node.introspection_disabled {
216 visit_rule(
217 &mut MultiVisitorNil.with(rules::disable_introspection::factory()),
218 &mut ctx,
219 &document,
220 );
221 }
222
223 let errors = ctx.into_errors();
224 if !errors.is_empty() {
225 return Err(GraphQLError::ValidationError(errors));
226 }
227 }
228
229 let operation = get_operation(&document, operation_name)?;
230
231 {
232 let errors = validate_input_values(variables, operation, &root_node.schema);
233
234 if !errors.is_empty() {
235 return Err(GraphQLError::ValidationError(errors));
236 }
237 }
238
239 executor::execute_validated_query_async(&document, operation, root_node, variables, context)
240 .await
241}
242
243pub async fn resolve_into_stream<'a, S, QueryT, MutationT, SubscriptionT>(
245 document_source: &'a str,
246 operation_name: Option<&str>,
247 root_node: &'a RootNode<'a, QueryT, MutationT, SubscriptionT, S>,
248 variables: &Variables<S>,
249 context: &'a QueryT::Context,
250) -> Result<(Value<ValuesStream<'a, S>>, Vec<ExecutionError<S>>), GraphQLError>
251where
252 QueryT: GraphQLTypeAsync<S>,
253 QueryT::TypeInfo: Sync,
254 QueryT::Context: Sync,
255 MutationT: GraphQLTypeAsync<S, Context = QueryT::Context>,
256 MutationT::TypeInfo: Sync,
257 SubscriptionT: GraphQLSubscriptionType<S, Context = QueryT::Context>,
258 SubscriptionT::TypeInfo: Sync,
259 S: ScalarValue + Send + Sync,
260{
261 let document: crate::ast::OwnedDocument<'a, S> =
262 parse_document_source(document_source, &root_node.schema)?;
263
264 {
265 let mut ctx = ValidatorContext::new(&root_node.schema, &document);
266 visit_all_rules(&mut ctx, &document);
267 if root_node.introspection_disabled {
268 visit_rule(
269 &mut MultiVisitorNil.with(rules::disable_introspection::factory()),
270 &mut ctx,
271 &document,
272 );
273 }
274
275 let errors = ctx.into_errors();
276 if !errors.is_empty() {
277 return Err(GraphQLError::ValidationError(errors));
278 }
279 }
280
281 let operation = get_operation(&document, operation_name)?;
282
283 {
284 let errors = validate_input_values(variables, operation, &root_node.schema);
285
286 if !errors.is_empty() {
287 return Err(GraphQLError::ValidationError(errors));
288 }
289 }
290
291 executor::resolve_validated_subscription(&document, operation, root_node, variables, context)
292 .await
293}
294
295pub fn introspect<S, QueryT, MutationT, SubscriptionT>(
297 root_node: &RootNode<QueryT, MutationT, SubscriptionT, S>,
298 context: &QueryT::Context,
299 format: IntrospectionFormat,
300) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError>
301where
302 S: ScalarValue,
303 QueryT: GraphQLType<S>,
304 MutationT: GraphQLType<S, Context = QueryT::Context>,
305 SubscriptionT: GraphQLType<S, Context = QueryT::Context>,
306{
307 execute_sync(
308 match format {
309 IntrospectionFormat::All => INTROSPECTION_QUERY,
310 IntrospectionFormat::WithoutDescriptions => INTROSPECTION_QUERY_WITHOUT_DESCRIPTIONS,
311 },
312 None,
313 root_node,
314 &Variables::new(),
315 context,
316 )
317}
318
319impl From<Spanning<ParseError>> for GraphQLError {
320 fn from(err: Spanning<ParseError>) -> Self {
321 Self::ParseError(err)
322 }
323}