grafbase_sdk_mock/
lib.rs

1//! Provides a dynamic GraphQL schema and subgraph implementation that can be built and executed at runtime.
2//!
3//! This crate allows creating GraphQL schemas dynamically from SDL (Schema Definition Language) strings
4//! and executing queries against them. It also provides functionality for running mock GraphQL servers
5//! using these dynamic schemas.
6
7#![deny(missing_docs)]
8
9mod builder;
10mod entity_resolver;
11mod resolver;
12mod server;
13
14use std::{
15    hash::{DefaultHasher, Hasher as _},
16    sync::Arc,
17};
18
19pub use builder::GraphqlSubgraphBuilder;
20pub use server::MockGraphQlServer;
21
22/// A dynamic subgraph implementation that can be started as a mock GraphQL server.
23#[derive(Debug, Clone)]
24pub struct GraphqlSubgraph {
25    executable_schema: async_graphql::dynamic::Schema,
26    schema: String,
27    name: String,
28}
29
30impl GraphqlSubgraph {
31    /// Creates a builder for constructing a new dynamic subgraph schema from SDL.
32    ///
33    /// # Arguments
34    ///
35    /// * `sdl` - GraphQL schema definition language string to build from
36    pub fn with_schema(sdl: impl AsRef<str>) -> GraphqlSubgraphBuilder {
37        let sdl = sdl.as_ref();
38        GraphqlSubgraphBuilder::new(sdl.to_string(), anonymous_name(sdl))
39    }
40
41    /// Starts this subgraph as a mock GraphQL server.
42    ///
43    /// Returns a handle to the running server that can be used to stop it.
44    pub async fn start(self) -> MockGraphQlServer {
45        MockGraphQlServer::new(self.name, Arc::new((self.executable_schema, self.schema))).await
46    }
47
48    /// Returns the GraphQL schema in SDL (Schema Definition Language)
49    pub fn schema(&self) -> &str {
50        &self.schema
51    }
52
53    /// Returns the name of this subgraph
54    pub fn name(&self) -> &str {
55        &self.name
56    }
57}
58
59/// A subgraph that only contains extension definitions. We do not spawn a GraphQL server for this subgraph.
60#[derive(Debug, Clone)]
61pub struct VirtualSubgraph {
62    schema: String,
63    name: String,
64}
65
66impl VirtualSubgraph {
67    /// Creates a new virtual subgraph with the given SDL and name.
68    pub fn new(name: &str, schema: &str) -> Self {
69        VirtualSubgraph {
70            name: name.to_string(),
71            schema: schema.to_string(),
72        }
73    }
74
75    /// Returns the GraphQL schema in SDL (Schema Definition Language)
76    pub fn schema(&self) -> &str {
77        &self.schema
78    }
79
80    /// Returns the name of this subgraph
81    pub fn name(&self) -> &str {
82        &self.name
83    }
84}
85
86/// A mock subgraph that can either be a full dynamic GraphQL service or just extension definitions.
87#[derive(Debug, Clone)]
88pub enum Subgraph {
89    /// A full dynamic subgraph that can be started as a GraphQL server
90    Graphql(GraphqlSubgraph),
91    /// A subgraph that only contains extension definitions and is not started as a server
92    Virtual(VirtualSubgraph),
93}
94
95impl From<GraphqlSubgraph> for Subgraph {
96    fn from(subgraph: GraphqlSubgraph) -> Self {
97        Subgraph::Graphql(subgraph)
98    }
99}
100
101impl From<GraphqlSubgraphBuilder> for Subgraph {
102    fn from(builder: GraphqlSubgraphBuilder) -> Self {
103        builder.build().into()
104    }
105}
106
107impl From<VirtualSubgraph> for Subgraph {
108    fn from(subgraph: VirtualSubgraph) -> Self {
109        Subgraph::Virtual(subgraph)
110    }
111}
112
113impl<T: Into<Subgraph>> From<(String, T)> for Subgraph {
114    fn from((name, subgraph): (String, T)) -> Self {
115        (name.as_str(), subgraph).into()
116    }
117}
118
119impl<T: Into<Subgraph>> From<(&str, T)> for Subgraph {
120    fn from((name, subgraph): (&str, T)) -> Self {
121        match subgraph.into() {
122            Subgraph::Graphql(mut graphql_subgraph) => {
123                graphql_subgraph.name = name.to_string();
124                Subgraph::Graphql(graphql_subgraph)
125            }
126            Subgraph::Virtual(mut virtual_subgraph) => {
127                virtual_subgraph.name = name.to_string();
128                Subgraph::Virtual(virtual_subgraph)
129            }
130        }
131    }
132}
133
134impl From<String> for Subgraph {
135    fn from(sdl: String) -> Self {
136        sdl.as_str().into()
137    }
138}
139
140impl From<&str> for Subgraph {
141    fn from(schema: &str) -> Self {
142        Subgraph::Virtual(VirtualSubgraph::new(&anonymous_name(schema), schema))
143    }
144}
145
146fn anonymous_name(schema: &str) -> String {
147    let mut hasher = DefaultHasher::default();
148    hasher.write(schema.as_bytes());
149    format!("anonymous{:X}", hasher.finish())
150}