Skip to content

Commit 0200542

Browse files
alloytrevor-scheer
authored andcommitted
[language server] Allow configuration of validation rules. (#1288)
* [language server] Allow configuration of validation rules. * [language server] Allow filtering of default validation rules Closes #1056
1 parent 979873d commit 0200542

8 files changed

Lines changed: 62 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
- <First `apollo-graphql` related entry goes here>
2121
- `apollo-language-server`
2222
- update stats window types [#1292](https://github.com/apollographql/apollo-tooling/pull/1292)
23+
- Allow configuration of validation rules [#1288](https://github.com/apollographql/apollo-tooling/pull/1288)
2324
- `apollo-tools`
2425
- <First `apollo-tools` related entry goes here>
2526
- `vscode-apollo`

packages/apollo-codegen-core/src/loading.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ function extractDocumentsWithAST(
8787
// This currently ignores the anti-pattern of including an interpolated
8888
// string as anything other than a fragment definition, for example a
8989
// literal(these cases could be covered during the replacement of
90-
// literals in the signature calcluation)
90+
// literals in the signature calculation)
9191
finished.push(
9292
(path.value.quasi.quasis as Array<{
9393
value: { cooked: string; raw: string };

packages/apollo-language-server/src/config/config.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ServiceID, ServiceSpecifier, ClientID } from "../engine";
44
import URI from "vscode-uri";
55
import { WithRequired } from "apollo-env";
66
import { getServiceName, parseServiceSpecifier } from "./utils";
7+
import { ValidationRule } from "graphql/validation/ValidationContext";
78

89
export interface EngineStatsWindow {
910
to: number;
@@ -67,6 +68,32 @@ export interface ClientConfigFormat extends ConfigBase {
6768
tagName?: string;
6869
// stats window config
6970
statsWindow?: EngineStatsWindow;
71+
72+
/**
73+
* Rules that will be applied when validating GraphQL documents.
74+
*
75+
* If you wish to modify the default list of validation rules, import them from the apollo package and
76+
* assign your custom list:
77+
*
78+
* ```js
79+
* const { defaultValidationRules } = require("apollo/lib/defaultValidationRules");
80+
*
81+
* module.exports = {
82+
* // ...
83+
* validationRules: [...defaultValidationRules, ...customRules]
84+
* }
85+
* ```
86+
*
87+
* Or, if you simply wish to filter out some rules from the default list, you can specify a filter function:
88+
*
89+
* ```js
90+
* module.exports = {
91+
* // ...
92+
* validationRules: rule => rule.name !== "NoAnonymousQueries"
93+
* }
94+
* ```
95+
*/
96+
validationRules?: ValidationRule[] | ((rule: ValidationRule) => boolean);
7097
}
7198

7299
export const DefaultClientConfig = {

packages/apollo-language-server/src/config/loadConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ export async function loadConfig({
184184

185185
let { config, filepath } = loadedConfig;
186186

187-
// selectivly apply defaults when loading the config
187+
// selectively apply defaults when loading the config
188188
// this is just the includes/excludes defaults.
189189
// These need to go on _all_ configs. That's why this is last.
190190
if (config.client) config = merge({ client: DefaultClientConfig }, config);

packages/apollo-language-server/src/diagnostics.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ import { rangeForASTNode } from "./utilities/source";
1414

1515
import { getValidationErrors } from "./errors/validation";
1616
import { DocumentUri } from "./project/base";
17+
import { ValidationRule } from "graphql/validation/ValidationContext";
1718

1819
/**
1920
* Build an array of code diagnostics for all executable definitions in a document.
2021
*/
2122
export function collectExecutableDefinitionDiagnositics(
2223
schema: GraphQLSchema,
2324
queryDocument: GraphQLDocument,
24-
fragments: { [fragmentName: string]: FragmentDefinitionNode } = {}
25+
fragments: { [fragmentName: string]: FragmentDefinitionNode } = {},
26+
rules?: ValidationRule[]
2527
): Diagnostic[] {
2628
const ast = queryDocument.ast;
2729
if (!ast) return queryDocument.syntaxErrors;
@@ -36,7 +38,8 @@ export function collectExecutableDefinitionDiagnositics(
3638
for (const error of getValidationErrors(
3739
schema,
3840
astWithExecutableDefinitions,
39-
fragments
41+
fragments,
42+
rules
4043
)) {
4144
diagnostics.push(
4245
...diagnosticsFromError(error, DiagnosticSeverity.Error, "Validation")

packages/apollo-language-server/src/errors/validation.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,22 @@ import {
1616
} from "graphql";
1717

1818
import { ToolError, logError } from "./logger";
19+
import { ValidationRule } from "graphql/validation/ValidationContext";
20+
21+
const specifiedRulesToBeRemoved = [NoUnusedFragmentsRule, KnownDirectivesRule];
22+
23+
export const defaultValidationRules: ValidationRule[] = [
24+
NoAnonymousQueries,
25+
NoTypenameAlias,
26+
...specifiedRules.filter(rule => !specifiedRulesToBeRemoved.includes(rule))
27+
];
1928

2029
export function getValidationErrors(
2130
schema: GraphQLSchema,
2231
document: DocumentNode,
23-
fragments?: { [fragmentName: string]: FragmentDefinitionNode }
32+
fragments?: { [fragmentName: string]: FragmentDefinitionNode },
33+
rules: ValidationRule[] = defaultValidationRules
2434
) {
25-
const specifiedRulesToBeRemoved = [
26-
NoUnusedFragmentsRule,
27-
KnownDirectivesRule
28-
];
29-
30-
const rules = [
31-
NoAnonymousQueries,
32-
NoTypenameAlias,
33-
...specifiedRules.filter(rule => !specifiedRulesToBeRemoved.includes(rule))
34-
];
35-
3635
const typeInfo = new TypeInfo(schema);
3736
const context = new ValidationContext(schema, document, typeInfo);
3837

packages/apollo-language-server/src/project/client.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
FieldNode,
1919
ObjectTypeDefinitionNode
2020
} from "graphql";
21+
import { ValidationRule } from "graphql/validation/ValidationContext";
2122

2223
import { NotificationHandler, DiagnosticSeverity } from "vscode-languageserver";
2324

@@ -33,6 +34,7 @@ import {
3334
removeDirectiveAnnotatedFields,
3435
withTypenameFieldAddedWhereNeeded
3536
} from "../utilities/graphql";
37+
import { defaultValidationRules } from "../errors/validation";
3638

3739
import {
3840
collectExecutableDefinitionDiagnositics,
@@ -84,6 +86,8 @@ export class GraphQLClientProject extends GraphQLProject {
8486

8587
private fieldStats?: FieldStats;
8688

89+
private _validationRules?: ValidationRule[];
90+
8791
constructor({
8892
config,
8993
loadingHandler,
@@ -125,6 +129,13 @@ export class GraphQLClientProject extends GraphQLProject {
125129
);
126130
}
127131

132+
const { validationRules } = this.config.client;
133+
if (typeof validationRules === "function") {
134+
this._validationRules = defaultValidationRules.filter(validationRules);
135+
} else {
136+
this._validationRules = validationRules;
137+
}
138+
128139
this.loadEngineData();
129140
}
130141

@@ -237,7 +248,8 @@ export class GraphQLClientProject extends GraphQLProject {
237248
collectExecutableDefinitionDiagnositics(
238249
this.schema,
239250
document,
240-
fragments
251+
fragments,
252+
this._validationRules
241253
)
242254
);
243255
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export {
2+
defaultValidationRules
3+
} from "apollo-language-server/lib/errors/validation";

0 commit comments

Comments
 (0)