Skip to content

Commit 8cfab2c

Browse files
committed
introduce simple user provided caches for parse/validate
BREAKING CHANGE: parse/validate become potentially async when provided with a cache with an async getter, Introduces parseSync/validateSync to throw when an async value is returned. Updates all non-cache tests to use parseSync/validateSync.
1 parent cca3f98 commit 8cfab2c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+951
-55
lines changed

src/__tests__/starWarsValidation-test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { expect } from 'chai';
22
import { describe, it } from 'mocha';
33

4-
import { parse } from '../language/parser.js';
4+
import { parseSync as parse } from '../language/parser.js';
55
import { Source } from '../language/source.js';
66

7-
import { validate } from '../validation/validate.js';
7+
import { validateSync as validate } from '../validation/validate.js';
88

99
import { StarWarsSchema } from './starWarsSchema.js';
1010

src/error/__tests__/GraphQLError-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { describe, it } from 'mocha';
44
import { dedent } from '../../__testUtils__/dedent.js';
55

66
import { Kind } from '../../language/kinds.js';
7-
import { parse } from '../../language/parser.js';
7+
import { parseSync as parse } from '../../language/parser.js';
88
import { Source } from '../../language/source.js';
99

1010
import { GraphQLError } from '../GraphQLError.js';

src/execution/__tests__/abort-signal-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { expectJSON } from '../../__testUtils__/expectJSON.js';
55
import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick.js';
66

77
import type { DocumentNode } from '../../language/ast.js';
8-
import { parse } from '../../language/parser.js';
8+
import { parseSync as parse } from '../../language/parser.js';
99

1010
import { buildSchema } from '../../utilities/buildASTSchema.js';
1111

src/execution/__tests__/abstract-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { describe, it } from 'mocha';
33

44
import { expectJSON } from '../../__testUtils__/expectJSON.js';
55

6-
import { parse } from '../../language/parser.js';
6+
import { parseSync as parse } from '../../language/parser.js';
77

88
import {
99
assertInterfaceType,

src/execution/__tests__/defer-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick.js';
88
import { promiseWithResolvers } from '../../jsutils/promiseWithResolvers.js';
99

1010
import type { DocumentNode } from '../../language/ast.js';
11-
import { parse } from '../../language/parser.js';
11+
import { parseSync as parse } from '../../language/parser.js';
1212

1313
import {
1414
GraphQLList,

src/execution/__tests__/directives-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from 'chai';
22
import { describe, it } from 'mocha';
33

4-
import { parse } from '../../language/parser.js';
4+
import { parseSync as parse } from '../../language/parser.js';
55

66
import { GraphQLObjectType } from '../../type/definition.js';
77
import { GraphQLString } from '../../type/scalars.js';

src/execution/__tests__/executor-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick.js';
77
import { inspect } from '../../jsutils/inspect.js';
88

99
import { Kind } from '../../language/kinds.js';
10-
import { parse } from '../../language/parser.js';
10+
import { parseSync as parse } from '../../language/parser.js';
1111

1212
import {
1313
GraphQLInterfaceType,

src/execution/__tests__/lists-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { expectJSON } from '../../__testUtils__/expectJSON.js';
55

66
import type { PromiseOrValue } from '../../jsutils/PromiseOrValue.js';
77

8-
import { parse } from '../../language/parser.js';
8+
import { parseSync as parse } from '../../language/parser.js';
99

1010
import type { GraphQLFieldResolver } from '../../type/definition.js';
1111
import {

src/execution/__tests__/mutations-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { describe, it } from 'mocha';
44
import { expectJSON } from '../../__testUtils__/expectJSON.js';
55
import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick.js';
66

7-
import { parse } from '../../language/parser.js';
7+
import { parseSync as parse } from '../../language/parser.js';
88

99
import { GraphQLObjectType } from '../../type/definition.js';
1010
import { GraphQLInt } from '../../type/scalars.js';

src/execution/__tests__/nonnull-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick.js';
66

77
import type { PromiseOrValue } from '../../jsutils/PromiseOrValue.js';
88

9-
import { parse } from '../../language/parser.js';
9+
import { parseSync as parse } from '../../language/parser.js';
1010

1111
import { GraphQLNonNull, GraphQLObjectType } from '../../type/definition.js';
1212
import { GraphQLString } from '../../type/scalars.js';

src/execution/__tests__/oneof-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { describe, it } from 'mocha';
22

33
import { expectJSON } from '../../__testUtils__/expectJSON.js';
44

5-
import { parse } from '../../language/parser.js';
5+
import { parseSync as parse } from '../../language/parser.js';
66

77
import { buildSchema } from '../../utilities/buildASTSchema.js';
88

src/execution/__tests__/resolve-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from 'chai';
22
import { describe, it } from 'mocha';
33

4-
import { parse } from '../../language/parser.js';
4+
import { parseSync as parse } from '../../language/parser.js';
55

66
import type { GraphQLFieldConfig } from '../../type/definition.js';
77
import { GraphQLObjectType } from '../../type/definition.js';

src/execution/__tests__/schema-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from 'chai';
22
import { describe, it } from 'mocha';
33

4-
import { parse } from '../../language/parser.js';
4+
import { parseSync as parse } from '../../language/parser.js';
55

66
import {
77
GraphQLList,

src/execution/__tests__/stream-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { PromiseOrValue } from '../../jsutils/PromiseOrValue.js';
99
import { promiseWithResolvers } from '../../jsutils/promiseWithResolvers.js';
1010

1111
import type { DocumentNode } from '../../language/ast.js';
12-
import { parse } from '../../language/parser.js';
12+
import { parseSync as parse } from '../../language/parser.js';
1313

1414
import {
1515
GraphQLList,

src/execution/__tests__/subscribe-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { isAsyncIterable } from '../../jsutils/isAsyncIterable.js';
1010
import { isPromise } from '../../jsutils/isPromise.js';
1111
import type { PromiseOrValue } from '../../jsutils/PromiseOrValue.js';
1212

13-
import { parse } from '../../language/parser.js';
13+
import { parseSync as parse } from '../../language/parser.js';
1414

1515
import { GraphQLList, GraphQLObjectType } from '../../type/definition.js';
1616
import {

src/execution/__tests__/sync-test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { describe, it } from 'mocha';
33

44
import { expectJSON } from '../../__testUtils__/expectJSON.js';
55

6-
import { parse } from '../../language/parser.js';
6+
import { parseSync as parse } from '../../language/parser.js';
77

88
import { GraphQLObjectType } from '../../type/definition.js';
99
import { GraphQLString } from '../../type/scalars.js';
1010
import { GraphQLSchema } from '../../type/schema.js';
1111

12-
import { validate } from '../../validation/validate.js';
12+
import { validateSync as validate } from '../../validation/validate.js';
1313

1414
import { graphqlSync } from '../../graphql.js';
1515

src/execution/__tests__/union-interface-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { describe, it } from 'mocha';
33

44
import { expectJSON } from '../../__testUtils__/expectJSON.js';
55

6-
import { parse } from '../../language/parser.js';
6+
import { parseSync as parse } from '../../language/parser.js';
77

88
import {
99
GraphQLInterfaceType,

src/execution/__tests__/variables-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { GraphQLError } from '../../error/GraphQLError.js';
99

1010
import { DirectiveLocation } from '../../language/directiveLocation.js';
1111
import { Kind } from '../../language/kinds.js';
12-
import { parse } from '../../language/parser.js';
12+
import { parseSync as parse } from '../../language/parser.js';
1313

1414
import type {
1515
GraphQLArgumentConfig,

src/graphql.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { isPromise } from './jsutils/isPromise.js';
22
import type { Maybe } from './jsutils/Maybe.js';
33
import type { PromiseOrValue } from './jsutils/PromiseOrValue.js';
44

5-
import { parse } from './language/parser.js';
5+
import { parseSync as parse } from './language/parser.js';
66
import type { Source } from './language/source.js';
77

88
import type {
@@ -12,7 +12,7 @@ import type {
1212
import type { GraphQLSchema } from './type/schema.js';
1313
import { validateSchema } from './type/validate.js';
1414

15-
import { validate } from './validation/validate.js';
15+
import { validateSync as validate } from './validation/validate.js';
1616

1717
import { execute } from './execution/execute.js';
1818
import type { ExecutionResult } from './execution/types.js';

src/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ export {
220220
TokenKind,
221221
// Parse
222222
parse,
223+
parseSync,
223224
parseValue,
224225
parseConstValue,
225226
parseType,
@@ -247,6 +248,7 @@ export {
247248

248249
export type {
249250
ParseOptions,
251+
ParseCache,
250252
SourceLocation,
251253
// Visitor utilities
252254
ASTVisitor,
@@ -356,6 +358,7 @@ export type {
356358
// Validate GraphQL documents.
357359
export {
358360
validate,
361+
validateSync,
359362
ValidationContext,
360363
// All validation rules in the GraphQL Specification.
361364
specifiedRules,
@@ -402,7 +405,11 @@ export {
402405
NoSchemaIntrospectionCustomRule,
403406
} from './validation/index.js';
404407

405-
export type { ValidationRule } from './validation/index.js';
408+
export type {
409+
ValidationRule,
410+
ValidateOptions,
411+
ValidateCache,
412+
} from './validation/index.js';
406413

407414
// Create, format, and print GraphQL errors.
408415
export { GraphQLError, syntaxError, locatedError } from './error/index.js';
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { expect } from 'chai';
2+
import { describe, it } from 'mocha';
3+
4+
import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick.js';
5+
6+
import { isPromise } from '../isPromise.js';
7+
import { withCache } from '../withCache.js';
8+
9+
describe('withCache', () => {
10+
it('returns asynchronously using asynchronous cache', async () => {
11+
let cached: string | undefined;
12+
let getAttempts = 0;
13+
let cacheHits = 0;
14+
const customCache = {
15+
set: async (result: string) => {
16+
await resolveOnNextTick();
17+
cached = result;
18+
},
19+
get: () => {
20+
getAttempts++;
21+
if (cached !== undefined) {
22+
cacheHits++;
23+
}
24+
return Promise.resolve(cached);
25+
},
26+
};
27+
28+
const fnWithCache = withCache((arg: string) => arg, customCache);
29+
30+
const firstResultPromise = fnWithCache('arg');
31+
expect(isPromise(firstResultPromise)).to.equal(true);
32+
const firstResult = await firstResultPromise;
33+
34+
expect(firstResult).to.equal('arg');
35+
expect(getAttempts).to.equal(1);
36+
expect(cacheHits).to.equal(0);
37+
38+
const secondResultPromise = fnWithCache('arg');
39+
40+
expect(isPromise(secondResultPromise)).to.equal(true);
41+
42+
const secondResult = await secondResultPromise;
43+
expect(secondResult).to.equal('arg');
44+
expect(getAttempts).to.equal(2);
45+
expect(cacheHits).to.equal(1);
46+
});
47+
48+
it('returns synchronously using cache with sync getter and async setter', async () => {
49+
let cached: string | undefined;
50+
let getAttempts = 0;
51+
let cacheHits = 0;
52+
const customCache = {
53+
set: async (result: string) => {
54+
await resolveOnNextTick();
55+
cached = result;
56+
},
57+
get: () => {
58+
getAttempts++;
59+
if (cached !== undefined) {
60+
cacheHits++;
61+
}
62+
return cached;
63+
},
64+
};
65+
66+
const fnWithCache = withCache((arg: string) => arg, customCache);
67+
68+
const firstResult = fnWithCache('arg');
69+
expect(firstResult).to.equal('arg');
70+
expect(getAttempts).to.equal(1);
71+
expect(cacheHits).to.equal(0);
72+
73+
await resolveOnNextTick();
74+
75+
const secondResult = fnWithCache('arg');
76+
77+
expect(secondResult).to.equal('arg');
78+
expect(getAttempts).to.equal(2);
79+
expect(cacheHits).to.equal(1);
80+
});
81+
82+
it('returns asynchronously using cache with async getter and sync setter', async () => {
83+
let cached: string | undefined;
84+
let getAttempts = 0;
85+
let cacheHits = 0;
86+
const customCache = {
87+
set: (result: string) => {
88+
cached = result;
89+
},
90+
get: () => {
91+
getAttempts++;
92+
if (cached !== undefined) {
93+
cacheHits++;
94+
}
95+
return Promise.resolve(cached);
96+
},
97+
};
98+
99+
const fnWithCache = withCache((arg: string) => arg, customCache);
100+
101+
const firstResultPromise = fnWithCache('arg');
102+
expect(isPromise(firstResultPromise)).to.equal(true);
103+
const firstResult = await firstResultPromise;
104+
105+
expect(firstResult).to.equal('arg');
106+
expect(getAttempts).to.equal(1);
107+
expect(cacheHits).to.equal(0);
108+
109+
const secondResultPromise = fnWithCache('arg');
110+
111+
expect(isPromise(secondResultPromise)).to.equal(true);
112+
113+
const secondResult = await secondResultPromise;
114+
expect(secondResult).to.equal('arg');
115+
expect(getAttempts).to.equal(2);
116+
expect(cacheHits).to.equal(1);
117+
});
118+
119+
it('ignores async setter errors', async () => {
120+
let cached: string | undefined;
121+
let getAttempts = 0;
122+
let cacheHits = 0;
123+
const customCache = {
124+
set: () => Promise.reject(new Error('Oops')),
125+
get: () => {
126+
getAttempts++;
127+
/* c8 ignore next 3 */
128+
if (cached !== undefined) {
129+
cacheHits++;
130+
}
131+
return Promise.resolve(cached);
132+
},
133+
};
134+
135+
const fnWithCache = withCache((arg: string) => arg, customCache);
136+
137+
const firstResultPromise = fnWithCache('arg');
138+
expect(isPromise(firstResultPromise)).to.equal(true);
139+
const firstResult = await firstResultPromise;
140+
141+
expect(firstResult).to.equal('arg');
142+
expect(getAttempts).to.equal(1);
143+
expect(cacheHits).to.equal(0);
144+
145+
const secondResultPromise = fnWithCache('arg');
146+
147+
expect(isPromise(secondResultPromise)).to.equal(true);
148+
149+
const secondResult = await secondResultPromise;
150+
expect(secondResult).to.equal('arg');
151+
expect(getAttempts).to.equal(2);
152+
expect(cacheHits).to.equal(0);
153+
});
154+
});

0 commit comments

Comments
 (0)