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
package/src/generator.ts
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IMiddleware,
|
|
3
|
+
IMiddlewareFunction,
|
|
4
|
+
IMiddlewareGeneratorConstructor,
|
|
5
|
+
} from 'graphql-middleware'
|
|
6
|
+
import {
|
|
7
|
+
GraphQLSchema,
|
|
8
|
+
GraphQLObjectType,
|
|
9
|
+
isObjectType,
|
|
10
|
+
isIntrospectionType,
|
|
11
|
+
GraphQLResolveInfo,
|
|
12
|
+
} from 'graphql'
|
|
13
|
+
import {
|
|
14
|
+
IRules,
|
|
15
|
+
IOptions,
|
|
16
|
+
ShieldRule,
|
|
17
|
+
IRuleFieldMap,
|
|
18
|
+
IShieldContext,
|
|
19
|
+
} from './types.js'
|
|
20
|
+
import {
|
|
21
|
+
isRuleFunction,
|
|
22
|
+
isRuleFieldMap,
|
|
23
|
+
isRule,
|
|
24
|
+
isLogicRule,
|
|
25
|
+
withDefault,
|
|
26
|
+
} from './utils.js'
|
|
27
|
+
import { ValidationError } from './validation.js'
|
|
28
|
+
import { IMiddlewareWithOptions } from 'graphql-middleware/dist/types'
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
*
|
|
32
|
+
* @param options
|
|
33
|
+
*
|
|
34
|
+
* Generates a middleware function from a given rule and
|
|
35
|
+
* initializes the cache object in context.
|
|
36
|
+
*
|
|
37
|
+
*/
|
|
38
|
+
function generateFieldMiddlewareFromRule(
|
|
39
|
+
rule: ShieldRule,
|
|
40
|
+
options: IOptions,
|
|
41
|
+
): IMiddlewareFunction<object, object, IShieldContext> {
|
|
42
|
+
async function middleware(
|
|
43
|
+
resolve: (
|
|
44
|
+
parent: object,
|
|
45
|
+
args: object,
|
|
46
|
+
ctx: IShieldContext,
|
|
47
|
+
info: GraphQLResolveInfo,
|
|
48
|
+
) => Promise<any>,
|
|
49
|
+
parent: { [key: string]: any },
|
|
50
|
+
args: { [key: string]: any },
|
|
51
|
+
ctx: IShieldContext,
|
|
52
|
+
info: GraphQLResolveInfo,
|
|
53
|
+
) {
|
|
54
|
+
// Cache
|
|
55
|
+
if (!ctx) {
|
|
56
|
+
ctx = {} as IShieldContext
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!ctx._shield) {
|
|
60
|
+
ctx._shield = {
|
|
61
|
+
cache: {},
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Execution
|
|
66
|
+
try {
|
|
67
|
+
const res = await rule.resolve(parent, args, ctx, info, options)
|
|
68
|
+
|
|
69
|
+
if (res === true) {
|
|
70
|
+
return await resolve(parent, args, ctx, info)
|
|
71
|
+
} else if (res === false) {
|
|
72
|
+
if (typeof options.fallbackError === 'function') {
|
|
73
|
+
return await options.fallbackError(null, parent, args, ctx, info)
|
|
74
|
+
}
|
|
75
|
+
return options.fallbackError
|
|
76
|
+
} else {
|
|
77
|
+
return res
|
|
78
|
+
}
|
|
79
|
+
} catch (err) {
|
|
80
|
+
if (options.debug) {
|
|
81
|
+
throw err
|
|
82
|
+
} else if (options.allowExternalErrors) {
|
|
83
|
+
return err
|
|
84
|
+
} else {
|
|
85
|
+
if (typeof options.fallbackError === 'function') {
|
|
86
|
+
return await options.fallbackError(err, parent, args, ctx, info)
|
|
87
|
+
}
|
|
88
|
+
return options.fallbackError
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (isRule(rule) && rule.extractFragment()) {
|
|
94
|
+
return {
|
|
95
|
+
fragment: rule.extractFragment(),
|
|
96
|
+
resolve: middleware,
|
|
97
|
+
} as IMiddlewareWithOptions<object, object, IShieldContext>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (isLogicRule(rule)) {
|
|
101
|
+
return {
|
|
102
|
+
fragments: rule.extractFragments(),
|
|
103
|
+
resolve: middleware,
|
|
104
|
+
} as IMiddlewareWithOptions<object, object, IShieldContext>
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return middleware as IMiddlewareFunction<object, object, IShieldContext>
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
*
|
|
112
|
+
* @param type
|
|
113
|
+
* @param rules
|
|
114
|
+
* @param options
|
|
115
|
+
*
|
|
116
|
+
* Generates middleware from rule for a particular type.
|
|
117
|
+
*
|
|
118
|
+
*/
|
|
119
|
+
function applyRuleToType(
|
|
120
|
+
type: GraphQLObjectType,
|
|
121
|
+
rules: ShieldRule | IRuleFieldMap,
|
|
122
|
+
options: IOptions,
|
|
123
|
+
): IMiddleware {
|
|
124
|
+
if (isRuleFunction(rules)) {
|
|
125
|
+
/* Apply defined rule function to every field */
|
|
126
|
+
const fieldMap = type.getFields()
|
|
127
|
+
|
|
128
|
+
const middleware = Object.keys(fieldMap).reduce((middleware, field) => {
|
|
129
|
+
return {
|
|
130
|
+
...middleware,
|
|
131
|
+
[field]: generateFieldMiddlewareFromRule(rules, options),
|
|
132
|
+
}
|
|
133
|
+
}, {})
|
|
134
|
+
|
|
135
|
+
return middleware
|
|
136
|
+
} else if (isRuleFieldMap(rules)) {
|
|
137
|
+
/* Apply rules assigned to each field to each field */
|
|
138
|
+
const fieldMap = type.getFields()
|
|
139
|
+
|
|
140
|
+
/* Extract default type wildcard if any and remove it for validation */
|
|
141
|
+
const defaultTypeRule = rules['*']
|
|
142
|
+
const {'*': _, ...rulesWithoutWildcard} = rules;
|
|
143
|
+
/* Validation */
|
|
144
|
+
|
|
145
|
+
const fieldErrors = Object.keys(rulesWithoutWildcard)
|
|
146
|
+
.filter((type) => !Object.prototype.hasOwnProperty.call(fieldMap, type))
|
|
147
|
+
.map((field) => `${type.name}.${field}`)
|
|
148
|
+
.join(', ')
|
|
149
|
+
|
|
150
|
+
if (fieldErrors.length > 0) {
|
|
151
|
+
throw new ValidationError(
|
|
152
|
+
`It seems like you have applied rules to ${fieldErrors} fields but Shield cannot find them in your schema.`,
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Generation */
|
|
157
|
+
|
|
158
|
+
const middleware = Object.keys(fieldMap).reduce(
|
|
159
|
+
(middleware, field) => ({
|
|
160
|
+
...middleware,
|
|
161
|
+
[field]: generateFieldMiddlewareFromRule(
|
|
162
|
+
withDefault(defaultTypeRule || options.fallbackRule)(rules[field]),
|
|
163
|
+
options,
|
|
164
|
+
),
|
|
165
|
+
}),
|
|
166
|
+
{},
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
return middleware
|
|
170
|
+
} else {
|
|
171
|
+
/* Apply fallbackRule to type with no defined rule */
|
|
172
|
+
const fieldMap = type.getFields()
|
|
173
|
+
|
|
174
|
+
const middleware = Object.keys(fieldMap).reduce(
|
|
175
|
+
(middleware, field) => ({
|
|
176
|
+
...middleware,
|
|
177
|
+
[field]: generateFieldMiddlewareFromRule(options.fallbackRule, options),
|
|
178
|
+
}),
|
|
179
|
+
{},
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return middleware
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
*
|
|
188
|
+
* @param schema
|
|
189
|
+
* @param rule
|
|
190
|
+
* @param options
|
|
191
|
+
*
|
|
192
|
+
* Applies the same rule over entire schema.
|
|
193
|
+
*
|
|
194
|
+
*/
|
|
195
|
+
function applyRuleToSchema(
|
|
196
|
+
schema: GraphQLSchema,
|
|
197
|
+
rule: ShieldRule,
|
|
198
|
+
options: IOptions,
|
|
199
|
+
): IMiddleware {
|
|
200
|
+
const typeMap = schema.getTypeMap()
|
|
201
|
+
|
|
202
|
+
const middleware = Object.keys(typeMap)
|
|
203
|
+
.filter((type) => !isIntrospectionType(typeMap[type]))
|
|
204
|
+
.reduce((middleware, typeName) => {
|
|
205
|
+
const type = typeMap[typeName]
|
|
206
|
+
|
|
207
|
+
if (isObjectType(type)) {
|
|
208
|
+
return {
|
|
209
|
+
...middleware,
|
|
210
|
+
[typeName]: applyRuleToType(type, rule, options),
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
return middleware
|
|
214
|
+
}
|
|
215
|
+
}, {})
|
|
216
|
+
|
|
217
|
+
return middleware
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
*
|
|
222
|
+
* @param rules
|
|
223
|
+
* @param wrapper
|
|
224
|
+
*
|
|
225
|
+
* Converts rule tree to middleware.
|
|
226
|
+
*
|
|
227
|
+
*/
|
|
228
|
+
function generateMiddlewareFromSchemaAndRuleTree(
|
|
229
|
+
schema: GraphQLSchema,
|
|
230
|
+
rules: IRules,
|
|
231
|
+
options: IOptions,
|
|
232
|
+
): IMiddleware {
|
|
233
|
+
if (isRuleFunction(rules)) {
|
|
234
|
+
/* Applies rule to entire schema. */
|
|
235
|
+
return applyRuleToSchema(schema, rules, options)
|
|
236
|
+
} else {
|
|
237
|
+
/**
|
|
238
|
+
* Checks type map and field map and applies rules
|
|
239
|
+
* to particular fields.
|
|
240
|
+
*/
|
|
241
|
+
const typeMap = schema.getTypeMap()
|
|
242
|
+
|
|
243
|
+
/* Validation */
|
|
244
|
+
|
|
245
|
+
const typeErrors = Object.keys(rules)
|
|
246
|
+
.filter((type) => !Object.prototype.hasOwnProperty.call(typeMap, type))
|
|
247
|
+
.join(', ')
|
|
248
|
+
|
|
249
|
+
if (typeErrors.length > 0) {
|
|
250
|
+
throw new ValidationError(
|
|
251
|
+
`It seems like you have applied rules to ${typeErrors} types but Shield cannot find them in your schema.`,
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Generation
|
|
256
|
+
|
|
257
|
+
const middleware = Object.keys(typeMap)
|
|
258
|
+
.filter((type) => !isIntrospectionType(typeMap[type]))
|
|
259
|
+
.reduce<IMiddleware>((middleware, typeName) => {
|
|
260
|
+
const type = typeMap[typeName]
|
|
261
|
+
|
|
262
|
+
if (isObjectType(type)) {
|
|
263
|
+
return {
|
|
264
|
+
...middleware,
|
|
265
|
+
[typeName]: applyRuleToType(type, rules[typeName], options),
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
return middleware
|
|
269
|
+
}
|
|
270
|
+
}, {})
|
|
271
|
+
|
|
272
|
+
return middleware
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
*
|
|
278
|
+
* @param ruleTree
|
|
279
|
+
* @param options
|
|
280
|
+
*
|
|
281
|
+
* Generates middleware from given rules.
|
|
282
|
+
*
|
|
283
|
+
*/
|
|
284
|
+
export function generateMiddlewareGeneratorFromRuleTree<
|
|
285
|
+
TSource = any,
|
|
286
|
+
TContext = any,
|
|
287
|
+
TArgs = any
|
|
288
|
+
>(
|
|
289
|
+
ruleTree: IRules,
|
|
290
|
+
options: IOptions,
|
|
291
|
+
): IMiddlewareGeneratorConstructor<TSource, TContext, TArgs> {
|
|
292
|
+
return (schema: GraphQLSchema) =>
|
|
293
|
+
generateMiddlewareFromSchemaAndRuleTree(schema, ruleTree, options)
|
|
294
|
+
}
|