Skip to content

Commit 937bab3

Browse files
committed
autocompletion support for @OneOf
1 parent 484a9ad commit 937bab3

File tree

5 files changed

+108
-35
lines changed

5 files changed

+108
-35
lines changed

packages/graphiql/test/schema.js

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -50,40 +50,54 @@ const TestEnum = new GraphQLEnumType({
5050
const TestInputObject = new GraphQLInputObjectType({
5151
name: 'TestInput',
5252
description: 'Test all sorts of inputs in this input object type.',
53-
fields: () => ({
54-
string: {
55-
type: GraphQLString,
56-
description: 'Repeats back this string',
57-
},
58-
int: { type: GraphQLInt },
59-
float: { type: GraphQLFloat },
60-
boolean: { type: GraphQLBoolean },
61-
id: { type: GraphQLID },
62-
enum: { type: TestEnum },
63-
object: { type: TestInputObject },
64-
defaultValueString: {
65-
type: GraphQLString,
66-
defaultValue: 'test default value',
67-
},
68-
defaultValueBoolean: {
69-
type: GraphQLBoolean,
70-
defaultValue: false,
71-
},
72-
defaultValueInt: {
73-
type: GraphQLInt,
74-
defaultValue: 5,
75-
},
76-
// List
77-
listString: { type: new GraphQLList(GraphQLString) },
78-
listInt: { type: new GraphQLList(GraphQLInt) },
79-
listFloat: { type: new GraphQLList(GraphQLFloat) },
80-
listBoolean: { type: new GraphQLList(GraphQLBoolean) },
81-
listID: { type: new GraphQLList(GraphQLID) },
82-
listEnum: { type: new GraphQLList(TestEnum) },
83-
listObject: { type: new GraphQLList(TestInputObject) },
84-
}),
53+
fields: () => inputFields,
54+
});
55+
56+
const TestOneOfInputObject = new GraphQLInputObjectType({
57+
name: 'TestOneOfInput',
58+
description: 'Test @oneOf input types with this input object type.',
59+
isOneOf: true,
60+
fields: () =>
61+
// remove defaultValue which is not compatible with @oneOf
62+
Object.entries(inputFields).reduce((a, [k, { defaultValue, ...value }]) => {
63+
a[k] = value;
64+
return a;
65+
}, {}),
8566
});
8667

68+
const inputFields = {
69+
string: {
70+
type: GraphQLString,
71+
description: 'Repeats back this string',
72+
},
73+
int: { type: GraphQLInt },
74+
float: { type: GraphQLFloat },
75+
boolean: { type: GraphQLBoolean },
76+
id: { type: GraphQLID },
77+
enum: { type: TestEnum },
78+
object: { type: TestInputObject },
79+
defaultValueString: {
80+
type: GraphQLString,
81+
defaultValue: 'test default value',
82+
},
83+
defaultValueBoolean: {
84+
type: GraphQLBoolean,
85+
defaultValue: false,
86+
},
87+
defaultValueInt: {
88+
type: GraphQLInt,
89+
defaultValue: 5,
90+
},
91+
// List
92+
listString: { type: new GraphQLList(GraphQLString) },
93+
listInt: { type: new GraphQLList(GraphQLInt) },
94+
listFloat: { type: new GraphQLList(GraphQLFloat) },
95+
listBoolean: { type: new GraphQLList(GraphQLBoolean) },
96+
listID: { type: new GraphQLList(GraphQLID) },
97+
listEnum: { type: new GraphQLList(TestEnum) },
98+
listObject: { type: new GraphQLList(TestInputObject) },
99+
};
100+
87101
const TestInterface = new GraphQLInterfaceType({
88102
name: 'TestInterface',
89103
description: 'Test interface.',
@@ -250,6 +264,7 @@ const TestType = new GraphQLObjectType({
250264
description: '`test` field from `Test` type.',
251265
resolve: () => ({}),
252266
},
267+
253268
deferrable: {
254269
type: DeferrableObject,
255270
resolve: () => ({}),
@@ -333,6 +348,7 @@ const TestType = new GraphQLObjectType({
333348
id: { type: GraphQLID },
334349
enum: { type: TestEnum },
335350
object: { type: TestInputObject },
351+
oneOfObject: { type: TestOneOfInputObject },
336352
defaultValue: {
337353
type: GraphQLString,
338354
defaultValue: 'test default value',

packages/graphql-language-service/src/interface/__tests__/__schema__/StarWarsSchema.graphql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ input InputType {
4646
obj: InputType
4747
}
4848

49+
input OneOfInputType @oneOf {
50+
key: String!
51+
value: Int
52+
obj: InputType
53+
}
54+
4955
interface TestInterface {
5056
"""
5157
example
@@ -69,6 +75,7 @@ type Query {
6975
inputTypeTest(args: InputType = { key: "key" }): TestType
7076
deprecatedField: TestType @deprecated(reason: "Use test instead.")
7177
union: TestUnion
78+
oneOfInputTypeTest(oneOf: OneOfInputType): String
7279
}
7380

7481
union TestUnion = Droid | TestType

packages/graphql-language-service/src/interface/__tests__/getAutocompleteSuggestions-test.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ const expectedResults = {
4949
label: 'inputTypeTest',
5050
detail: 'TestType',
5151
},
52+
oneOfInputTypeTest: {
53+
detail: 'String',
54+
label: 'oneOfInputTypeTest',
55+
},
5256
appearsIn: {
5357
label: 'appearsIn',
5458
detail: '[Episode]',
@@ -163,7 +167,7 @@ describe('getAutocompleteSuggestions', () => {
163167
},
164168

165169
{
166-
sortText: '7__schema',
170+
sortText: '8__schema',
167171
label: '__schema',
168172
detail: '__Schema!',
169173
},
@@ -213,6 +217,7 @@ describe('getAutocompleteSuggestions', () => {
213217
expectedResults.hero,
214218
expectedResults.human,
215219
expectedResults.inputTypeTest,
220+
expectedResults.oneOfInputTypeTest,
216221
expectedResults.union,
217222
]);
218223

@@ -236,6 +241,7 @@ describe('getAutocompleteSuggestions', () => {
236241
expectedResults.hero,
237242
expectedResults.human,
238243
expectedResults.inputTypeTest,
244+
expectedResults.oneOfInputTypeTest,
239245
expectedResults.union,
240246
]);
241247
});
@@ -250,6 +256,7 @@ describe('getAutocompleteSuggestions', () => {
250256
expectedResults.hero,
251257
expectedResults.human,
252258
expectedResults.inputTypeTest,
259+
expectedResults.oneOfInputTypeTest,
253260
expectedResults.union,
254261
]);
255262
});
@@ -322,6 +329,13 @@ describe('getAutocompleteSuggestions', () => {
322329
insertText: 'inputTypeTest {\n $1\n}',
323330
labelDetails: { detail: ' TestType' },
324331
},
332+
{
333+
...expectedResults.oneOfInputTypeTest,
334+
command: suggestionCommand,
335+
insertTextFormat: 2,
336+
insertText: 'oneOfInputTypeTest\n',
337+
labelDetails: { detail: ' String' },
338+
},
325339
{
326340
label: 'union',
327341
insertTextFormat: 2,
@@ -419,7 +433,9 @@ describe('getAutocompleteSuggestions', () => {
419433
{ label: 'Boolean', documentation: GraphQLBoolean.description },
420434
{ label: 'Episode' },
421435
{ label: 'InputType' },
436+
422437
{ label: 'Int', documentation: GraphQLInt.description },
438+
{ label: 'OneOfInputType' },
423439
{ label: 'String', documentation: GraphQLString.description },
424440
]);
425441
});
@@ -433,7 +449,9 @@ describe('getAutocompleteSuggestions', () => {
433449
...metaArgs,
434450

435451
{ label: 'InputType' },
452+
436453
{ label: 'Int', documentation: GraphQLInt.description },
454+
{ label: 'OneOfInputType' },
437455
{ label: 'String', documentation: GraphQLString.description },
438456
]);
439457
});
@@ -654,7 +672,7 @@ describe('getAutocompleteSuggestions', () => {
654672
];
655673
it('provides correct testInput type field suggestions', () => {
656674
expect(
657-
testSuggestions('{ inputTypeTest(args: {', new Position(0, 23)),
675+
testSuggestions('{ inputTypeTest(args: { ', new Position(0, 24)),
658676
).toEqual(inputArgs);
659677
});
660678

@@ -664,6 +682,21 @@ describe('getAutocompleteSuggestions', () => {
664682
).toEqual(inputArgs);
665683
});
666684

685+
it('provides correct oneOf input type field suggestions', () => {
686+
expect(
687+
testSuggestions('{ oneOfInputTypeTest(oneOf: {', new Position(0, 29)),
688+
).toEqual(inputArgs);
689+
});
690+
691+
it('provides no more field suggestions once a oneOf field is chosen', () => {
692+
expect(
693+
testSuggestions(
694+
'{ oneOfInputTypeTest(oneOf: { value: 2 ',
695+
new Position(0, 40),
696+
),
697+
).toEqual([]);
698+
});
699+
667700
it('provides correct field name suggestion inside inline fragment', () => {
668701
expect(
669702
testSuggestions(
@@ -813,6 +846,7 @@ describe('getAutocompleteSuggestions', () => {
813846
it('provides input objects to be extended', () => {
814847
expect(testSuggestions('extend input ', new Position(0, 13))).toEqual([
815848
{ label: 'InputType' },
849+
{ label: 'OneOfInputType' },
816850
]);
817851
});
818852

@@ -847,8 +881,10 @@ describe('getAutocompleteSuggestions', () => {
847881
).toEqual([
848882
{ label: 'Boolean' },
849883
{ label: 'Episode' },
884+
850885
{ label: 'InputType' },
851886
{ label: 'Int' },
887+
{ label: 'OneOfInputType' },
852888
{ label: 'String' },
853889
]));
854890

packages/graphql-language-service/src/interface/getAutocompleteSuggestions.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,15 @@ export function getAutocompleteSuggestions(
317317
kind === RuleKinds.OBJECT_VALUE
318318
? CompletionItemKind.Value
319319
: CompletionItemKind.Field;
320+
// @oneOf logic!
321+
if (
322+
typeInfo?.inputType &&
323+
'isOneOf' in typeInfo.inputType &&
324+
typeInfo.inputType.isOneOf === true &&
325+
context.token.string !== '{'
326+
) {
327+
return [];
328+
}
320329
return hintList(
321330
token,
322331
objectFields.map(field => ({

yarn.lock

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10538,7 +10538,12 @@ [email protected], graphql-ws@^5.5.5:
1053810538
resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.14.0.tgz#766f249f3974fc2c48fae0d1fb20c2c4c79cd591"
1053910539
integrity sha512-itrUTQZP/TgswR4GSSYuwWUzrE/w5GhbwM2GX3ic2U7aw33jgEsayfIlvaj7/GcIvZgNMzsPTrE5hqPuFUiE5g==
1054010540

10541-
"graphql@^16.8.1 || ^17.0.0-alpha.2", graphql@^16.9.0:
10541+
"graphql@^16.8.1 || ^17.0.0-alpha.2":
10542+
version "17.0.0-alpha.7"
10543+
resolved "https://registry.yarnpkg.com/graphql/-/graphql-17.0.0-alpha.7.tgz#707e7457d7ed5316a8d7940f78809a2eb5854383"
10544+
integrity sha512-kdteHez9s0lfNAGntSwnDBpxSl09sBWEFxFRPS/Z8K1nCD4FZ2wVGwXuj5dvrTKcqOA+O8ujAJ3CiY/jXhs14g==
10545+
10546+
graphql@^16.9.0:
1054210547
version "16.9.0"
1054310548
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f"
1054410549
integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==

0 commit comments

Comments
 (0)