graphql_composition/
lib.rs

1#![deny(unsafe_code, missing_docs, rust_2018_idioms)]
2#![allow(clippy::option_option)]
3#![doc = include_str!("../README.md")]
4
5mod compose;
6mod composition_ir;
7mod diagnostics;
8mod emit_federated_graph;
9mod grafbase_extensions;
10mod ingest_subgraph;
11mod result;
12mod subgraphs;
13mod validate;
14
15pub use self::{
16    diagnostics::Diagnostics, grafbase_extensions::LoadedExtension, result::CompositionResult, subgraphs::IngestError,
17    subgraphs::Subgraphs,
18};
19pub use graphql_federated_graph::{
20    self as graphql_federated_graph, render_api_sdl, render_federated_sdl, FederatedGraph,
21};
22
23use self::{
24    compose::{compose_subgraphs, ComposeContext},
25    emit_federated_graph::emit_federated_graph,
26    ingest_subgraph::ast_value_to_subgraph_value,
27};
28
29/// Compose subgraphs into a federated graph.
30pub fn compose(subgraphs: &Subgraphs) -> CompositionResult {
31    let mut diagnostics = Diagnostics::default();
32
33    if subgraphs.iter_subgraphs().len() == 0 {
34        let error = "No graphs found for composition build. You must have at least one active graph.";
35        diagnostics.push_fatal(error.to_owned());
36
37        return CompositionResult {
38            federated_graph: None,
39            diagnostics,
40        };
41    }
42
43    let mut context = ComposeContext::new(subgraphs, &mut diagnostics);
44
45    validate::validate(&mut context);
46
47    if context.diagnostics.any_fatal() {
48        return CompositionResult {
49            federated_graph: None,
50            diagnostics,
51        };
52    }
53
54    compose_subgraphs(&mut context);
55
56    if context.diagnostics.any_fatal() {
57        CompositionResult {
58            federated_graph: None,
59            diagnostics,
60        }
61    } else {
62        let federated_graph = emit_federated_graph(context.into_ir(), subgraphs);
63
64        CompositionResult {
65            federated_graph: Some(federated_graph),
66            diagnostics,
67        }
68    }
69}
70
71trait VecExt<T> {
72    fn push_return_idx(&mut self, elem: T) -> usize;
73}
74
75impl<T> VecExt<T> for Vec<T> {
76    fn push_return_idx(&mut self, elem: T) -> usize {
77        let idx = self.len();
78        self.push(elem);
79        idx
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn grafbase_schema_can_be_composed() {
89        use std::{fs, path::Path};
90        let schema_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../cli/src/backend/api/graphql/api.graphql");
91        let schema = fs::read_to_string(schema_path).unwrap();
92
93        let mut subgraphs = Subgraphs::default();
94        subgraphs
95            .ingest_str(&schema, "grafbase-api", Some("https://api.grafbase.com"))
96            .unwrap();
97        let result = compose(&subgraphs);
98        assert!(!result.diagnostics().any_fatal());
99    }
100}