UNPKG

@envelop/extended-validation

Version:

Extended validation plugin adds support for writing GraphQL validation rules, that has access to all `execute` parameters, including variables.

76 lines (75 loc) 3.51 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OneOfInputObjectsRule = exports.ONE_OF_DIRECTIVE_SDL = void 0; const graphql_1 = require("graphql"); const utils_1 = require("@graphql-tools/utils"); const common_js_1 = require("../common.js"); exports.ONE_OF_DIRECTIVE_SDL = ` directive @oneOf on INPUT_OBJECT | FIELD_DEFINITION `; const OneOfInputObjectsRule = (validationContext, executionArgs) => { return { Field: node => { if (node.arguments?.length) { const fieldType = validationContext.getFieldDef(); if (!fieldType) { return; } const values = (0, utils_1.getArgumentValues)(fieldType, node, executionArgs.variableValues || undefined); const isOneOfFieldType = fieldType.extensions?.oneOf || (fieldType.astNode && (0, common_js_1.getDirectiveFromAstNode)(fieldType.astNode, 'oneOf')); if (isOneOfFieldType && Object.keys(values).length !== 1) { validationContext.reportError(new graphql_1.GraphQLError(`Exactly one key must be specified for input for field "${fieldType.type.toString()}.${node.name.value}"`, [node])); } for (const arg of node.arguments) { const argType = fieldType.args.find(typeArg => typeArg.name === arg.name.value); if (argType) { traverseVariables(validationContext, arg, argType.type, values[arg.name.value]); } } } }, }; }; exports.OneOfInputObjectsRule = OneOfInputObjectsRule; function getNonNullType(ttype) { if (ttype instanceof graphql_1.GraphQLNonNull) { return ttype.ofType; } return ttype; } function traverseVariables(validationContext, arg, graphqlType, currentValue) { // if the current value is empty we don't need to traverse deeper // if it shouldn't be empty, the "original" validation phase should complain. if (currentValue == null) { return; } const unwrappedType = getNonNullType(graphqlType); if ((0, graphql_1.isListType)(unwrappedType)) { if (!Array.isArray(currentValue)) { // because of graphql type coercion a single object should be treated as an array of one object currentValue = [currentValue]; } currentValue.forEach(value => { traverseVariables(validationContext, arg, unwrappedType.ofType, value); }); return; } if (typeof currentValue !== 'object' || currentValue == null) { // in case the value is not an object, the "original" validation phase should complain. return; } const inputType = (0, graphql_1.getNamedType)(graphqlType); const isOneOfInputType = inputType.extensions?.oneOf || (inputType.astNode && (0, common_js_1.getDirectiveFromAstNode)(inputType.astNode, 'oneOf')); if (isOneOfInputType && Object.keys(currentValue).length !== 1) { validationContext.reportError((0, utils_1.createGraphQLError)(`Exactly one key must be specified for input type "${inputType.name}"`, { nodes: [arg], })); } if (inputType instanceof graphql_1.GraphQLInputObjectType) { for (const [name, fieldConfig] of Object.entries(inputType.getFields())) { traverseVariables(validationContext, arg, fieldConfig.type, currentValue[name]); } } }