vscode-apollo 1.20.0 → 2.0.1
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.
- package/.circleci/config.yml +27 -18
- package/.git-blame-ignore-revs +2 -0
- package/.nvmrc +1 -1
- package/.vscode/launch.json +9 -4
- package/.vscode/tasks.json +58 -16
- package/.vscodeignore +12 -1
- package/CHANGELOG.md +84 -0
- package/CODEOWNERS +4 -0
- package/README.md +97 -48
- package/graphql.configuration.json +5 -1
- package/images/marketplace/apollo-wordmark.png +0 -0
- package/jest.config.ts +14 -4
- package/jest.e2e.config.js +17 -0
- package/package.json +67 -68
- package/renovate.json +7 -0
- package/sampleWorkspace/clientSchema/apollo.config.cjs +10 -0
- package/sampleWorkspace/clientSchema/src/clientSchema.js +16 -0
- package/sampleWorkspace/clientSchema/src/test.js +18 -0
- package/sampleWorkspace/fixtures/starwarsSchema.graphql +299 -0
- package/sampleWorkspace/httpSchema/apollo.config.ts +8 -0
- package/sampleWorkspace/httpSchema/src/test.js +9 -0
- package/sampleWorkspace/localSchema/apollo.config.js +8 -0
- package/sampleWorkspace/localSchema/src/test.js +8 -0
- package/sampleWorkspace/localSchemaArray/apollo.config.js +12 -0
- package/sampleWorkspace/localSchemaArray/planets.graphql +20 -0
- package/sampleWorkspace/localSchemaArray/src/test.js +12 -0
- package/sampleWorkspace/sampleWorkspace.code-workspace +20 -0
- package/sampleWorkspace/spotifyGraph/apollo.config.mjs +5 -0
- package/sampleWorkspace/spotifyGraph/src/test.js +11 -0
- package/src/__e2e__/mockServer.js +117 -0
- package/src/__e2e__/mocks.js +13094 -0
- package/src/__e2e__/run.js +23 -0
- package/src/__e2e__/runTests.js +44 -0
- package/src/__e2e__/setup.js +1 -0
- package/src/__e2e__/vscode-environment.js +16 -0
- package/src/__e2e__/vscode.js +1 -0
- package/src/build.js +57 -0
- package/src/env/index.ts +0 -3
- package/src/extension.ts +251 -225
- package/src/language-server/__e2e__/clientSchema.e2e.ts +147 -0
- package/src/language-server/__e2e__/httpSchema.e2e.ts +21 -0
- package/src/language-server/__e2e__/localSchema.e2e.ts +25 -0
- package/src/language-server/__e2e__/localSchemaArray.e2e.ts +31 -0
- package/src/language-server/__e2e__/studioGraph.e2e.ts +65 -0
- package/src/language-server/__e2e__/utils.ts +151 -0
- package/src/language-server/__tests__/diagnostics.test.ts +8 -8
- package/src/language-server/__tests__/fileSet.test.ts +1 -1
- package/src/language-server/__tests__/fixtures/starwarsSchema.ts +2 -2
- package/src/language-server/config/__tests__/config.ts +22 -96
- package/src/language-server/config/__tests__/loadConfig.ts +120 -220
- package/src/language-server/config/__tests__/utils.ts +22 -29
- package/src/language-server/config/config.ts +221 -156
- package/src/language-server/config/loadConfig.ts +32 -153
- package/src/language-server/config/loadTsConfig.ts +70 -0
- package/src/language-server/config/utils.ts +5 -16
- package/src/language-server/diagnostics.ts +17 -8
- package/src/language-server/document.ts +16 -16
- package/src/language-server/engine/index.ts +57 -39
- package/src/language-server/engine/operations/frontendUrlRoot.ts +9 -1
- package/src/language-server/engine/operations/schemaTagsAndFieldStats.ts +9 -1
- package/src/language-server/errors/__tests__/NoMissingClientDirectives.test.ts +10 -5
- package/src/language-server/errors/logger.ts +1 -1
- package/src/language-server/errors/validation.ts +20 -23
- package/src/language-server/fileSet.ts +10 -12
- package/src/language-server/format.ts +1 -1
- package/src/language-server/graphqlTypes.ts +13020 -3455
- package/src/language-server/index.ts +0 -1
- package/src/language-server/languageProvider.ts +29 -32
- package/src/language-server/loadingHandler.ts +10 -27
- package/src/language-server/project/base.ts +32 -25
- package/src/language-server/project/client.ts +80 -114
- package/src/language-server/project/defaultClientSchema.ts +29 -4
- package/src/language-server/providers/schema/__tests__/file.ts +60 -19
- package/src/language-server/providers/schema/base.ts +2 -2
- package/src/language-server/providers/schema/endpoint.ts +15 -34
- package/src/language-server/providers/schema/engine.ts +25 -18
- package/src/language-server/providers/schema/file.ts +41 -32
- package/src/language-server/providers/schema/index.ts +5 -21
- package/src/language-server/server.ts +72 -50
- package/src/language-server/typings/graphql.d.ts +3 -3
- package/src/language-server/utilities/__tests__/graphql.test.ts +42 -54
- package/src/language-server/utilities/debouncer.ts +1 -1
- package/src/language-server/utilities/debug.ts +6 -5
- package/src/language-server/utilities/graphql.ts +17 -16
- package/src/language-server/utilities/source.ts +16 -16
- package/src/language-server/utilities/uri.ts +2 -2
- package/src/language-server/workspace.ts +29 -37
- package/src/languageServerClient.ts +4 -4
- package/src/messages.ts +38 -47
- package/src/tools/__tests__/buildServiceDefinition.test.ts +2 -2
- package/src/tools/buildServiceDefinition.ts +11 -11
- package/src/tools/schema/resolveObject.ts +1 -1
- package/src/tools/utilities/predicates.ts +1 -1
- package/src/utils.ts +7 -6
- package/syntaxes/graphql.dart.json +2 -4
- package/syntaxes/graphql.ex.json +1 -4
- package/tsconfig.build.json +8 -1
- package/tsconfig.json +5 -3
- package/src/env/fetch/fetch.ts +0 -32
- package/src/env/fetch/global.ts +0 -30
- package/src/env/fetch/index.d.ts +0 -2
- package/src/env/fetch/index.ts +0 -2
- package/src/env/fetch/url.ts +0 -9
- package/src/env/polyfills/array.ts +0 -17
- package/src/env/polyfills/index.ts +0 -2
- package/src/env/polyfills/object.ts +0 -7
- package/src/language-server/engine/GraphQLDataSource.ts +0 -124
- package/src/language-server/project/service.ts +0 -48
- 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 {
|
|
26
|
+
import {
|
|
27
|
+
NotificationHandler,
|
|
28
|
+
DiagnosticSeverity,
|
|
29
|
+
} from "vscode-languageserver/node";
|
|
27
30
|
import LZString from "lz-string";
|
|
28
|
-
import {
|
|
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 "
|
|
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(
|
|
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
|
|
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.
|
|
415
|
+
const graphId = this.studioGraphId;
|
|
410
416
|
|
|
411
|
-
const { client
|
|
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
|
-
:
|
|
422
|
+
: undefined;
|
|
417
423
|
const endpoint = remoteServiceConfig?.url;
|
|
418
424
|
|
|
419
|
-
const
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
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/
|
|
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/
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
118
|
-
|
|
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(
|
|
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
|
-
|
|
4
|
-
import {
|
|
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 {
|
|
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:
|
|
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:
|
|
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
|
|