graphql

Search for an npm package
import { didYouMean } from '../../jsutils/didYouMean.mjs';
import { inspect } from '../../jsutils/inspect.mjs';
import { keyMap } from '../../jsutils/keyMap.mjs';
import { suggestionList } from '../../jsutils/suggestionList.mjs';
import { GraphQLError } from '../../error/GraphQLError.mjs';
import { Kind } from '../../language/kinds.mjs';
import { print } from '../../language/printer.mjs';
import {
getNamedType,
getNullableType,
isInputObjectType,
isLeafType,
isListType,
isNonNullType,
isRequiredInputField,
} from '../../type/definition.mjs';
/**
* Value literals of correct type
*
* A GraphQL document is only valid if all value literals are of the type
* expected at their position.
*
* See https://spec.graphql.org/draft/#sec-Values-of-Correct-Type
*/
export function ValuesOfCorrectTypeRule(context) {
let variableDefinitions = {};
return {
OperationDefinition: {
enter() {
variableDefinitions = {};
},
},
VariableDefinition(definition) {
variableDefinitions[definition.variable.name.value] = definition;
},
ListValue(node) {
// Note: TypeInfo will traverse into a list's item type, so look to the
// parent input type to check if it is a list.
const type = getNullableType(context.getParentInputType());
if (!isListType(type)) {
isValidValueNode(context, node);
return false; // Don't traverse further.
}
},
ObjectValue(node) {
const type = getNamedType(context.getInputType());
if (!isInputObjectType(type)) {
isValidValueNode(context, node);
return false; // Don't traverse further.
} // Ensure every required field exists.
const fieldNodeMap = keyMap(node.fields, (field) => field.name.value);
for (const fieldDef of Object.values(type.getFields())) {
const fieldNode = fieldNodeMap[fieldDef.name];
if (!fieldNode && isRequiredInputField(fieldDef)) {
const typeStr = inspect(fieldDef.type);
context.reportError(
new GraphQLError(
`Field "${type.name}.${fieldDef.name}" of required type "${typeStr}" was not provided.`,
{
nodes: node,
},
),
);
}
}
if (type.isOneOf) {
validateOneOfInputObject(
context,
node,
type,
fieldNodeMap,
variableDefinitions,
);
}
},
ObjectField(node) {
const parentType = getNamedType(context.getParentInputType());
const fieldType = context.getInputType();
if (!fieldType && isInputObjectType(parentType)) {
const suggestions = suggestionList(
node.name.value,
Object.keys(parentType.getFields()),
);
context.reportError(
new GraphQLError(
`Field "${node.name.value}" is not defined by type "${parentType.name}".` +
didYouMean(suggestions),
{
nodes: node,
},
),
);
}
},
NullValue(node) {
const type = context.getInputType();
if (isNonNullType(type)) {
context.reportError(
new GraphQLError(
`Expected value of type "${inspect(type)}", found ${print(node)}.`,
{
nodes: node,
},
),
);
}
},
EnumValue: (node) => isValidValueNode(context, node),
IntValue: (node) => isValidValueNode(context, node),
FloatValue: (node) => isValidValueNode(context, node),
StringValue: (node) => isValidValueNode(context, node),
BooleanValue: (node) => isValidValueNode(context, node),
};
}
/**
* Any value literal may be a valid representation of a Scalar, depending on
* that scalar type.
*/
function isValidValueNode(context, node) {
// Report any error at the full type expected by the location.
const locationType = context.getInputType();
if (!locationType) {
return;
}
const type = getNamedType(locationType);
if (!isLeafType(type)) {
const typeStr = inspect(locationType);
context.reportError(
new GraphQLError(
`Expected value of type "${typeStr}", found ${print(node)}.`,
{
nodes: node,
},
),
);
return;
} // Scalars and Enums determine if a literal value is valid via parseLiteral(),
// which may throw or return an invalid value to indicate failure.
try {
const parseResult = type.parseLiteral(
node,
undefined,
/* variables */
);
if (parseResult === undefined) {
const typeStr = inspect(locationType);
context.reportError(
new GraphQLError(
`Expected value of type "${typeStr}", found ${print(node)}.`,
{
nodes: node,
},
),
);
}
} catch (error) {
const typeStr = inspect(locationType);
if (error instanceof GraphQLError) {
context.reportError(error);
} else {
context.reportError(
new GraphQLError(
`Expected value of type "${typeStr}", found ${print(node)}; ` +
error.message,
{
nodes: node,
originalError: error,
},
),
);
}
}
}
function validateOneOfInputObject(
context,
node,
type,
fieldNodeMap,
variableDefinitions,
) {
var _fieldNodeMap$keys$;
const keys = Object.keys(fieldNodeMap);
const isNotExactlyOneField = keys.length !== 1;
if (isNotExactlyOneField) {
context.reportError(
new GraphQLError(
`OneOf Input Object "${type.name}" must specify exactly one key.`,
{
nodes: [node],
},
),
);
return;
}
const value =
(_fieldNodeMap$keys$ = fieldNodeMap[keys[0]]) === null ||
_fieldNodeMap$keys$ === void 0
? void 0
: _fieldNodeMap$keys$.value;
const isNullLiteral = !value || value.kind === Kind.NULL;
const isVariable =
(value === null || value === void 0 ? void 0 : value.kind) ===
Kind.VARIABLE;
if (isNullLiteral) {
context.reportError(
new GraphQLError(`Field "${type.name}.${keys[0]}" must be non-null.`, {
nodes: [node],
}),
);
return;
}
if (isVariable) {
const variableName = value.name.value;
const definition = variableDefinitions[variableName];
const isNullableVariable = definition.type.kind !== Kind.NON_NULL_TYPE;
if (isNullableVariable) {
context.reportError(
new GraphQLError(
`Variable "${variableName}" must be non-nullable to be used for OneOf Input Object "${type.name}".`,
{
nodes: [node],
},
),
);
}
}
}