graphql-shield-node23 7.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/cjs/constructors.js +134 -0
- package/dist/cjs/generator.js +205 -0
- package/dist/cjs/index.js +15 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/rules.js +402 -0
- package/dist/cjs/shield.js +52 -0
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/utils.js +97 -0
- package/dist/cjs/validation.js +84 -0
- package/dist/esm/constructors.js +124 -0
- package/dist/esm/generator.js +201 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/rules.js +366 -0
- package/dist/esm/shield.js +45 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/utils.js +88 -0
- package/dist/esm/validation.js +79 -0
- package/dist/package.json +47 -0
- package/dist/typings/constructors.d.cts +91 -0
- package/dist/typings/constructors.d.ts +91 -0
- package/dist/typings/generator.d.cts +11 -0
- package/dist/typings/generator.d.ts +11 -0
- package/dist/typings/index.d.cts +3 -0
- package/dist/typings/index.d.ts +3 -0
- package/dist/typings/rules.d.cts +159 -0
- package/dist/typings/rules.d.ts +159 -0
- package/dist/typings/shield.d.cts +11 -0
- package/dist/typings/shield.d.ts +11 -0
- package/dist/typings/types.d.cts +64 -0
- package/dist/typings/types.d.ts +64 -0
- package/dist/typings/utils.d.cts +52 -0
- package/dist/typings/utils.d.ts +52 -0
- package/dist/typings/validation.d.cts +19 -0
- package/dist/typings/validation.d.ts +19 -0
- package/package.json +67 -0
- package/src/constructors.ts +157 -0
- package/src/generator.ts +294 -0
- package/src/index.ts +13 -0
- package/src/rules.ts +521 -0
- package/src/shield.ts +53 -0
- package/src/types.ts +94 -0
- package/src/utils.ts +101 -0
- package/src/validation.ts +90 -0
- package/tests/__snapshots__/input.test.ts.snap +7 -0
- package/tests/cache.test.ts +545 -0
- package/tests/constructors.test.ts +136 -0
- package/tests/fallback.test.ts +618 -0
- package/tests/fragments.test.ts +113 -0
- package/tests/generator.test.ts +356 -0
- package/tests/input.test.ts +63 -0
- package/tests/integration.test.ts +65 -0
- package/tests/logic.test.ts +530 -0
- package/tests/utils.test.ts +55 -0
- package/tests/validation.test.ts +139 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
import { graphql } from 'graphql'
|
|
2
|
+
import { applyMiddleware } from 'graphql-middleware'
|
|
3
|
+
import { makeExecutableSchema } from '@graphql-tools/schema'
|
|
4
|
+
import { shield, rule, allow } from '../src/index'
|
|
5
|
+
|
|
6
|
+
describe('fallbackError correctly handles errors', () => {
|
|
7
|
+
test('error in resolver returns fallback error.', async () => {
|
|
8
|
+
/* Schema */
|
|
9
|
+
|
|
10
|
+
const typeDefs = `
|
|
11
|
+
type Query {
|
|
12
|
+
test: String!
|
|
13
|
+
}
|
|
14
|
+
`
|
|
15
|
+
const resolvers = {
|
|
16
|
+
Query: {
|
|
17
|
+
test: async () => {
|
|
18
|
+
throw new Error()
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const schema = makeExecutableSchema({
|
|
24
|
+
typeDefs,
|
|
25
|
+
resolvers,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
/* Permissions */
|
|
29
|
+
|
|
30
|
+
const fallbackError = new Error('fallback')
|
|
31
|
+
|
|
32
|
+
const permissions = shield(
|
|
33
|
+
{
|
|
34
|
+
Query: allow,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
fallbackError,
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
42
|
+
|
|
43
|
+
/* Execution */
|
|
44
|
+
|
|
45
|
+
const query = `
|
|
46
|
+
query {
|
|
47
|
+
test
|
|
48
|
+
}
|
|
49
|
+
`
|
|
50
|
+
const res = await graphql({
|
|
51
|
+
schema: schemaWithPermissions,
|
|
52
|
+
source: query,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
/* Tests */
|
|
56
|
+
|
|
57
|
+
expect(res.data).toBeNull()
|
|
58
|
+
expect(res.errors?.[0]?.message).toBe(fallbackError.message)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('error in rule returns fallback error.', async () => {
|
|
62
|
+
/* Schema */
|
|
63
|
+
|
|
64
|
+
const typeDefs = `
|
|
65
|
+
type Query {
|
|
66
|
+
test: String!
|
|
67
|
+
}
|
|
68
|
+
`
|
|
69
|
+
|
|
70
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers: {} })
|
|
71
|
+
|
|
72
|
+
/* Permissions */
|
|
73
|
+
|
|
74
|
+
const fallbackError = new Error('fallback')
|
|
75
|
+
|
|
76
|
+
const allow = rule()(() => {
|
|
77
|
+
throw new Error()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const permissions = shield(
|
|
81
|
+
{
|
|
82
|
+
Query: allow,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
fallbackError,
|
|
86
|
+
},
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
90
|
+
|
|
91
|
+
/* Execution */
|
|
92
|
+
|
|
93
|
+
const query = `
|
|
94
|
+
query {
|
|
95
|
+
test
|
|
96
|
+
}
|
|
97
|
+
`
|
|
98
|
+
const res = await graphql({
|
|
99
|
+
schema: schemaWithPermissions,
|
|
100
|
+
source: query,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
/* Tests */
|
|
104
|
+
|
|
105
|
+
expect(res.data).toBeNull()
|
|
106
|
+
expect(res.errors?.[0]?.message).toBe(fallbackError.message)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('correctly converts string fallbackError to error fallbackError', async () => {
|
|
110
|
+
/* Schema */
|
|
111
|
+
|
|
112
|
+
const typeDefs = `
|
|
113
|
+
type Query {
|
|
114
|
+
test: String!
|
|
115
|
+
}
|
|
116
|
+
`
|
|
117
|
+
|
|
118
|
+
const resolvers = {
|
|
119
|
+
Query: {
|
|
120
|
+
test: () => {
|
|
121
|
+
throw new Error()
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
127
|
+
|
|
128
|
+
/* Permissions */
|
|
129
|
+
|
|
130
|
+
const fallbackMessage = Math.random().toString()
|
|
131
|
+
const permissions = shield(
|
|
132
|
+
{
|
|
133
|
+
Query: allow,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
fallbackError: fallbackMessage,
|
|
137
|
+
},
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
141
|
+
|
|
142
|
+
/* Execution */
|
|
143
|
+
const query = `
|
|
144
|
+
query {
|
|
145
|
+
test
|
|
146
|
+
}
|
|
147
|
+
`
|
|
148
|
+
const res = await graphql({
|
|
149
|
+
schema: schemaWithPermissions,
|
|
150
|
+
source: query,
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
/* Tests */
|
|
154
|
+
|
|
155
|
+
expect(res.data).toBeNull()
|
|
156
|
+
expect(res.errors?.[0]?.message).toBe(fallbackMessage)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test('error in rule can be mapped.', async () => {
|
|
160
|
+
/* Schema */
|
|
161
|
+
|
|
162
|
+
const typeDefs = `
|
|
163
|
+
type Query {
|
|
164
|
+
test: String!
|
|
165
|
+
}
|
|
166
|
+
`
|
|
167
|
+
|
|
168
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers: {} })
|
|
169
|
+
|
|
170
|
+
/* Permissions */
|
|
171
|
+
|
|
172
|
+
const fallbackError = () => new Error('fallback')
|
|
173
|
+
|
|
174
|
+
const allow = rule()(() => {
|
|
175
|
+
throw new Error()
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
const permissions = shield(
|
|
179
|
+
{
|
|
180
|
+
Query: allow,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
fallbackError,
|
|
184
|
+
},
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
188
|
+
|
|
189
|
+
/* Execution */
|
|
190
|
+
|
|
191
|
+
const query = `
|
|
192
|
+
query {
|
|
193
|
+
test
|
|
194
|
+
}
|
|
195
|
+
`
|
|
196
|
+
const res = await graphql({
|
|
197
|
+
schema: schemaWithPermissions,
|
|
198
|
+
source: query,
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
/* Tests */
|
|
202
|
+
|
|
203
|
+
expect(res.data).toBeNull()
|
|
204
|
+
expect(res.errors?.[0]?.message).toBe(fallbackError().message)
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
test('error in resolver can be mapped.', async () => {
|
|
208
|
+
/* Schema */
|
|
209
|
+
|
|
210
|
+
const typeDefs = `
|
|
211
|
+
type Query {
|
|
212
|
+
test: String!
|
|
213
|
+
}
|
|
214
|
+
`
|
|
215
|
+
const resolvers = {
|
|
216
|
+
Query: {
|
|
217
|
+
test: async () => {
|
|
218
|
+
throw new Error()
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const schema = makeExecutableSchema({
|
|
224
|
+
typeDefs,
|
|
225
|
+
resolvers,
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
/* Permissions */
|
|
229
|
+
|
|
230
|
+
const fallbackError = () => new Error('fallback')
|
|
231
|
+
|
|
232
|
+
const permissions = shield(
|
|
233
|
+
{
|
|
234
|
+
Query: allow,
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
fallbackError,
|
|
238
|
+
},
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
242
|
+
|
|
243
|
+
/* Execution */
|
|
244
|
+
|
|
245
|
+
const query = `
|
|
246
|
+
query {
|
|
247
|
+
test
|
|
248
|
+
}
|
|
249
|
+
`
|
|
250
|
+
const res = await graphql({
|
|
251
|
+
schema: schemaWithPermissions,
|
|
252
|
+
source: query,
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
/* Tests */
|
|
256
|
+
|
|
257
|
+
expect(res.data).toBeNull()
|
|
258
|
+
expect(res.errors?.[0]?.message).toBe(fallbackError().message)
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
describe('external errors can be controled correctly', () => {
|
|
263
|
+
test('error in resolver with allowExternalErrors returns external error.', async () => {
|
|
264
|
+
/* Schema */
|
|
265
|
+
|
|
266
|
+
const typeDefs = `
|
|
267
|
+
type Query {
|
|
268
|
+
test: String!
|
|
269
|
+
}
|
|
270
|
+
`
|
|
271
|
+
|
|
272
|
+
const resolvers = {
|
|
273
|
+
Query: {
|
|
274
|
+
test: () => {
|
|
275
|
+
throw new Error('external')
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
281
|
+
|
|
282
|
+
/* Permissions */
|
|
283
|
+
|
|
284
|
+
const permissions = shield(
|
|
285
|
+
{
|
|
286
|
+
Query: allow,
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
allowExternalErrors: true,
|
|
290
|
+
},
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
294
|
+
|
|
295
|
+
/* Execution */
|
|
296
|
+
|
|
297
|
+
const query = `
|
|
298
|
+
query {
|
|
299
|
+
test
|
|
300
|
+
}
|
|
301
|
+
`
|
|
302
|
+
const res = await graphql({
|
|
303
|
+
schema: schemaWithPermissions,
|
|
304
|
+
source: query,
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
/* Tests */
|
|
308
|
+
|
|
309
|
+
expect(res.data).toBeNull()
|
|
310
|
+
expect(res.errors?.[0]?.message).toBe('external')
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
test('error in rule with allowExternalErrors returns fallback.', async () => {
|
|
314
|
+
/* Schema */
|
|
315
|
+
const typeDefs = `
|
|
316
|
+
type Query {
|
|
317
|
+
test: String!
|
|
318
|
+
}
|
|
319
|
+
`
|
|
320
|
+
|
|
321
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers: {} })
|
|
322
|
+
|
|
323
|
+
/* Permissions */
|
|
324
|
+
|
|
325
|
+
const allow = rule()(() => {
|
|
326
|
+
throw new Error('external')
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
const permissions = shield(
|
|
330
|
+
{
|
|
331
|
+
Query: allow,
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
allowExternalErrors: true,
|
|
335
|
+
},
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
339
|
+
|
|
340
|
+
/* Execution */
|
|
341
|
+
|
|
342
|
+
const query = `
|
|
343
|
+
query {
|
|
344
|
+
test
|
|
345
|
+
}
|
|
346
|
+
`
|
|
347
|
+
const res = await graphql({
|
|
348
|
+
schema: schemaWithPermissions,
|
|
349
|
+
source: query,
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
/* Tests */
|
|
353
|
+
|
|
354
|
+
expect(res.data).toBeNull()
|
|
355
|
+
expect(res.errors?.[0]?.message).toBe('Not Authorised!')
|
|
356
|
+
})
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
describe('debug mode works as expected', () => {
|
|
360
|
+
test('returns original error in debug mode when rule error occurs', async () => {
|
|
361
|
+
/* Schema */
|
|
362
|
+
const typeDefs = `
|
|
363
|
+
type Query {
|
|
364
|
+
test: String!
|
|
365
|
+
}
|
|
366
|
+
`
|
|
367
|
+
|
|
368
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers: {} })
|
|
369
|
+
|
|
370
|
+
/* Permissions */
|
|
371
|
+
|
|
372
|
+
const allow = rule()(() => {
|
|
373
|
+
throw new Error('debug')
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
const permissions = shield(
|
|
377
|
+
{
|
|
378
|
+
Query: allow,
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
debug: true,
|
|
382
|
+
},
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
386
|
+
|
|
387
|
+
/* Execution */
|
|
388
|
+
|
|
389
|
+
const query = `
|
|
390
|
+
query {
|
|
391
|
+
test
|
|
392
|
+
}
|
|
393
|
+
`
|
|
394
|
+
const res = await graphql({
|
|
395
|
+
schema: schemaWithPermissions,
|
|
396
|
+
source: query,
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
/* Tests */
|
|
400
|
+
|
|
401
|
+
expect(res.data).toBeNull()
|
|
402
|
+
expect(res.errors?.[0]?.message).toBe('debug')
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
test('returns original error in debug mode when resolver error occurs.', async () => {
|
|
406
|
+
/* Schema */
|
|
407
|
+
|
|
408
|
+
const typeDefs = `
|
|
409
|
+
type Query {
|
|
410
|
+
test: String!
|
|
411
|
+
}
|
|
412
|
+
`
|
|
413
|
+
|
|
414
|
+
const resolvers = {
|
|
415
|
+
Query: {
|
|
416
|
+
test: () => {
|
|
417
|
+
throw new Error('debug')
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
423
|
+
|
|
424
|
+
/* Permissions */
|
|
425
|
+
|
|
426
|
+
const permissions = shield(
|
|
427
|
+
{
|
|
428
|
+
Query: allow,
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
debug: true,
|
|
432
|
+
},
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
436
|
+
|
|
437
|
+
/* Execution */
|
|
438
|
+
|
|
439
|
+
const query = `
|
|
440
|
+
query {
|
|
441
|
+
test
|
|
442
|
+
}
|
|
443
|
+
`
|
|
444
|
+
const res = await graphql({
|
|
445
|
+
schema: schemaWithPermissions,
|
|
446
|
+
source: query,
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
/* Tests */
|
|
450
|
+
|
|
451
|
+
expect(res.data).toBeNull()
|
|
452
|
+
expect(res.errors?.[0]?.message).toBe('debug')
|
|
453
|
+
})
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
describe('custom errors work as expected', () => {
|
|
457
|
+
test('custom error in rule returns custom error.', async () => {
|
|
458
|
+
/* Schema */
|
|
459
|
+
|
|
460
|
+
const typeDefs = `
|
|
461
|
+
type Query {
|
|
462
|
+
test: String!
|
|
463
|
+
}
|
|
464
|
+
`
|
|
465
|
+
|
|
466
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers: {} })
|
|
467
|
+
|
|
468
|
+
/* Permissions */
|
|
469
|
+
|
|
470
|
+
const error = new Error(`${Math.random()}`)
|
|
471
|
+
const permissions = shield({
|
|
472
|
+
Query: rule()(() => {
|
|
473
|
+
return error
|
|
474
|
+
}),
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
478
|
+
|
|
479
|
+
/* Execution */
|
|
480
|
+
const query = `
|
|
481
|
+
query {
|
|
482
|
+
test
|
|
483
|
+
}
|
|
484
|
+
`
|
|
485
|
+
const res = await graphql({
|
|
486
|
+
schema: schemaWithPermissions,
|
|
487
|
+
source: query,
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
/* Tests */
|
|
491
|
+
|
|
492
|
+
expect(res.data).toBeNull()
|
|
493
|
+
expect(res.errors?.[0]?.message).toBe(error.message)
|
|
494
|
+
})
|
|
495
|
+
|
|
496
|
+
test('custom error message in rule returns custom error.', async () => {
|
|
497
|
+
/* Schema */
|
|
498
|
+
|
|
499
|
+
const typeDefs = `
|
|
500
|
+
type Query {
|
|
501
|
+
test: String!
|
|
502
|
+
}
|
|
503
|
+
`
|
|
504
|
+
|
|
505
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers: {} })
|
|
506
|
+
|
|
507
|
+
/* Permissions */
|
|
508
|
+
|
|
509
|
+
const error = `${Math.random()}`
|
|
510
|
+
|
|
511
|
+
const permissions = shield({
|
|
512
|
+
Query: rule()(() => {
|
|
513
|
+
return error
|
|
514
|
+
}),
|
|
515
|
+
})
|
|
516
|
+
|
|
517
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
518
|
+
|
|
519
|
+
/* Execution */
|
|
520
|
+
const query = `
|
|
521
|
+
query {
|
|
522
|
+
test
|
|
523
|
+
}
|
|
524
|
+
`
|
|
525
|
+
const res = await graphql({
|
|
526
|
+
schema: schemaWithPermissions,
|
|
527
|
+
source: query,
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
/* Tests */
|
|
531
|
+
|
|
532
|
+
expect(res.data).toBeNull()
|
|
533
|
+
expect(res.errors?.[0]?.message).toBe(error)
|
|
534
|
+
})
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
describe('fallbackRule correctly applies fallback rule', () => {
|
|
538
|
+
test('correctly applies fallback rule on undefined fields', async () => {
|
|
539
|
+
/* Schema */
|
|
540
|
+
|
|
541
|
+
const typeDefs = `
|
|
542
|
+
type Query {
|
|
543
|
+
a: String
|
|
544
|
+
b: String
|
|
545
|
+
type: Type
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
type Type {
|
|
549
|
+
a: String
|
|
550
|
+
b: String
|
|
551
|
+
}
|
|
552
|
+
`
|
|
553
|
+
|
|
554
|
+
const resolvers = {
|
|
555
|
+
Query: {
|
|
556
|
+
a: () => 'a',
|
|
557
|
+
b: () => 'b',
|
|
558
|
+
type: () => ({}),
|
|
559
|
+
},
|
|
560
|
+
Type: {
|
|
561
|
+
a: () => 'a',
|
|
562
|
+
b: () => 'b',
|
|
563
|
+
},
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const schema = makeExecutableSchema({ typeDefs, resolvers })
|
|
567
|
+
|
|
568
|
+
/* Permissions */
|
|
569
|
+
|
|
570
|
+
const fallbackRuleMock = jest.fn().mockResolvedValue(true)
|
|
571
|
+
const fallbackRule = rule({ cache: 'no_cache' })(fallbackRuleMock)
|
|
572
|
+
|
|
573
|
+
const permissions = shield(
|
|
574
|
+
{
|
|
575
|
+
Query: {
|
|
576
|
+
a: allow,
|
|
577
|
+
type: allow,
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
fallbackRule: fallbackRule,
|
|
582
|
+
},
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
const schemaWithPermissions = applyMiddleware(schema, permissions)
|
|
586
|
+
|
|
587
|
+
/* Execution */
|
|
588
|
+
|
|
589
|
+
const query = `
|
|
590
|
+
query {
|
|
591
|
+
a
|
|
592
|
+
b
|
|
593
|
+
type {
|
|
594
|
+
a
|
|
595
|
+
b
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
`
|
|
599
|
+
const res = await graphql({
|
|
600
|
+
schema: schemaWithPermissions,
|
|
601
|
+
source: query,
|
|
602
|
+
})
|
|
603
|
+
|
|
604
|
+
/* Tests */
|
|
605
|
+
|
|
606
|
+
expect(res).toEqual({
|
|
607
|
+
data: {
|
|
608
|
+
a: 'a',
|
|
609
|
+
b: 'b',
|
|
610
|
+
type: {
|
|
611
|
+
a: 'a',
|
|
612
|
+
b: 'b',
|
|
613
|
+
},
|
|
614
|
+
},
|
|
615
|
+
})
|
|
616
|
+
expect(fallbackRuleMock).toBeCalledTimes(3)
|
|
617
|
+
})
|
|
618
|
+
})
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { applyMiddleware } from 'graphql-middleware'
|
|
2
|
+
import { makeExecutableSchema } from '@graphql-tools/schema'
|
|
3
|
+
import { shield, rule, and, not, or } from '../src/index'
|
|
4
|
+
import { allow } from '../src/constructors'
|
|
5
|
+
|
|
6
|
+
describe('Fragment extraction', () => {
|
|
7
|
+
test('Extracts fragment from rule correctly.', async () => {
|
|
8
|
+
const ruleWithFragment = rule({ fragment: 'pass' })(() => true)
|
|
9
|
+
expect(ruleWithFragment.extractFragment()).toBe('pass')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
test('Extracts fragment from logic rule correctly.', async () => {
|
|
13
|
+
const ruleWithNoFragment = rule()(() => true)
|
|
14
|
+
const ruleWithFragmentA = rule({ fragment: 'pass-A' })(() => true)
|
|
15
|
+
const ruleWithFragmentB = rule({ fragment: 'pass-B' })(() => true)
|
|
16
|
+
const ruleWithFragmentC = rule({ fragment: 'pass-C' })(() => true)
|
|
17
|
+
|
|
18
|
+
const logicRuleAND = and(
|
|
19
|
+
ruleWithNoFragment,
|
|
20
|
+
ruleWithFragmentA,
|
|
21
|
+
ruleWithFragmentB,
|
|
22
|
+
)
|
|
23
|
+
const logicRuleNOT = not(logicRuleAND)
|
|
24
|
+
const logicRuleOR = or(ruleWithFragmentB, ruleWithFragmentC, logicRuleNOT)
|
|
25
|
+
|
|
26
|
+
expect(logicRuleOR.extractFragments()).toEqual([
|
|
27
|
+
'pass-B',
|
|
28
|
+
'pass-C',
|
|
29
|
+
'pass-A',
|
|
30
|
+
'pass-B',
|
|
31
|
+
])
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('Fragment application', () => {
|
|
36
|
+
test('Applies rule-fragment correctly.', async () => {
|
|
37
|
+
/* Schema */
|
|
38
|
+
const typeDefs = `
|
|
39
|
+
type Query {
|
|
40
|
+
user: User
|
|
41
|
+
events: [Event!]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type User {
|
|
45
|
+
id: ID!
|
|
46
|
+
name: String!
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type Event {
|
|
50
|
+
id: ID!
|
|
51
|
+
location: String!
|
|
52
|
+
published: Boolean
|
|
53
|
+
}
|
|
54
|
+
`
|
|
55
|
+
|
|
56
|
+
/* Permissions */
|
|
57
|
+
|
|
58
|
+
const isUserSelf = rule({
|
|
59
|
+
fragment: 'fragment UserId on User { id }',
|
|
60
|
+
})(async (parent, args, ctx, info) => {
|
|
61
|
+
return true
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const isProfilePublic = rule({
|
|
65
|
+
fragment: 'fragment UserPublic on User { public }',
|
|
66
|
+
})(async (parent, args, ctx, info) => {
|
|
67
|
+
return true
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
const isEventPublished = rule({
|
|
71
|
+
fragment: '... on Event { published }',
|
|
72
|
+
})(async () => {
|
|
73
|
+
return true
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const permissions = shield({
|
|
77
|
+
Query: {
|
|
78
|
+
user: allow,
|
|
79
|
+
events: allow,
|
|
80
|
+
},
|
|
81
|
+
User: or(isUserSelf, isProfilePublic),
|
|
82
|
+
Event: isEventPublished,
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const { fragmentReplacements } = applyMiddleware(
|
|
86
|
+
makeExecutableSchema({ typeDefs, resolvers: {} }),
|
|
87
|
+
permissions,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
expect(fragmentReplacements).toEqual([
|
|
91
|
+
{
|
|
92
|
+
field: 'id',
|
|
93
|
+
fragment: '... on User {\n public\n}',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
field: 'name',
|
|
97
|
+
fragment: '... on User {\n id\n}',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
field: 'name',
|
|
101
|
+
fragment: '... on User {\n public\n}',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
field: 'id',
|
|
105
|
+
fragment: '... on Event {\n published\n}',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
field: 'location',
|
|
109
|
+
fragment: '... on Event {\n published\n}',
|
|
110
|
+
},
|
|
111
|
+
])
|
|
112
|
+
})
|
|
113
|
+
})
|