1
1
use std:: collections:: HashMap ;
2
2
use std:: iter:: once;
3
3
4
- use apollo_compiler:: schema:: ExtendedType ;
4
+ use apollo_compiler:: { schema:: ExtendedType , Schema } ;
5
5
use apollo_federation:: sources:: connect:: {
6
6
expand:: { expand_connectors, Connectors , ExpansionResult } ,
7
7
validation:: { validate, Severity as ValidationSeverity , ValidationResult } ,
@@ -66,37 +66,43 @@ pub trait HybridComposition {
66
66
/// 3. Run Rust-based validation on the supergraph
67
67
/// 4. Call [`validate_satisfiability`] to run JavaScript-based validation on the supergraph
68
68
async fn compose ( & mut self , subgraph_definitions : Vec < SubgraphDefinition > ) {
69
- let validation_results = subgraph_definitions
70
- . iter ( )
71
- . map ( |subgraph| {
72
- (
73
- subgraph. name . clone ( ) ,
74
- validate ( & subgraph. sdl , & subgraph. name ) ,
75
- )
76
- } )
77
- . collect :: < Vec < _ > > ( ) ;
78
- let subgraph_validation_errors = validation_results
79
- . iter ( )
80
- . flat_map ( |( name, validation_result) | {
81
- validation_result
82
- . errors
83
- . iter ( )
84
- . cloned ( )
85
- . map ( |validation_error| Issue {
86
- code : validation_error. code . to_string ( ) ,
87
- message : validation_error. message ,
88
- locations : validation_error
69
+ let mut subgraph_validation_errors = Vec :: new ( ) ;
70
+ let mut parsed_schemas = HashMap :: new ( ) ;
71
+ let subgraph_definitions = subgraph_definitions
72
+ . into_iter ( )
73
+ . map ( |mut subgraph| {
74
+ let ValidationResult {
75
+ errors,
76
+ has_connectors,
77
+ schema,
78
+ transformed,
79
+ } = validate ( subgraph. sdl , & subgraph. name ) ;
80
+ subgraph. sdl = transformed;
81
+ for error in errors {
82
+ subgraph_validation_errors. push ( Issue {
83
+ code : error. code . to_string ( ) ,
84
+ message : error. message ,
85
+ locations : error
89
86
. locations
90
87
. into_iter ( )
91
88
. map ( |range| SubgraphLocation {
92
- subgraph : Some ( name. clone ( ) ) ,
89
+ subgraph : Some ( subgraph . name . clone ( ) ) ,
93
90
range : Some ( range) ,
94
91
} )
95
92
. collect ( ) ,
96
- severity : convert_severity ( validation_error . code . severity ( ) ) ,
93
+ severity : convert_severity ( error . code . severity ( ) ) ,
97
94
} )
95
+ }
96
+ parsed_schemas. insert (
97
+ subgraph. name . clone ( ) ,
98
+ SubgraphSchema {
99
+ schema,
100
+ has_connectors,
101
+ } ,
102
+ ) ;
103
+ subgraph
98
104
} )
99
- . collect :: < Vec < _ > > ( ) ;
105
+ . collect ( ) ;
100
106
101
107
let run_composition = subgraph_validation_errors
102
108
. iter ( )
@@ -115,7 +121,7 @@ pub trait HybridComposition {
115
121
116
122
// Any issues with overrides are fatal since they'll cause errors in expansion,
117
123
// so we return early if we see any.
118
- let override_errors = validate_overrides ( validation_results ) ;
124
+ let override_errors = validate_overrides ( parsed_schemas ) ;
119
125
if !override_errors. is_empty ( ) {
120
126
self . add_issues ( override_errors. into_iter ( ) ) ;
121
127
return ;
@@ -168,16 +174,20 @@ pub trait HybridComposition {
168
174
}
169
175
}
170
176
177
+ struct SubgraphSchema {
178
+ schema : Schema ,
179
+ has_connectors : bool ,
180
+ }
181
+
171
182
/// Validate overrides for connector-related subgraphs
172
183
///
173
184
/// Overrides mess with the supergraph in ways that can be difficult to detect when
174
185
/// expanding connectors; the supergraph may omit overridden fields and other shenanigans.
175
186
/// To allow for a better developer experience, we check here if any connector-enabled subgraphs
176
187
/// have fields overridden.
177
- fn validate_overrides ( schemas : impl IntoIterator < Item = ( String , ValidationResult ) > ) -> Vec < Issue > {
178
- let validations_by_subgraph_name = HashMap :: < _ , _ > :: from_iter ( schemas) ;
188
+ fn validate_overrides ( schemas : HashMap < String , SubgraphSchema > ) -> Vec < Issue > {
179
189
let mut override_errors = Vec :: new ( ) ;
180
- for ( subgraph_name, ValidationResult { schema, .. } ) in validations_by_subgraph_name . iter ( ) {
190
+ for ( subgraph_name, SubgraphSchema { schema, .. } ) in & schemas {
181
191
// We need to grab all fields in the schema since only fields can have the @override
182
192
// directive attached
183
193
macro_rules! extract_directives {
@@ -218,32 +228,31 @@ fn validate_overrides(schemas: impl IntoIterator<Item = (String, ValidationResul
218
228
for ( field, directive) in override_directives {
219
229
// If the override directive does not have a valid `from` field, then there is
220
230
// no point trying to validate it, as later steps will validate the entire schema.
221
- let Ok ( Some ( overriden_subgraph_name ) ) = directive
231
+ let Ok ( Some ( overridden_subgraph_name ) ) = directive
222
232
. argument_by_name ( "from" , schema)
223
233
. map ( |node| node. as_str ( ) )
224
234
else {
225
235
continue ;
226
236
} ;
227
237
228
- if let Some ( overriden_subgraph) =
229
- validations_by_subgraph_name. get ( overriden_subgraph_name)
238
+ if schemas
239
+ . get ( overridden_subgraph_name)
240
+ . is_some_and ( |schema| schema. has_connectors )
230
241
{
231
- if overriden_subgraph. has_connectors {
232
- override_errors. push ( Issue {
242
+ override_errors. push ( Issue {
233
243
code : "OVERRIDE_ON_CONNECTOR" . to_string ( ) ,
234
244
message : format ! (
235
245
r#"Field "{}" on subgraph "{}" is trying to override connector-enabled subgraph "{}", which is not yet supported. See https://go.apollo.dev/connectors/limitations#override-is-partially-unsupported"# ,
236
246
field,
237
247
subgraph_name,
238
- overriden_subgraph_name ,
248
+ overridden_subgraph_name ,
239
249
) ,
240
250
locations : vec ! [ SubgraphLocation {
241
- subgraph: Some ( subgraph_name . clone ( ) ) ,
251
+ subgraph: Some ( String :: from ( overridden_subgraph_name ) ) ,
242
252
range: directive. line_column_range( & schema. sources) ,
243
253
} ] ,
244
254
severity : Severity :: Error ,
245
255
} ) ;
246
- }
247
256
}
248
257
}
249
258
}
0 commit comments