UNPKG

graphql-validate-directive

Version:
132 lines 4.42 kB
import { getDirective } from '@graphql-tools/utils'; import Ajv from 'ajv'; import { GraphQLFloat, GraphQLInt, GraphQLString, isInputObjectType, isListType, isNonNullType, isObjectType, isScalarType, } from 'graphql'; import pick from 'lodash/pick'; import { DefaultValidateDirectiveName, KeywordsForArray, KeywordsForNumber, KeywordsForString, } from './constants'; export class GraphQLValidateDirective { schema; directiveName; ajv; constructor(schema, directiveName = DefaultValidateDirectiveName, ajv = new Ajv()) { this.schema = schema; this.directiveName = directiveName; this.ajv = ajv; } getScalerTypeSchema(type, opt) { if (type === GraphQLInt) { return { type: 'integer', ...pick(opt, KeywordsForNumber), }; } if (type === GraphQLFloat) { return { type: 'number', ...pick(opt, KeywordsForNumber), }; } if (type === GraphQLString) { return { type: 'string', ...pick(opt, KeywordsForString), }; } return {}; } getListTypeSchema(type, opt) { return { type: 'array', ...pick(opt, KeywordsForArray), items: this.getInputTypeSchema(type.ofType, opt), }; } getInputTypeSchema(type, opt) { if (isScalarType(type) && opt) { return this.getScalerTypeSchema(type, opt); } if (isListType(type) && opt) { return this.getListTypeSchema(type, opt); } if (isNonNullType(type)) { return this.getInputTypeSchema(type.ofType, opt); } if (isInputObjectType(type)) { return { $ref: this.getInputTypeRefName(type.name), }; } return {}; } getInputTypeRefName(name) { return `#/definitions/customInputType${name}`; } getTypeSchema(type) { if (!isInputObjectType(type)) return; const fields = type.getFields(); const properties = {}; const jsonSchema = { type: 'object', properties, additionalProperties: true, }; for (const [name, field] of Object.entries(fields)) { const directive = getDirective(this.schema, field, this.directiveName); properties[name] = this.getInputTypeSchema(field.type, directive?.[0]); } return jsonSchema; } registerTypeSchemas(typeMap) { for (const [name, type] of Object.entries(typeMap)) { const jsonSchema = this.getTypeSchema(type); if (!jsonSchema) continue; this.ajv.addSchema(jsonSchema, this.getInputTypeRefName(name)); } } getArgsSchema(args) { const properties = {}; const jsonSchema = { $async: true, type: 'object', properties, additionalProperties: true, }; for (const arg of args) { const directive = getDirective(this.schema, arg, this.directiveName); properties[arg.name] = this.getInputTypeSchema(arg.type, directive?.[0]); } return jsonSchema; } createValidator(args) { const schema = this.getArgsSchema(args); return this.ajv.compile(schema); } composeResolverForField(field) { if (field.args.length === 0) return; const validate = this.createValidator(field.args); const originalResolve = field.resolve; field.resolve = async function resolverWithValidator(...args) { await validate(args[1]); return originalResolve?.apply(this, args); }; } composeResolver(type) { if (!type) return; const fields = type.getFields(); for (const [, field] of Object.entries(fields)) { this.composeResolverForField(field); } } transformSchema() { const typeMap = this.schema.getTypeMap(); this.registerTypeSchemas(typeMap); const objectTypes = Object.values(typeMap).filter(isObjectType); for (const objectType of objectTypes) { this.composeResolver(objectType); } } } //# sourceMappingURL=GraphQLValidateDirective.js.map