graphql-validate-directive
Version:
A GraphQL directive for input validation
139 lines • 4.87 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GraphQLValidateDirective = void 0;
const utils_1 = require("@graphql-tools/utils");
const ajv_1 = __importDefault(require("ajv"));
const graphql_1 = require("graphql");
const pick_1 = __importDefault(require("lodash/pick"));
const constants_1 = require("./constants");
class GraphQLValidateDirective {
schema;
directiveName;
ajv;
constructor(schema, directiveName = constants_1.DefaultValidateDirectiveName, ajv = new ajv_1.default()) {
this.schema = schema;
this.directiveName = directiveName;
this.ajv = ajv;
}
getScalerTypeSchema(type, opt) {
if (type === graphql_1.GraphQLInt) {
return {
type: 'integer',
...(0, pick_1.default)(opt, constants_1.KeywordsForNumber),
};
}
if (type === graphql_1.GraphQLFloat) {
return {
type: 'number',
...(0, pick_1.default)(opt, constants_1.KeywordsForNumber),
};
}
if (type === graphql_1.GraphQLString) {
return {
type: 'string',
...(0, pick_1.default)(opt, constants_1.KeywordsForString),
};
}
return {};
}
getListTypeSchema(type, opt) {
return {
type: 'array',
...(0, pick_1.default)(opt, constants_1.KeywordsForArray),
items: this.getInputTypeSchema(type.ofType, opt),
};
}
getInputTypeSchema(type, opt) {
if ((0, graphql_1.isScalarType)(type) && opt) {
return this.getScalerTypeSchema(type, opt);
}
if ((0, graphql_1.isListType)(type) && opt) {
return this.getListTypeSchema(type, opt);
}
if ((0, graphql_1.isNonNullType)(type)) {
return this.getInputTypeSchema(type.ofType, opt);
}
if ((0, graphql_1.isInputObjectType)(type)) {
return {
$ref: this.getInputTypeRefName(type.name),
};
}
return {};
}
getInputTypeRefName(name) {
return `#/definitions/customInputType${name}`;
}
getTypeSchema(type) {
if (!(0, graphql_1.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 = (0, utils_1.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 = (0, utils_1.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(graphql_1.isObjectType);
for (const objectType of objectTypes) {
this.composeResolver(objectType);
}
}
}
exports.GraphQLValidateDirective = GraphQLValidateDirective;
//# sourceMappingURL=GraphQLValidateDirective.js.map