Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add env option to required-fields rule config #75

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules
lib
test/schema.json
test/second-schema.json
.vscode/
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Change log

### vNEXT
- Add env config option for required-fields rule [Justin Schulz](https://github.com/PepperTeasdale) in [#75](https://github.com/apollographql/eslint-plugin-graphql/pull/75)

### v1.1.0
- Add option to pass schema as string [Christopher Cliff](https://github.com/christophercliff) in [#78](https://github.com/apollographql/eslint-plugin-graphql/pull/78)
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,12 @@ The full list of available validators is:
- `FragmentsOnCompositeTypes`
- `KnownArgumentNames`
- `KnownDirectives` (*disabled by default in `relay`*)
- `KnownFragmentNames` (*disabled by default in `apollo`, `lokka`, and `relay`*)
- `KnownFragmentNames` (*disabled by default in all envs*)
- `KnownTypeNames`
- `LoneAnonymousOperation`
- `NoFragmentCycles`
- `NoUndefinedVariables` (*disabled by default in `relay`*)
- `NoUnusedFragments` (*disabled by default in `apollo`, `lokka`, and `relay`*)
- `NoUnusedFragments` (*disabled by default in all envs*)
- `NoUnusedVariables`
- `OverlappingFieldsCanBeMerged`
- `PossibleFragmentSpreads`
Expand Down Expand Up @@ -359,7 +359,7 @@ query ViewerName {
}
```

The rule is defined as `graphql/required-fields` and requires a `schema` and `requiredFields`, with an optional `tagName`.
The rule is defined as `graphql/required-fields` and requires a `schema` and `requiredFields`, with an optional `tagName` and `env`.

```js
// In a file called .eslintrc.js
Expand All @@ -368,7 +368,8 @@ module.exports = {
'graphql/required-fields': [
'error',
{
schemaJsonFilepath: require('./schema.json'),
env: 'apollo',
schemaJsonFilepath: path.resolve(__dirname, './schema.json'),
requiredFields: ['uuid'],
},
],
Expand Down
19 changes: 14 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ function createRule(context, optionParser) {
tagNames.add(tagName);
tagRules.push({schema, env, tagName, validators: boundValidators});
}

return {
TaggedTemplateExpression(node) {
for (const {schema, env, tagName, validators} of tagRules) {
Expand All @@ -86,7 +87,7 @@ function createRule(context, optionParser) {
};
}

const rules = {
export const rules = {
'template-strings': {
meta: {
schema: {
Expand Down Expand Up @@ -171,6 +172,14 @@ const rules = {
additionalProperties: false,
properties: {
...defaultRuleProperties,
env: {
enum: [
'lokka',
'relay',
'apollo',
'literal',
],
},
requiredFields: {
type: 'array',
items: {
Expand Down Expand Up @@ -444,11 +453,11 @@ const gqlProcessor = {
}
}

const processors = reduce(gqlFiles, (result, value) => {
export const processors = reduce(gqlFiles, (result, value) => {
return { ...result, [`.${value}`]: gqlProcessor };
}, {})

module.exports = {
rules,
export default {
rules,
processors
};
}
1 change: 1 addition & 0 deletions test/__fixtures__/required-fields-invalid-array.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
query { stories { comments { text } } }
1 change: 1 addition & 0 deletions test/__fixtures__/required-fields-invalid-no-id.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
query { greetings { hello } }
1 change: 1 addition & 0 deletions test/__fixtures__/required-fields-valid-array.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
query { stories { id comments { text } } }
1 change: 1 addition & 0 deletions test/__fixtures__/required-fields-valid-id.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
query { greetings { id, hello, foo } }
1 change: 1 addition & 0 deletions test/__fixtures__/required-fields-valid-no-id.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
query { allFilms { films { title } } }
73 changes: 65 additions & 8 deletions test/makeProcessors.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,38 @@
import assert from 'assert';
import {
includes,
keys,
} from 'lodash';
import { CLIEngine } from 'eslint';
import { includes, keys } from 'lodash';
import path from 'path';

import { processors } from '../src';
import schemaJson from './schema.json';
import plugin, { processors } from '../src';

function execute(file) {
const cli = new CLIEngine({
extensions: ['.gql', '.graphql'],
baseConfig: {
rules: {
'graphql/required-fields': [
'error',
{
schemaJson,
env: 'literal',
requiredFields: ['id']
}
]
}
},
ignore: false,
useEslintrc: false,
parserOptions: {
ecmaVersion: 6,
sourceType: 'module'
}
});
cli.addPlugin('eslint-plugin-graphql', plugin);
return cli.executeOnFiles([
path.join(__dirname, '__fixtures__', `${file}.graphql`)
]);
}

describe('processors', () => {
it('should define processors', () => {
Expand All @@ -15,8 +43,8 @@ describe('processors', () => {
});

it('should escape backticks and prepend internalTag', () => {
const query = 'query { someValueWith` }'
const expected = 'ESLintPluginGraphQLFile`query { someValueWith\\` }`'
const query = 'query { someValueWith` }';
const expected = 'ESLintPluginGraphQLFile`query { someValueWith\\` }`';
const preprocess = processors['.gql'].preprocess;
const result = preprocess(query);

Expand All @@ -28,7 +56,7 @@ describe('processors', () => {
{ ruleId: 'no-undef' },
{ ruleId: 'semi' },
{ ruleId: 'graphql/randomString' },
{ ruleId: 'graphql/template-strings' },
{ ruleId: 'graphql/template-strings' }
];
const expected = { ruleId: 'graphql/template-strings' };
const postprocess = processors['.gql'].postprocess;
Expand All @@ -37,4 +65,33 @@ describe('processors', () => {
assert.equal(result.length, 1);
assert.equal(result[0].ruleId, expected.ruleId);
});

describe('graphql/required-fields', () => {
describe('valid', () => {
[
'required-fields-valid-no-id',
'required-fields-valid-id',
'required-fields-valid-array'
].forEach(filename => {
it(`does not warn on file ${filename}`, () => {
const results = execute(filename);
assert.equal(results.errorCount, 0);
});
});
});

describe('invalid', () => {
[
'required-fields-invalid-no-id',
'required-fields-invalid-array'
].forEach(filename => {
it(`warns on file ${filename}`, () => {
const results = execute(filename);
assert.equal(results.errorCount, 1);
const message = results.results[0].messages[0].message;
assert.ok(new RegExp("'id' field required").test(message));
});
});
});
});
});
15 changes: 14 additions & 1 deletion test/makeRule.js
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,20 @@ const requiredFieldsTestCases = {
invalid: values(namedOperationsValidatorCases).map(({fail: code, errors}) => ({options, parserOptions, code, errors})),
});

// Validate the required-fields rule
// Validate the required-fields rule with env specified
options = [{
schemaJson,
env: 'apollo',
tagName: 'gql',
requiredFields: ['id'],
}];

ruleTester.run('testing required-fields rule', rules['required-fields'], {
valid: requiredFieldsTestCases.pass.map((code) => ({options, parserOptions, code})),
invalid: requiredFieldsTestCases.fail.map(({code, errors}) => ({options, parserOptions, code, errors})),
});

// Validate required-fields without optional env argument
options = [{
schemaJson,
tagName: 'gql',
Expand Down