@envelop/extended-validation
Advanced tools
Comparing version 1.3.5-alpha-ea62296.0 to 2.0.0-alpha-36a1267.0
86
index.js
@@ -8,5 +8,8 @@ 'use strict'; | ||
const SYMBOL_EXTENDED_VALIDATION_RULES = Symbol('SYMBOL_EXTENDED_VALIDATION_RULES'); | ||
const symbolExtendedValidationRules = Symbol('extendedValidationContext'); | ||
const useExtendedValidation = (options) => { | ||
let schemaTypeInfo; | ||
function getTypeInfo() { | ||
return schemaTypeInfo; | ||
} | ||
return { | ||
@@ -16,37 +19,58 @@ onSchemaChange({ schema }) { | ||
}, | ||
onParse({ context, extendContext }) { | ||
var _a; | ||
const rules = (_a = context === null || context === void 0 ? void 0 : context[SYMBOL_EXTENDED_VALIDATION_RULES]) !== null && _a !== void 0 ? _a : []; | ||
rules.push(...options.rules); | ||
extendContext({ | ||
[SYMBOL_EXTENDED_VALIDATION_RULES]: rules, | ||
}); | ||
}, | ||
onExecute({ args, setResultAndStopExecution }) { | ||
// We hook into onExecute even though this is a validation pattern. The reasoning behind | ||
// it is that hooking right after validation and before execution has started is the | ||
// same as hooking into the validation step. The benefit of this approach is that | ||
// we may use execution context in the validation rules. | ||
const rules = args.contextValue[SYMBOL_EXTENDED_VALIDATION_RULES]; | ||
const errors = []; | ||
// We replicate the default validation step manually before execution starts. | ||
const typeInfo = schemaTypeInfo || new graphql.TypeInfo(args.schema); | ||
const validationContext = new graphql.ValidationContext(args.schema, args.document, typeInfo, e => { | ||
errors.push(e); | ||
}); | ||
const visitor = graphql.visitInParallel(rules.map(rule => rule(validationContext, args))); | ||
graphql.visit(args.document, graphql.visitWithTypeInfo(typeInfo, visitor)); | ||
if (errors.length > 0) { | ||
let result = { | ||
data: null, | ||
errors, | ||
onContextBuilding({ context, extendContext }) { | ||
// We initialize the validationRules context in onContextBuilding as onExecute is already too late! | ||
let validationRulesContext = context[symbolExtendedValidationRules]; | ||
if (validationRulesContext === undefined) { | ||
validationRulesContext = { | ||
rules: [], | ||
didRun: false, | ||
}; | ||
if (options.onValidationFailed) { | ||
options.onValidationFailed({ args, result, setResult: newResult => (result = newResult) }); | ||
} | ||
return setResultAndStopExecution(result); | ||
extendContext({ | ||
[symbolExtendedValidationRules]: validationRulesContext, | ||
}); | ||
} | ||
validationRulesContext.rules.push(...options.rules); | ||
}, | ||
onSubscribe: buildHandler('subscribe', getTypeInfo, options.onValidationFailed), | ||
onExecute: buildHandler('execute', getTypeInfo, options.onValidationFailed), | ||
}; | ||
}; | ||
function buildHandler(name, getTypeInfo, onValidationFailed) { | ||
return function handler({ args, setResultAndStopExecution, }) { | ||
var _a; | ||
// We hook into onExecute/onSubscribe even though this is a validation pattern. The reasoning behind | ||
// it is that hooking right after validation and before execution has started is the | ||
// same as hooking into the validation step. The benefit of this approach is that | ||
// we may use execution context in the validation rules. | ||
const validationRulesContext = args.contextValue[symbolExtendedValidationRules]; | ||
if (validationRulesContext === undefined) { | ||
throw new Error('Plugin has not been properly set up. ' + | ||
`The 'contextFactory' function is not invoked and the result has not been passed to '${name}'.`); | ||
} | ||
// we only want to run the extended execution once. | ||
if (validationRulesContext.didRun === false) { | ||
validationRulesContext.didRun = true; | ||
if (validationRulesContext.rules.length !== 0) { | ||
const errors = []; | ||
// We replicate the default validation step manually before execution starts. | ||
const typeInfo = (_a = getTypeInfo()) !== null && _a !== void 0 ? _a : new graphql.TypeInfo(args.schema); | ||
const validationContext = new graphql.ValidationContext(args.schema, args.document, typeInfo, e => { | ||
errors.push(e); | ||
}); | ||
const visitor = graphql.visitInParallel(validationRulesContext.rules.map(rule => rule(validationContext, args))); | ||
graphql.visit(args.document, graphql.visitWithTypeInfo(typeInfo, visitor)); | ||
if (errors.length > 0) { | ||
let result = { | ||
data: null, | ||
errors, | ||
}; | ||
if (onValidationFailed) { | ||
onValidationFailed({ args, result, setResult: newResult => (result = newResult) }); | ||
} | ||
setResultAndStopExecution(result); | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
@@ -53,0 +77,0 @@ function getDirectiveFromAstNode(astNode, names) { |
{ | ||
"name": "@envelop/extended-validation", | ||
"version": "1.3.5-alpha-ea62296.0", | ||
"version": "2.0.0-alpha-36a1267.0", | ||
"sideEffects": false, | ||
"peerDependencies": { | ||
"@envelop/core": "2.0.1-alpha-ea62296.0", | ||
"@envelop/core": "2.1.0-alpha-36a1267.0", | ||
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" | ||
@@ -8,0 +8,0 @@ }, |
import { Plugin } from '@envelop/core'; | ||
import { ExecutionArgs, ExecutionResult } from 'graphql'; | ||
import { ExtendedValidationRule } from './common'; | ||
declare type OnValidationFailedCallback = (params: { | ||
args: ExecutionArgs; | ||
result: ExecutionResult; | ||
setResult: (result: ExecutionResult) => void; | ||
}) => void; | ||
export declare const useExtendedValidation: (options: { | ||
rules: ExtendedValidationRule[]; | ||
rules: Array<ExtendedValidationRule>; | ||
/** | ||
* Callback that is invoked if the extended validation yields any errors. | ||
*/ | ||
onValidationFailed?: ((params: { | ||
args: ExecutionArgs; | ||
result: ExecutionResult; | ||
setResult: (result: ExecutionResult) => void; | ||
}) => void) | undefined; | ||
onValidationFailed?: OnValidationFailedCallback; | ||
}) => Plugin; | ||
export {}; |
Sorry, the diff of this file is not supported yet
20214
326