@envelop/extended-validation 1.3.5-alpha-304a55e.0 → 1.4.0
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/index.js +55 -31
- package/index.mjs +55 -31
- package/package.json +2 -2
- package/plugin.d.ts +8 -6
package/index.js
CHANGED
|
@@ -5,48 +5,72 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
const graphql = require('graphql');
|
|
6
6
|
const utils = require('@graphql-tools/utils');
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const symbolExtendedValidationRules = Symbol('extendedValidationContext');
|
|
9
9
|
const useExtendedValidation = (options) => {
|
|
10
10
|
let schemaTypeInfo;
|
|
11
|
+
function getTypeInfo() {
|
|
12
|
+
return schemaTypeInfo;
|
|
13
|
+
}
|
|
11
14
|
return {
|
|
12
15
|
onSchemaChange({ schema }) {
|
|
13
16
|
schemaTypeInfo = new graphql.TypeInfo(schema);
|
|
14
17
|
},
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
},
|
|
23
|
-
onExecute({ args, setResultAndStopExecution }) {
|
|
24
|
-
// We hook into onExecute even though this is a validation pattern. The reasoning behind
|
|
25
|
-
// it is that hooking right after validation and before execution has started is the
|
|
26
|
-
// same as hooking into the validation step. The benefit of this approach is that
|
|
27
|
-
// we may use execution context in the validation rules.
|
|
28
|
-
const rules = args.contextValue[SYMBOL_EXTENDED_VALIDATION_RULES];
|
|
29
|
-
const errors = [];
|
|
30
|
-
// We replicate the default validation step manually before execution starts.
|
|
31
|
-
const typeInfo = schemaTypeInfo || new graphql.TypeInfo(args.schema);
|
|
32
|
-
const validationContext = new graphql.ValidationContext(args.schema, args.document, typeInfo, e => {
|
|
33
|
-
errors.push(e);
|
|
34
|
-
});
|
|
35
|
-
const visitor = graphql.visitInParallel(rules.map(rule => rule(validationContext, args)));
|
|
36
|
-
graphql.visit(args.document, graphql.visitWithTypeInfo(typeInfo, visitor));
|
|
37
|
-
if (errors.length > 0) {
|
|
38
|
-
let result = {
|
|
39
|
-
data: null,
|
|
40
|
-
errors,
|
|
18
|
+
onContextBuilding({ context, extendContext }) {
|
|
19
|
+
// We initialize the validationRules context in onContextBuilding as onExecute is already too late!
|
|
20
|
+
let validationRulesContext = context[symbolExtendedValidationRules];
|
|
21
|
+
if (validationRulesContext === undefined) {
|
|
22
|
+
validationRulesContext = {
|
|
23
|
+
rules: [],
|
|
24
|
+
didRun: false,
|
|
41
25
|
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
return setResultAndStopExecution(result);
|
|
26
|
+
extendContext({
|
|
27
|
+
[symbolExtendedValidationRules]: validationRulesContext,
|
|
28
|
+
});
|
|
46
29
|
}
|
|
30
|
+
validationRulesContext.rules.push(...options.rules);
|
|
47
31
|
},
|
|
32
|
+
onSubscribe: buildHandler('subscribe', getTypeInfo, options.onValidationFailed),
|
|
33
|
+
onExecute: buildHandler('execute', getTypeInfo, options.onValidationFailed),
|
|
48
34
|
};
|
|
49
35
|
};
|
|
36
|
+
function buildHandler(name, getTypeInfo, onValidationFailed) {
|
|
37
|
+
return function handler({ args, setResultAndStopExecution, }) {
|
|
38
|
+
var _a;
|
|
39
|
+
// We hook into onExecute/onSubscribe even though this is a validation pattern. The reasoning behind
|
|
40
|
+
// it is that hooking right after validation and before execution has started is the
|
|
41
|
+
// same as hooking into the validation step. The benefit of this approach is that
|
|
42
|
+
// we may use execution context in the validation rules.
|
|
43
|
+
const validationRulesContext = args.contextValue[symbolExtendedValidationRules];
|
|
44
|
+
if (validationRulesContext === undefined) {
|
|
45
|
+
throw new Error('Plugin has not been properly set up. ' +
|
|
46
|
+
`The 'contextFactory' function is not invoked and the result has not been passed to '${name}'.`);
|
|
47
|
+
}
|
|
48
|
+
// we only want to run the extended execution once.
|
|
49
|
+
if (validationRulesContext.didRun === false) {
|
|
50
|
+
validationRulesContext.didRun = true;
|
|
51
|
+
if (validationRulesContext.rules.length !== 0) {
|
|
52
|
+
const errors = [];
|
|
53
|
+
// We replicate the default validation step manually before execution starts.
|
|
54
|
+
const typeInfo = (_a = getTypeInfo()) !== null && _a !== void 0 ? _a : new graphql.TypeInfo(args.schema);
|
|
55
|
+
const validationContext = new graphql.ValidationContext(args.schema, args.document, typeInfo, e => {
|
|
56
|
+
errors.push(e);
|
|
57
|
+
});
|
|
58
|
+
const visitor = graphql.visitInParallel(validationRulesContext.rules.map(rule => rule(validationContext, args)));
|
|
59
|
+
graphql.visit(args.document, graphql.visitWithTypeInfo(typeInfo, visitor));
|
|
60
|
+
if (errors.length > 0) {
|
|
61
|
+
let result = {
|
|
62
|
+
data: null,
|
|
63
|
+
errors,
|
|
64
|
+
};
|
|
65
|
+
if (onValidationFailed) {
|
|
66
|
+
onValidationFailed({ args, result, setResult: newResult => (result = newResult) });
|
|
67
|
+
}
|
|
68
|
+
setResultAndStopExecution(result);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
50
74
|
|
|
51
75
|
function getDirectiveFromAstNode(astNode, names) {
|
|
52
76
|
const directives = astNode.directives || [];
|
package/index.mjs
CHANGED
|
@@ -1,48 +1,72 @@
|
|
|
1
1
|
import { TypeInfo, ValidationContext, visitInParallel, visit, visitWithTypeInfo, isNonNullType, isListType, GraphQLError, GraphQLInputObjectType } from 'graphql';
|
|
2
2
|
import { getArgumentValues } from '@graphql-tools/utils';
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const symbolExtendedValidationRules = Symbol('extendedValidationContext');
|
|
5
5
|
const useExtendedValidation = (options) => {
|
|
6
6
|
let schemaTypeInfo;
|
|
7
|
+
function getTypeInfo() {
|
|
8
|
+
return schemaTypeInfo;
|
|
9
|
+
}
|
|
7
10
|
return {
|
|
8
11
|
onSchemaChange({ schema }) {
|
|
9
12
|
schemaTypeInfo = new TypeInfo(schema);
|
|
10
13
|
},
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
},
|
|
19
|
-
onExecute({ args, setResultAndStopExecution }) {
|
|
20
|
-
// We hook into onExecute even though this is a validation pattern. The reasoning behind
|
|
21
|
-
// it is that hooking right after validation and before execution has started is the
|
|
22
|
-
// same as hooking into the validation step. The benefit of this approach is that
|
|
23
|
-
// we may use execution context in the validation rules.
|
|
24
|
-
const rules = args.contextValue[SYMBOL_EXTENDED_VALIDATION_RULES];
|
|
25
|
-
const errors = [];
|
|
26
|
-
// We replicate the default validation step manually before execution starts.
|
|
27
|
-
const typeInfo = schemaTypeInfo || new TypeInfo(args.schema);
|
|
28
|
-
const validationContext = new ValidationContext(args.schema, args.document, typeInfo, e => {
|
|
29
|
-
errors.push(e);
|
|
30
|
-
});
|
|
31
|
-
const visitor = visitInParallel(rules.map(rule => rule(validationContext, args)));
|
|
32
|
-
visit(args.document, visitWithTypeInfo(typeInfo, visitor));
|
|
33
|
-
if (errors.length > 0) {
|
|
34
|
-
let result = {
|
|
35
|
-
data: null,
|
|
36
|
-
errors,
|
|
14
|
+
onContextBuilding({ context, extendContext }) {
|
|
15
|
+
// We initialize the validationRules context in onContextBuilding as onExecute is already too late!
|
|
16
|
+
let validationRulesContext = context[symbolExtendedValidationRules];
|
|
17
|
+
if (validationRulesContext === undefined) {
|
|
18
|
+
validationRulesContext = {
|
|
19
|
+
rules: [],
|
|
20
|
+
didRun: false,
|
|
37
21
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
return setResultAndStopExecution(result);
|
|
22
|
+
extendContext({
|
|
23
|
+
[symbolExtendedValidationRules]: validationRulesContext,
|
|
24
|
+
});
|
|
42
25
|
}
|
|
26
|
+
validationRulesContext.rules.push(...options.rules);
|
|
43
27
|
},
|
|
28
|
+
onSubscribe: buildHandler('subscribe', getTypeInfo, options.onValidationFailed),
|
|
29
|
+
onExecute: buildHandler('execute', getTypeInfo, options.onValidationFailed),
|
|
44
30
|
};
|
|
45
31
|
};
|
|
32
|
+
function buildHandler(name, getTypeInfo, onValidationFailed) {
|
|
33
|
+
return function handler({ args, setResultAndStopExecution, }) {
|
|
34
|
+
var _a;
|
|
35
|
+
// We hook into onExecute/onSubscribe even though this is a validation pattern. The reasoning behind
|
|
36
|
+
// it is that hooking right after validation and before execution has started is the
|
|
37
|
+
// same as hooking into the validation step. The benefit of this approach is that
|
|
38
|
+
// we may use execution context in the validation rules.
|
|
39
|
+
const validationRulesContext = args.contextValue[symbolExtendedValidationRules];
|
|
40
|
+
if (validationRulesContext === undefined) {
|
|
41
|
+
throw new Error('Plugin has not been properly set up. ' +
|
|
42
|
+
`The 'contextFactory' function is not invoked and the result has not been passed to '${name}'.`);
|
|
43
|
+
}
|
|
44
|
+
// we only want to run the extended execution once.
|
|
45
|
+
if (validationRulesContext.didRun === false) {
|
|
46
|
+
validationRulesContext.didRun = true;
|
|
47
|
+
if (validationRulesContext.rules.length !== 0) {
|
|
48
|
+
const errors = [];
|
|
49
|
+
// We replicate the default validation step manually before execution starts.
|
|
50
|
+
const typeInfo = (_a = getTypeInfo()) !== null && _a !== void 0 ? _a : new TypeInfo(args.schema);
|
|
51
|
+
const validationContext = new ValidationContext(args.schema, args.document, typeInfo, e => {
|
|
52
|
+
errors.push(e);
|
|
53
|
+
});
|
|
54
|
+
const visitor = visitInParallel(validationRulesContext.rules.map(rule => rule(validationContext, args)));
|
|
55
|
+
visit(args.document, visitWithTypeInfo(typeInfo, visitor));
|
|
56
|
+
if (errors.length > 0) {
|
|
57
|
+
let result = {
|
|
58
|
+
data: null,
|
|
59
|
+
errors,
|
|
60
|
+
};
|
|
61
|
+
if (onValidationFailed) {
|
|
62
|
+
onValidationFailed({ args, result, setResult: newResult => (result = newResult) });
|
|
63
|
+
}
|
|
64
|
+
setResultAndStopExecution(result);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
46
70
|
|
|
47
71
|
function getDirectiveFromAstNode(astNode, names) {
|
|
48
72
|
const directives = astNode.directives || [];
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@envelop/extended-validation",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"peerDependencies": {
|
|
6
|
-
"@envelop/core": "2.
|
|
6
|
+
"@envelop/core": "^2.1.0",
|
|
7
7
|
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
package/plugin.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { Plugin } from '@envelop/core';
|
|
2
2
|
import { ExecutionArgs, ExecutionResult } from 'graphql';
|
|
3
3
|
import { ExtendedValidationRule } from './common';
|
|
4
|
+
declare type OnValidationFailedCallback = (params: {
|
|
5
|
+
args: ExecutionArgs;
|
|
6
|
+
result: ExecutionResult;
|
|
7
|
+
setResult: (result: ExecutionResult) => void;
|
|
8
|
+
}) => void;
|
|
4
9
|
export declare const useExtendedValidation: (options: {
|
|
5
|
-
rules: ExtendedValidationRule
|
|
10
|
+
rules: Array<ExtendedValidationRule>;
|
|
6
11
|
/**
|
|
7
12
|
* Callback that is invoked if the extended validation yields any errors.
|
|
8
13
|
*/
|
|
9
|
-
onValidationFailed?:
|
|
10
|
-
args: ExecutionArgs;
|
|
11
|
-
result: ExecutionResult;
|
|
12
|
-
setResult: (result: ExecutionResult) => void;
|
|
13
|
-
}) => void) | undefined;
|
|
14
|
+
onValidationFailed?: OnValidationFailedCallback;
|
|
14
15
|
}) => Plugin;
|
|
16
|
+
export {};
|