Coverage Report

Created: 2025-11-28 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/async-graphql-7.0.17/src/extensions/mod.rs
Line
Count
Source
1
//! Extensions for schema
2
3
mod analyzer;
4
#[cfg(feature = "apollo_persisted_queries")]
5
pub mod apollo_persisted_queries;
6
#[cfg(feature = "apollo_tracing")]
7
mod apollo_tracing;
8
#[cfg(feature = "log")]
9
mod logger;
10
#[cfg(feature = "opentelemetry")]
11
mod opentelemetry;
12
#[cfg(feature = "tracing")]
13
mod tracing;
14
15
use std::{
16
    any::{Any, TypeId},
17
    future::Future,
18
    sync::Arc,
19
};
20
21
use futures_util::{FutureExt, future::BoxFuture, stream::BoxStream};
22
23
pub use self::analyzer::Analyzer;
24
#[cfg(feature = "apollo_tracing")]
25
pub use self::apollo_tracing::ApolloTracing;
26
#[cfg(feature = "log")]
27
pub use self::logger::Logger;
28
#[cfg(feature = "opentelemetry")]
29
pub use self::opentelemetry::OpenTelemetry;
30
#[cfg(feature = "tracing")]
31
pub use self::tracing::Tracing;
32
use crate::{
33
    Data, DataContext, Error, QueryPathNode, Request, Response, Result, SDLExportOptions,
34
    SchemaEnv, ServerError, ServerResult, ValidationResult, Value, Variables,
35
    parser::types::{ExecutableDocument, Field},
36
};
37
38
/// Context for extension
39
pub struct ExtensionContext<'a> {
40
    /// Schema-scope context data, [`Registry`], and custom directives.
41
    pub schema_env: &'a SchemaEnv,
42
43
    /// Extension-scoped context data shared across all extensions.
44
    ///
45
    /// Can be accessed only from hooks that implement the [`Extension`] trait.
46
    ///
47
    /// It is created with each new [`Request`] and is empty by default.
48
    ///
49
    /// For subscriptions, the session ends when the subscription is closed.
50
    pub session_data: &'a Data,
51
52
    /// Request-scoped context data shared across all resolvers.
53
    ///
54
    /// This is a reference to [`Request::data`](Request) field.
55
    /// If the request has not initialized yet, the value is seen as `None`
56
    /// inside the [`Extension::request`], [`Extension::subscribe`], and
57
    /// [`Extension::prepare_request`] hooks.
58
    pub query_data: Option<&'a Data>,
59
}
60
61
impl<'a> DataContext<'a> for ExtensionContext<'a> {
62
0
    fn data<D: Any + Send + Sync>(&self) -> Result<&'a D> {
63
0
        ExtensionContext::data::<D>(self)
64
0
    }
65
66
0
    fn data_unchecked<D: Any + Send + Sync>(&self) -> &'a D {
67
0
        ExtensionContext::data_unchecked::<D>(self)
68
0
    }
69
70
0
    fn data_opt<D: Any + Send + Sync>(&self) -> Option<&'a D> {
71
0
        ExtensionContext::data_opt::<D>(self)
72
0
    }
73
}
74
75
impl<'a> ExtensionContext<'a> {
76
    /// Convert the specified [ExecutableDocument] into a query string.
77
    ///
78
    /// Usually used for log extension, it can hide secret arguments.
79
0
    pub fn stringify_execute_doc(&self, doc: &ExecutableDocument, variables: &Variables) -> String {
80
0
        self.schema_env
81
0
            .registry
82
0
            .stringify_exec_doc(variables, doc)
83
0
            .unwrap_or_default()
84
0
    }
85
86
    /// Returns SDL(Schema Definition Language) of this schema.
87
0
    pub fn sdl(&self) -> String {
88
0
        self.schema_env.registry.export_sdl(Default::default())
89
0
    }
90
91
    /// Returns SDL(Schema Definition Language) of this schema with options.
92
0
    pub fn sdl_with_options(&self, options: SDLExportOptions) -> String {
93
0
        self.schema_env.registry.export_sdl(options)
94
0
    }
95
96
    /// Gets the global data defined in the `Context` or `Schema`.
97
    ///
98
    /// If both `Schema` and `Query` have the same data type, the data in the
99
    /// `Query` is obtained.
100
    ///
101
    /// # Errors
102
    ///
103
    /// Returns a `Error` if the specified type data does not exist.
104
0
    pub fn data<D: Any + Send + Sync>(&self) -> Result<&'a D> {
105
0
        self.data_opt::<D>().ok_or_else(|| {
106
0
            Error::new(format!(
107
0
                "Data `{}` does not exist.",
108
0
                std::any::type_name::<D>()
109
            ))
110
0
        })
111
0
    }
112
113
    /// Gets the global data defined in the `Context` or `Schema`.
114
    ///
115
    /// # Panics
116
    ///
117
    /// It will panic if the specified data type does not exist.
118
0
    pub fn data_unchecked<D: Any + Send + Sync>(&self) -> &'a D {
119
0
        self.data_opt::<D>()
120
0
            .unwrap_or_else(|| panic!("Data `{}` does not exist.", std::any::type_name::<D>()))
121
0
    }
122
123
    /// Gets the global data defined in the `Context` or `Schema` or `None` if
124
    /// the specified type data does not exist.
125
0
    pub fn data_opt<D: Any + Send + Sync>(&self) -> Option<&'a D> {
126
0
        self.query_data
127
0
            .and_then(|query_data| query_data.get(&TypeId::of::<D>()))
128
0
            .or_else(|| self.session_data.get(&TypeId::of::<D>()))
129
0
            .or_else(|| self.schema_env.data.get(&TypeId::of::<D>()))
130
0
            .and_then(|d| d.downcast_ref::<D>())
131
0
    }
132
}
133
134
/// Parameters for `Extension::resolve_field_start`
135
pub struct ResolveInfo<'a> {
136
    /// Current path node, You can go through the entire path.
137
    pub path_node: &'a QueryPathNode<'a>,
138
139
    /// Parent type
140
    pub parent_type: &'a str,
141
142
    /// Current return type, is qualified name.
143
    pub return_type: &'a str,
144
145
    /// Current field name
146
    pub name: &'a str,
147
148
    /// Current field alias
149
    pub alias: Option<&'a str>,
150
151
    /// If `true` means the current field is for introspection.
152
    pub is_for_introspection: bool,
153
154
    /// Current field
155
    pub field: &'a Field,
156
}
157
158
type RequestFut<'a> = &'a mut (dyn Future<Output = Response> + Send + Unpin);
159
160
type ParseFut<'a> = &'a mut (dyn Future<Output = ServerResult<ExecutableDocument>> + Send + Unpin);
161
162
type ValidationFut<'a> =
163
    &'a mut (dyn Future<Output = Result<ValidationResult, Vec<ServerError>>> + Send + Unpin);
164
165
type ExecuteFutFactory<'a> = Box<dyn FnOnce(Option<Data>) -> BoxFuture<'a, Response> + Send + 'a>;
166
167
/// A future type used to resolve the field
168
pub type ResolveFut<'a> = &'a mut (dyn Future<Output = ServerResult<Option<Value>>> + Send + Unpin);
169
170
/// The remainder of a extension chain for request.
171
pub struct NextRequest<'a> {
172
    chain: &'a [Arc<dyn Extension>],
173
    request_fut: RequestFut<'a>,
174
}
175
176
impl NextRequest<'_> {
177
    /// Call the [Extension::request] function of next extension.
178
0
    pub async fn run(self, ctx: &ExtensionContext<'_>) -> Response {
179
0
        if let Some((first, next)) = self.chain.split_first() {
180
0
            first
181
0
                .request(
182
0
                    ctx,
183
0
                    NextRequest {
184
0
                        chain: next,
185
0
                        request_fut: self.request_fut,
186
0
                    },
187
0
                )
188
0
                .await
189
        } else {
190
0
            self.request_fut.await
191
        }
192
0
    }
193
}
194
195
/// The remainder of a extension chain for subscribe.
196
pub struct NextSubscribe<'a> {
197
    chain: &'a [Arc<dyn Extension>],
198
}
199
200
impl NextSubscribe<'_> {
201
    /// Call the [Extension::subscribe] function of next extension.
202
0
    pub fn run<'s>(
203
0
        self,
204
0
        ctx: &ExtensionContext<'_>,
205
0
        stream: BoxStream<'s, Response>,
206
0
    ) -> BoxStream<'s, Response> {
207
0
        if let Some((first, next)) = self.chain.split_first() {
208
0
            first.subscribe(ctx, stream, NextSubscribe { chain: next })
209
        } else {
210
0
            stream
211
        }
212
0
    }
213
}
214
215
/// The remainder of a extension chain for subscribe.
216
pub struct NextPrepareRequest<'a> {
217
    chain: &'a [Arc<dyn Extension>],
218
}
219
220
impl NextPrepareRequest<'_> {
221
    /// Call the [Extension::prepare_request] function of next extension.
222
0
    pub async fn run(self, ctx: &ExtensionContext<'_>, request: Request) -> ServerResult<Request> {
223
0
        if let Some((first, next)) = self.chain.split_first() {
224
0
            first
225
0
                .prepare_request(ctx, request, NextPrepareRequest { chain: next })
226
0
                .await
227
        } else {
228
0
            Ok(request)
229
        }
230
0
    }
231
}
232
233
/// The remainder of a extension chain for parse query.
234
pub struct NextParseQuery<'a> {
235
    chain: &'a [Arc<dyn Extension>],
236
    parse_query_fut: ParseFut<'a>,
237
}
238
239
impl NextParseQuery<'_> {
240
    /// Call the [Extension::parse_query] function of next extension.
241
0
    pub async fn run(
242
0
        self,
243
0
        ctx: &ExtensionContext<'_>,
244
0
        query: &str,
245
0
        variables: &Variables,
246
0
    ) -> ServerResult<ExecutableDocument> {
247
0
        if let Some((first, next)) = self.chain.split_first() {
248
0
            first
249
0
                .parse_query(
250
0
                    ctx,
251
0
                    query,
252
0
                    variables,
253
0
                    NextParseQuery {
254
0
                        chain: next,
255
0
                        parse_query_fut: self.parse_query_fut,
256
0
                    },
257
0
                )
258
0
                .await
259
        } else {
260
0
            self.parse_query_fut.await
261
        }
262
0
    }
263
}
264
265
/// The remainder of a extension chain for validation.
266
pub struct NextValidation<'a> {
267
    chain: &'a [Arc<dyn Extension>],
268
    validation_fut: ValidationFut<'a>,
269
}
270
271
impl NextValidation<'_> {
272
    /// Call the [Extension::validation] function of next extension.
273
0
    pub async fn run(
274
0
        self,
275
0
        ctx: &ExtensionContext<'_>,
276
0
    ) -> Result<ValidationResult, Vec<ServerError>> {
277
0
        if let Some((first, next)) = self.chain.split_first() {
278
0
            first
279
0
                .validation(
280
0
                    ctx,
281
0
                    NextValidation {
282
0
                        chain: next,
283
0
                        validation_fut: self.validation_fut,
284
0
                    },
285
0
                )
286
0
                .await
287
        } else {
288
0
            self.validation_fut.await
289
        }
290
0
    }
291
}
292
293
/// The remainder of a extension chain for execute.
294
pub struct NextExecute<'a> {
295
    chain: &'a [Arc<dyn Extension>],
296
    execute_fut_factory: ExecuteFutFactory<'a>,
297
    execute_data: Option<Data>,
298
}
299
300
impl NextExecute<'_> {
301
0
    async fn internal_run(
302
0
        self,
303
0
        ctx: &ExtensionContext<'_>,
304
0
        operation_name: Option<&str>,
305
0
        data: Option<Data>,
306
0
    ) -> Response {
307
0
        let execute_data = match (self.execute_data, data) {
308
0
            (Some(mut data1), Some(data2)) => {
309
0
                data1.merge(data2);
310
0
                Some(data1)
311
            }
312
0
            (Some(data), None) => Some(data),
313
0
            (None, Some(data)) => Some(data),
314
0
            (None, None) => None,
315
        };
316
317
0
        if let Some((first, next)) = self.chain.split_first() {
318
0
            first
319
0
                .execute(
320
0
                    ctx,
321
0
                    operation_name,
322
0
                    NextExecute {
323
0
                        chain: next,
324
0
                        execute_fut_factory: self.execute_fut_factory,
325
0
                        execute_data,
326
0
                    },
327
0
                )
328
0
                .await
329
        } else {
330
0
            (self.execute_fut_factory)(execute_data).await
331
        }
332
0
    }
333
334
    /// Call the [Extension::execute] function of next extension.
335
0
    pub async fn run(self, ctx: &ExtensionContext<'_>, operation_name: Option<&str>) -> Response {
336
0
        self.internal_run(ctx, operation_name, None).await
337
0
    }
338
339
    /// Call the [Extension::execute] function of next extension with context
340
    /// data.
341
0
    pub async fn run_with_data(
342
0
        self,
343
0
        ctx: &ExtensionContext<'_>,
344
0
        operation_name: Option<&str>,
345
0
        data: Data,
346
0
    ) -> Response {
347
0
        self.internal_run(ctx, operation_name, Some(data)).await
348
0
    }
349
}
350
351
/// The remainder of a extension chain for resolve.
352
pub struct NextResolve<'a> {
353
    chain: &'a [Arc<dyn Extension>],
354
    resolve_fut: ResolveFut<'a>,
355
}
356
357
impl NextResolve<'_> {
358
    /// Call the [Extension::resolve] function of next extension.
359
0
    pub async fn run(
360
0
        self,
361
0
        ctx: &ExtensionContext<'_>,
362
0
        info: ResolveInfo<'_>,
363
0
    ) -> ServerResult<Option<Value>> {
364
0
        if let Some((first, next)) = self.chain.split_first() {
365
0
            first
366
0
                .resolve(
367
0
                    ctx,
368
0
                    info,
369
0
                    NextResolve {
370
0
                        chain: next,
371
0
                        resolve_fut: self.resolve_fut,
372
0
                    },
373
0
                )
374
0
                .await
375
        } else {
376
0
            self.resolve_fut.await
377
        }
378
0
    }
379
}
380
381
/// Represents a GraphQL extension
382
#[async_trait::async_trait]
383
pub trait Extension: Sync + Send + 'static {
384
    /// Called at start query/mutation request.
385
0
    async fn request(&self, ctx: &ExtensionContext<'_>, next: NextRequest<'_>) -> Response {
386
0
        next.run(ctx).await
387
0
    }
Unexecuted instantiation: <_ as async_graphql::extensions::Extension>::request::{closure#0}
Unexecuted instantiation: <_ as async_graphql::extensions::Extension>::request
388
389
    /// Called at subscribe request.
390
0
    fn subscribe<'s>(
391
0
        &self,
392
0
        ctx: &ExtensionContext<'_>,
393
0
        stream: BoxStream<'s, Response>,
394
0
        next: NextSubscribe<'_>,
395
0
    ) -> BoxStream<'s, Response> {
396
0
        next.run(ctx, stream)
397
0
    }
398
399
    /// Called at prepare request.
400
    async fn prepare_request(
401
        &self,
402
        ctx: &ExtensionContext<'_>,
403
        request: Request,
404
        next: NextPrepareRequest<'_>,
405
0
    ) -> ServerResult<Request> {
406
0
        next.run(ctx, request).await
407
0
    }
Unexecuted instantiation: <async_graphql::extensions::analyzer::AnalyzerExtension as async_graphql::extensions::Extension>::prepare_request::{closure#0}
Unexecuted instantiation: <async_graphql::extensions::analyzer::AnalyzerExtension as async_graphql::extensions::Extension>::prepare_request
408
409
    /// Called at parse query.
410
    async fn parse_query(
411
        &self,
412
        ctx: &ExtensionContext<'_>,
413
        query: &str,
414
        variables: &Variables,
415
        next: NextParseQuery<'_>,
416
0
    ) -> ServerResult<ExecutableDocument> {
417
0
        next.run(ctx, query, variables).await
418
0
    }
Unexecuted instantiation: <async_graphql::extensions::analyzer::AnalyzerExtension as async_graphql::extensions::Extension>::parse_query::{closure#0}
Unexecuted instantiation: <async_graphql::extensions::analyzer::AnalyzerExtension as async_graphql::extensions::Extension>::parse_query
419
420
    /// Called at validation query.
421
    async fn validation(
422
        &self,
423
        ctx: &ExtensionContext<'_>,
424
        next: NextValidation<'_>,
425
0
    ) -> Result<ValidationResult, Vec<ServerError>> {
426
0
        next.run(ctx).await
427
0
    }
Unexecuted instantiation: <_ as async_graphql::extensions::Extension>::validation::{closure#0}
Unexecuted instantiation: <_ as async_graphql::extensions::Extension>::validation
428
429
    /// Called at execute query.
430
    async fn execute(
431
        &self,
432
        ctx: &ExtensionContext<'_>,
433
        operation_name: Option<&str>,
434
        next: NextExecute<'_>,
435
0
    ) -> Response {
436
0
        next.run(ctx, operation_name).await
437
0
    }
Unexecuted instantiation: <async_graphql::extensions::analyzer::AnalyzerExtension as async_graphql::extensions::Extension>::execute::{closure#0}
Unexecuted instantiation: <async_graphql::extensions::analyzer::AnalyzerExtension as async_graphql::extensions::Extension>::execute
438
439
    /// Called at resolve field.
440
    async fn resolve(
441
        &self,
442
        ctx: &ExtensionContext<'_>,
443
        info: ResolveInfo<'_>,
444
        next: NextResolve<'_>,
445
0
    ) -> ServerResult<Option<Value>> {
446
0
        next.run(ctx, info).await
447
0
    }
Unexecuted instantiation: <async_graphql::extensions::analyzer::AnalyzerExtension as async_graphql::extensions::Extension>::resolve::{closure#0}
Unexecuted instantiation: <async_graphql::extensions::analyzer::AnalyzerExtension as async_graphql::extensions::Extension>::resolve
448
}
449
450
/// Extension factory
451
///
452
/// Used to create an extension instance.
453
pub trait ExtensionFactory: Send + Sync + 'static {
454
    /// Create an extended instance.
455
    fn create(&self) -> Arc<dyn Extension>;
456
}
457
458
#[derive(Clone)]
459
#[doc(hidden)]
460
pub struct Extensions {
461
    extensions: Vec<Arc<dyn Extension>>,
462
    schema_env: SchemaEnv,
463
    session_data: Arc<Data>,
464
    query_data: Option<Arc<Data>>,
465
}
466
467
#[doc(hidden)]
468
impl Extensions {
469
0
    pub(crate) fn new(
470
0
        extensions: impl IntoIterator<Item = Arc<dyn Extension>>,
471
0
        schema_env: SchemaEnv,
472
0
        session_data: Arc<Data>,
473
0
    ) -> Self {
474
0
        Extensions {
475
0
            extensions: extensions.into_iter().collect(),
476
0
            schema_env,
477
0
            session_data,
478
0
            query_data: None,
479
0
        }
480
0
    }
481
482
    #[inline]
483
0
    pub(crate) fn attach_query_data(&mut self, data: Arc<Data>) {
484
0
        self.query_data = Some(data);
485
0
    }
486
487
    #[inline]
488
0
    pub(crate) fn is_empty(&self) -> bool {
489
0
        self.extensions.is_empty()
490
0
    }
491
492
    #[inline]
493
0
    fn create_context(&self) -> ExtensionContext {
494
0
        ExtensionContext {
495
0
            schema_env: &self.schema_env,
496
0
            session_data: &self.session_data,
497
0
            query_data: self.query_data.as_deref(),
498
0
        }
499
0
    }
500
501
0
    pub async fn request(&self, request_fut: RequestFut<'_>) -> Response {
502
0
        let next = NextRequest {
503
0
            chain: &self.extensions,
504
0
            request_fut,
505
0
        };
506
0
        next.run(&self.create_context()).await
507
0
    }
508
509
0
    pub fn subscribe<'s>(&self, stream: BoxStream<'s, Response>) -> BoxStream<'s, Response> {
510
0
        let next = NextSubscribe {
511
0
            chain: &self.extensions,
512
0
        };
513
0
        next.run(&self.create_context(), stream)
514
0
    }
515
516
0
    pub async fn prepare_request(&self, request: Request) -> ServerResult<Request> {
517
0
        let next = NextPrepareRequest {
518
0
            chain: &self.extensions,
519
0
        };
520
0
        next.run(&self.create_context(), request).await
521
0
    }
522
523
0
    pub async fn parse_query(
524
0
        &self,
525
0
        query: &str,
526
0
        variables: &Variables,
527
0
        parse_query_fut: ParseFut<'_>,
528
0
    ) -> ServerResult<ExecutableDocument> {
529
0
        let next = NextParseQuery {
530
0
            chain: &self.extensions,
531
0
            parse_query_fut,
532
0
        };
533
0
        next.run(&self.create_context(), query, variables).await
534
0
    }
535
536
0
    pub async fn validation(
537
0
        &self,
538
0
        validation_fut: ValidationFut<'_>,
539
0
    ) -> Result<ValidationResult, Vec<ServerError>> {
540
0
        let next = NextValidation {
541
0
            chain: &self.extensions,
542
0
            validation_fut,
543
0
        };
544
0
        next.run(&self.create_context()).await
545
0
    }
546
547
0
    pub async fn execute<'a, 'b, F, T>(
548
0
        &'a self,
549
0
        operation_name: Option<&str>,
550
0
        execute_fut_factory: F,
551
0
    ) -> Response
552
0
    where
553
0
        F: FnOnce(Option<Data>) -> T + Send + 'a,
554
0
        T: Future<Output = Response> + Send + 'a,
555
0
    {
556
0
        let next = NextExecute {
557
0
            chain: &self.extensions,
558
0
            execute_fut_factory: Box::new(|data| execute_fut_factory(data).boxed()),
559
0
            execute_data: None,
560
        };
561
0
        next.run(&self.create_context(), operation_name).await
562
0
    }
563
564
0
    pub async fn resolve(
565
0
        &self,
566
0
        info: ResolveInfo<'_>,
567
0
        resolve_fut: ResolveFut<'_>,
568
0
    ) -> ServerResult<Option<Value>> {
569
0
        let next = NextResolve {
570
0
            chain: &self.extensions,
571
0
            resolve_fut,
572
0
        };
573
0
        next.run(&self.create_context(), info).await
574
0
    }
575
}