Skip to content

Commit aad57b0

Browse files
committed
fix: accept graphql files that only consist of comments
We already allowed whitespace-only files, so it only makes sense to also allow comment-only files.
1 parent 9636341 commit aad57b0

File tree

3 files changed

+124
-4
lines changed

3 files changed

+124
-4
lines changed

spec/project/project.spec.ts

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import { expect } from 'chai';
22
import { graphql } from 'graphql';
33
import { Logger, LoggerProvider } from '../../src/config/logging';
44
import { DatabaseAdapter, FlexSearchTokenizable } from '../../src/database/database-adapter';
5-
import { FlexSearchLanguage, Model } from '../../src/model';
5+
import { Model } from '../../src/model';
66
import { Project } from '../../src/project/project';
77
import { ProjectSource } from '../../src/project/source';
88
import { QueryNode } from '../../src/query-tree';
99
import { FlexSearchTokenization } from '../../src/query-tree/flex-search';
10-
import { createSchema } from '../../src/schema/schema-builder';
10+
import gql from 'graphql-tag';
11+
import { expectSingleError, expectToBeValid } from '../model/implementation/validation-utils';
1112

1213
class FakeDBAdatper implements DatabaseAdapter {
1314
async execute(queryTree: QueryNode): Promise<any> {
@@ -30,6 +31,99 @@ class FakeDBAdatper implements DatabaseAdapter {
3031
}
3132

3233
describe('project', () => {
34+
describe('validate', () => {
35+
it('accepts a valid simple project', async () => {
36+
const project = new Project([
37+
gql`
38+
type Test @rootEntity {
39+
key: String @key
40+
}
41+
`.loc!.source,
42+
]);
43+
expectToBeValid(project);
44+
});
45+
46+
it('accepts a valid project with multiple sources', async () => {
47+
const project = new Project([
48+
gql`
49+
type Test @rootEntity {
50+
key: String @key
51+
children: [Child]
52+
}
53+
`.loc!.source,
54+
gql`
55+
# make sure this file is not skipped just because it begins with a comment
56+
type Child @childEntity {
57+
key: String
58+
}
59+
`.loc!.source,
60+
]);
61+
expectToBeValid(project);
62+
});
63+
64+
it('rejects an invalid project with multiple sources', async () => {
65+
const project = new Project([
66+
gql`
67+
type Test @rootEntity {
68+
key: String @key
69+
children: [Child]
70+
}
71+
`.loc!.source,
72+
gql`
73+
type OtherChild @childEntity {
74+
key: String
75+
}
76+
`.loc!.source,
77+
]);
78+
expectSingleError(project, 'Type "Child" not found.');
79+
});
80+
81+
it('accepts a valid project with an additional empty file', async () => {
82+
const project = new Project([
83+
gql`
84+
type Test @rootEntity {
85+
key: String @key
86+
}
87+
`.loc!.source,
88+
{
89+
name: 'other.graphqls',
90+
body: '',
91+
},
92+
]);
93+
expectToBeValid(project);
94+
});
95+
96+
it('accepts a valid project with an additional file that only contains comments', async () => {
97+
const project = new Project([
98+
gql`
99+
type Test @rootEntity {
100+
key: String @key
101+
}
102+
`.loc!.source,
103+
{
104+
name: 'other.graphqls',
105+
body: '# this is a comment',
106+
},
107+
]);
108+
expectToBeValid(project);
109+
});
110+
111+
it('accepts a project without any source', async () => {
112+
const project = new Project([]);
113+
expectToBeValid(project);
114+
});
115+
116+
it('accepts a project with just a comment-only source', async () => {
117+
const project = new Project([
118+
{
119+
name: 'other.graphqls',
120+
body: '# this is a comment',
121+
},
122+
]);
123+
expectToBeValid(project);
124+
});
125+
});
126+
33127
describe('createSchema', () => {
34128
it('schema resolvers log to logger specified in project', async () => {
35129
let logs: string[] = [];
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { GraphQLError, Lexer, TokenKind } from 'graphql/index';
2+
import { Source } from 'graphql';
3+
4+
/**
5+
* Checks if the given graphql source string only contains comments and whitespace
6+
* @param source
7+
*/
8+
export function isCommentOnlySource(source: string) {
9+
const lexer = new Lexer(new Source(source));
10+
try {
11+
// lookahead() gets the first non-comment token
12+
const firstToken = lexer.lookahead();
13+
return firstToken.kind === TokenKind.EOF;
14+
} catch (e) {
15+
if (e instanceof GraphQLError) {
16+
// syntax error means there is something
17+
return false;
18+
}
19+
throw e;
20+
}
21+
}

src/schema/schema-builder.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import {
33
getLocation,
44
GraphQLError,
55
GraphQLSchema,
6-
parse,
76
Kind as GraphQLKind,
7+
Lexer,
8+
parse,
9+
TokenKind,
810
} from 'graphql';
911
import { parse as JSONparse } from 'json-source-map';
1012
import { compact } from 'lodash';
@@ -53,6 +55,7 @@ import {
5355
import { getLineEndPosition } from './schema-utils';
5456
import jsonLint = require('json-lint');
5557
import stripJsonComments = require('strip-json-comments');
58+
import { isCommentOnlySource } from '../graphql/is-comment-only-source';
5659

5760
/**
5861
* Validates a project and thus determines whether createSchema() would succeed
@@ -311,7 +314,9 @@ function parseGraphQLsSource(
311314
options: ProjectOptions,
312315
validationContext: ValidationContext,
313316
): ParsedGraphQLProjectSource | undefined {
314-
if (projectSource.body.trim() === '') {
317+
// parse() does not accept documents, and there is no option to make it accept them either
318+
// it would be annoying if you could not have empty files
319+
if (isCommentOnlySource(projectSource.body)) {
315320
return undefined;
316321
}
317322

0 commit comments

Comments
 (0)