@apollo/gateway 2.0.0-alpha.5 → 2.0.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +4 -2
  4. package/dist/index.js.map +1 -1
  5. package/dist/supergraphManagers/LegacyFetcher/index.js +1 -1
  6. package/dist/supergraphManagers/LocalCompose/index.js +1 -1
  7. package/dist/supergraphManagers/LocalCompose/index.js.map +1 -1
  8. package/dist/supergraphManagers/UplinkFetcher/index.d.ts +1 -0
  9. package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -1
  10. package/dist/supergraphManagers/UplinkFetcher/index.js +2 -0
  11. package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -1
  12. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts +5 -1
  13. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -1
  14. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js +29 -31
  15. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -1
  16. package/dist/supergraphManagers/index.d.ts +1 -0
  17. package/dist/supergraphManagers/index.d.ts.map +1 -1
  18. package/dist/supergraphManagers/index.js +3 -1
  19. package/dist/supergraphManagers/index.js.map +1 -1
  20. package/package.json +5 -4
  21. package/src/__tests__/build-query-plan.feature +52 -0
  22. package/src/__tests__/integration/nockMocks.ts +3 -2
  23. package/src/index.ts +4 -1
  24. package/src/supergraphManagers/LegacyFetcher/index.ts +1 -1
  25. package/src/supergraphManagers/LocalCompose/index.ts +1 -1
  26. package/src/supergraphManagers/UplinkFetcher/__tests__/loadSupergraphSdlFromStorage.test.ts +82 -28
  27. package/src/supergraphManagers/UplinkFetcher/index.ts +2 -0
  28. package/src/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts +31 -26
  29. package/src/supergraphManagers/index.ts +1 -0
@@ -27,6 +27,7 @@ Scenario: should not confuse union types with overlapping field names
27
27
  "kind": "Fetch",
28
28
  "serviceName": "documents",
29
29
  "variableUsages": [],
30
+ "operationKind": "query",
30
31
  "operation": "{body{__typename ...on Image{attributes{url}}...on Text{attributes{bold text}}}}"
31
32
  }
32
33
  }
@@ -51,6 +52,7 @@ Scenario: should use a single fetch when requesting a root field from one servic
51
52
  "kind": "Fetch",
52
53
  "serviceName": "accounts",
53
54
  "variableUsages": [],
55
+ "operationKind": "query",
54
56
  "operation": "{me{name{first}}}"
55
57
  }
56
58
  }
@@ -81,6 +83,7 @@ Scenario: should use two independent fetches when requesting root fields from tw
81
83
  "kind": "Fetch",
82
84
  "serviceName": "accounts",
83
85
  "variableUsages": [],
86
+ "operationKind": "query",
84
87
  "operation": "{me{name{first}}}"
85
88
  },
86
89
  {
@@ -90,6 +93,7 @@ Scenario: should use two independent fetches when requesting root fields from tw
90
93
  "kind": "Fetch",
91
94
  "serviceName": "product",
92
95
  "variableUsages": [],
96
+ "operationKind": "query",
93
97
  "operation": "{topProducts{__typename ...on Book{__typename isbn}...on Furniture{name}}}"
94
98
  },
95
99
  {
@@ -109,6 +113,7 @@ Scenario: should use two independent fetches when requesting root fields from tw
109
113
  }
110
114
  ],
111
115
  "variableUsages": [],
116
+ "operationKind": "query",
112
117
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}"
113
118
  }
114
119
  },
@@ -131,6 +136,7 @@ Scenario: should use two independent fetches when requesting root fields from tw
131
136
  }
132
137
  ],
133
138
  "variableUsages": [],
139
+ "operationKind": "query",
134
140
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}"
135
141
  }
136
142
  }
@@ -164,6 +170,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th
164
170
  "kind": "Fetch",
165
171
  "serviceName": "product",
166
172
  "variableUsages": [],
173
+ "operationKind": "query",
167
174
  "operation": "{topProducts{__typename ...on Book{__typename isbn}...on Furniture{name}}product(upc:\"1\"){__typename ...on Book{__typename isbn}...on Furniture{name}}}"
168
175
  },
169
176
  {
@@ -189,6 +196,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th
189
196
  }
190
197
  ],
191
198
  "variableUsages": [],
199
+ "operationKind": "query",
192
200
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}"
193
201
  }
194
202
  },
@@ -211,6 +219,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th
211
219
  }
212
220
  ],
213
221
  "variableUsages": [],
222
+ "operationKind": "query",
214
223
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}"
215
224
  }
216
225
  }
@@ -236,6 +245,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th
236
245
  }
237
246
  ],
238
247
  "variableUsages": [],
248
+ "operationKind": "query",
239
249
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{title year}}}"
240
250
  }
241
251
  },
@@ -258,6 +268,7 @@ Scenario: should use a single fetch when requesting multiple root fields from th
258
268
  }
259
269
  ],
260
270
  "variableUsages": [],
271
+ "operationKind": "query",
261
272
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}"
262
273
  }
263
274
  }
@@ -292,6 +303,7 @@ Scenario: should use a single fetch when requesting relationship subfields from
292
303
  "kind": "Fetch",
293
304
  "serviceName": "reviews",
294
305
  "variableUsages": [],
306
+ "operationKind": "query",
295
307
  "operation": "{topReviews{body author{reviews{body}}}}"
296
308
  }
297
309
  }
@@ -320,6 +332,7 @@ Scenario: should use a single fetch when requesting relationship subfields and p
320
332
  "kind": "Fetch",
321
333
  "serviceName": "reviews",
322
334
  "variableUsages": [],
335
+ "operationKind": "query",
323
336
  "operation": "{topReviews{body author{id reviews{body}}}}"
324
337
  }
325
338
  }
@@ -350,6 +363,7 @@ Scenario: when requesting an extension field from another service, it should add
350
363
  "kind": "Fetch",
351
364
  "serviceName": "accounts",
352
365
  "variableUsages": [],
366
+ "operationKind": "query",
353
367
  "operation": "{me{__typename id name{first}}}"
354
368
  },
355
369
  {
@@ -369,6 +383,7 @@ Scenario: when requesting an extension field from another service, it should add
369
383
  }
370
384
  ],
371
385
  "variableUsages": [],
386
+ "operationKind": "query",
372
387
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{body}}}}"
373
388
  }
374
389
  }
@@ -399,6 +414,7 @@ Scenario: when requesting an extension field from another service, when the pare
399
414
  "kind": "Fetch",
400
415
  "serviceName": "accounts",
401
416
  "variableUsages": [],
417
+ "operationKind": "query",
402
418
  "operation": "{me{__typename id}}"
403
419
  },
404
420
  {
@@ -418,6 +434,7 @@ Scenario: when requesting an extension field from another service, when the pare
418
434
  }
419
435
  ],
420
436
  "variableUsages": [],
437
+ "operationKind": "query",
421
438
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{body}}}}"
422
439
  }
423
440
  }
@@ -449,6 +466,7 @@ Scenario: when requesting an extension field from another service, should only a
449
466
  "kind": "Fetch",
450
467
  "serviceName": "accounts",
451
468
  "variableUsages": [],
469
+ "operationKind": "query",
452
470
  "operation": "{me{__typename id}}"
453
471
  },
454
472
  {
@@ -468,6 +486,7 @@ Scenario: when requesting an extension field from another service, should only a
468
486
  }
469
487
  ],
470
488
  "variableUsages": [],
489
+ "operationKind": "query",
471
490
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{body}numberOfReviews}}}"
472
491
  }
473
492
  }
@@ -501,6 +520,7 @@ Scenario: when requesting a composite field with subfields from another service,
501
520
  "kind": "Fetch",
502
521
  "serviceName": "reviews",
503
522
  "variableUsages": [],
523
+ "operationKind": "query",
504
524
  "operation": "{topReviews{body author{__typename id}}}"
505
525
  },
506
526
  {
@@ -520,6 +540,7 @@ Scenario: when requesting a composite field with subfields from another service,
520
540
  }
521
541
  ],
522
542
  "variableUsages": [],
543
+ "operationKind": "query",
523
544
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{name{first}}}}"
524
545
  }
525
546
  }
@@ -548,6 +569,7 @@ Scenario: when requesting a composite field with subfields from another service,
548
569
  "kind": "Fetch",
549
570
  "serviceName": "product",
550
571
  "variableUsages": [],
572
+ "operationKind": "query",
551
573
  "operation": "{topCars{__typename id price}}"
552
574
  },
553
575
  {
@@ -568,6 +590,7 @@ Scenario: when requesting a composite field with subfields from another service,
568
590
  }
569
591
  ],
570
592
  "variableUsages": [],
593
+ "operationKind": "query",
571
594
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Car{retailPrice}}}"
572
595
  }
573
596
  }
@@ -600,6 +623,7 @@ Scenario: when requesting a composite field with subfields from another service,
600
623
  "kind": "Fetch",
601
624
  "serviceName": "reviews",
602
625
  "variableUsages": [],
626
+ "operationKind": "query",
603
627
  "operation": "{topReviews{author{__typename id}}}"
604
628
  },
605
629
  {
@@ -619,6 +643,7 @@ Scenario: when requesting a composite field with subfields from another service,
619
643
  }
620
644
  ],
621
645
  "variableUsages": [],
646
+ "operationKind": "query",
622
647
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{name{first}}}}"
623
648
  }
624
649
  }
@@ -649,6 +674,7 @@ Scenario: when requesting a relationship field with extension subfields from a d
649
674
  "kind": "Fetch",
650
675
  "serviceName": "reviews",
651
676
  "variableUsages": [],
677
+ "operationKind": "query",
652
678
  "operation": "{topReviews{author{__typename id}}}"
653
679
  },
654
680
  {
@@ -668,6 +694,7 @@ Scenario: when requesting a relationship field with extension subfields from a d
668
694
  }
669
695
  ],
670
696
  "variableUsages": [],
697
+ "operationKind": "query",
671
698
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{birthDate}}}"
672
699
  }
673
700
  }
@@ -693,6 +720,7 @@ Scenario: for abstract types, it should add __typename when fetching objects of
693
720
  "kind": "Fetch",
694
721
  "serviceName": "product",
695
722
  "variableUsages": [],
723
+ "operationKind": "query",
696
724
  "operation": "{topProducts{__typename price}}"
697
725
  }
698
726
  }
@@ -721,6 +749,7 @@ Scenario: should break up when traversing an extension field on an interface typ
721
749
  "kind": "Fetch",
722
750
  "serviceName": "product",
723
751
  "variableUsages": [],
752
+ "operationKind": "query",
724
753
  "operation": "{topProducts{__typename price ...on Book{__typename isbn}...on Furniture{__typename upc}}}"
725
754
  },
726
755
  {
@@ -748,6 +777,7 @@ Scenario: should break up when traversing an extension field on an interface typ
748
777
  }
749
778
  ],
750
779
  "variableUsages": [],
780
+ "operationKind": "query",
751
781
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{reviews{body}}...on Furniture{reviews{body}}}}"
752
782
  }
753
783
  }
@@ -781,6 +811,7 @@ Scenario: interface fragments should expand into possible types only
781
811
  "kind": "Fetch",
782
812
  "serviceName": "books",
783
813
  "variableUsages": [],
814
+ "operationKind": "query",
784
815
  "operation": "{books{__typename isbn title year}}"
785
816
  },
786
817
  {
@@ -802,6 +833,7 @@ Scenario: interface fragments should expand into possible types only
802
833
  }
803
834
  ],
804
835
  "variableUsages": [],
836
+ "operationKind": "query",
805
837
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{name}}}"
806
838
  }
807
839
  }
@@ -829,6 +861,7 @@ Scenario: interface inside interface should expand into possible types only
829
861
  "kind": "Fetch",
830
862
  "serviceName": "product",
831
863
  "variableUsages": [],
864
+ "operationKind": "query",
832
865
  "operation": "{product(upc:\"\"){__typename details{__typename country}}}"
833
866
  }
834
867
  }
@@ -870,6 +903,7 @@ Scenario: should properly expand nested unions with inline fragments
870
903
  "kind": "Fetch",
871
904
  "serviceName": "documents",
872
905
  "variableUsages": [],
906
+ "operationKind": "query",
873
907
  "operation": "{body{__typename ...on Image{attributes{url}}...on Text{attributes{bold}}}}"
874
908
  }
875
909
  }
@@ -912,6 +946,7 @@ Scenario: deduplicates fields / selections regardless of adjacency and type cond
912
946
  "kind": "Fetch",
913
947
  "serviceName": "documents",
914
948
  "variableUsages": [],
949
+ "operationKind": "query",
915
950
  "operation": "{body{__typename ...on Text{attributes{bold text}}}}"
916
951
  }
917
952
  }
@@ -947,6 +982,7 @@ Scenario: deduplicates fields / selections regardless of adjacency and type cond
947
982
  "kind": "Fetch",
948
983
  "serviceName": "documents",
949
984
  "variableUsages": [],
985
+ "operationKind": "query",
950
986
  "operation": "{body{__typename ...on Text{attributes{bold text}}}}"
951
987
  }
952
988
  }
@@ -972,6 +1008,7 @@ Scenario: supports basic, single-service mutation
972
1008
  "username",
973
1009
  "password"
974
1010
  ],
1011
+ "operationKind": "mutation",
975
1012
  "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){id}}"
976
1013
  }
977
1014
  }
@@ -1005,6 +1042,7 @@ Scenario: supports mutations with a cross-service request
1005
1042
  "username",
1006
1043
  "password"
1007
1044
  ],
1045
+ "operationKind": "mutation",
1008
1046
  "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){__typename id}}"
1009
1047
  },
1010
1048
  {
@@ -1032,6 +1070,7 @@ Scenario: supports mutations with a cross-service request
1032
1070
  }
1033
1071
  ],
1034
1072
  "variableUsages": [],
1073
+ "operationKind": "query",
1035
1074
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{upc}}}}}}"
1036
1075
  }
1037
1076
  },
@@ -1063,6 +1102,7 @@ Scenario: supports mutations with a cross-service request
1063
1102
  }
1064
1103
  ],
1065
1104
  "variableUsages": [],
1105
+ "operationKind": "query",
1066
1106
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{upc}}}"
1067
1107
  }
1068
1108
  }
@@ -1097,6 +1137,7 @@ Scenario: returning across service boundaries
1097
1137
  "upc",
1098
1138
  "body"
1099
1139
  ],
1140
+ "operationKind": "mutation",
1100
1141
  "operation": "mutation($upc:String!$body:String!){reviewProduct(input:{upc:$upc body:$body}){__typename ...on Furniture{__typename upc}}}"
1101
1142
  },
1102
1143
  {
@@ -1124,6 +1165,7 @@ Scenario: returning across service boundaries
1124
1165
  }
1125
1166
  ],
1126
1167
  "variableUsages": [],
1168
+ "operationKind": "query",
1127
1169
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Furniture{name}}}"
1128
1170
  }
1129
1171
  }
@@ -1170,6 +1212,7 @@ Scenario: supports multiple root mutations
1170
1212
  "username",
1171
1213
  "password"
1172
1214
  ],
1215
+ "operationKind": "mutation",
1173
1216
  "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){__typename id}}"
1174
1217
  },
1175
1218
  {
@@ -1197,6 +1240,7 @@ Scenario: supports multiple root mutations
1197
1240
  }
1198
1241
  ],
1199
1242
  "variableUsages": [],
1243
+ "operationKind": "query",
1200
1244
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{upc}}}}}}"
1201
1245
  }
1202
1246
  },
@@ -1228,6 +1272,7 @@ Scenario: supports multiple root mutations
1228
1272
  }
1229
1273
  ],
1230
1274
  "variableUsages": [],
1275
+ "operationKind": "query",
1231
1276
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{upc}}}"
1232
1277
  }
1233
1278
  },
@@ -1238,6 +1283,7 @@ Scenario: supports multiple root mutations
1238
1283
  "upc",
1239
1284
  "body"
1240
1285
  ],
1286
+ "operationKind": "mutation",
1241
1287
  "operation": "mutation($upc:String!$body:String!){reviewProduct(input:{upc:$upc body:$body}){__typename ...on Furniture{__typename upc}}}"
1242
1288
  },
1243
1289
  {
@@ -1265,6 +1311,7 @@ Scenario: supports multiple root mutations
1265
1311
  }
1266
1312
  ],
1267
1313
  "variableUsages": [],
1314
+ "operationKind": "query",
1268
1315
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Furniture{name}}}"
1269
1316
  }
1270
1317
  }
@@ -1319,6 +1366,7 @@ Scenario: multiple root mutations with correct service order
1319
1366
  "body",
1320
1367
  "updatedReview"
1321
1368
  ],
1369
+ "operationKind": "mutation",
1322
1370
  "operation": "mutation($upc:String!$body:String!$updatedReview:UpdateReviewInput!){reviewProduct(input:{upc:$upc body:$body}){__typename ...on Furniture{upc}}updateReview(review:$updatedReview){id body}}"
1323
1371
  },
1324
1372
  {
@@ -1328,6 +1376,7 @@ Scenario: multiple root mutations with correct service order
1328
1376
  "username",
1329
1377
  "password"
1330
1378
  ],
1379
+ "operationKind": "mutation",
1331
1380
  "operation": "mutation($username:String!$password:String!){login(username:$username password:$password){__typename id}}"
1332
1381
  },
1333
1382
  {
@@ -1355,6 +1404,7 @@ Scenario: multiple root mutations with correct service order
1355
1404
  }
1356
1405
  ],
1357
1406
  "variableUsages": [],
1407
+ "operationKind": "query",
1358
1408
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on User{reviews{product{__typename ...on Book{__typename isbn}...on Furniture{upc}}}}}}"
1359
1409
  }
1360
1410
  },
@@ -1386,6 +1436,7 @@ Scenario: multiple root mutations with correct service order
1386
1436
  }
1387
1437
  ],
1388
1438
  "variableUsages": [],
1439
+ "operationKind": "query",
1389
1440
  "operation": "query($representations:[_Any!]!){_entities(representations:$representations){...on Book{upc}}}"
1390
1441
  }
1391
1442
  },
@@ -1395,6 +1446,7 @@ Scenario: multiple root mutations with correct service order
1395
1446
  "variableUsages": [
1396
1447
  "reviewId"
1397
1448
  ],
1449
+ "operationKind": "mutation",
1398
1450
  "operation": "mutation($reviewId:ID!){deleteReview(id:$reviewId)}"
1399
1451
  }
1400
1452
  ]
@@ -121,9 +121,10 @@ export function mockSupergraphSdlRequestSuccessIfAfter(
121
121
  }
122
122
 
123
123
  export function mockSupergraphSdlRequestIfAfterUnchanged(
124
- ifAfter: string | null = null,
124
+ ifAfter: string | null = null,
125
+ url: string = mockCloudConfigUrl1,
125
126
  ) {
126
- return mockSupergraphSdlRequestIfAfter(ifAfter).reply(
127
+ return mockSupergraphSdlRequestIfAfter(ifAfter, url).reply(
127
128
  200,
128
129
  JSON.stringify({
129
130
  data: {
package/src/index.ts CHANGED
@@ -402,7 +402,7 @@ export class ApolloGateway implements GraphQLService {
402
402
  apiKey: this.apolloConfig!.key!,
403
403
  uplinkEndpoints,
404
404
  maxRetries:
405
- this.config.uplinkMaxRetries ?? uplinkEndpoints.length * 3,
405
+ this.config.uplinkMaxRetries ?? uplinkEndpoints.length * 3 - 1, // -1 for the initial request
406
406
  subgraphHealthCheck: this.config.serviceHealthCheck,
407
407
  fetcher: this.fetcher,
408
408
  logger: this.logger,
@@ -1127,3 +1127,6 @@ export {
1127
1127
  SupergraphSdlHook,
1128
1128
  SupergraphManager
1129
1129
  } from './config';
1130
+
1131
+ export { UplinkFetcherError } from "./supergraphManagers"
1132
+
@@ -219,7 +219,7 @@ export class LegacyFetcher implements SupergraphManager {
219
219
 
220
220
  private logUpdateFailure(e: any) {
221
221
  this.config.logger?.error(
222
- 'UplinkFetcher failed to update supergraph with the following error: ' +
222
+ 'LegacyFetcher failed to update supergraph with the following error: ' +
223
223
  (e.message ?? e),
224
224
  );
225
225
  }
@@ -72,7 +72,7 @@ export class LocalCompose implements SupergraphManager {
72
72
 
73
73
  private logUpdateFailure(e: any) {
74
74
  this.config.logger?.error(
75
- 'UplinkFetcher failed to update supergraph with the following error: ' +
75
+ 'LocalCompose failed to update supergraph with the following error: ' +
76
76
  (e.message ?? e),
77
77
  );
78
78
  }
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  loadSupergraphSdlFromStorage,
3
- loadSupergraphSdlFromUplinks
3
+ loadSupergraphSdlFromUplinks,
4
+ UplinkFetcherError,
4
5
  } from '../loadSupergraphSdlFromStorage';
5
6
  import { getDefaultFetcher } from '../../..';
6
7
  import {
@@ -62,7 +63,8 @@ describe('loadSupergraphSdlFromStorage', () => {
62
63
  errorReportingEndpoint: undefined,
63
64
  fetcher,
64
65
  compositionId: "originalId-1234",
65
- maxRetries: 1
66
+ maxRetries: 1,
67
+ roundRobinSeed: 0,
66
68
  });
67
69
 
68
70
  expect(result).toMatchObject({
@@ -84,10 +86,13 @@ describe('loadSupergraphSdlFromStorage', () => {
84
86
  errorReportingEndpoint: undefined,
85
87
  fetcher,
86
88
  compositionId: "originalId-1234",
87
- maxRetries: 1
89
+ maxRetries: 1,
90
+ roundRobinSeed: 0,
88
91
  }),
89
- ).rejects.toThrowErrorMatchingInlineSnapshot(
90
- `"An error occurred while fetching your schema from Apollo: 500 Internal Server Error"`,
92
+ ).rejects.toThrowError(
93
+ new UplinkFetcherError(
94
+ "An error occurred while fetching your schema from Apollo: 500 Internal Server Error",
95
+ )
91
96
  );
92
97
  })
93
98
 
@@ -105,8 +110,10 @@ describe('loadSupergraphSdlFromStorage', () => {
105
110
  fetcher,
106
111
  compositionId: null,
107
112
  }),
108
- ).rejects.toThrowErrorMatchingInlineSnapshot(
109
- `"An error occurred while fetching your schema from Apollo: 200 invalid json response body at https://example1.cloud-config-url.com/cloudconfig/ reason: Unexpected token I in JSON at position 0"`,
113
+ ).rejects.toThrowError(
114
+ new UplinkFetcherError(
115
+ "An error occurred while fetching your schema from Apollo: 200 invalid json response body at https://example1.cloud-config-url.com/cloudconfig/ reason: Unexpected token I in JSON at position 0"
116
+ )
110
117
  );
111
118
  });
112
119
 
@@ -129,7 +136,9 @@ describe('loadSupergraphSdlFromStorage', () => {
129
136
  fetcher,
130
137
  compositionId: null,
131
138
  }),
132
- ).rejects.toThrowError(message);
139
+ ).rejects.toThrowError(
140
+ new UplinkFetcherError(`An error occurred while fetching your schema from Apollo: \n${message}`)
141
+ );
133
142
  });
134
143
 
135
144
  it("throws on non-OK status codes when `errors` isn't present in a JSON response", async () => {
@@ -146,8 +155,10 @@ describe('loadSupergraphSdlFromStorage', () => {
146
155
  fetcher,
147
156
  compositionId: null,
148
157
  }),
149
- ).rejects.toThrowErrorMatchingInlineSnapshot(
150
- `"An error occurred while fetching your schema from Apollo: 500 Internal Server Error"`,
158
+ ).rejects.toThrowError(
159
+ new UplinkFetcherError(
160
+ "An error occurred while fetching your schema from Apollo: 500 Internal Server Error"
161
+ )
151
162
  );
152
163
  });
153
164
 
@@ -166,8 +177,10 @@ describe('loadSupergraphSdlFromStorage', () => {
166
177
  fetcher,
167
178
  compositionId: null,
168
179
  }),
169
- ).rejects.toThrowErrorMatchingInlineSnapshot(
170
- `"An error occurred while fetching your schema from Apollo: 400 invalid json response body at https://example1.cloud-config-url.com/cloudconfig/ reason: Unexpected end of JSON input"`,
180
+ ).rejects.toThrowError(
181
+ new UplinkFetcherError(
182
+ "An error occurred while fetching your schema from Apollo: 400 invalid json response body at https://example1.cloud-config-url.com/cloudconfig/ reason: Unexpected end of JSON input",
183
+ )
171
184
  );
172
185
  });
173
186
 
@@ -185,8 +198,10 @@ describe('loadSupergraphSdlFromStorage', () => {
185
198
  fetcher,
186
199
  compositionId: null,
187
200
  }),
188
- ).rejects.toThrowErrorMatchingInlineSnapshot(
189
- `"An error occurred while fetching your schema from Apollo: 400 invalid json response body at https://example1.cloud-config-url.com/cloudconfig/ reason: Unexpected end of JSON input"`,
201
+ ).rejects.toThrowError(
202
+ new UplinkFetcherError(
203
+ "An error occurred while fetching your schema from Apollo: 400 invalid json response body at https://example1.cloud-config-url.com/cloudconfig/ reason: Unexpected end of JSON input",
204
+ )
190
205
  );
191
206
  });
192
207
 
@@ -204,8 +219,10 @@ describe('loadSupergraphSdlFromStorage', () => {
204
219
  fetcher,
205
220
  compositionId: null,
206
221
  }),
207
- ).rejects.toThrowErrorMatchingInlineSnapshot(
208
- `"An error occurred while fetching your schema from Apollo: 413 Payload Too Large"`,
222
+ ).rejects.toThrowError(
223
+ new UplinkFetcherError(
224
+ "An error occurred while fetching your schema from Apollo: 413 Payload Too Large",
225
+ )
209
226
  );
210
227
  });
211
228
 
@@ -223,8 +240,10 @@ describe('loadSupergraphSdlFromStorage', () => {
223
240
  fetcher,
224
241
  compositionId: null,
225
242
  }),
226
- ).rejects.toThrowErrorMatchingInlineSnapshot(
227
- `"An error occurred while fetching your schema from Apollo: 422 Unprocessable Entity"`,
243
+ ).rejects.toThrowError(
244
+ new UplinkFetcherError(
245
+ "An error occurred while fetching your schema from Apollo: 422 Unprocessable Entity",
246
+ )
228
247
  );
229
248
  });
230
249
 
@@ -242,8 +261,10 @@ describe('loadSupergraphSdlFromStorage', () => {
242
261
  fetcher,
243
262
  compositionId: null,
244
263
  }),
245
- ).rejects.toThrowErrorMatchingInlineSnapshot(
246
- `"An error occurred while fetching your schema from Apollo: 408 Request Timeout"`,
264
+ ).rejects.toThrowError(
265
+ new UplinkFetcherError(
266
+ "An error occurred while fetching your schema from Apollo: 408 Request Timeout",
267
+ )
247
268
  );
248
269
  });
249
270
  });
@@ -263,8 +284,10 @@ describe('loadSupergraphSdlFromStorage', () => {
263
284
  fetcher,
264
285
  compositionId: null,
265
286
  }),
266
- ).rejects.toThrowErrorMatchingInlineSnapshot(
267
- `"An error occurred while fetching your schema from Apollo: 504 Gateway Timeout"`,
287
+ ).rejects.toThrowError(
288
+ new UplinkFetcherError(
289
+ "An error occurred while fetching your schema from Apollo: 504 Gateway Timeout",
290
+ )
268
291
  );
269
292
  });
270
293
 
@@ -282,8 +305,10 @@ describe('loadSupergraphSdlFromStorage', () => {
282
305
  fetcher,
283
306
  compositionId: null,
284
307
  }),
285
- ).rejects.toThrowErrorMatchingInlineSnapshot(
286
- `"An error occurred while fetching your schema from Apollo: request to https://example1.cloud-config-url.com/cloudconfig/ failed, reason: no response"`,
308
+ ).rejects.toThrowError(
309
+ new UplinkFetcherError(
310
+ "An error occurred while fetching your schema from Apollo: request to https://example1.cloud-config-url.com/cloudconfig/ failed, reason: no response",
311
+ )
287
312
  );
288
313
  });
289
314
 
@@ -301,8 +326,10 @@ describe('loadSupergraphSdlFromStorage', () => {
301
326
  fetcher,
302
327
  compositionId: null,
303
328
  }),
304
- ).rejects.toThrowErrorMatchingInlineSnapshot(
305
- `"An error occurred while fetching your schema from Apollo: 502 Bad Gateway"`,
329
+ ).rejects.toThrowError(
330
+ new UplinkFetcherError(
331
+ "An error occurred while fetching your schema from Apollo: 502 Bad Gateway",
332
+ )
306
333
  );
307
334
  });
308
335
 
@@ -320,8 +347,10 @@ describe('loadSupergraphSdlFromStorage', () => {
320
347
  fetcher,
321
348
  compositionId: null,
322
349
  }),
323
- ).rejects.toThrowErrorMatchingInlineSnapshot(
324
- `"An error occurred while fetching your schema from Apollo: 503 Service Unavailable"`,
350
+ ).rejects.toThrowError(
351
+ new UplinkFetcherError(
352
+ "An error occurred while fetching your schema from Apollo: 503 Service Unavailable",
353
+ )
325
354
  );
326
355
  });
327
356
 
@@ -341,3 +370,28 @@ describe('loadSupergraphSdlFromStorage', () => {
341
370
  });
342
371
  });
343
372
 
373
+
374
+ describe("loadSupergraphSdlFromUplinks", () => {
375
+ beforeEach(nockBeforeEach);
376
+ afterEach(nockAfterEach);
377
+
378
+ it("doesn't retry in the unchanged / null case", async () => {
379
+ mockSupergraphSdlRequestIfAfterUnchanged("id-1234", mockCloudConfigUrl1);
380
+
381
+ const fetcher = jest.fn(getDefaultFetcher());
382
+ const result = await loadSupergraphSdlFromUplinks({
383
+ graphRef,
384
+ apiKey,
385
+ endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
386
+ errorReportingEndpoint: mockOutOfBandReporterUrl,
387
+ fetcher: fetcher as any,
388
+ compositionId: "id-1234",
389
+ maxRetries: 5,
390
+ roundRobinSeed: 0,
391
+ });
392
+
393
+ expect(result).toBeNull();
394
+ expect(fetcher).toHaveBeenCalledTimes(1);
395
+ });
396
+ });
397
+
@@ -30,6 +30,7 @@ export class UplinkFetcher implements SupergraphManager {
30
30
  private errorReportingEndpoint: string | undefined =
31
31
  process.env.APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT ?? undefined;
32
32
  private compositionId?: string;
33
+ private fetchCount: number = 0;
33
34
 
34
35
  constructor(options: UplinkFetcherOptions) {
35
36
  this.config = options;
@@ -81,6 +82,7 @@ export class UplinkFetcher implements SupergraphManager {
81
82
  fetcher: this.config.fetcher,
82
83
  compositionId: this.compositionId ?? null,
83
84
  maxRetries: this.config.maxRetries,
85
+ roundRobinSeed: this.fetchCount++,
84
86
  });
85
87
 
86
88
  if (!result) {