UNPKG

@apollo/federation-internals

Version:
590 lines 23.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.collectVariablesInValue = exports.argumentsFromAST = exports.isValidLeafValue = exports.valueFromASTUntyped = exports.valueFromAST = exports.isValidValueApplication = exports.isValidValue = exports.valueToAST = exports.valueNodeToConstValueNode = exports.withDefaultValues = exports.argumentsEquals = exports.valueEquals = exports.valueToString = void 0; const definitions_1 = require("./definitions"); const graphql_1 = require("graphql"); const suggestions_1 = require("./suggestions"); const util_1 = require("util"); const types_1 = require("./types"); const utils_1 = require("./utils"); const error_1 = require("./error"); const MAX_INT = 2147483647; const MIN_INT = -2147483648; function valueToString(v, expectedType) { if (v === undefined || v === null) { return "null"; } if (expectedType && (0, definitions_1.isNonNullType)(expectedType)) { return valueToString(v, expectedType.ofType); } if (expectedType && (0, definitions_1.isCustomScalarType)(expectedType)) { expectedType = undefined; } if ((0, definitions_1.isVariable)(v)) { return v.toString(); } if (Array.isArray(v)) { let elementsType = undefined; if (expectedType && (0, definitions_1.isListType)(expectedType)) { elementsType = expectedType.ofType; } return '[' + v.map(e => valueToString(e, elementsType)).join(', ') + ']'; } if (expectedType && (0, definitions_1.isListType)(expectedType)) { return valueToString(v, expectedType.ofType); } if (typeof v === 'object') { if (expectedType && !(0, definitions_1.isInputObjectType)(expectedType)) { expectedType = undefined; } return '{' + Object.keys(v).map(k => { var _a; const valueType = expectedType ? (_a = expectedType.field(k)) === null || _a === void 0 ? void 0 : _a.type : undefined; return `${k}: ${valueToString(v[k], valueType)}`; }).join(', ') + '}'; } if (typeof v === 'string') { if (expectedType) { if ((0, definitions_1.isEnumType)(expectedType)) { return expectedType.value(v) ? v : JSON.stringify(v); } if (expectedType === expectedType.schema().idType() && integerStringRegExp.test(v)) { return v; } } return JSON.stringify(v); } return String(v); } exports.valueToString = valueToString; function valueEquals(a, b) { if (a === b) { return true; } if (Array.isArray(a)) { return Array.isArray(b) && arrayValueEquals(a, b); } if (a !== null && typeof a === 'object') { return b !== null && typeof b === 'object' && objectEquals(a, b); } return a === b; } exports.valueEquals = valueEquals; function arrayValueEquals(a, b) { if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; ++i) { if (!valueEquals(a[i], b[i])) { return false; } } return true; } function objectEquals(a, b) { const keys1 = Object.keys(a); const keys2 = Object.keys(b); if (keys1.length != keys2.length) { return false; } for (const key of keys1) { const v1 = a[key]; const v2 = b[key]; if (v2 === undefined && !keys2.includes(key)) { return false; } if (!valueEquals(v1, v2)) { return false; } } return true; } function argumentsEquals(args1, args2) { if (args1 === args2) { return true; } return objectEquals(args1, args2); } exports.argumentsEquals = argumentsEquals; function buildError(message) { return new Error(message); } function applyDefaultValues(value, type) { if ((0, definitions_1.isVariable)(value)) { return value; } if (value === null) { if ((0, definitions_1.isNonNullType)(type)) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Invalid null value for non-null type ${type} while computing default values`); } return null; } if ((0, definitions_1.isNonNullType)(type)) { return applyDefaultValues(value, type.ofType); } if ((0, definitions_1.isListType)(type)) { if (Array.isArray(value)) { return value.map(v => applyDefaultValues(v, type.ofType)); } else { return applyDefaultValues(value, type.ofType); } } if ((0, definitions_1.isInputObjectType)(type)) { if (typeof value !== 'object') { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Expected value for type ${type} to be an object, but is ${typeof value}.`); } const updated = Object.create(null); for (const field of type.fields()) { if (!field.type) { throw buildError(`Cannot compute default value for field ${field.name} of ${type} as the field type is undefined`); } const fieldValue = value[field.name]; if (fieldValue === undefined) { if (field.defaultValue !== undefined) { updated[field.name] = applyDefaultValues(field.defaultValue, field.type); } else if (!(0, definitions_1.isNonNullType)(field.type)) { updated[field.name] = null; } else { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Required field "${field.name}" of type ${type} was not provided.`); } } else { updated[field.name] = applyDefaultValues(fieldValue, field.type); } } for (const fieldName of Object.keys(value)) { if (!type.field(fieldName)) { const suggestions = (0, suggestions_1.suggestionList)(fieldName, type.fields().map(f => f.name)); throw error_1.ERRORS.INVALID_GRAPHQL.err(`Field "${fieldName}" is not defined by type "${type}".` + (0, suggestions_1.didYouMean)(suggestions)); } } return updated; } return value; } function withDefaultValues(value, argument) { if (!argument.type) { throw buildError(`Cannot compute default value for argument ${argument} as the type is undefined`); } if (value === undefined) { if (argument.defaultValue !== undefined) { return applyDefaultValues(argument.defaultValue, argument.type); } else if (!(0, definitions_1.isNonNullType)(argument.type)) { return null; } else { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Required argument "${argument.coordinate}" was not provided.`); } } return applyDefaultValues(value, argument.type); } exports.withDefaultValues = withDefaultValues; const integerStringRegExp = /^-?(?:0|[1-9][0-9]*)$/; function objectFieldNodeToConst(field) { return { ...field, value: valueNodeToConstValueNode(field.value) }; } function valueNodeToConstValueNode(value) { if (value.kind === graphql_1.Kind.NULL || value.kind === graphql_1.Kind.INT || value.kind === graphql_1.Kind.FLOAT || value.kind === graphql_1.Kind.STRING || value.kind === graphql_1.Kind.BOOLEAN || value.kind === graphql_1.Kind.ENUM) { return value; } if (value.kind === graphql_1.Kind.LIST) { const constValues = value.values.map(v => valueNodeToConstValueNode(v)); return { ...value, values: constValues }; } if (value.kind === graphql_1.Kind.OBJECT) { const constFields = value.fields.map(f => objectFieldNodeToConst(f)); return { ...value, fields: constFields }; } if (value.kind === graphql_1.Kind.VARIABLE) { throw new Error('Unexpected VariableNode in const AST'); } (0, utils_1.assertUnreachable)(value); } exports.valueNodeToConstValueNode = valueNodeToConstValueNode; function valueToAST(value, type) { if (value === undefined) { return undefined; } if ((0, definitions_1.isNonNullType)(type)) { const astValue = valueToAST(value, type.ofType); if ((astValue === null || astValue === void 0 ? void 0 : astValue.kind) === graphql_1.Kind.NULL) { throw buildError(`Invalid null value ${valueToString(value)} for non-null type ${type}`); } return astValue; } if (value === null) { return { kind: graphql_1.Kind.NULL }; } if ((0, definitions_1.isVariable)(value)) { return { kind: graphql_1.Kind.VARIABLE, name: { kind: graphql_1.Kind.NAME, value: value.name } }; } if ((0, definitions_1.isCustomScalarType)(type)) { return valueToASTUntyped(value); } if ((0, definitions_1.isListType)(type)) { const itemType = type.ofType; const items = Array.from(value); if (items != null) { const valuesNodes = []; for (const item of items) { const itemNode = valueToAST(item, itemType); if (itemNode != null) { valuesNodes.push(itemNode); } } return { kind: graphql_1.Kind.LIST, values: valuesNodes }; } return valueToAST(value, itemType); } if ((0, definitions_1.isInputObjectType)(type)) { if (typeof value !== 'object') { throw buildError(`Invalid non-objet value for input type ${type}, cannot be converted to AST: ${(0, util_1.inspect)(value, true, 10, true)}`); } const fieldNodes = []; for (const field of type.fields()) { if (!field.type) { throw buildError(`Cannot convert value ${valueToString(value)} as field ${field} has no type set`); } const fieldValue = valueToAST(value[field.name], field.type); if (fieldValue) { fieldNodes.push({ kind: graphql_1.Kind.OBJECT_FIELD, name: { kind: graphql_1.Kind.NAME, value: field.name }, value: fieldValue, }); } } return { kind: graphql_1.Kind.OBJECT, fields: fieldNodes }; } if (typeof value === 'boolean') { return { kind: graphql_1.Kind.BOOLEAN, value: value }; } if (typeof value === 'number' && isFinite(value)) { const stringNum = String(value); return integerStringRegExp.test(stringNum) ? { kind: graphql_1.Kind.INT, value: stringNum } : { kind: graphql_1.Kind.FLOAT, value: stringNum }; } if (typeof value === 'string') { if ((0, definitions_1.isEnumType)(type)) { return { kind: graphql_1.Kind.ENUM, value: value }; } if (type === type.schema().idType() && integerStringRegExp.test(value)) { return { kind: graphql_1.Kind.INT, value: value }; } return { kind: graphql_1.Kind.STRING, value: value, }; } throw buildError(`Invalid value for type ${type}, cannot be converted to AST: ${(0, util_1.inspect)(value)}`); } exports.valueToAST = valueToAST; function valueToASTUntyped(value) { if (value === undefined) { return undefined; } if (value === null) { return { kind: graphql_1.Kind.NULL }; } if ((0, definitions_1.isVariable)(value)) { return { kind: graphql_1.Kind.VARIABLE, name: { kind: graphql_1.Kind.NAME, value: value.name } }; } if (Array.isArray(value)) { const valuesNodes = []; for (const item of value) { const itemNode = valueToASTUntyped(item); if (itemNode !== undefined) { valuesNodes.push(itemNode); } } return { kind: graphql_1.Kind.LIST, values: valuesNodes }; } if (typeof value === 'object') { const fieldNodes = []; for (const key of Object.keys(value)) { const fieldValue = valueToASTUntyped(value[key]); if (fieldValue) { fieldNodes.push({ kind: graphql_1.Kind.OBJECT_FIELD, name: { kind: graphql_1.Kind.NAME, value: key }, value: fieldValue, }); } } return { kind: graphql_1.Kind.OBJECT, fields: fieldNodes }; } if (typeof value === 'boolean') { return { kind: graphql_1.Kind.BOOLEAN, value: value }; } if (typeof value === 'number' && isFinite(value)) { const stringNum = String(value); return integerStringRegExp.test(stringNum) ? { kind: graphql_1.Kind.INT, value: stringNum } : { kind: graphql_1.Kind.FLOAT, value: stringNum }; } if (typeof value === 'string') { return { kind: graphql_1.Kind.STRING, value: value }; } throw buildError(`Invalid value, cannot be converted to AST: ${(0, util_1.inspect)(value, true, 10, true)}`); } function isValidVariable(variable, locationType, locationDefault) { const variableType = variable.type; if ((0, definitions_1.isNonNullType)(locationType) && !(0, definitions_1.isNonNullType)(variableType)) { const hasVariableDefault = variable.defaultValue !== undefined && variable.defaultValue !== null; const hasLocationDefault = locationDefault !== undefined; if (!hasVariableDefault && !hasLocationDefault) { return false; } return areTypesCompatible(variableType, locationType.ofType); } return areTypesCompatible(variableType, locationType); } function areTypesCompatible(variableType, locationType) { if ((0, definitions_1.isNonNullType)(locationType)) { if (!(0, definitions_1.isNonNullType)(variableType)) { return false; } return areTypesCompatible(variableType.ofType, locationType.ofType); } if ((0, definitions_1.isNonNullType)(variableType)) { return areTypesCompatible(variableType.ofType, locationType); } if ((0, definitions_1.isListType)(locationType)) { if (!(0, definitions_1.isListType)(variableType)) { return false; } return areTypesCompatible(variableType.ofType, locationType.ofType); } return !(0, definitions_1.isListType)(variableType) && (0, types_1.sameType)(variableType, locationType); } function isValidValue(value, argument, variableDefinitions) { return isValidValueApplication(value, argument.type, argument.defaultValue, variableDefinitions); } exports.isValidValue = isValidValue; function isValidValueApplication(value, locationType, locationDefault, variableDefinitions) { if ((0, definitions_1.isVariable)(value)) { const definition = variableDefinitions.definition(value); return !!definition && isValidVariable(definition, locationType, locationDefault); } if ((0, definitions_1.isNonNullType)(locationType)) { return value !== null && isValidValueApplication(value, locationType.ofType, undefined, variableDefinitions); } if (value === null || value === undefined) { return true; } if ((0, definitions_1.isListType)(locationType)) { const itemType = locationType.ofType; if (Array.isArray(value)) { return value.every(item => isValidValueApplication(item, itemType, undefined, variableDefinitions)); } return isValidValueApplication(value, itemType, locationDefault, variableDefinitions); } if ((0, definitions_1.isInputObjectType)(locationType)) { if (typeof value !== 'object') { return false; } const valueKeys = new Set(Object.keys(value)); const fieldsAreValid = locationType.fields().every(field => { valueKeys.delete(field.name); return isValidValueApplication(value[field.name], field.type, field.defaultValue, variableDefinitions); }); const hasUnexpectedField = valueKeys.size !== 0; return fieldsAreValid && !hasUnexpectedField; } return isValidLeafValue(locationType.schema(), value, locationType); } exports.isValidValueApplication = isValidValueApplication; function valueFromAST(node, expectedType) { if (node.kind === graphql_1.Kind.NULL) { if ((0, definitions_1.isNonNullType)(expectedType)) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Invalid null value for non-null type "${expectedType}"`); } return null; } if (node.kind === graphql_1.Kind.VARIABLE) { return new definitions_1.Variable(node.name.value); } if ((0, definitions_1.isNonNullType)(expectedType)) { expectedType = expectedType.ofType; } if ((0, definitions_1.isListType)(expectedType)) { const baseType = expectedType.ofType; if (node.kind === graphql_1.Kind.LIST) { return node.values.map(v => valueFromAST(v, baseType)); } return [valueFromAST(node, baseType)]; } if ((0, definitions_1.isIntType)(expectedType)) { if (node.kind !== graphql_1.Kind.INT) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Int cannot represent non-integer value ${(0, graphql_1.print)(node)}.`); } const i = parseInt(node.value, 10); if (i > MAX_INT || i < MIN_INT) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Int cannot represent non 32-bit signed integer value ${i}.`); } return i; } if ((0, definitions_1.isFloatType)(expectedType)) { let parsed; if (node.kind === graphql_1.Kind.INT) { parsed = parseInt(node.value, 10); } else if (node.kind === graphql_1.Kind.FLOAT) { parsed = parseFloat(node.value); } else { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Float can only represent integer or float value, but got a ${node.kind}.`); } if (!isFinite(parsed)) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Float cannot represent non numeric value ${parsed}.`); } return parsed; } if ((0, definitions_1.isBooleanType)(expectedType)) { if (node.kind !== graphql_1.Kind.BOOLEAN) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Boolean cannot represent a non boolean value ${(0, graphql_1.print)(node)}.`); } return node.value; } if ((0, definitions_1.isStringType)(expectedType)) { if (node.kind !== graphql_1.Kind.STRING) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`String cannot represent non string value ${(0, graphql_1.print)(node)}.`); } return node.value; } if ((0, definitions_1.isIDType)(expectedType)) { if (node.kind !== graphql_1.Kind.STRING && node.kind !== graphql_1.Kind.INT) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`ID cannot represent value ${(0, graphql_1.print)(node)}.`); } return node.value; } if ((0, definitions_1.isScalarType)(expectedType)) { return valueFromASTUntyped(node); } if ((0, definitions_1.isInputObjectType)(expectedType)) { if (node.kind !== graphql_1.Kind.OBJECT) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Input Object Type ${expectedType} cannot represent non-object value ${(0, graphql_1.print)(node)}.`); } const obj = Object.create(null); for (const f of node.fields) { const name = f.name.value; const field = expectedType.field(name); if (!field) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Unknown field "${name}" found in value for Input Object Type "${expectedType}".`); } obj[name] = valueFromAST(f.value, field.type); } return obj; } if ((0, definitions_1.isEnumType)(expectedType)) { if (node.kind !== graphql_1.Kind.STRING && node.kind !== graphql_1.Kind.ENUM) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Enum Type ${expectedType} cannot represent value ${(0, graphql_1.print)(node)}.`); } if (!expectedType.value(node.value)) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Enum Type ${expectedType} has no value ${node.value}.`); } return node.value; } (0, utils_1.assert)(false, () => `Unexpected input type ${expectedType} of kind ${expectedType.kind}.`); } exports.valueFromAST = valueFromAST; function valueFromASTUntyped(node) { switch (node.kind) { case graphql_1.Kind.NULL: return null; case graphql_1.Kind.INT: return parseInt(node.value, 10); case graphql_1.Kind.FLOAT: return parseFloat(node.value); case graphql_1.Kind.STRING: case graphql_1.Kind.ENUM: case graphql_1.Kind.BOOLEAN: return node.value; case graphql_1.Kind.LIST: return node.values.map(valueFromASTUntyped); case graphql_1.Kind.OBJECT: const obj = Object.create(null); node.fields.forEach(f => obj[f.name.value] = valueFromASTUntyped(f.value)); return obj; case graphql_1.Kind.VARIABLE: return new definitions_1.Variable(node.name.value); } } exports.valueFromASTUntyped = valueFromASTUntyped; function isValidLeafValue(schema, value, type) { if ((0, definitions_1.isCustomScalarType)(type)) { return true; } if (typeof value === 'boolean') { return type === schema.booleanType(); } if (typeof value === 'number' && isFinite(value)) { const stringNum = String(value); if (type === schema.intType() || type === schema.idType()) { return integerStringRegExp.test(stringNum); } return type === schema.floatType(); } if (typeof value === 'string') { if ((0, definitions_1.isEnumType)(type)) { return type.value(value) !== undefined; } return type !== schema.booleanType() && type !== schema.intType() && type !== schema.floatType(); } return false; } exports.isValidLeafValue = isValidLeafValue; function argumentsFromAST(context, args, argsDefiner) { var _a; if (!args || args.length === 0) { return undefined; } const values = Object.create(null); for (const argNode of args) { const name = argNode.name.value; const expectedType = (_a = argsDefiner.argument(name)) === null || _a === void 0 ? void 0 : _a.type; if (!expectedType) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Unknown argument "${name}" found in value: "${context}" has no argument named "${name}"`); } try { values[name] = valueFromAST(argNode.value, expectedType); } catch (e) { if (e instanceof graphql_1.GraphQLError) { throw error_1.ERRORS.INVALID_GRAPHQL.err(`Invalid value for argument "${name}": ${e.message}`); } throw e; } } return values; } exports.argumentsFromAST = argumentsFromAST; function collectVariablesInValue(value, collector) { if ((0, definitions_1.isVariable)(value)) { collector.add(value); return; } if (!value) { return; } if (Array.isArray(value)) { value.forEach(v => collectVariablesInValue(v, collector)); } if (typeof value === 'object') { Object.keys(value).forEach(k => collectVariablesInValue(value[k], collector)); } } exports.collectVariablesInValue = collectVariablesInValue; //# sourceMappingURL=values.js.map