@@ -7,13 +7,11 @@ use apollo_compiler::ast::DirectiveLocation;
7
7
use apollo_compiler:: collections:: HashSet ;
8
8
use itertools:: Itertools ;
9
9
10
- use crate :: bail;
11
10
use crate :: error:: FederationError ;
12
11
use crate :: merger:: hints:: HintCode ;
13
12
use crate :: merger:: merge:: Merger ;
14
13
use crate :: merger:: merge:: Sources ;
15
14
use crate :: merger:: merge:: map_sources;
16
- use crate :: schema:: position:: DirectiveArgumentDefinitionPosition ;
17
15
use crate :: schema:: position:: DirectiveDefinitionPosition ;
18
16
use crate :: schema:: referencer:: DirectiveReferencers ;
19
17
use crate :: subgraph:: typestate:: Subgraph ;
@@ -180,17 +178,30 @@ impl Merger {
180
178
{
181
179
self . merge_custom_core_directive ( name) ?;
182
180
} else {
183
- let sources = self . get_sources_for_directive ( name) ?;
181
+ let sources = self
182
+ . subgraphs
183
+ . iter ( )
184
+ . enumerate ( )
185
+ . filter_map ( |( idx, subgraph) | {
186
+ subgraph
187
+ . schema ( )
188
+ . get_directive_definition ( name)
189
+ . map ( |def| ( idx, Some ( def) ) )
190
+ } )
191
+ . collect ( ) ;
184
192
if Self :: some_sources ( & sources, |source, idx| {
185
193
let Some ( source) = source else {
186
194
return false ;
187
195
} ;
188
- self . is_merged_directive_definition ( & self . names [ idx] , source)
196
+ let Some ( def) = source. try_get ( self . subgraphs [ idx] . schema ( ) . schema ( ) ) else {
197
+ return false ;
198
+ } ;
199
+ self . is_merged_directive_definition ( & self . names [ idx] , def)
189
200
} ) {
190
201
self . merge_executable_directive_definition (
191
202
name,
192
203
& sources,
193
- DirectiveDefinitionPosition {
204
+ & DirectiveDefinitionPosition {
194
205
directive_name : name. clone ( ) ,
195
206
} ,
196
207
) ?;
@@ -203,59 +214,63 @@ impl Merger {
203
214
& mut self ,
204
215
name : & Name ,
205
216
) -> Result < ( ) , FederationError > {
206
- let def = self
217
+ let Some ( def) = self
207
218
. compose_directive_manager
208
- . get_latest_directive_definition ( name) ?;
209
- let Some ( def ) = def else {
219
+ . get_latest_directive_definition ( name) ?
220
+ else {
210
221
return Ok ( ( ) ) ;
211
222
} ;
212
- let Some ( target) = self . merged . get_directive_definition ( name) else {
213
- bail ! ( "Directive definition not found in supergraph" ) ;
214
- } ;
215
223
224
+ let dest = DirectiveDefinitionPosition {
225
+ directive_name : name. clone ( ) ,
226
+ } ;
216
227
// This replaces the calls to target.set_description, target.set_repeatable, and target.add_locations in the JS implementation
217
- target . insert ( & mut self . merged , def. clone ( ) ) ?;
228
+ dest . insert ( & mut self . merged , def. clone ( ) ) ?;
218
229
219
- let mut sources: Sources < Node < DirectiveDefinition > > = Default :: default ( ) ;
220
- sources. insert ( 0 , Some ( def. clone ( ) ) ) ;
221
- self . add_arguments_shallow_placeholder ( & sources, & target) ;
230
+ let sources = self
231
+ . subgraphs
232
+ . iter ( )
233
+ . enumerate ( )
234
+ . map ( |( idx, subgraph) | ( idx, subgraph. schema ( ) . get_directive_definition ( name) ) )
235
+ . collect ( ) ;
236
+ let arg_names = self . add_arguments_shallow ( & sources, & dest) ?;
222
237
223
- for arg in & def. arguments {
224
- let dest_arg = target. argument ( arg. name . clone ( ) ) ;
225
- let sources = map_sources ( & self . subgraph_sources ( ) , |subgraph| {
226
- subgraph
227
- . as_ref ( )
228
- . and_then ( |s| dest_arg. get ( s. schema ( ) . schema ( ) ) . ok ( ) . cloned ( ) )
238
+ for arg_name in arg_names {
239
+ let sources = map_sources ( & sources, |source| {
240
+ source. as_ref ( ) . map ( |s| s. argument ( arg_name. clone ( ) ) )
229
241
} ) ;
230
- self . merge_directive_argument ( & sources, & dest_arg) ?;
242
+ let dest_arg = dest. argument ( arg_name) ;
243
+ self . merge_argument ( & sources, & dest_arg) ?;
231
244
}
232
245
Ok ( ( ) )
233
246
}
234
247
235
248
fn merge_executable_directive_definition (
236
249
& mut self ,
237
250
name : & Name ,
238
- sources : & Sources < Node < DirectiveDefinition > > ,
239
- dest : DirectiveDefinitionPosition ,
251
+ sources : & Sources < DirectiveDefinitionPosition > ,
252
+ dest : & DirectiveDefinitionPosition ,
240
253
) -> Result < ( ) , FederationError > {
241
254
let mut repeatable: Option < bool > = None ;
242
255
let mut inconsistent_repeatable = false ;
243
256
let mut locations: Vec < DirectiveLocation > = Vec :: new ( ) ;
244
257
let mut inconsistent_locations = false ;
245
258
246
259
let supergraph_dest = dest. get ( self . merged . schema ( ) ) ?. clone ( ) ;
247
- let position_sources = map_sources ( sources, |_| Some ( dest. clone ( ) ) ) ;
248
260
249
- for ( _, source) in sources {
250
- let Some ( source) = source else {
261
+ for ( idx, source) in sources {
262
+ let Some ( source) = source
263
+ . as_ref ( )
264
+ . and_then ( |s| s. try_get ( self . subgraphs [ * idx] . schema ( ) . schema ( ) ) )
265
+ else {
251
266
// An executable directive could appear in any place of a query and thus get to any subgraph, so we cannot keep an
252
267
// executable directive unless it is in all subgraphs. We use an 'intersection' strategy.
253
268
dest. remove ( & mut self . merged ) ?;
254
269
self . error_reporter . report_mismatch_hint :: < DirectiveDefinitionPosition , DirectiveDefinitionPosition , ( ) > (
255
270
HintCode :: InconsistentExecutableDirectivePresence ,
256
271
format ! ( "Executable directive \" {name}\" will not be part of the supergraph as it does not appear in all subgraphs: " ) ,
257
- & dest,
258
- & position_sources ,
272
+ dest,
273
+ sources ,
259
274
|_elt| Some ( "yes" . to_string ( ) ) ,
260
275
|_elt, _idx| Some ( "yes" . to_string ( ) ) ,
261
276
|_, subgraphs| format ! ( "it is defined in {}" , subgraphs. unwrap_or_default( ) ) ,
@@ -285,13 +300,14 @@ impl Merger {
285
300
locations. retain ( |loc| source_locations. contains ( loc) ) ;
286
301
287
302
if locations. is_empty ( ) {
288
- self . error_reporter . report_mismatch_hint :: < Node < DirectiveDefinition > , Node < DirectiveDefinition > , ( ) > (
303
+ self . error_reporter . report_mismatch_hint :: < Node < DirectiveDefinition > , DirectiveDefinitionPosition , ( ) > (
289
304
HintCode :: NoExecutableDirectiveLocationsIntersection ,
290
305
format ! ( "Executable directive \" {name}\" has no location that is common to all subgraphs: " ) ,
291
306
& supergraph_dest,
292
307
sources,
293
308
|elt| Some ( location_string ( & extract_executable_locations ( elt) ) ) ,
294
- |elt, _idx| Some ( location_string ( & extract_executable_locations ( elt) ) ) ,
309
+ |pos, idx| pos. try_get ( self . subgraphs [ idx] . schema ( ) . schema ( ) )
310
+ . map ( |elt| location_string ( & extract_executable_locations ( elt) ) ) ,
295
311
|_, _subgraphs| "it will not appear in the supergraph as there no intersection between " . to_string ( ) ,
296
312
|locs, subgraphs| format ! ( "{locs} in {subgraphs}" ) ,
297
313
false ,
@@ -303,32 +319,33 @@ impl Merger {
303
319
dest. set_repeatable ( & mut self . merged , repeatable. unwrap_or_default ( ) ) ?; // repeatable will always be Some() here
304
320
dest. add_locations ( & mut self . merged , & locations) ?;
305
321
306
- self . merge_description ( & position_sources , & dest) ?;
322
+ self . merge_description ( sources , dest) ?;
307
323
308
324
if inconsistent_repeatable {
309
- self . error_reporter . report_mismatch_hint :: < Node < DirectiveDefinition > , Node < DirectiveDefinition > , ( ) > (
325
+ self . error_reporter . report_mismatch_hint :: < Node < DirectiveDefinition > , DirectiveDefinitionPosition , ( ) > (
310
326
HintCode :: InconsistentExecutableDirectiveRepeatable ,
311
327
format ! ( "Executable directive \" {name}\" will not be marked repeatable in the supergraph as it is inconsistently marked repeatable in subgraphs: " ) ,
312
328
& supergraph_dest,
313
329
sources,
314
330
|elt| if elt. repeatable { Some ( "yes" . to_string ( ) ) } else { Some ( "no" . to_string ( ) ) } ,
315
- |elt, _idx| if elt. repeatable { Some ( "yes" . to_string ( ) ) } else { Some ( "no" . to_string ( ) ) } ,
331
+ |pos, idx| pos. try_get ( self . subgraphs [ idx] . schema ( ) . schema ( ) )
332
+ . map ( |elt| if elt. repeatable { "yes" . to_string ( ) } else { "no" . to_string ( ) } ) ,
316
333
|_, subgraphs| format ! ( "it is not repeatable in {}" , subgraphs. unwrap_or_default( ) ) ,
317
334
|_, subgraphs| format ! ( " but is repeatable in {}" , subgraphs) ,
318
335
false ,
319
336
false ,
320
337
) ;
321
338
}
322
339
if inconsistent_locations {
323
- self . error_reporter . report_mismatch_hint :: < Node < DirectiveDefinition > , Node < DirectiveDefinition > , ( ) > (
340
+ self . error_reporter . report_mismatch_hint :: < Node < DirectiveDefinition > , DirectiveDefinitionPosition , ( ) > (
324
341
HintCode :: InconsistentExecutableDirectiveLocations ,
325
342
format ! (
326
343
"Executable directive \" {name}\" has inconsistent locations across subgraphs: "
327
344
) ,
328
345
& supergraph_dest,
329
346
sources,
330
347
|elt| Some ( location_string ( & extract_executable_locations ( elt) ) ) ,
331
- |elt , _idx| Some ( location_string ( & extract_executable_locations ( elt) ) ) ,
348
+ |pos , idx| pos . try_get ( self . subgraphs [ idx ] . schema ( ) . schema ( ) ) . map ( |elt| location_string ( & extract_executable_locations ( elt) ) ) ,
332
349
|_, _subgraphs| {
333
350
"it will not appear in the supergraph as there no intersection between "
334
351
. to_string ( )
@@ -340,19 +357,12 @@ impl Merger {
340
357
}
341
358
342
359
// Doing args last, mostly so we don't bother adding if the directive doesn't make it in.
343
- self . add_arguments_shallow ( & position_sources , & dest) ?;
360
+ self . add_arguments_shallow ( sources , dest) ?;
344
361
for arg in & supergraph_dest. arguments {
345
362
let subgraph_args = map_sources ( sources, |src| {
346
- src. as_ref ( )
347
- . and_then ( |src| src. arguments . iter ( ) . find ( |a| a. name == arg. name ) . cloned ( ) )
363
+ src. as_ref ( ) . map ( |src| src. argument ( arg. name . clone ( ) ) )
348
364
} ) ;
349
- self . merge_directive_argument (
350
- & subgraph_args,
351
- & DirectiveArgumentDefinitionPosition {
352
- directive_name : name. clone ( ) ,
353
- argument_name : arg. name . clone ( ) ,
354
- } ,
355
- ) ?;
365
+ self . merge_argument ( & subgraph_args, & dest. argument ( arg. name . clone ( ) ) ) ?;
356
366
}
357
367
Ok ( ( ) )
358
368
}
@@ -373,34 +383,6 @@ impl Merger {
373
383
self . applied_directives_to_merge . clear ( ) ;
374
384
Ok ( ( ) )
375
385
}
376
-
377
- fn get_sources_for_directive (
378
- & self ,
379
- name : & Name ,
380
- ) -> Result < Sources < Node < DirectiveDefinition > > , FederationError > {
381
- let sources = self
382
- . subgraphs
383
- . iter ( )
384
- . enumerate ( )
385
- . filter_map ( |( index, subgraph) | {
386
- subgraph
387
- . schema ( )
388
- . schema ( )
389
- . directive_definitions
390
- . get ( name)
391
- . map ( |directive_def| ( index, Some ( directive_def. clone ( ) ) ) )
392
- } )
393
- . collect ( ) ;
394
- Ok ( sources)
395
- }
396
-
397
- fn add_arguments_shallow_placeholder (
398
- & self ,
399
- _sources : & Sources < Node < DirectiveDefinition > > ,
400
- _dest : & DirectiveDefinitionPosition ,
401
- ) {
402
- todo ! ( "Implement add_arguments_shallow_placeholder" )
403
- }
404
386
}
405
387
406
388
fn extract_executable_locations ( source : & Node < DirectiveDefinition > ) -> Vec < DirectiveLocation > {
0 commit comments