[go: up one dir, main page]

juniper/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3// Due to `schema_introspection` test.
4#![cfg_attr(test, recursion_limit = "256")]
5#![warn(missing_docs)]
6
7// Required for using `juniper_codegen` macros inside this crate to resolve
8// absolute `::juniper` path correctly, without errors.
9extern crate core;
10extern crate self as juniper;
11
12use std::fmt;
13
14// These are required by the code generated via the `juniper_codegen` macros.
15#[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
21// Depend on juniper_codegen and re-export everything in it.
22// This allows users to just depend on juniper and get the derive
23// functionality automatically.
24pub 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;
41// This needs to be public until docs have support for private modules:
42// https://github.com/rust-lang/cargo/issues/1520
43pub 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
54// Needs to be public because macros use it.
55pub 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/// An error that prevented query execution
101#[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
146/// Execute a query synchronously in a provided schema
147pub 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
192/// Execute a query in a provided schema
193pub 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
243/// Resolve subscription into `ValuesStream`
244pub 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
295/// Execute the reference introspection query in the provided schema
296pub 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}