vscode-apollo 1.20.0 → 2.0.0

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 (108) hide show
  1. package/.circleci/config.yml +27 -18
  2. package/.git-blame-ignore-revs +2 -0
  3. package/.nvmrc +1 -1
  4. package/.vscode/launch.json +9 -4
  5. package/.vscode/tasks.json +58 -16
  6. package/.vscodeignore +12 -1
  7. package/CHANGELOG.md +78 -0
  8. package/CODEOWNERS +4 -0
  9. package/README.md +97 -48
  10. package/graphql.configuration.json +5 -1
  11. package/images/marketplace/apollo-wordmark.png +0 -0
  12. package/jest.config.ts +14 -4
  13. package/jest.e2e.config.js +17 -0
  14. package/package.json +67 -68
  15. package/renovate.json +7 -0
  16. package/sampleWorkspace/clientSchema/apollo.config.cjs +10 -0
  17. package/sampleWorkspace/clientSchema/src/clientSchema.js +16 -0
  18. package/sampleWorkspace/clientSchema/src/test.js +18 -0
  19. package/sampleWorkspace/fixtures/starwarsSchema.graphql +299 -0
  20. package/sampleWorkspace/httpSchema/apollo.config.ts +8 -0
  21. package/sampleWorkspace/httpSchema/src/test.js +9 -0
  22. package/sampleWorkspace/localSchema/apollo.config.js +8 -0
  23. package/sampleWorkspace/localSchema/src/test.js +8 -0
  24. package/sampleWorkspace/localSchemaArray/apollo.config.js +12 -0
  25. package/sampleWorkspace/localSchemaArray/planets.graphql +20 -0
  26. package/sampleWorkspace/localSchemaArray/src/test.js +12 -0
  27. package/sampleWorkspace/sampleWorkspace.code-workspace +20 -0
  28. package/sampleWorkspace/spotifyGraph/apollo.config.mjs +5 -0
  29. package/sampleWorkspace/spotifyGraph/src/test.js +11 -0
  30. package/src/__e2e__/mockServer.js +117 -0
  31. package/src/__e2e__/mocks.js +13094 -0
  32. package/src/__e2e__/run.js +23 -0
  33. package/src/__e2e__/runTests.js +44 -0
  34. package/src/__e2e__/setup.js +1 -0
  35. package/src/__e2e__/vscode-environment.js +16 -0
  36. package/src/__e2e__/vscode.js +1 -0
  37. package/src/build.js +57 -0
  38. package/src/env/index.ts +0 -3
  39. package/src/extension.ts +251 -225
  40. package/src/language-server/__e2e__/clientSchema.e2e.ts +147 -0
  41. package/src/language-server/__e2e__/httpSchema.e2e.ts +21 -0
  42. package/src/language-server/__e2e__/localSchema.e2e.ts +25 -0
  43. package/src/language-server/__e2e__/localSchemaArray.e2e.ts +31 -0
  44. package/src/language-server/__e2e__/studioGraph.e2e.ts +65 -0
  45. package/src/language-server/__e2e__/utils.ts +151 -0
  46. package/src/language-server/__tests__/diagnostics.test.ts +8 -8
  47. package/src/language-server/__tests__/fileSet.test.ts +1 -1
  48. package/src/language-server/__tests__/fixtures/starwarsSchema.ts +2 -2
  49. package/src/language-server/config/__tests__/config.ts +22 -96
  50. package/src/language-server/config/__tests__/loadConfig.ts +97 -221
  51. package/src/language-server/config/__tests__/utils.ts +22 -29
  52. package/src/language-server/config/config.ts +221 -156
  53. package/src/language-server/config/loadConfig.ts +26 -153
  54. package/src/language-server/config/utils.ts +5 -16
  55. package/src/language-server/diagnostics.ts +17 -8
  56. package/src/language-server/document.ts +16 -16
  57. package/src/language-server/engine/index.ts +57 -39
  58. package/src/language-server/engine/operations/frontendUrlRoot.ts +9 -1
  59. package/src/language-server/engine/operations/schemaTagsAndFieldStats.ts +9 -1
  60. package/src/language-server/errors/__tests__/NoMissingClientDirectives.test.ts +10 -5
  61. package/src/language-server/errors/logger.ts +1 -1
  62. package/src/language-server/errors/validation.ts +20 -23
  63. package/src/language-server/fileSet.ts +10 -12
  64. package/src/language-server/format.ts +1 -1
  65. package/src/language-server/graphqlTypes.ts +13020 -3455
  66. package/src/language-server/index.ts +0 -1
  67. package/src/language-server/languageProvider.ts +29 -32
  68. package/src/language-server/loadingHandler.ts +10 -27
  69. package/src/language-server/project/base.ts +32 -25
  70. package/src/language-server/project/client.ts +80 -114
  71. package/src/language-server/project/defaultClientSchema.ts +29 -4
  72. package/src/language-server/providers/schema/__tests__/file.ts +60 -19
  73. package/src/language-server/providers/schema/base.ts +2 -2
  74. package/src/language-server/providers/schema/endpoint.ts +15 -34
  75. package/src/language-server/providers/schema/engine.ts +25 -18
  76. package/src/language-server/providers/schema/file.ts +41 -32
  77. package/src/language-server/providers/schema/index.ts +5 -21
  78. package/src/language-server/server.ts +72 -50
  79. package/src/language-server/typings/graphql.d.ts +3 -3
  80. package/src/language-server/utilities/__tests__/graphql.test.ts +42 -54
  81. package/src/language-server/utilities/debouncer.ts +1 -1
  82. package/src/language-server/utilities/debug.ts +6 -5
  83. package/src/language-server/utilities/graphql.ts +17 -16
  84. package/src/language-server/utilities/source.ts +16 -16
  85. package/src/language-server/utilities/uri.ts +2 -2
  86. package/src/language-server/workspace.ts +29 -37
  87. package/src/languageServerClient.ts +4 -4
  88. package/src/messages.ts +38 -47
  89. package/src/tools/__tests__/buildServiceDefinition.test.ts +2 -2
  90. package/src/tools/buildServiceDefinition.ts +11 -11
  91. package/src/tools/schema/resolveObject.ts +1 -1
  92. package/src/tools/utilities/predicates.ts +1 -1
  93. package/src/utils.ts +7 -6
  94. package/syntaxes/graphql.dart.json +2 -4
  95. package/syntaxes/graphql.ex.json +1 -4
  96. package/tsconfig.build.json +8 -1
  97. package/tsconfig.json +5 -3
  98. package/src/env/fetch/fetch.ts +0 -32
  99. package/src/env/fetch/global.ts +0 -30
  100. package/src/env/fetch/index.d.ts +0 -2
  101. package/src/env/fetch/index.ts +0 -2
  102. package/src/env/fetch/url.ts +0 -9
  103. package/src/env/polyfills/array.ts +0 -17
  104. package/src/env/polyfills/index.ts +0 -2
  105. package/src/env/polyfills/object.ts +0 -7
  106. package/src/language-server/engine/GraphQLDataSource.ts +0 -124
  107. package/src/language-server/project/service.ts +0 -48
  108. package/src/language-server/typings/codemirror.d.ts +0 -4
@@ -23,9 +23,12 @@ import {
23
23
  print,
24
24
  } from "graphql";
25
25
  import { ValidationRule } from "graphql/validation/ValidationContext";
26
- import { NotificationHandler, DiagnosticSeverity } from "vscode-languageserver";
26
+ import {
27
+ NotificationHandler,
28
+ DiagnosticSeverity,
29
+ } from "vscode-languageserver/node";
27
30
  import LZString from "lz-string";
28
- import { stringifyUrl } from "query-string";
31
+ import { URL } from "node:url";
29
32
 
30
33
  import { rangeForASTNode } from "../utilities/source";
31
34
  import { formatMS } from "../format";
@@ -53,8 +56,8 @@ import {
53
56
  DiagnosticSet,
54
57
  diagnosticsFromError,
55
58
  } from "../diagnostics";
56
- import URI from "vscode-uri";
57
- import type { EngineDecoration } from "src/messages";
59
+ import { URI } from "vscode-uri";
60
+ import type { EngineDecoration } from "../../messages";
58
61
  import { join } from "path";
59
62
 
60
63
  type Maybe<T> = null | undefined | T;
@@ -65,7 +68,7 @@ function schemaHasASTNodes(schema: GraphQLSchema): boolean {
65
68
  }
66
69
 
67
70
  function augmentSchemaWithGeneratedSDLIfNeeded(
68
- schema: GraphQLSchema
71
+ schema: GraphQLSchema,
69
72
  ): GraphQLSchema {
70
73
  if (schemaHasASTNodes(schema)) return schema;
71
74
 
@@ -74,18 +77,21 @@ function augmentSchemaWithGeneratedSDLIfNeeded(
74
77
  return buildSchema(
75
78
  // Rebuild the schema from a generated source file and attach the source to a `graphql-schema:/`
76
79
  // URI that can be loaded as an in-memory file by VS Code.
77
- new Source(sdl, `graphql-schema:/schema.graphql?${encodeURIComponent(sdl)}`)
80
+ new Source(
81
+ sdl,
82
+ `graphql-schema:/schema.graphql?${encodeURIComponent(sdl)}`,
83
+ ),
78
84
  );
79
85
  }
80
86
 
81
87
  export function isClientProject(
82
- project: GraphQLProject
88
+ project: GraphQLProject,
83
89
  ): project is GraphQLClientProject {
84
90
  return project instanceof GraphQLClientProject;
85
91
  }
86
92
 
87
93
  export interface GraphQLClientProjectConfig {
88
- clientIdentity?: ClientIdentity;
94
+ clientIdentity: ClientIdentity;
89
95
  config: ClientConfig;
90
96
  configFolderURI: URI;
91
97
  loadingHandler: LoadingHandler;
@@ -133,7 +139,7 @@ export class GraphQLClientProject extends GraphQLProject {
133
139
  "⚠️ It looks like there are 0 files associated with this Apollo Project. " +
134
140
  "This may be because you don't have any files yet, or your includes/excludes " +
135
141
  "fields are configured incorrectly, and Apollo can't find your files. " +
136
- "For help configuring Apollo projects, see this guide: https://go.apollo.dev/t/config"
142
+ "For help configuring Apollo projects, see this guide: https://go.apollo.dev/t/config",
137
143
  );
138
144
  }
139
145
 
@@ -203,11 +209,11 @@ export class GraphQLClientProject extends GraphQLProject {
203
209
  await this.schemaProvider.resolveSchema({
204
210
  tag: tag || this.config.variant,
205
211
  force: true,
206
- })
212
+ }),
207
213
  );
208
214
 
209
215
  this.schema = extendSchema(this.serviceSchema, this.clientSchema);
210
- })()
216
+ })(),
211
217
  );
212
218
  }
213
219
 
@@ -265,7 +271,7 @@ export class GraphQLClientProject extends GraphQLProject {
265
271
  visit(this.clientSchema, {
266
272
  ObjectTypeExtension(node) {
267
273
  const type = schema.getType(
268
- node.name.value
274
+ node.name.value,
269
275
  ) as Maybe<GraphQLObjectType>;
270
276
  const { fields } = node;
271
277
  if (!fields || !type) return;
@@ -297,7 +303,7 @@ export class GraphQLClientProject extends GraphQLProject {
297
303
  if (uri) {
298
304
  diagnosticSet.addDiagnostics(
299
305
  uri,
300
- diagnosticsFromError(error, DiagnosticSeverity.Error, "Validation")
306
+ diagnosticsFromError(error, DiagnosticSeverity.Error, "Validation"),
301
307
  );
302
308
  }
303
309
  } else {
@@ -316,8 +322,8 @@ export class GraphQLClientProject extends GraphQLProject {
316
322
  this.schema,
317
323
  document,
318
324
  fragments,
319
- this._validationRules
320
- )
325
+ this._validationRules,
326
+ ),
321
327
  );
322
328
  }
323
329
  }
@@ -354,7 +360,7 @@ export class GraphQLClientProject extends GraphQLProject {
354
360
  } catch (e) {
355
361
  console.error(e);
356
362
  }
357
- })()
363
+ })(),
358
364
  );
359
365
  }
360
366
 
@@ -399,54 +405,42 @@ export class GraphQLClientProject extends GraphQLProject {
399
405
  .join("\n\n");
400
406
  const explorerURLState =
401
407
  LZString.compressToEncodedURIComponent(
402
- JSON.stringify({ document })
408
+ JSON.stringify({ document }),
403
409
  );
404
410
 
405
411
  const frontendUrlRoot =
406
412
  this.frontendUrlRoot ?? "https://studio.apollographql.com";
407
413
 
408
414
  const variant = this.config.variant;
409
- const graphId = this.config.graph;
415
+ const graphId = this.studioGraphId;
410
416
 
411
- const { client, service } = this.config;
417
+ const { client } = this.config;
412
418
  const remoteServiceConfig =
413
419
  typeof client.service === "object" &&
414
420
  "url" in client.service
415
421
  ? client.service
416
- : service?.endpoint;
422
+ : undefined;
417
423
  const endpoint = remoteServiceConfig?.url;
418
424
 
419
- const runInExplorerPath = graphId
420
- ? stringifyUrl({
421
- url: `/graph/${graphId}/explorer`,
422
- query: {
423
- variant,
424
- explorerURLState,
425
- referrer: "vscode",
426
- },
427
- })
428
- : stringifyUrl({
429
- url: "/sandbox/explorer",
430
- query: {
431
- endpoint,
432
- explorerURLState,
433
- referrer: "vscode",
434
- },
435
- });
436
- const runInExplorerLink = join(
425
+ const runInExplorerLink = buildExplorerURL({
437
426
  frontendUrlRoot,
438
- runInExplorerPath
439
- );
440
-
441
- decorations.push({
442
- type: "runGlyph",
443
- document: uri,
444
- range: rangeForASTNode(node),
445
- hoverMessage: `[Run in Studio](${runInExplorerLink})`,
427
+ variant,
428
+ explorerURLState,
429
+ endpoint,
430
+ graphId,
446
431
  });
432
+
433
+ if (runInExplorerLink) {
434
+ decorations.push({
435
+ type: "runGlyph",
436
+ document: uri,
437
+ range: rangeForASTNode(node),
438
+ hoverMessage: `[Run in Studio](${runInExplorerLink})`,
439
+ });
440
+ }
447
441
  }
448
442
  },
449
- })
443
+ }),
450
444
  );
451
445
  }
452
446
  }
@@ -468,73 +462,9 @@ export class GraphQLClientProject extends GraphQLProject {
468
462
  return fragments;
469
463
  }
470
464
 
471
- get operations(): { [operationName: string]: OperationDefinitionNode } {
472
- const operations = Object.create(null);
473
- for (const document of this.documents) {
474
- if (!document.ast) continue;
475
- for (const definition of document.ast.definitions) {
476
- if (definition.kind === Kind.OPERATION_DEFINITION) {
477
- if (!definition.name) {
478
- throw new GraphQLError(
479
- "Apollo does not support anonymous operations",
480
- [definition]
481
- );
482
- }
483
- operations[definition.name.value] = definition;
484
- }
485
- }
486
- }
487
- return operations;
488
- }
489
-
490
- get mergedOperationsAndFragments(): {
491
- [operationName: string]: DocumentNode;
492
- } {
493
- return separateOperations({
494
- kind: Kind.DOCUMENT,
495
- definitions: [
496
- ...Object.values(this.fragments),
497
- ...Object.values(this.operations),
498
- ],
499
- });
500
- }
501
-
502
- get mergedOperationsAndFragmentsForService(): {
503
- [operationName: string]: DocumentNode;
504
- } {
505
- const { clientOnlyDirectives, clientSchemaDirectives, addTypename } =
506
- this.config.client;
507
- const current = this.mergedOperationsAndFragments;
508
- if (
509
- (!clientOnlyDirectives || !clientOnlyDirectives.length) &&
510
- (!clientSchemaDirectives || !clientSchemaDirectives.length)
511
- )
512
- return current;
513
-
514
- const filtered = Object.create(null);
515
- for (const operationName in current) {
516
- const document = current[operationName];
517
-
518
- let serviceOnly = removeDirectiveAnnotatedFields(
519
- removeDirectives(document, clientOnlyDirectives as string[]),
520
- clientSchemaDirectives as string[]
521
- );
522
-
523
- if (addTypename)
524
- serviceOnly = withTypenameFieldAddedWhereNeeded(serviceOnly);
525
- // In the case we've made a document empty by filtering client directives,
526
- // we don't want to include that in the result we pass on.
527
- if (serviceOnly.definitions.filter(Boolean).length) {
528
- filtered[operationName] = serviceOnly;
529
- }
530
- }
531
-
532
- return filtered;
533
- }
534
-
535
465
  getOperationFieldsFromFieldDefinition(
536
466
  fieldName: string,
537
- parent: ObjectTypeDefinitionNode | null
467
+ parent: ObjectTypeDefinitionNode | null,
538
468
  ): FieldNode[] {
539
469
  if (!this.schema || !parent) return [];
540
470
  const fields: FieldNode[] = [];
@@ -552,7 +482,7 @@ export class GraphQLClientProject extends GraphQLProject {
552
482
  }
553
483
  return;
554
484
  },
555
- })
485
+ }),
556
486
  );
557
487
  }
558
488
  return fields;
@@ -573,7 +503,7 @@ export class GraphQLClientProject extends GraphQLProject {
573
503
  return fragmentSpreads;
574
504
  }
575
505
  getOperationWithFragments(
576
- operationDefinition: OperationDefinitionNode
506
+ operationDefinition: OperationDefinitionNode,
577
507
  ): ExecutableDefinitionNode[] {
578
508
  const fragments = this.fragments;
579
509
  const seenFragmentNames = new Set<string>([]);
@@ -599,4 +529,40 @@ export class GraphQLClientProject extends GraphQLProject {
599
529
 
600
530
  return allDefinitions;
601
531
  }
532
+
533
+ private get studioGraphId() {
534
+ // if we don't have an `engineClient`, we are not in a studio project and `this.config.graph` could be just about anything
535
+ return this.engineClient ? this.config.graph : undefined;
536
+ }
537
+ }
538
+
539
+ function buildExplorerURL({
540
+ frontendUrlRoot,
541
+ variant,
542
+ explorerURLState,
543
+ endpoint,
544
+ graphId,
545
+ }: {
546
+ frontendUrlRoot: string;
547
+ variant: string;
548
+ explorerURLState: string;
549
+ endpoint: string | undefined;
550
+ graphId: string | undefined;
551
+ }) {
552
+ const url = new URL(
553
+ graphId ? `/graph/${graphId}/explorer` : "/sandbox/explorer",
554
+ frontendUrlRoot,
555
+ );
556
+ url.searchParams.set("explorerURLState", explorerURLState);
557
+ url.searchParams.set("referrer", "vscode");
558
+ if (graphId) {
559
+ url.searchParams.set("variant", variant);
560
+ } else if (endpoint) {
561
+ url.searchParams.set("endpoint", endpoint);
562
+ } else {
563
+ // we don't know a graphId or endpoint, so we can't build a URL
564
+ // in that case, we don't want to show a broken 'Run in explorer' gutter link
565
+ return null;
566
+ }
567
+ return url.toString();
602
568
  }
@@ -8,14 +8,14 @@ Direct the client to resolve this field locally, either from the cache or local
8
8
  directive @client(
9
9
  """
10
10
  When true, the client will never use the cache for this value. See
11
- https://www.apollographql.com/docs/react/essentials/local-state/#forcing-resolvers-with-clientalways-true
11
+ https://www.apollographql.com/docs/react/local-state/local-resolvers/#forcing-resolvers-with-clientalways-true
12
12
  """
13
13
  always: Boolean
14
14
  ) on FIELD | FRAGMENT_DEFINITION | INLINE_FRAGMENT
15
15
 
16
16
  """
17
17
  Export this locally resolved field as a variable to be used in the remainder of this query. See
18
- https://www.apollographql.com/docs/react/essentials/local-state/#using-client-fields-as-variables
18
+ https://www.apollographql.com/docs/react/local-state/local-resolvers/#using-client-fields-as-variables
19
19
  """
20
20
  directive @export(
21
21
  """
@@ -26,7 +26,7 @@ directive @export(
26
26
 
27
27
  """
28
28
  Specify a custom store key for this result. See
29
- https://www.apollographql.com/docs/react/advanced/caching/#the-connection-directive
29
+ https://www.apollographql.com/docs/react/caching/advanced-topics/#the-connection-directive
30
30
  """
31
31
  directive @connection(
32
32
  """
@@ -38,8 +38,33 @@ directive @connection(
38
38
  """
39
39
  filter: [String!]
40
40
  ) on FIELD
41
+
42
+ """
43
+ The @nonreactive directive can be used to mark query fields or fragment spreads and is used to indicate that changes to the data contained within the subtrees marked @nonreactive should not trigger rerendering.
44
+ This allows parent components to fetch data to be rendered by their children without rerendering themselves when the data corresponding with fields marked as @nonreactive change.
45
+ https://www.apollographql.com/docs/react/data/directives#nonreactive
46
+ """
47
+ directive @nonreactive on FIELD
48
+
49
+ """
50
+ This directive enables your queries to receive data for specific fields incrementally, instead of receiving all field data at the same time.
51
+ This is helpful whenever some fields in a query take much longer to resolve than others.
52
+ https://www.apollographql.com/docs/react/data/directives#defer
53
+ """
54
+ directive @defer(
55
+ """
56
+ When true fragment may be deferred, if omitted defaults to true.
57
+ """
58
+ if: Boolean
59
+ """
60
+ A unique label across all @defer and @stream directives in an operation.
61
+ This label should be used by GraphQL clients to identify the data from patch responses and associate it with the correct fragment.
62
+ If provided, the GraphQL Server must add it to the payload.
63
+ """
64
+ label: String
65
+ ) on FRAGMENT_SPREAD | INLINE_FRAGMENT
41
66
  `;
42
67
 
43
68
  export const apolloClientSchemaDocument = new GraphQLDocument(
44
- new Source(apolloClientSchema)
69
+ new Source(apolloClientSchema),
45
70
  );
@@ -2,13 +2,14 @@ import { FileSchemaProvider } from "../file";
2
2
  import * as path from "path";
3
3
  import * as fs from "fs";
4
4
  import { Debug } from "../../../utilities";
5
+ import { URI } from "vscode-uri";
5
6
 
6
7
  const makeNestedDir = (dir: string) => {
7
8
  if (fs.existsSync(dir)) return;
8
9
 
9
10
  try {
10
11
  fs.mkdirSync(dir);
11
- } catch (err) {
12
+ } catch (err: any) {
12
13
  if (err.code == "ENOENT") {
13
14
  makeNestedDir(path.dirname(dir)); //create parent dir
14
15
  makeNestedDir(dir); //create dir
@@ -77,9 +78,12 @@ describe("FileSchemaProvider", () => {
77
78
  `,
78
79
  });
79
80
 
80
- const provider = new FileSchemaProvider({
81
- path: dir + "/schema.graphql",
82
- });
81
+ const provider = new FileSchemaProvider(
82
+ {
83
+ path: "./schema.graphql",
84
+ },
85
+ URI.from({ scheme: "file", path: dirPath }),
86
+ );
83
87
  const sdl = await provider.resolveFederatedServiceSDL();
84
88
  expect(sdl).toMatchInlineSnapshot;
85
89
  });
@@ -102,23 +106,57 @@ describe("FileSchemaProvider", () => {
102
106
  }`,
103
107
  });
104
108
 
105
- const provider = new FileSchemaProvider({
106
- paths: [dir + "/schema.graphql", dir + "/schema2.graphql"],
107
- });
109
+ const provider = new FileSchemaProvider(
110
+ {
111
+ paths: ["schema.graphql", "schema2.graphql"],
112
+ },
113
+ URI.from({ scheme: "file", path: dirPath }),
114
+ );
108
115
  const sdl = await provider.resolveFederatedServiceSDL();
109
116
  expect(sdl).toMatchInlineSnapshot(`
110
- "type Product @key(fields: \\"id\\") {
111
- id: ID
112
- sku: ID
113
- name: String
114
- weight: Float
115
- }
117
+ "directive @key(fields: _FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
116
118
 
117
- extend type Query {
118
- myProduct: Product
119
- }
120
- "
121
- `);
119
+ directive @requires(fields: _FieldSet!) on FIELD_DEFINITION
120
+
121
+ directive @provides(fields: _FieldSet!) on FIELD_DEFINITION
122
+
123
+ directive @external(reason: String) on OBJECT | FIELD_DEFINITION
124
+
125
+ directive @tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION
126
+
127
+ directive @extends on OBJECT | INTERFACE
128
+
129
+ type Query {
130
+ _entities(representations: [_Any!]!): [_Entity]!
131
+ _service: _Service!
132
+ }
133
+
134
+ extend type Query {
135
+ myProduct: Product
136
+ }
137
+
138
+ type Product
139
+ @key(fields: \\"id\\")
140
+ {
141
+ id: ID
142
+ sku: ID
143
+ name: String
144
+ }
145
+
146
+ extend type Product {
147
+ weight: Float
148
+ }
149
+
150
+ scalar _FieldSet
151
+
152
+ scalar _Any
153
+
154
+ type _Service {
155
+ sdl: String
156
+ }
157
+
158
+ union _Entity = Product"
159
+ `);
122
160
  });
123
161
 
124
162
  it("errors when sdl file is not a graphql file", async () => {
@@ -142,7 +180,10 @@ describe("FileSchemaProvider", () => {
142
180
  const errorSpy = jest.spyOn(Debug, "error");
143
181
  errorSpy.mockImplementation(() => {});
144
182
 
145
- const provider = new FileSchemaProvider({ path: dir + "/schema.js" });
183
+ const provider = new FileSchemaProvider(
184
+ { path: "./schema.js" },
185
+ URI.from({ scheme: "file", path: dirPath }),
186
+ );
146
187
  const sdl = await provider.resolveFederatedServiceSDL();
147
188
  expect(errorSpy).toBeCalledTimes(2);
148
189
  });
@@ -1,5 +1,5 @@
1
1
  import { GraphQLSchema } from "graphql";
2
- import { NotificationHandler } from "vscode-languageserver";
2
+ import { NotificationHandler } from "vscode-languageserver/node";
3
3
 
4
4
  export interface SchemaResolveConfig {
5
5
  tag?: string;
@@ -9,7 +9,7 @@ export type SchemaChangeUnsubscribeHandler = () => void;
9
9
  export interface GraphQLSchemaProvider {
10
10
  resolveSchema(config?: SchemaResolveConfig): Promise<GraphQLSchema>;
11
11
  onSchemaChange(
12
- handler: NotificationHandler<GraphQLSchema>
12
+ handler: NotificationHandler<GraphQLSchema>,
13
13
  ): SchemaChangeUnsubscribeHandler;
14
14
  resolveFederatedServiceSDL(): Promise<string | void>;
15
15
  }
@@ -1,7 +1,9 @@
1
1
  // IntrospectionSchemaProvider (http => IntrospectionResult => schema)
2
- import { NotificationHandler } from "vscode-languageserver";
3
- import { execute as linkExecute, toPromise } from "apollo-link";
4
- import { createHttpLink, HttpLink } from "apollo-link-http";
2
+ import { NotificationHandler } from "vscode-languageserver/node";
3
+
4
+ import { execute as linkExecute } from "@apollo/client/link/core";
5
+ import { toPromise } from "@apollo/client/link/utils";
6
+ import { createHttpLink, HttpOptions } from "@apollo/client/link/http";
5
7
  import {
6
8
  GraphQLSchema,
7
9
  buildClientSchema,
@@ -11,8 +13,7 @@ import {
11
13
  parse,
12
14
  } from "graphql";
13
15
  import { Agent as HTTPSAgent } from "https";
14
- import { fetch } from "../../../env";
15
- import { RemoteServiceConfig, DefaultServiceConfig } from "../../config";
16
+ import { RemoteServiceConfig } from "../../config";
16
17
  import { GraphQLSchemaProvider, SchemaChangeUnsubscribeHandler } from "./base";
17
18
  import { Debug } from "../../utilities";
18
19
  import { isString } from "util";
@@ -25,9 +26,8 @@ export class EndpointSchemaProvider implements GraphQLSchemaProvider {
25
26
  async resolveSchema() {
26
27
  if (this.schema) return this.schema;
27
28
  const { skipSSLValidation, url, headers } = this.config;
28
- const options: HttpLink.Options = {
29
+ const options: HttpOptions = {
29
30
  uri: url,
30
- fetch,
31
31
  };
32
32
  if (url.startsWith("https:") && skipSSLValidation) {
33
33
  options.fetchOptions = {
@@ -39,7 +39,7 @@ export class EndpointSchemaProvider implements GraphQLSchemaProvider {
39
39
  linkExecute(createHttpLink(options), {
40
40
  query: parse(getIntrospectionQuery()),
41
41
  context: { headers },
42
- })
42
+ }),
43
43
  ).catch((e) => {
44
44
  // html response from introspection
45
45
  if (isString(e.message) && e.message.includes("token <")) {
@@ -51,35 +51,16 @@ export class EndpointSchemaProvider implements GraphQLSchemaProvider {
51
51
  "-----------------------------\n" +
52
52
  "For more information, please refer to: https://go.apollo.dev/t/config \n\n" +
53
53
  "The following error occurred:\n-----------------------------\n" +
54
- e.message
54
+ e.message,
55
55
  );
56
56
  }
57
57
 
58
- // 404 encountered with the default url
59
- if (
60
- url === DefaultServiceConfig.endpoint.url &&
61
- isString(e.message) &&
62
- e.message.includes("ECONNREFUSED")
63
- ) {
64
- throw new Error(
65
- "Failed to connect to a running GraphQL endpoint at " +
66
- url +
67
- "\nThis may be because you didn't start your service.\n" +
68
- "By default, when an endpoint, Apollo API key, or localSchemaFile isn't provided, Apollo tries to fetch a schema from " +
69
- DefaultServiceConfig.endpoint.url +
70
- "\n-----------------------------\n" +
71
- "\nFor more information, please refer to: https://go.apollo.dev/t/config \n\n" +
72
- "The following error occurred: \n" +
73
- "-----------------------------\n" +
74
- e.message
75
- );
76
- }
77
58
  // 404 with a non-default url
78
59
  if (isString(e.message) && e.message.includes("ECONNREFUSED")) {
79
60
  throw new Error(
80
61
  "Failed to connect to a running GraphQL endpoint at " +
81
62
  url +
82
- "\nThis may be because you didn't start your service or the endpoint URL is incorrect."
63
+ "\nThis may be because you didn't start your service or the endpoint URL is incorrect.",
83
64
  );
84
65
  }
85
66
  throw new Error(e);
@@ -99,7 +80,7 @@ export class EndpointSchemaProvider implements GraphQLSchemaProvider {
99
80
  }
100
81
 
101
82
  onSchemaChange(
102
- _handler: NotificationHandler<GraphQLSchema>
83
+ _handler: NotificationHandler<GraphQLSchema>,
103
84
  ): SchemaChangeUnsubscribeHandler {
104
85
  throw new Error("Polling of endpoint not implemented yet");
105
86
  return () => {};
@@ -109,7 +90,7 @@ export class EndpointSchemaProvider implements GraphQLSchemaProvider {
109
90
  if (this.federatedServiceSDL) return this.federatedServiceSDL;
110
91
 
111
92
  const { skipSSLValidation, url, headers } = this.config;
112
- const options: HttpLink.Options = {
93
+ const options: HttpOptions = {
113
94
  uri: url,
114
95
  fetch,
115
96
  };
@@ -131,18 +112,18 @@ export class EndpointSchemaProvider implements GraphQLSchemaProvider {
131
112
  linkExecute(createHttpLink(options), {
132
113
  query: parse(getFederationInfoQuery),
133
114
  context: { headers },
134
- })
115
+ }),
135
116
  )) as ExecutionResult<{ _service: { sdl: string } }>;
136
117
 
137
118
  if (errors && errors.length) {
138
119
  return Debug.error(
139
- errors.map(({ message }: Error) => message).join("\n")
120
+ errors.map(({ message }: Error) => message).join("\n"),
140
121
  );
141
122
  }
142
123
 
143
124
  if (!data || !data._service) {
144
125
  return Debug.error(
145
- "No data received from server when querying for _service."
126
+ "No data received from server when querying for _service.",
146
127
  );
147
128
  }
148
129