@apollo/gateway 0.300.0-alpha.2 → 2.0.0-alpha.2

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 (198) hide show
  1. package/LICENSE +95 -0
  2. package/README.md +1 -1
  3. package/dist/__generated__/graphqlTypes.d.ts +130 -0
  4. package/dist/__generated__/graphqlTypes.d.ts.map +1 -0
  5. package/dist/__generated__/graphqlTypes.js +25 -0
  6. package/dist/__generated__/graphqlTypes.js.map +1 -0
  7. package/dist/config.d.ts +104 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +47 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/datasources/LocalGraphQLDataSource.d.ts +3 -3
  12. package/dist/datasources/LocalGraphQLDataSource.d.ts.map +1 -1
  13. package/dist/datasources/LocalGraphQLDataSource.js +5 -5
  14. package/dist/datasources/LocalGraphQLDataSource.js.map +1 -1
  15. package/dist/datasources/RemoteGraphQLDataSource.d.ts +6 -4
  16. package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
  17. package/dist/datasources/RemoteGraphQLDataSource.js +60 -17
  18. package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
  19. package/dist/datasources/index.d.ts +1 -1
  20. package/dist/datasources/index.d.ts.map +1 -1
  21. package/dist/datasources/index.js +1 -0
  22. package/dist/datasources/index.js.map +1 -1
  23. package/dist/datasources/parseCacheControlHeader.d.ts +2 -0
  24. package/dist/datasources/parseCacheControlHeader.d.ts.map +1 -0
  25. package/dist/datasources/parseCacheControlHeader.js +16 -0
  26. package/dist/datasources/parseCacheControlHeader.js.map +1 -0
  27. package/dist/datasources/types.d.ts +16 -1
  28. package/dist/datasources/types.d.ts.map +1 -1
  29. package/dist/datasources/types.js +7 -0
  30. package/dist/datasources/types.js.map +1 -1
  31. package/dist/executeQueryPlan.d.ts +2 -1
  32. package/dist/executeQueryPlan.d.ts.map +1 -1
  33. package/dist/executeQueryPlan.js +199 -112
  34. package/dist/executeQueryPlan.js.map +1 -1
  35. package/dist/index.d.ts +62 -80
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +543 -234
  38. package/dist/index.js.map +1 -1
  39. package/dist/loadServicesFromRemoteEndpoint.d.ts +9 -9
  40. package/dist/loadServicesFromRemoteEndpoint.d.ts.map +1 -1
  41. package/dist/loadServicesFromRemoteEndpoint.js +13 -8
  42. package/dist/loadServicesFromRemoteEndpoint.js.map +1 -1
  43. package/dist/loadSupergraphSdlFromStorage.d.ts +13 -0
  44. package/dist/loadSupergraphSdlFromStorage.d.ts.map +1 -0
  45. package/dist/loadSupergraphSdlFromStorage.js +101 -0
  46. package/dist/loadSupergraphSdlFromStorage.js.map +1 -0
  47. package/dist/operationContext.d.ts +17 -0
  48. package/dist/operationContext.d.ts.map +1 -0
  49. package/dist/operationContext.js +42 -0
  50. package/dist/operationContext.js.map +1 -0
  51. package/dist/outOfBandReporter.d.ts +15 -0
  52. package/dist/outOfBandReporter.d.ts.map +1 -0
  53. package/dist/outOfBandReporter.js +88 -0
  54. package/dist/outOfBandReporter.js.map +1 -0
  55. package/dist/utilities/array.d.ts +1 -2
  56. package/dist/utilities/array.d.ts.map +1 -1
  57. package/dist/utilities/array.js +7 -14
  58. package/dist/utilities/array.js.map +1 -1
  59. package/dist/utilities/assert.d.ts +2 -0
  60. package/dist/utilities/assert.d.ts.map +1 -0
  61. package/dist/utilities/assert.js +10 -0
  62. package/dist/utilities/assert.js.map +1 -0
  63. package/dist/utilities/cleanErrorOfInaccessibleNames.d.ts +3 -0
  64. package/dist/utilities/cleanErrorOfInaccessibleNames.d.ts.map +1 -0
  65. package/dist/utilities/cleanErrorOfInaccessibleNames.js +27 -0
  66. package/dist/utilities/cleanErrorOfInaccessibleNames.js.map +1 -0
  67. package/dist/utilities/deepMerge.js +2 -2
  68. package/dist/utilities/deepMerge.js.map +1 -1
  69. package/dist/utilities/graphql.d.ts +1 -4
  70. package/dist/utilities/graphql.d.ts.map +1 -1
  71. package/dist/utilities/graphql.js +3 -36
  72. package/dist/utilities/graphql.js.map +1 -1
  73. package/dist/utilities/opentelemetry.d.ts +10 -0
  74. package/dist/utilities/opentelemetry.d.ts.map +1 -0
  75. package/dist/utilities/opentelemetry.js +19 -0
  76. package/dist/utilities/opentelemetry.js.map +1 -0
  77. package/package.json +30 -21
  78. package/src/__generated__/graphqlTypes.ts +140 -0
  79. package/src/__mocks__/apollo-server-env.ts +56 -0
  80. package/src/__mocks__/make-fetch-happen-fetcher.ts +55 -0
  81. package/src/__mocks__/tsconfig.json +7 -0
  82. package/src/__tests__/build-query-plan.feature +40 -311
  83. package/src/__tests__/buildQueryPlan.test.ts +246 -426
  84. package/src/__tests__/executeQueryPlan.test.ts +1691 -194
  85. package/src/__tests__/execution-utils.ts +33 -26
  86. package/src/__tests__/gateway/__snapshots__/opentelemetry.test.ts.snap +195 -0
  87. package/src/__tests__/gateway/buildService.test.ts +16 -19
  88. package/src/__tests__/gateway/composedSdl.test.ts +44 -0
  89. package/src/__tests__/gateway/endToEnd.test.ts +166 -0
  90. package/src/__tests__/gateway/executor.test.ts +49 -43
  91. package/src/__tests__/gateway/lifecycle-hooks.test.ts +58 -29
  92. package/src/__tests__/gateway/opentelemetry.test.ts +123 -0
  93. package/src/__tests__/gateway/queryPlanCache.test.ts +19 -20
  94. package/src/__tests__/gateway/reporting.test.ts +76 -55
  95. package/src/__tests__/integration/abstract-types.test.ts +1086 -22
  96. package/src/__tests__/integration/aliases.test.ts +5 -6
  97. package/src/__tests__/integration/boolean.test.ts +40 -38
  98. package/src/__tests__/integration/complex-key.test.ts +41 -56
  99. package/src/__tests__/integration/configuration.test.ts +321 -0
  100. package/src/__tests__/integration/custom-directives.test.ts +61 -46
  101. package/src/__tests__/integration/fragments.test.ts +8 -2
  102. package/src/__tests__/integration/list-key.test.ts +2 -2
  103. package/src/__tests__/integration/logger.test.ts +2 -2
  104. package/src/__tests__/integration/multiple-key.test.ts +11 -12
  105. package/src/__tests__/integration/mutations.test.ts +8 -5
  106. package/src/__tests__/integration/networkRequests.test.ts +447 -289
  107. package/src/__tests__/integration/nockMocks.ts +95 -66
  108. package/src/__tests__/integration/provides.test.ts +9 -6
  109. package/src/__tests__/integration/requires.test.ts +17 -15
  110. package/src/__tests__/integration/scope.test.ts +557 -0
  111. package/src/__tests__/integration/unions.test.ts +1 -1
  112. package/src/__tests__/integration/value-types.test.ts +35 -32
  113. package/src/__tests__/integration/variables.test.ts +8 -2
  114. package/src/__tests__/loadServicesFromRemoteEndpoint.test.ts +6 -2
  115. package/src/__tests__/loadSupergraphSdlFromStorage.test.ts +694 -0
  116. package/src/__tests__/queryPlanCucumber.test.ts +11 -61
  117. package/src/__tests__/testSetup.ts +1 -4
  118. package/src/__tests__/tsconfig.json +2 -1
  119. package/src/config.ts +225 -0
  120. package/src/core/__tests__/core.test.ts +412 -0
  121. package/src/datasources/LocalGraphQLDataSource.ts +9 -10
  122. package/src/datasources/RemoteGraphQLDataSource.ts +117 -43
  123. package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +11 -4
  124. package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +148 -79
  125. package/src/datasources/__tests__/tsconfig.json +4 -2
  126. package/src/datasources/index.ts +1 -1
  127. package/src/datasources/parseCacheControlHeader.ts +43 -0
  128. package/src/datasources/types.ts +47 -2
  129. package/src/executeQueryPlan.ts +264 -153
  130. package/src/index.ts +925 -480
  131. package/src/loadServicesFromRemoteEndpoint.ts +24 -17
  132. package/src/loadSupergraphSdlFromStorage.ts +140 -0
  133. package/src/make-fetch-happen.d.ts +2 -2
  134. package/src/operationContext.ts +70 -0
  135. package/src/outOfBandReporter.ts +128 -0
  136. package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +104 -0
  137. package/src/utilities/__tests__/tsconfig.json +8 -0
  138. package/src/utilities/array.ts +6 -28
  139. package/src/utilities/assert.ts +14 -0
  140. package/src/utilities/cleanErrorOfInaccessibleNames.ts +29 -0
  141. package/src/utilities/graphql.ts +0 -64
  142. package/src/utilities/opentelemetry.ts +13 -0
  143. package/CHANGELOG.md +0 -226
  144. package/LICENSE.md +0 -20
  145. package/dist/FieldSet.d.ts +0 -18
  146. package/dist/FieldSet.d.ts.map +0 -1
  147. package/dist/FieldSet.js +0 -96
  148. package/dist/FieldSet.js.map +0 -1
  149. package/dist/QueryPlan.d.ts +0 -41
  150. package/dist/QueryPlan.d.ts.map +0 -1
  151. package/dist/QueryPlan.js +0 -15
  152. package/dist/QueryPlan.js.map +0 -1
  153. package/dist/buildQueryPlan.d.ts +0 -44
  154. package/dist/buildQueryPlan.d.ts.map +0 -1
  155. package/dist/buildQueryPlan.js +0 -670
  156. package/dist/buildQueryPlan.js.map +0 -1
  157. package/dist/loadServicesFromStorage.d.ts +0 -21
  158. package/dist/loadServicesFromStorage.d.ts.map +0 -1
  159. package/dist/loadServicesFromStorage.js +0 -64
  160. package/dist/loadServicesFromStorage.js.map +0 -1
  161. package/dist/snapshotSerializers/astSerializer.d.ts +0 -3
  162. package/dist/snapshotSerializers/astSerializer.d.ts.map +0 -1
  163. package/dist/snapshotSerializers/astSerializer.js +0 -14
  164. package/dist/snapshotSerializers/astSerializer.js.map +0 -1
  165. package/dist/snapshotSerializers/index.d.ts +0 -13
  166. package/dist/snapshotSerializers/index.d.ts.map +0 -1
  167. package/dist/snapshotSerializers/index.js +0 -15
  168. package/dist/snapshotSerializers/index.js.map +0 -1
  169. package/dist/snapshotSerializers/queryPlanSerializer.d.ts +0 -3
  170. package/dist/snapshotSerializers/queryPlanSerializer.d.ts.map +0 -1
  171. package/dist/snapshotSerializers/queryPlanSerializer.js +0 -78
  172. package/dist/snapshotSerializers/queryPlanSerializer.js.map +0 -1
  173. package/dist/snapshotSerializers/selectionSetSerializer.d.ts +0 -3
  174. package/dist/snapshotSerializers/selectionSetSerializer.d.ts.map +0 -1
  175. package/dist/snapshotSerializers/selectionSetSerializer.js +0 -12
  176. package/dist/snapshotSerializers/selectionSetSerializer.js.map +0 -1
  177. package/dist/snapshotSerializers/typeSerializer.d.ts +0 -3
  178. package/dist/snapshotSerializers/typeSerializer.d.ts.map +0 -1
  179. package/dist/snapshotSerializers/typeSerializer.js +0 -12
  180. package/dist/snapshotSerializers/typeSerializer.js.map +0 -1
  181. package/dist/utilities/MultiMap.d.ts +0 -4
  182. package/dist/utilities/MultiMap.d.ts.map +0 -1
  183. package/dist/utilities/MultiMap.js +0 -17
  184. package/dist/utilities/MultiMap.js.map +0 -1
  185. package/src/FieldSet.ts +0 -169
  186. package/src/QueryPlan.ts +0 -57
  187. package/src/__tests__/matchers/toCallService.ts +0 -105
  188. package/src/__tests__/matchers/toHaveBeenCalledBefore.ts +0 -40
  189. package/src/__tests__/matchers/toHaveFetched.ts +0 -81
  190. package/src/__tests__/matchers/toMatchAST.ts +0 -64
  191. package/src/buildQueryPlan.ts +0 -1190
  192. package/src/loadServicesFromStorage.ts +0 -170
  193. package/src/snapshotSerializers/astSerializer.ts +0 -21
  194. package/src/snapshotSerializers/index.ts +0 -21
  195. package/src/snapshotSerializers/queryPlanSerializer.ts +0 -144
  196. package/src/snapshotSerializers/selectionSetSerializer.ts +0 -13
  197. package/src/snapshotSerializers/typeSerializer.ts +0 -11
  198. package/src/utilities/MultiMap.ts +0 -11
@@ -0,0 +1,694 @@
1
+ import { loadSupergraphSdlFromStorage } from '../loadSupergraphSdlFromStorage';
2
+ import { getDefaultFetcher } from '../..';
3
+ import {
4
+ graphRef,
5
+ apiKey,
6
+ mockCloudConfigUrl,
7
+ mockSupergraphSdlRequest,
8
+ mockOutOfBandReporterUrl,
9
+ mockOutOfBandReportRequestSuccess,
10
+ mockSupergraphSdlRequestSuccess,
11
+ mockSupergraphSdlRequestIfAfterUnchanged,
12
+ } from './integration/nockMocks';
13
+ import mockedEnv from 'mocked-env';
14
+
15
+ describe('loadSupergraphSdlFromStorage', () => {
16
+ let cleanUp: (() => void) | null = null;
17
+
18
+ afterAll(async () => {
19
+ if (cleanUp) {
20
+ cleanUp();
21
+ cleanUp = null;
22
+ }
23
+ });
24
+
25
+ it('fetches Supergraph SDL as expected', async () => {
26
+ mockSupergraphSdlRequestSuccess();
27
+
28
+ const fetcher = getDefaultFetcher();
29
+ const result = await loadSupergraphSdlFromStorage({
30
+ graphRef,
31
+ apiKey,
32
+ endpoint: mockCloudConfigUrl,
33
+ fetcher,
34
+ compositionId: null,
35
+
36
+ });
37
+
38
+ expect(result).toMatchInlineSnapshot(`
39
+ Object {
40
+ "id": "originalId-1234",
41
+ "supergraphSdl": "schema
42
+ @core(feature: \\"https://specs.apollo.dev/core/v0.2\\")
43
+ @core(feature: \\"https://specs.apollo.dev/join/v0.2\\", for: EXECUTION)
44
+ @core(feature: \\"https://specs.apollo.dev/tag/v0.1\\")
45
+ {
46
+ query: Query
47
+ mutation: Mutation
48
+ }
49
+
50
+ directive @core(feature: String!, as: String, for: core__Purpose) repeatable on SCHEMA
51
+
52
+ directive @join__field(graph: join__Graph!, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
53
+
54
+ directive @join__graph(name: String!, url: String!) on ENUM_VALUE
55
+
56
+ directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE
57
+
58
+ directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR
59
+
60
+ directive @stream on FIELD
61
+
62
+ directive @tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
63
+
64
+ directive @transform(from: String!) on FIELD
65
+
66
+ union AccountType
67
+ @join__type(graph: ACCOUNTS)
68
+ @tag(name: \\"from-accounts\\")
69
+ = PasswordAccount | SMSAccount
70
+
71
+ type Amazon
72
+ @join__type(graph: PRODUCT)
73
+ {
74
+ referrer: String
75
+ }
76
+
77
+ union Body
78
+ @join__type(graph: DOCUMENTS)
79
+ = Image | Text
80
+
81
+ type Book implements Product
82
+ @join__implements(graph: INVENTORY, interface: \\"Product\\")
83
+ @join__implements(graph: PRODUCT, interface: \\"Product\\")
84
+ @join__implements(graph: REVIEWS, interface: \\"Product\\")
85
+ @join__type(graph: BOOKS, key: \\"isbn\\")
86
+ @join__type(graph: INVENTORY, key: \\"isbn\\", extension: true)
87
+ @join__type(graph: PRODUCT, key: \\"isbn\\", extension: true)
88
+ @join__type(graph: REVIEWS, key: \\"isbn\\", extension: true)
89
+ {
90
+ isbn: String!
91
+ title: String @join__field(graph: BOOKS) @join__field(graph: PRODUCT, external: true)
92
+ year: Int @join__field(graph: BOOKS) @join__field(graph: PRODUCT, external: true)
93
+ similarBooks: [Book]! @join__field(graph: BOOKS) @join__field(graph: REVIEWS, external: true)
94
+ metadata: [MetadataOrError] @join__field(graph: BOOKS)
95
+ inStock: Boolean @join__field(graph: INVENTORY)
96
+ isCheckedOut: Boolean @join__field(graph: INVENTORY)
97
+ upc: String! @join__field(graph: PRODUCT)
98
+ sku: String! @join__field(graph: PRODUCT)
99
+ name(delimeter: String = \\" \\"): String @join__field(graph: PRODUCT, requires: \\"title year\\")
100
+ price: String @join__field(graph: PRODUCT)
101
+ details: ProductDetailsBook @join__field(graph: PRODUCT)
102
+ reviews: [Review] @join__field(graph: REVIEWS)
103
+ relatedReviews: [Review!]! @join__field(graph: REVIEWS, requires: \\"similarBooks { isbn }\\")
104
+ }
105
+
106
+ union Brand
107
+ @join__type(graph: PRODUCT)
108
+ = Ikea | Amazon
109
+
110
+ enum CacheControlScope
111
+ @join__type(graph: ACCOUNTS)
112
+ @join__type(graph: BOOKS)
113
+ @join__type(graph: PRODUCT)
114
+ {
115
+ PUBLIC
116
+ PRIVATE
117
+ }
118
+
119
+ type Car implements Vehicle
120
+ @join__implements(graph: PRODUCT, interface: \\"Vehicle\\")
121
+ @join__implements(graph: REVIEWS, interface: \\"Vehicle\\")
122
+ @join__type(graph: PRODUCT, key: \\"id\\")
123
+ @join__type(graph: REVIEWS, key: \\"id\\", extension: true)
124
+ {
125
+ id: String!
126
+ description: String @join__field(graph: PRODUCT)
127
+ price: String @join__field(graph: PRODUCT) @join__field(graph: REVIEWS, external: true)
128
+ retailPrice: String @join__field(graph: REVIEWS, requires: \\"price\\")
129
+ }
130
+
131
+ enum core__Purpose {
132
+ \\"\\"\\"
133
+ \`SECURITY\` features provide metadata necessary to securely resolve fields.
134
+ \\"\\"\\"
135
+ SECURITY
136
+
137
+ \\"\\"\\"
138
+ \`EXECUTION\` features provide metadata necessary for operation execution.
139
+ \\"\\"\\"
140
+ EXECUTION
141
+ }
142
+
143
+ type Error
144
+ @join__type(graph: BOOKS)
145
+ @join__type(graph: PRODUCT)
146
+ @join__type(graph: REVIEWS)
147
+ {
148
+ code: Int
149
+ message: String
150
+ }
151
+
152
+ type Furniture implements Product
153
+ @join__implements(graph: INVENTORY, interface: \\"Product\\")
154
+ @join__implements(graph: PRODUCT, interface: \\"Product\\")
155
+ @join__implements(graph: REVIEWS, interface: \\"Product\\")
156
+ @join__type(graph: INVENTORY, key: \\"sku\\", extension: true)
157
+ @join__type(graph: PRODUCT, key: \\"upc\\")
158
+ @join__type(graph: PRODUCT, key: \\"sku\\")
159
+ @join__type(graph: REVIEWS, key: \\"upc\\", extension: true)
160
+ {
161
+ sku: String! @join__field(graph: INVENTORY) @join__field(graph: PRODUCT)
162
+ inStock: Boolean @join__field(graph: INVENTORY)
163
+ isHeavy: Boolean @join__field(graph: INVENTORY)
164
+ upc: String! @join__field(graph: PRODUCT) @join__field(graph: REVIEWS)
165
+ name: String @join__field(graph: PRODUCT)
166
+ price: String @join__field(graph: PRODUCT)
167
+ brand: Brand @join__field(graph: PRODUCT)
168
+ metadata: [MetadataOrError] @join__field(graph: PRODUCT)
169
+ details: ProductDetailsFurniture @join__field(graph: PRODUCT)
170
+ reviews: [Review] @join__field(graph: REVIEWS)
171
+ }
172
+
173
+ type Ikea
174
+ @join__type(graph: PRODUCT)
175
+ {
176
+ asile: Int
177
+ }
178
+
179
+ type Image implements NamedObject
180
+ @join__implements(graph: DOCUMENTS, interface: \\"NamedObject\\")
181
+ @join__type(graph: DOCUMENTS)
182
+ {
183
+ name: String!
184
+ attributes: ImageAttributes!
185
+ }
186
+
187
+ type ImageAttributes
188
+ @join__type(graph: DOCUMENTS)
189
+ {
190
+ url: String!
191
+ }
192
+
193
+ scalar join__FieldSet
194
+
195
+ enum join__Graph {
196
+ ACCOUNTS @join__graph(name: \\"accounts\\", url: \\"https://accounts.api.com.invalid\\")
197
+ BOOKS @join__graph(name: \\"books\\", url: \\"https://books.api.com.invalid\\")
198
+ DOCUMENTS @join__graph(name: \\"documents\\", url: \\"https://documents.api.com.invalid\\")
199
+ INVENTORY @join__graph(name: \\"inventory\\", url: \\"https://inventory.api.com.invalid\\")
200
+ PRODUCT @join__graph(name: \\"product\\", url: \\"https://product.api.com.invalid\\")
201
+ REVIEWS @join__graph(name: \\"reviews\\", url: \\"https://reviews.api.com.invalid\\")
202
+ }
203
+
204
+ scalar JSON
205
+ @join__type(graph: ACCOUNTS)
206
+ @specifiedBy(url: \\"https://json-spec.dev\\")
207
+
208
+ type KeyValue
209
+ @join__type(graph: BOOKS)
210
+ @join__type(graph: PRODUCT)
211
+ @join__type(graph: REVIEWS)
212
+ {
213
+ key: String!
214
+ value: String!
215
+ }
216
+
217
+ type Library
218
+ @join__type(graph: ACCOUNTS, key: \\"id\\", extension: true)
219
+ @join__type(graph: BOOKS, key: \\"id\\")
220
+ {
221
+ id: ID!
222
+ name: String @join__field(graph: ACCOUNTS, external: true) @join__field(graph: BOOKS)
223
+ userAccount(id: ID! = 1): User @join__field(graph: ACCOUNTS, requires: \\"name\\")
224
+ }
225
+
226
+ union MetadataOrError
227
+ @join__type(graph: BOOKS)
228
+ @join__type(graph: PRODUCT)
229
+ @join__type(graph: REVIEWS)
230
+ = KeyValue | Error
231
+
232
+ type Mutation
233
+ @join__type(graph: ACCOUNTS)
234
+ @join__type(graph: REVIEWS)
235
+ {
236
+ login(username: String!, password: String!, userId: String @deprecated(reason: \\"Use username instead\\")): User @join__field(graph: ACCOUNTS)
237
+ reviewProduct(input: ReviewProduct!): Product @join__field(graph: REVIEWS)
238
+ updateReview(review: UpdateReviewInput!): Review @join__field(graph: REVIEWS)
239
+ deleteReview(id: ID!): Boolean @join__field(graph: REVIEWS)
240
+ }
241
+
242
+ type Name
243
+ @join__type(graph: ACCOUNTS)
244
+ {
245
+ first: String
246
+ last: String
247
+ }
248
+
249
+ interface NamedObject
250
+ @join__type(graph: DOCUMENTS)
251
+ {
252
+ name: String!
253
+ }
254
+
255
+ type PasswordAccount
256
+ @join__type(graph: ACCOUNTS, key: \\"email\\")
257
+ {
258
+ email: String!
259
+ }
260
+
261
+ interface Product
262
+ @join__type(graph: INVENTORY)
263
+ @join__type(graph: PRODUCT)
264
+ @join__type(graph: REVIEWS)
265
+ @tag(name: \\"from-reviews\\")
266
+ {
267
+ inStock: Boolean @join__field(graph: INVENTORY)
268
+ upc: String! @join__field(graph: PRODUCT)
269
+ sku: String! @join__field(graph: PRODUCT)
270
+ name: String @join__field(graph: PRODUCT)
271
+ price: String @join__field(graph: PRODUCT)
272
+ details: ProductDetails @join__field(graph: PRODUCT)
273
+ reviews: [Review] @join__field(graph: REVIEWS)
274
+ }
275
+
276
+ interface ProductDetails
277
+ @join__type(graph: PRODUCT)
278
+ {
279
+ country: String
280
+ }
281
+
282
+ type ProductDetailsBook implements ProductDetails
283
+ @join__implements(graph: PRODUCT, interface: \\"ProductDetails\\")
284
+ @join__type(graph: PRODUCT)
285
+ {
286
+ country: String
287
+ pages: Int
288
+ }
289
+
290
+ type ProductDetailsFurniture implements ProductDetails
291
+ @join__implements(graph: PRODUCT, interface: \\"ProductDetails\\")
292
+ @join__type(graph: PRODUCT)
293
+ {
294
+ country: String
295
+ color: String
296
+ }
297
+
298
+ type Query
299
+ @join__type(graph: ACCOUNTS)
300
+ @join__type(graph: BOOKS)
301
+ @join__type(graph: DOCUMENTS)
302
+ @join__type(graph: INVENTORY)
303
+ @join__type(graph: PRODUCT)
304
+ @join__type(graph: REVIEWS)
305
+ {
306
+ user(id: ID!): User @join__field(graph: ACCOUNTS)
307
+ me: User @join__field(graph: ACCOUNTS)
308
+ book(isbn: String!): Book @join__field(graph: BOOKS)
309
+ books: [Book] @join__field(graph: BOOKS)
310
+ library(id: ID!): Library @join__field(graph: BOOKS)
311
+ body: Body! @join__field(graph: DOCUMENTS)
312
+ product(upc: String!): Product @join__field(graph: PRODUCT)
313
+ vehicle(id: String!): Vehicle @join__field(graph: PRODUCT)
314
+ topProducts(first: Int = 5): [Product] @join__field(graph: PRODUCT)
315
+ topCars(first: Int = 5): [Car] @join__field(graph: PRODUCT)
316
+ topReviews(first: Int = 5): [Review] @join__field(graph: REVIEWS)
317
+ }
318
+
319
+ type Review
320
+ @join__type(graph: REVIEWS, key: \\"id\\")
321
+ {
322
+ id: ID!
323
+ body(format: Boolean = false): String
324
+ author: User @join__field(graph: REVIEWS, provides: \\"username\\")
325
+ product: Product
326
+ metadata: [MetadataOrError]
327
+ }
328
+
329
+ input ReviewProduct
330
+ @join__type(graph: REVIEWS)
331
+ {
332
+ upc: String!
333
+ body: String!
334
+ stars: Int @deprecated(reason: \\"Stars are no longer in use\\")
335
+ }
336
+
337
+ type SMSAccount
338
+ @join__type(graph: ACCOUNTS, key: \\"number\\")
339
+ {
340
+ number: String
341
+ }
342
+
343
+ type Text implements NamedObject
344
+ @join__implements(graph: DOCUMENTS, interface: \\"NamedObject\\")
345
+ @join__type(graph: DOCUMENTS)
346
+ {
347
+ name: String!
348
+ attributes: TextAttributes!
349
+ }
350
+
351
+ type TextAttributes
352
+ @join__type(graph: DOCUMENTS)
353
+ {
354
+ bold: Boolean
355
+ text: String
356
+ }
357
+
358
+ union Thing
359
+ @join__type(graph: PRODUCT)
360
+ = Car | Ikea
361
+
362
+ input UpdateReviewInput
363
+ @join__type(graph: REVIEWS)
364
+ {
365
+ id: ID!
366
+ body: String
367
+ }
368
+
369
+ type User
370
+ @join__type(graph: ACCOUNTS, key: \\"id\\")
371
+ @join__type(graph: ACCOUNTS, key: \\"username name { first last }\\")
372
+ @join__type(graph: INVENTORY, key: \\"id\\", extension: true)
373
+ @join__type(graph: PRODUCT, key: \\"id\\", extension: true)
374
+ @join__type(graph: REVIEWS, key: \\"id\\", extension: true)
375
+ @tag(name: \\"from-accounts\\")
376
+ @tag(name: \\"from-reviews\\")
377
+ {
378
+ id: ID! @tag(name: \\"accounts\\")
379
+ name: Name @join__field(graph: ACCOUNTS)
380
+ username: String @join__field(graph: ACCOUNTS) @join__field(graph: REVIEWS, external: true)
381
+ birthDate(locale: String): String @tag(name: \\"admin\\") @tag(name: \\"dev\\") @join__field(graph: ACCOUNTS)
382
+ account: AccountType @join__field(graph: ACCOUNTS)
383
+ metadata: [UserMetadata] @join__field(graph: ACCOUNTS) @join__field(graph: INVENTORY, external: true) @join__field(graph: REVIEWS, external: true)
384
+ ssn: String @join__field(graph: ACCOUNTS)
385
+ goodDescription: Boolean @join__field(graph: INVENTORY, requires: \\"metadata { description }\\")
386
+ vehicle: Vehicle @join__field(graph: PRODUCT)
387
+ thing: Thing @join__field(graph: PRODUCT)
388
+ reviews: [Review] @join__field(graph: REVIEWS)
389
+ numberOfReviews: Int! @join__field(graph: REVIEWS)
390
+ goodAddress: Boolean @join__field(graph: REVIEWS, requires: \\"metadata { address }\\")
391
+ }
392
+
393
+ type UserMetadata
394
+ @join__type(graph: ACCOUNTS)
395
+ @join__type(graph: INVENTORY)
396
+ @join__type(graph: REVIEWS)
397
+ {
398
+ name: String @join__field(graph: ACCOUNTS)
399
+ address: String @join__field(graph: ACCOUNTS) @join__field(graph: REVIEWS, external: true)
400
+ description: String @join__field(graph: ACCOUNTS) @join__field(graph: INVENTORY, external: true)
401
+ }
402
+
403
+ type Van implements Vehicle
404
+ @join__implements(graph: PRODUCT, interface: \\"Vehicle\\")
405
+ @join__implements(graph: REVIEWS, interface: \\"Vehicle\\")
406
+ @join__type(graph: PRODUCT, key: \\"id\\")
407
+ @join__type(graph: REVIEWS, key: \\"id\\", extension: true)
408
+ {
409
+ id: String!
410
+ description: String @join__field(graph: PRODUCT)
411
+ price: String @join__field(graph: PRODUCT) @join__field(graph: REVIEWS, external: true)
412
+ retailPrice: String @join__field(graph: REVIEWS, requires: \\"price\\")
413
+ }
414
+
415
+ interface Vehicle
416
+ @join__type(graph: PRODUCT)
417
+ @join__type(graph: REVIEWS)
418
+ {
419
+ id: String! @join__field(graph: PRODUCT)
420
+ description: String @join__field(graph: PRODUCT)
421
+ price: String @join__field(graph: PRODUCT)
422
+ retailPrice: String @join__field(graph: REVIEWS)
423
+ }",
424
+ }
425
+ `);
426
+ });
427
+
428
+ describe('errors', () => {
429
+ it('throws on a malformed response', async () => {
430
+ mockSupergraphSdlRequest().reply(200, 'Invalid JSON');
431
+
432
+ const fetcher = getDefaultFetcher();
433
+ await expect(
434
+ loadSupergraphSdlFromStorage({
435
+ graphRef,
436
+ apiKey,
437
+ endpoint: mockCloudConfigUrl,
438
+ fetcher,
439
+ compositionId: null,
440
+
441
+ }),
442
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
443
+ `"An error occurred while fetching your schema from Apollo: 200 invalid json response body at https://example.cloud-config-url.com/cloudconfig/ reason: Unexpected token I in JSON at position 0"`,
444
+ );
445
+ });
446
+
447
+ it('throws errors from JSON on 400', async () => {
448
+ const message = 'Query syntax error';
449
+ mockSupergraphSdlRequest().reply(
450
+ 400,
451
+ JSON.stringify({
452
+ errors: [{ message }],
453
+ }),
454
+ );
455
+
456
+ const fetcher = getDefaultFetcher();
457
+ await expect(
458
+ loadSupergraphSdlFromStorage({
459
+ graphRef,
460
+ apiKey,
461
+ endpoint: mockCloudConfigUrl,
462
+ fetcher,
463
+ compositionId: null,
464
+ }),
465
+ ).rejects.toThrowError(message);
466
+ });
467
+
468
+ it("throws on non-OK status codes when `errors` isn't present in a JSON response", async () => {
469
+ mockSupergraphSdlRequest().reply(500);
470
+
471
+ const fetcher = getDefaultFetcher();
472
+ await expect(
473
+ loadSupergraphSdlFromStorage({
474
+ graphRef,
475
+ apiKey,
476
+ endpoint: mockCloudConfigUrl,
477
+ fetcher,
478
+ compositionId: null,
479
+ }),
480
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
481
+ `"An error occurred while fetching your schema from Apollo: 500 Internal Server Error"`,
482
+ );
483
+ });
484
+
485
+ // if an additional request were made by the out of band reporter, nock would throw since it's unmocked
486
+ // and this test would fail
487
+ it("Out of band reporting doesn't submit reports when endpoint is not configured", async () => {
488
+ mockSupergraphSdlRequest().reply(400);
489
+
490
+ const fetcher = getDefaultFetcher();
491
+ await expect(
492
+ loadSupergraphSdlFromStorage({
493
+ graphRef,
494
+ apiKey,
495
+ endpoint: mockCloudConfigUrl,
496
+ fetcher,
497
+ compositionId: null,
498
+ }),
499
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
500
+ `"An error occurred while fetching your schema from Apollo: 400 invalid json response body at https://example.cloud-config-url.com/cloudconfig/ reason: Unexpected end of JSON input"`,
501
+ );
502
+ });
503
+
504
+ it('throws on 400 status response and successfully submits an out of band error', async () => {
505
+ cleanUp = mockedEnv({
506
+ APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT: mockOutOfBandReporterUrl,
507
+ });
508
+
509
+ mockSupergraphSdlRequest().reply(400);
510
+ mockOutOfBandReportRequestSuccess();
511
+
512
+ const fetcher = getDefaultFetcher();
513
+ await expect(
514
+ loadSupergraphSdlFromStorage({
515
+ graphRef,
516
+ apiKey,
517
+ endpoint: mockCloudConfigUrl,
518
+ fetcher,
519
+ compositionId: null,
520
+ }),
521
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
522
+ `"An error occurred while fetching your schema from Apollo: 400 invalid json response body at https://example.cloud-config-url.com/cloudconfig/ reason: Unexpected end of JSON input"`,
523
+ );
524
+ });
525
+
526
+ it('throws on 413 status response and successfully submits an out of band error', async () => {
527
+ cleanUp = mockedEnv({
528
+ APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT: mockOutOfBandReporterUrl,
529
+ });
530
+
531
+ mockSupergraphSdlRequest().reply(413);
532
+ mockOutOfBandReportRequestSuccess();
533
+
534
+ const fetcher = getDefaultFetcher();
535
+ await expect(
536
+ loadSupergraphSdlFromStorage({
537
+ graphRef,
538
+ apiKey,
539
+ endpoint: mockCloudConfigUrl,
540
+ fetcher,
541
+ compositionId: null,
542
+ }),
543
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
544
+ `"An error occurred while fetching your schema from Apollo: 413 Payload Too Large"`,
545
+ );
546
+ });
547
+
548
+ it('throws on 422 status response and successfully submits an out of band error', async () => {
549
+ cleanUp = mockedEnv({
550
+ APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT: mockOutOfBandReporterUrl,
551
+ });
552
+
553
+ mockSupergraphSdlRequest().reply(422);
554
+ mockOutOfBandReportRequestSuccess();
555
+
556
+ const fetcher = getDefaultFetcher();
557
+ await expect(
558
+ loadSupergraphSdlFromStorage({
559
+ graphRef,
560
+ apiKey,
561
+ endpoint: mockCloudConfigUrl,
562
+ fetcher,
563
+ compositionId: null,
564
+ }),
565
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
566
+ `"An error occurred while fetching your schema from Apollo: 422 Unprocessable Entity"`,
567
+ );
568
+ });
569
+
570
+ it('throws on 408 status response and successfully submits an out of band error', async () => {
571
+ cleanUp = mockedEnv({
572
+ APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT: mockOutOfBandReporterUrl,
573
+ });
574
+
575
+ mockSupergraphSdlRequest().reply(408);
576
+ mockOutOfBandReportRequestSuccess();
577
+
578
+ const fetcher = getDefaultFetcher();
579
+ await expect(
580
+ loadSupergraphSdlFromStorage({
581
+ graphRef,
582
+ apiKey,
583
+ endpoint: mockCloudConfigUrl,
584
+ fetcher,
585
+ compositionId: null,
586
+ }),
587
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
588
+ `"An error occurred while fetching your schema from Apollo: 408 Request Timeout"`,
589
+ );
590
+ });
591
+ });
592
+
593
+ it('throws on 504 status response and successfully submits an out of band error', async () => {
594
+ cleanUp = mockedEnv({
595
+ APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT: mockOutOfBandReporterUrl,
596
+ });
597
+
598
+ mockSupergraphSdlRequest().reply(504);
599
+ mockOutOfBandReportRequestSuccess();
600
+
601
+ const fetcher = getDefaultFetcher();
602
+ await expect(
603
+ loadSupergraphSdlFromStorage({
604
+ graphRef,
605
+ apiKey,
606
+ endpoint: mockCloudConfigUrl,
607
+ fetcher,
608
+ compositionId: null,
609
+ }),
610
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
611
+ `"An error occurred while fetching your schema from Apollo: 504 Gateway Timeout"`,
612
+ );
613
+ });
614
+
615
+ it('throws when there is no response and successfully submits an out of band error', async () => {
616
+ cleanUp = mockedEnv({
617
+ APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT: mockOutOfBandReporterUrl,
618
+ });
619
+
620
+ mockSupergraphSdlRequest().replyWithError('no response');
621
+ mockOutOfBandReportRequestSuccess();
622
+
623
+ const fetcher = getDefaultFetcher();
624
+ await expect(
625
+ loadSupergraphSdlFromStorage({
626
+ graphRef,
627
+ apiKey,
628
+ endpoint: mockCloudConfigUrl,
629
+ fetcher,
630
+ compositionId: null,
631
+ }),
632
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
633
+ `"An error occurred while fetching your schema from Apollo: request to https://example.cloud-config-url.com/cloudconfig/ failed, reason: no response"`,
634
+ );
635
+ });
636
+
637
+ it('throws on 502 status response and successfully submits an out of band error', async () => {
638
+ cleanUp = mockedEnv({
639
+ APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT: mockOutOfBandReporterUrl,
640
+ });
641
+
642
+ mockSupergraphSdlRequest().reply(502);
643
+ mockOutOfBandReportRequestSuccess();
644
+
645
+ const fetcher = getDefaultFetcher();
646
+ await expect(
647
+ loadSupergraphSdlFromStorage({
648
+ graphRef,
649
+ apiKey,
650
+ endpoint: mockCloudConfigUrl,
651
+ fetcher,
652
+ compositionId: null,
653
+ }),
654
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
655
+ `"An error occurred while fetching your schema from Apollo: 502 Bad Gateway"`,
656
+ );
657
+ });
658
+
659
+ it('throws on 503 status response and successfully submits an out of band error', async () => {
660
+ cleanUp = mockedEnv({
661
+ APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT: mockOutOfBandReporterUrl,
662
+ });
663
+
664
+ mockSupergraphSdlRequest().reply(503);
665
+ mockOutOfBandReportRequestSuccess();
666
+
667
+ const fetcher = getDefaultFetcher();
668
+ await expect(
669
+ loadSupergraphSdlFromStorage({
670
+ graphRef,
671
+ apiKey,
672
+ endpoint: mockCloudConfigUrl,
673
+ fetcher,
674
+ compositionId: null,
675
+ }),
676
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
677
+ `"An error occurred while fetching your schema from Apollo: 503 Service Unavailable"`,
678
+ );
679
+ });
680
+
681
+ it('successfully responds to SDL unchanged by returning null', async () => {
682
+ mockSupergraphSdlRequestIfAfterUnchanged("id-1234");
683
+
684
+ const fetcher = getDefaultFetcher();
685
+ const result = await loadSupergraphSdlFromStorage({
686
+ graphRef,
687
+ apiKey,
688
+ endpoint: mockCloudConfigUrl,
689
+ fetcher,
690
+ compositionId: "id-1234",
691
+ });
692
+ expect(result).toBeNull();
693
+ });
694
+ });