Skip to content

Commit 438078d

Browse files
authored
feat(require-selections rule): introduce new option requireAllFields to require all values of fieldName: string[] option (#2790)
* aa * aa * aa * aa * aa * yoyo
1 parent bac2606 commit 438078d

File tree

5 files changed

+114
-8
lines changed

5 files changed

+114
-8
lines changed

.changeset/six-rice-help.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@graphql-eslint/eslint-plugin': minor
3+
---
4+
5+
feat(`require-selections` rule): introduce new option `requireAllFields` to require all values of
6+
`fieldName: string[]` option

packages/plugin/src/rules/require-selections/index.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,5 +501,29 @@ ruleTester.run<RuleOptions, true>('require-selections', rule, {
501501
},
502502
errors: 2,
503503
},
504+
{
505+
name: 'should require all fields with `requireAllFields` option',
506+
code: '{ hasId { id } }',
507+
options: [{ requireAllFields: true, fieldName: ['name', '_id'] }],
508+
parserOptions: {
509+
graphQLConfig: {
510+
schema: TEST_SCHEMA,
511+
documents: '{ foo }',
512+
},
513+
},
514+
errors: 2,
515+
},
516+
{
517+
name: 'should require rest of all fields with `requireAllFields` option',
518+
code: '{ hasId { _id } }',
519+
options: [{ requireAllFields: true, fieldName: ['name', '_id'] }],
520+
parserOptions: {
521+
graphQLConfig: {
522+
schema: TEST_SCHEMA,
523+
documents: '{ foo }',
524+
},
525+
},
526+
errors: 1,
527+
},
504528
],
505529
});

packages/plugin/src/rules/require-selections/index.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ const schema = {
4242
oneOf: [{ $ref: '#/definitions/asString' }, { $ref: '#/definitions/asArray' }],
4343
default: DEFAULT_ID_FIELD_NAME,
4444
},
45+
requireAllFields: {
46+
type: 'boolean',
47+
description: 'Whether all fields of `fieldName` option must be included.',
48+
},
4549
},
4650
},
4751
} as const;
@@ -115,7 +119,7 @@ export const rule: GraphQLESLintRule<RuleOptions, true> = {
115119
create(context) {
116120
const schema = requireGraphQLSchema(RULE_ID, context);
117121
const siblings = requireGraphQLOperations(RULE_ID, context);
118-
const { fieldName = DEFAULT_ID_FIELD_NAME } = context.options[0] || {};
122+
const { fieldName = DEFAULT_ID_FIELD_NAME, requireAllFields } = context.options[0] || {};
119123
const idNames = asArray(fieldName);
120124

121125
// Check selections only in OperationDefinition,
@@ -203,6 +207,18 @@ export const rule: GraphQLESLintRule<RuleOptions, true> = {
203207
return;
204208
}
205209

210+
checkFragments(node as GraphQLESTreeNode<SelectionSetNode>);
211+
212+
if (requireAllFields) {
213+
for (const idName of idNames) {
214+
report([idName]);
215+
}
216+
} else {
217+
report(idNames);
218+
}
219+
}
220+
221+
function report(idNames: string[]) {
206222
function hasIdField({ selections }: typeof node): boolean {
207223
return selections.some(selection => {
208224
if (selection.kind === Kind.FIELD) {
@@ -228,20 +244,14 @@ export const rule: GraphQLESLintRule<RuleOptions, true> = {
228244
return false;
229245
});
230246
}
231-
232247
const hasId = hasIdField(node);
233-
234-
checkFragments(node as GraphQLESTreeNode<SelectionSetNode>);
235-
236248
if (hasId) {
237249
return;
238250
}
239-
240-
const pluralSuffix = idNames.length > 1 ? 's' : '';
241251
const fieldName = englishJoinWords(
242252
idNames.map(name => `\`${(parent.alias || parent.name).value}.${name}\``),
243253
);
244-
254+
const pluralSuffix = idNames.length > 1 ? 's' : '';
245255
const addition =
246256
checkedFragmentSpreads.size === 0
247257
? ''

packages/plugin/src/rules/require-selections/snapshot.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,68 @@ exports[`require-selections > invalid > should report an error with union and no
250250
3 | ...UnionFragment
251251
`;
252252

253+
exports[`require-selections > invalid > should require all fields with \`requireAllFields\` option 1`] = `
254+
#### ⌨️ Code
255+
256+
1 | { hasId { id } }
257+
258+
#### ⚙️ Options
259+
260+
{
261+
"requireAllFields": true,
262+
"fieldName": [
263+
"name",
264+
"_id"
265+
]
266+
}
267+
268+
#### ❌ Error 1/2
269+
270+
> 1 | { hasId { id } }
271+
| ^ Field \`hasId.name\` must be selected when it's available on a type.
272+
Include it in your selection set.
273+
274+
#### 💡 Suggestion: Add \`name\` selection
275+
276+
1 | { hasId { name id } }
277+
278+
#### ❌ Error 2/2
279+
280+
> 1 | { hasId { id } }
281+
| ^ Field \`hasId._id\` must be selected when it's available on a type.
282+
Include it in your selection set.
283+
284+
#### 💡 Suggestion: Add \`_id\` selection
285+
286+
1 | { hasId { _id id } }
287+
`;
288+
289+
exports[`require-selections > invalid > should require rest of all fields with \`requireAllFields\` option 1`] = `
290+
#### ⌨️ Code
291+
292+
1 | { hasId { _id } }
293+
294+
#### ⚙️ Options
295+
296+
{
297+
"requireAllFields": true,
298+
"fieldName": [
299+
"name",
300+
"_id"
301+
]
302+
}
303+
304+
#### ❌ Error
305+
306+
> 1 | { hasId { _id } }
307+
| ^ Field \`hasId.name\` must be selected when it's available on a type.
308+
Include it in your selection set.
309+
310+
#### 💡 Suggestion: Add \`name\` selection
311+
312+
1 | { hasId { name _id } }
313+
`;
314+
253315
exports[`require-selections > invalid > support multiple id field names 1`] = `
254316
#### ⌨️ Code
255317

website/content/rules/require-selections.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ The object must be one of the following types:
8080

8181
Default: `"id"`
8282

83+
### `requireAllFields` (boolean)
84+
85+
Whether all fields of `fieldName` option must be included.
86+
8387
---
8488

8589
# Sub Schemas

0 commit comments

Comments
 (0)