graphql_composition/
lib.rs1#![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, FederatedGraph, render_api_sdl, render_federated_sdl,
21};
22
23use self::{
24 compose::{ComposeContext, compose_subgraphs},
25 emit_federated_graph::emit_federated_graph,
26 ingest_subgraph::ast_value_to_subgraph_value,
27};
28
29pub 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 for (_, directive) in subgraphs.iter_extra_directives_on_schema_definition() {
55 let subgraphs::DirectiveProvenance::Linked {
56 linked_schema_id,
57 is_composed_directive: _,
58 } = directive.provenance
59 else {
60 continue;
61 };
62
63 if let Some(extension_id) = context.get_extension_for_linked_schema(linked_schema_id) {
64 context.mark_used_extension(extension_id);
65 }
66 }
67 compose_subgraphs(&mut context);
68
69 if context.diagnostics.any_fatal() {
70 CompositionResult {
71 federated_graph: None,
72 diagnostics,
73 }
74 } else {
75 let federated_graph = emit_federated_graph(context.into_ir(), subgraphs);
76
77 CompositionResult {
78 federated_graph: Some(federated_graph),
79 diagnostics,
80 }
81 }
82}
83
84trait VecExt<T> {
85 fn push_return_idx(&mut self, elem: T) -> usize;
86}
87
88impl<T> VecExt<T> for Vec<T> {
89 fn push_return_idx(&mut self, elem: T) -> usize {
90 let idx = self.len();
91 self.push(elem);
92 idx
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn grafbase_schema_can_be_composed() {
102 use std::{fs, path::Path};
103 let schema_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../cli/src/api/graphql/api.graphql");
104 let schema = fs::read_to_string(schema_path).unwrap();
105
106 let mut subgraphs = Subgraphs::default();
107 subgraphs
108 .ingest_str(&schema, "grafbase-api", Some("https://api.grafbase.com"))
109 .unwrap();
110 let result = compose(&subgraphs);
111 assert!(!result.diagnostics().any_fatal());
112 }
113}