vscode-apollo 2.2.1 → 2.3.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 +1 -17
- package/.github/workflows/E2E.yml +27 -0
- package/.github/workflows/build-prs.yml +1 -1
- package/CHANGELOG.md +22 -0
- package/README.md +71 -52
- package/jest.e2e.config.js +2 -0
- package/package.json +31 -3
- package/renovate.json +5 -5
- package/sampleWorkspace/localSchemaArray/apollo.config.json +9 -0
- package/sampleWorkspace/localSchemaArray/src/test.js +1 -1
- package/sampleWorkspace/rover/apollo.config.yaml +3 -0
- package/sampleWorkspace/rover/src/test.graphql +11 -10
- package/sampleWorkspace/rover/supergraph.yaml +0 -0
- package/schemas/apollo.config.schema.json +184 -0
- package/src/__e2e__/runTests.js +1 -0
- package/src/build.js +53 -1
- package/src/language-server/__e2e__/clientSchema.e2e.ts +58 -28
- package/src/language-server/__e2e__/httpSchema.e2e.ts +23 -4
- package/src/language-server/__e2e__/localSchema.e2e.ts +23 -4
- package/src/language-server/__e2e__/localSchemaArray.e2e.ts +31 -6
- package/src/language-server/__e2e__/rover.e2e.ts +150 -0
- package/src/language-server/__e2e__/studioGraph.e2e.ts +18 -6
- package/src/language-server/__e2e__/utils.ts +114 -13
- package/src/language-server/config/__tests__/loadConfig.ts +35 -2
- package/src/language-server/config/cache-busting-resolver.js +65 -0
- package/src/language-server/config/cache-busting-resolver.types.ts +45 -0
- package/src/language-server/config/config.ts +133 -57
- package/src/language-server/config/loadConfig.ts +27 -6
- package/src/language-server/config/loadTsConfig.ts +74 -38
- package/src/language-server/project/base.ts +8 -8
- package/src/language-server/project/rover/project.ts +6 -0
- package/src/language-server/server.ts +8 -7
- package/src/language-server/workspace.ts +2 -5
- package/src/languageServerClient.ts +3 -1
- package/sampleWorkspace/localSchemaArray/apollo.config.js +0 -12
- package/sampleWorkspace/rover/apollo.config.js +0 -3
- /package/sampleWorkspace/localSchema/{apollo.config.js → apollo.config.ts} +0 -0
package/src/build.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
const esbuild = require("esbuild");
|
|
2
|
+
const { zodToJsonSchema } = require("zod-to-json-schema");
|
|
3
|
+
const { writeFileSync } = require("fs");
|
|
4
|
+
const importFresh = require("import-fresh");
|
|
2
5
|
|
|
3
6
|
const production = process.argv.includes("--production");
|
|
4
7
|
const watch = process.argv.includes("--watch");
|
|
5
8
|
|
|
6
9
|
async function main() {
|
|
7
10
|
const ctx = await esbuild.context({
|
|
8
|
-
entryPoints: [
|
|
11
|
+
entryPoints: [
|
|
12
|
+
"src/extension.ts",
|
|
13
|
+
"src/language-server/server.ts",
|
|
14
|
+
"src/language-server/config/config.ts",
|
|
15
|
+
"src/language-server/config/cache-busting-resolver.js",
|
|
16
|
+
],
|
|
9
17
|
bundle: true,
|
|
10
18
|
format: "cjs",
|
|
11
19
|
minify: production,
|
|
@@ -19,6 +27,8 @@ async function main() {
|
|
|
19
27
|
plugins: [
|
|
20
28
|
/* add to the end of plugins array */
|
|
21
29
|
esbuildProblemMatcherPlugin,
|
|
30
|
+
buildJsonSchemaPlugin,
|
|
31
|
+
resolvePlugin,
|
|
22
32
|
],
|
|
23
33
|
});
|
|
24
34
|
if (watch) {
|
|
@@ -51,6 +61,48 @@ const esbuildProblemMatcherPlugin = {
|
|
|
51
61
|
},
|
|
52
62
|
};
|
|
53
63
|
|
|
64
|
+
const buildJsonSchemaPlugin = {
|
|
65
|
+
name: "build-json-schema",
|
|
66
|
+
setup(build) {
|
|
67
|
+
build.onEnd(() => {
|
|
68
|
+
const {
|
|
69
|
+
configSchema,
|
|
70
|
+
clientConfig,
|
|
71
|
+
// roverConfig,
|
|
72
|
+
engineConfig,
|
|
73
|
+
baseConfig,
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
} = importFresh("../lib/language-server/config/config.js");
|
|
76
|
+
|
|
77
|
+
const jsonSchema = zodToJsonSchema(configSchema, {
|
|
78
|
+
errorMessages: true,
|
|
79
|
+
definitions: {
|
|
80
|
+
clientConfig,
|
|
81
|
+
//roverConfig,
|
|
82
|
+
engineConfig,
|
|
83
|
+
baseConfig,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
writeFileSync(
|
|
87
|
+
"./schemas/apollo.config.schema.json",
|
|
88
|
+
JSON.stringify(jsonSchema, null, 2),
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const resolvePlugin = {
|
|
95
|
+
name: "resolve",
|
|
96
|
+
setup(build) {
|
|
97
|
+
build.onResolve(
|
|
98
|
+
{ filter: /^jsonc-parser$/ },
|
|
99
|
+
async ({ path, ...options }) => {
|
|
100
|
+
return build.resolve("jsonc-parser/lib/esm/main.js", options);
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
54
106
|
main().catch((e) => {
|
|
55
107
|
console.error(e);
|
|
56
108
|
process.exit(1);
|
|
@@ -1,24 +1,45 @@
|
|
|
1
1
|
import { TextEditor } from "vscode";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
closeAllEditors,
|
|
4
|
+
openEditor,
|
|
5
|
+
getCompletionItems,
|
|
6
|
+
getHover,
|
|
7
|
+
getPositionForEditor,
|
|
8
|
+
GetPositionFn,
|
|
9
|
+
} from "./utils";
|
|
3
10
|
|
|
4
11
|
let editor: TextEditor;
|
|
12
|
+
let getPosition: GetPositionFn;
|
|
5
13
|
beforeAll(async () => {
|
|
6
14
|
closeAllEditors();
|
|
7
15
|
editor = await openEditor("clientSchema/src/test.js");
|
|
16
|
+
getPosition = getPositionForEditor(editor);
|
|
8
17
|
});
|
|
9
18
|
|
|
10
19
|
test("completion", async () => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
expect(
|
|
21
|
+
(await getCompletionItems(editor, getPosition("dro|id")))[0],
|
|
22
|
+
).toStrictEqual({
|
|
23
|
+
label: "droid",
|
|
24
|
+
detail: "Droid",
|
|
25
|
+
});
|
|
26
|
+
expect(
|
|
27
|
+
(await getCompletionItems(editor, getPosition("na|me")))[0],
|
|
28
|
+
).toStrictEqual({
|
|
29
|
+
label: "name",
|
|
30
|
+
detail: "String!",
|
|
31
|
+
});
|
|
32
|
+
expect(
|
|
33
|
+
(await getCompletionItems(editor, getPosition("mo|del")))[0],
|
|
34
|
+
).toStrictEqual({
|
|
35
|
+
label: "model",
|
|
36
|
+
detail: "String",
|
|
37
|
+
});
|
|
17
38
|
});
|
|
18
39
|
|
|
19
40
|
test("hover", async () => {
|
|
20
|
-
|
|
21
|
-
|
|
41
|
+
expect(await getHover(editor, getPosition("featu|reFlagDefer")))
|
|
42
|
+
.toMatchInlineSnapshot(`
|
|
22
43
|
"\`\`\`graphql
|
|
23
44
|
Query.featureFlagDefer: Boolean!
|
|
24
45
|
\`\`\`
|
|
@@ -32,8 +53,8 @@ Query.featureFlagDefer: Boolean!
|
|
|
32
53
|
Whether to use defer"
|
|
33
54
|
`);
|
|
34
55
|
|
|
35
|
-
|
|
36
|
-
|
|
56
|
+
expect(await getHover(editor, getPosition("@c|lient(always: false)")))
|
|
57
|
+
.toMatchInlineSnapshot(`
|
|
37
58
|
"\`\`\`graphql
|
|
38
59
|
@client(always: Boolean)
|
|
39
60
|
\`\`\`
|
|
@@ -43,8 +64,8 @@ Whether to use defer"
|
|
|
43
64
|
Direct the client to resolve this field locally, either from the cache or local resolvers."
|
|
44
65
|
`);
|
|
45
66
|
|
|
46
|
-
|
|
47
|
-
|
|
67
|
+
expect(await getHover(editor, getPosition("@client(alwa|ys: false)")))
|
|
68
|
+
.toMatchInlineSnapshot(`
|
|
48
69
|
"\`\`\`graphql
|
|
49
70
|
always: Boolean
|
|
50
71
|
\`\`\`
|
|
@@ -55,8 +76,8 @@ When true, the client will never use the cache for this value. See
|
|
|
55
76
|
https://www.apollographql.com/docs/react/local-state/local-resolvers/#forcing-resolvers-with-clientalways-true"
|
|
56
77
|
`);
|
|
57
78
|
|
|
58
|
-
|
|
59
|
-
|
|
79
|
+
expect(await getHover(editor, getPosition('@expo|rt(as: "defer")')))
|
|
80
|
+
.toMatchInlineSnapshot(`
|
|
60
81
|
"\`\`\`graphql
|
|
61
82
|
@export(as: String!)
|
|
62
83
|
\`\`\`
|
|
@@ -66,7 +87,9 @@ https://www.apollographql.com/docs/react/local-state/local-resolvers/#forcing-re
|
|
|
66
87
|
Export this locally resolved field as a variable to be used in the remainder of this query. See
|
|
67
88
|
https://www.apollographql.com/docs/react/local-state/local-resolvers/#using-client-fields-as-variables"
|
|
68
89
|
`);
|
|
69
|
-
|
|
90
|
+
|
|
91
|
+
expect(await getHover(editor, getPosition('@export(a|s: "defer")')))
|
|
92
|
+
.toMatchInlineSnapshot(`
|
|
70
93
|
"\`\`\`graphql
|
|
71
94
|
as: String!
|
|
72
95
|
\`\`\`
|
|
@@ -76,8 +99,8 @@ as: String!
|
|
|
76
99
|
The variable name to export this field as."
|
|
77
100
|
`);
|
|
78
101
|
|
|
79
|
-
|
|
80
|
-
|
|
102
|
+
expect(await getHover(editor, getPosition("@nonre|active")))
|
|
103
|
+
.toMatchInlineSnapshot(`
|
|
81
104
|
"\`\`\`graphql
|
|
82
105
|
@nonreactive
|
|
83
106
|
\`\`\`
|
|
@@ -89,8 +112,9 @@ This allows parent components to fetch data to be rendered by their children wit
|
|
|
89
112
|
https://www.apollographql.com/docs/react/data/directives#nonreactive"
|
|
90
113
|
`);
|
|
91
114
|
|
|
92
|
-
|
|
93
|
-
|
|
115
|
+
expect(
|
|
116
|
+
await getHover(editor, getPosition('@def|er(if: $defer, label: "fc")')),
|
|
117
|
+
).toMatchInlineSnapshot(`
|
|
94
118
|
"\`\`\`graphql
|
|
95
119
|
@defer(if: Boolean, label: String)
|
|
96
120
|
\`\`\`
|
|
@@ -101,8 +125,10 @@ This directive enables your queries to receive data for specific fields incremen
|
|
|
101
125
|
This is helpful whenever some fields in a query take much longer to resolve than others.
|
|
102
126
|
https://www.apollographql.com/docs/react/data/directives#defer"
|
|
103
127
|
`);
|
|
104
|
-
|
|
105
|
-
expect(
|
|
128
|
+
|
|
129
|
+
expect(
|
|
130
|
+
await getHover(editor, getPosition('@defer(i|f: $defer, label: "fc")')),
|
|
131
|
+
).toMatchInlineSnapshot(`
|
|
106
132
|
"\`\`\`graphql
|
|
107
133
|
if: Boolean
|
|
108
134
|
\`\`\`
|
|
@@ -111,8 +137,10 @@ if: Boolean
|
|
|
111
137
|
|
|
112
138
|
When true fragment may be deferred, if omitted defaults to true."
|
|
113
139
|
`);
|
|
114
|
-
|
|
115
|
-
expect(
|
|
140
|
+
|
|
141
|
+
expect(
|
|
142
|
+
await getHover(editor, getPosition('@defer(if: $defer, labe|l: "fc")')),
|
|
143
|
+
).toMatchInlineSnapshot(`
|
|
116
144
|
"\`\`\`graphql
|
|
117
145
|
label: String
|
|
118
146
|
\`\`\`
|
|
@@ -123,8 +151,9 @@ A unique label across all @defer and @stream directives in an operation.
|
|
|
123
151
|
This label should be used by GraphQL clients to identify the data from patch responses and associate it with the correct fragment.
|
|
124
152
|
If provided, the GraphQL Server must add it to the payload."
|
|
125
153
|
`);
|
|
126
|
-
|
|
127
|
-
expect(await getHover(editor,
|
|
154
|
+
|
|
155
|
+
expect(await getHover(editor, getPosition('@connec|tion(key: "feed")')))
|
|
156
|
+
.toMatchInlineSnapshot(`
|
|
128
157
|
"\`\`\`graphql
|
|
129
158
|
@connection(key: String!, filter: [String!])
|
|
130
159
|
\`\`\`
|
|
@@ -134,8 +163,9 @@ If provided, the GraphQL Server must add it to the payload."
|
|
|
134
163
|
Specify a custom store key for this result. See
|
|
135
164
|
https://www.apollographql.com/docs/react/caching/advanced-topics/#the-connection-directive"
|
|
136
165
|
`);
|
|
137
|
-
|
|
138
|
-
expect(await getHover(editor,
|
|
166
|
+
|
|
167
|
+
expect(await getHover(editor, getPosition('@connection(ke|y: "feed")')))
|
|
168
|
+
.toMatchInlineSnapshot(`
|
|
139
169
|
"\`\`\`graphql
|
|
140
170
|
key: String!
|
|
141
171
|
\`\`\`
|
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
import { TextEditor } from "vscode";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
closeAllEditors,
|
|
4
|
+
openEditor,
|
|
5
|
+
getCompletionItems,
|
|
6
|
+
getHover,
|
|
7
|
+
getPositionForEditor,
|
|
8
|
+
GetPositionFn,
|
|
9
|
+
} from "./utils";
|
|
3
10
|
|
|
4
11
|
let editor: TextEditor;
|
|
12
|
+
let getPosition: GetPositionFn;
|
|
5
13
|
beforeAll(async () => {
|
|
6
14
|
closeAllEditors();
|
|
7
15
|
editor = await openEditor("httpSchema/src/test.js");
|
|
16
|
+
getPosition = getPositionForEditor(editor);
|
|
8
17
|
});
|
|
9
18
|
|
|
10
19
|
test("completion", async () => {
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
expect(
|
|
21
|
+
(await getCompletionItems(editor, getPosition("bo|oks")))[0],
|
|
22
|
+
).toStrictEqual({
|
|
23
|
+
label: "books",
|
|
24
|
+
detail: "[Book]",
|
|
25
|
+
});
|
|
26
|
+
expect(
|
|
27
|
+
(await getCompletionItems(editor, getPosition("au|thor")))[0],
|
|
28
|
+
).toStrictEqual({
|
|
29
|
+
label: "author",
|
|
30
|
+
detail: "String",
|
|
31
|
+
});
|
|
13
32
|
});
|
|
14
33
|
|
|
15
34
|
test("hover", async () => {
|
|
16
|
-
expect(await getHover(editor,
|
|
35
|
+
expect(await getHover(editor, getPosition("au|thor"))).toMatchInlineSnapshot(`
|
|
17
36
|
"\`\`\`graphql
|
|
18
37
|
Book.author: String
|
|
19
38
|
\`\`\`"
|
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
import { TextEditor } from "vscode";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
closeAllEditors,
|
|
4
|
+
openEditor,
|
|
5
|
+
getCompletionItems,
|
|
6
|
+
getHover,
|
|
7
|
+
GetPositionFn,
|
|
8
|
+
getPositionForEditor,
|
|
9
|
+
} from "./utils";
|
|
3
10
|
|
|
4
11
|
let editor: TextEditor;
|
|
12
|
+
let getPosition: GetPositionFn;
|
|
5
13
|
beforeAll(async () => {
|
|
6
14
|
closeAllEditors();
|
|
7
15
|
editor = await openEditor("localSchema/src/test.js");
|
|
16
|
+
getPosition = getPositionForEditor(editor);
|
|
8
17
|
});
|
|
9
18
|
|
|
10
19
|
test("completion", async () => {
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
expect(
|
|
21
|
+
(await getCompletionItems(editor, getPosition("dro|id")))[0],
|
|
22
|
+
).toStrictEqual({
|
|
23
|
+
label: "droid",
|
|
24
|
+
detail: "Droid",
|
|
25
|
+
});
|
|
26
|
+
expect(
|
|
27
|
+
(await getCompletionItems(editor, getPosition("na|me")))[0],
|
|
28
|
+
).toStrictEqual({
|
|
29
|
+
label: "name",
|
|
30
|
+
detail: "String!",
|
|
31
|
+
});
|
|
13
32
|
});
|
|
14
33
|
|
|
15
34
|
test("hover", async () => {
|
|
16
|
-
expect(await getHover(editor,
|
|
35
|
+
expect(await getHover(editor, getPosition("na|me"))).toMatchInlineSnapshot(`
|
|
17
36
|
"\`\`\`graphql
|
|
18
37
|
Droid.name: String!
|
|
19
38
|
\`\`\`
|
|
@@ -1,20 +1,45 @@
|
|
|
1
1
|
import { TextEditor } from "vscode";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
closeAllEditors,
|
|
4
|
+
openEditor,
|
|
5
|
+
getCompletionItems,
|
|
6
|
+
getHover,
|
|
7
|
+
GetPositionFn,
|
|
8
|
+
getPositionForEditor,
|
|
9
|
+
} from "./utils";
|
|
3
10
|
|
|
4
11
|
let editor: TextEditor;
|
|
12
|
+
let getPosition: GetPositionFn;
|
|
5
13
|
beforeAll(async () => {
|
|
6
14
|
closeAllEditors();
|
|
7
15
|
editor = await openEditor("localSchemaArray/src/test.js");
|
|
16
|
+
getPosition = getPositionForEditor(editor);
|
|
8
17
|
});
|
|
9
18
|
|
|
10
19
|
test("completion", async () => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
expect(
|
|
21
|
+
(await getCompletionItems(editor, getPosition("dro|id")))[0],
|
|
22
|
+
).toStrictEqual({
|
|
23
|
+
label: "droid",
|
|
24
|
+
detail: "Droid",
|
|
25
|
+
});
|
|
26
|
+
expect(
|
|
27
|
+
(await getCompletionItems(editor, getPosition("d|Name: name")))[0],
|
|
28
|
+
).toStrictEqual({
|
|
29
|
+
label: "name",
|
|
30
|
+
detail: "String!",
|
|
31
|
+
});
|
|
32
|
+
expect(
|
|
33
|
+
(await getCompletionItems(editor, getPosition("pl|anet")))[0],
|
|
34
|
+
).toStrictEqual({
|
|
35
|
+
label: "planets",
|
|
36
|
+
detail: "[Planet]",
|
|
37
|
+
});
|
|
14
38
|
});
|
|
15
39
|
|
|
16
40
|
test("hover", async () => {
|
|
17
|
-
expect(await getHover(editor,
|
|
41
|
+
expect(await getHover(editor, getPosition("d|Name: name")))
|
|
42
|
+
.toMatchInlineSnapshot(`
|
|
18
43
|
"\`\`\`graphql
|
|
19
44
|
Droid.name: String!
|
|
20
45
|
\`\`\`
|
|
@@ -23,7 +48,7 @@ Droid.name: String!
|
|
|
23
48
|
|
|
24
49
|
What others call this droid"
|
|
25
50
|
`);
|
|
26
|
-
expect(await getHover(editor,
|
|
51
|
+
expect(await getHover(editor, getPosition("pl|anet"))).toMatchInlineSnapshot(`
|
|
27
52
|
"\`\`\`graphql
|
|
28
53
|
Query.planets: [Planet]
|
|
29
54
|
\`\`\`"
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { test as origTest } from "@jest/globals";
|
|
2
|
+
import { load } from "js-yaml";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { ParsedApolloConfigFormat } from "../config";
|
|
7
|
+
import { TextEditor } from "vscode";
|
|
8
|
+
import {
|
|
9
|
+
closeAllEditors,
|
|
10
|
+
openEditor,
|
|
11
|
+
getCompletionItems,
|
|
12
|
+
getHover,
|
|
13
|
+
getPositionForEditor,
|
|
14
|
+
GetPositionFn,
|
|
15
|
+
getFullSemanticTokens,
|
|
16
|
+
getDefinitions,
|
|
17
|
+
} from "./utils";
|
|
18
|
+
|
|
19
|
+
// we want to skip these tests unless the user running them has a rover config profile named "VSCode-E2E"
|
|
20
|
+
let test = origTest.skip;
|
|
21
|
+
try {
|
|
22
|
+
const roverProjectDir = join(__dirname, "../../../sampleWorkspace/rover");
|
|
23
|
+
const config = load(
|
|
24
|
+
readFileSync(join(roverProjectDir, "apollo.config.yaml"), "utf-8"),
|
|
25
|
+
) as ParsedApolloConfigFormat;
|
|
26
|
+
const roverBin = join(roverProjectDir, config.rover!.bin);
|
|
27
|
+
const result = execFileSync(roverBin, [
|
|
28
|
+
"config",
|
|
29
|
+
"list",
|
|
30
|
+
"--format=json",
|
|
31
|
+
]).toString("utf8");
|
|
32
|
+
const parsed = JSON.parse(result);
|
|
33
|
+
if (parsed.data.profiles.includes("VSCode-E2E")) {
|
|
34
|
+
test = origTest;
|
|
35
|
+
}
|
|
36
|
+
} catch (e) {}
|
|
37
|
+
if (test === origTest.skip) {
|
|
38
|
+
console.info(
|
|
39
|
+
"Skipping rover E2E tests: no profile with the name 'VSCode-E2E'\n" +
|
|
40
|
+
"You can create one by running `rover config auth --profile VSCode-E2E`",
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let editor: TextEditor;
|
|
45
|
+
let getPosition: GetPositionFn;
|
|
46
|
+
beforeAll(async () => {
|
|
47
|
+
closeAllEditors();
|
|
48
|
+
editor = await openEditor("rover/src/test.graphql");
|
|
49
|
+
getPosition = getPositionForEditor(editor);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("hover", async () => {
|
|
53
|
+
expect(await getHover(editor, getPosition("@over|ride(from")))
|
|
54
|
+
.toMatchInlineSnapshot(`
|
|
55
|
+
"The [\`@override\`](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/#override) directive indicates that an object field is now resolved by this subgraph instead of another subgraph where it's also defined. This enables you to migrate a field from one subgraph to another.
|
|
56
|
+
|
|
57
|
+
You can apply \`@override\` to entity fields and fields of the root operation types (such as \`Query\` and \`Mutation\`). A second \`label\` argument can be used to progressively override a field. See [the docs](https://www.apollographql.com/docs/federation/entities/migrate-fields/#incremental-migration-with-progressive-override) for more information.
|
|
58
|
+
***
|
|
59
|
+
\`\`\`graphql
|
|
60
|
+
directive @override(from: String!, label: String) on FIELD_DEFINITION
|
|
61
|
+
\`\`\`"
|
|
62
|
+
`);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("completion", async () => {
|
|
66
|
+
expect(await getCompletionItems(editor, getPosition("@over|ride(from")))
|
|
67
|
+
.toMatchInlineSnapshot(`
|
|
68
|
+
[
|
|
69
|
+
{
|
|
70
|
+
"detail": undefined,
|
|
71
|
+
"label": "@deprecated",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"detail": undefined,
|
|
75
|
+
"label": "@external",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"detail": undefined,
|
|
79
|
+
"label": "@federation__authenticated",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"detail": undefined,
|
|
83
|
+
"label": "@federation__inaccessible",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"detail": undefined,
|
|
87
|
+
"label": "@federation__policy(…)",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"detail": undefined,
|
|
91
|
+
"label": "@federation__provides(…)",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"detail": undefined,
|
|
95
|
+
"label": "@federation__requiresScopes(…)",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"detail": undefined,
|
|
99
|
+
"label": "@federation__tag(…)",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"detail": undefined,
|
|
103
|
+
"label": "@override(…)",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"detail": undefined,
|
|
107
|
+
"label": "@requires(…)",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"detail": undefined,
|
|
111
|
+
"label": "@shareable",
|
|
112
|
+
},
|
|
113
|
+
]
|
|
114
|
+
`);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("semantic tokens", async () => {
|
|
118
|
+
const tokens = await getFullSemanticTokens(editor);
|
|
119
|
+
expect(tokens[0]).toStrictEqual({
|
|
120
|
+
startPosition: getPosition('fields: "|a"'),
|
|
121
|
+
endPosition: getPosition('fields: "a|"'),
|
|
122
|
+
tokenType: "property",
|
|
123
|
+
tokenModifiers: [],
|
|
124
|
+
});
|
|
125
|
+
expect(tokens[1]).toStrictEqual({
|
|
126
|
+
startPosition: getPosition('fields: "|c"'),
|
|
127
|
+
endPosition: getPosition('fields: "c|"'),
|
|
128
|
+
tokenType: "property",
|
|
129
|
+
tokenModifiers: [],
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("definitions", async () => {
|
|
134
|
+
const definitions = await getDefinitions(editor, getPosition("a: |A"));
|
|
135
|
+
|
|
136
|
+
expect(definitions[0].targetUri.toString()).toBe(
|
|
137
|
+
editor.document.uri.toString(),
|
|
138
|
+
);
|
|
139
|
+
expect(
|
|
140
|
+
editor.document.getText(definitions[0].targetSelectionRange!),
|
|
141
|
+
).toMatchInlineSnapshot(`"A"`);
|
|
142
|
+
expect(editor.document.getText(definitions[0].targetRange))
|
|
143
|
+
.toMatchInlineSnapshot(`
|
|
144
|
+
"type A @key(fields: "a") {
|
|
145
|
+
a: ID @override(from: "DNE")
|
|
146
|
+
b: String! @requires(fields: "c") @shareable
|
|
147
|
+
c: String! @external
|
|
148
|
+
}"
|
|
149
|
+
`);
|
|
150
|
+
});
|
|
@@ -2,15 +2,14 @@ import { TextEditor } from "vscode";
|
|
|
2
2
|
import {
|
|
3
3
|
closeAllEditors,
|
|
4
4
|
openEditor,
|
|
5
|
-
|
|
5
|
+
getCompletionItems,
|
|
6
6
|
getHover,
|
|
7
7
|
getExtension,
|
|
8
8
|
getOutputChannelDocument,
|
|
9
9
|
reloadService,
|
|
10
|
+
getPositionForEditor,
|
|
10
11
|
} from "./utils";
|
|
11
12
|
import mocks from "../../__e2e__/mocks.js";
|
|
12
|
-
import vscode from "vscode";
|
|
13
|
-
import { scheduler } from "node:timers/promises";
|
|
14
13
|
|
|
15
14
|
const mockPort = Number(process.env.MOCK_SERVER_PORT);
|
|
16
15
|
beforeAll(async () => {
|
|
@@ -19,13 +18,26 @@ beforeAll(async () => {
|
|
|
19
18
|
|
|
20
19
|
test("completion", async () => {
|
|
21
20
|
const editor = await openEditor("spotifyGraph/src/test.js");
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const getPosition = getPositionForEditor(editor);
|
|
22
|
+
expect(
|
|
23
|
+
(await getCompletionItems(editor, getPosition("pr|ofile")))[0],
|
|
24
|
+
).toStrictEqual({
|
|
25
|
+
label: "profile",
|
|
26
|
+
detail: "CurrentUserProfile!",
|
|
27
|
+
});
|
|
28
|
+
expect(
|
|
29
|
+
(await getCompletionItems(editor, getPosition("dis|playName")))[0],
|
|
30
|
+
).toStrictEqual({
|
|
31
|
+
label: "displayName",
|
|
32
|
+
detail: "String",
|
|
33
|
+
});
|
|
24
34
|
});
|
|
25
35
|
|
|
26
36
|
test("hover", async () => {
|
|
27
37
|
const editor = await openEditor("spotifyGraph/src/test.js");
|
|
28
|
-
|
|
38
|
+
const getPosition = getPositionForEditor(editor);
|
|
39
|
+
expect(await getHover(editor, getPosition("pr|ofile")))
|
|
40
|
+
.toMatchInlineSnapshot(`
|
|
29
41
|
"\`\`\`graphql
|
|
30
42
|
CurrentUser.profile: CurrentUserProfile!
|
|
31
43
|
\`\`\`
|