@apollo/federation-internals
Version:
Apollo Federation internal utilities
362 lines • 17.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.builtTypeReference = exports.buildSchemaFromAST = exports.buildSchema = void 0;
const graphql_1 = require("graphql");
const values_1 = require("./values");
const definitions_1 = require("./definitions");
const error_1 = require("./error");
const introspection_1 = require("./introspection");
function buildValue(value) {
return value ? (0, values_1.valueFromASTUntyped)(value) : undefined;
}
function buildSchema(source, options) {
return buildSchemaFromAST((0, graphql_1.parse)(source), options);
}
exports.buildSchema = buildSchema;
function buildSchemaFromAST(documentNode, options) {
var _a;
const errors = [];
const schema = new definitions_1.Schema(options === null || options === void 0 ? void 0 : options.blueprint);
const { directiveDefinitions, typeDefinitions, typeExtensions, schemaDefinitions, schemaExtensions, } = buildNamedTypeAndDirectivesShallow(documentNode, schema, errors);
for (const typeNode of typeDefinitions) {
if (typeNode.kind === graphql_1.Kind.ENUM_TYPE_DEFINITION) {
buildEnumTypeValuesWithoutDirectiveApplications(typeNode, schema.type(typeNode.name.value));
}
}
for (const typeExtensionNode of typeExtensions) {
if (typeExtensionNode.kind === graphql_1.Kind.ENUM_TYPE_EXTENSION) {
const toExtend = schema.type(typeExtensionNode.name.value);
const extension = toExtend.newExtension();
extension.sourceAST = typeExtensionNode;
buildEnumTypeValuesWithoutDirectiveApplications(typeExtensionNode, schema.type(typeExtensionNode.name.value), extension);
}
}
for (const directiveDefinitionNode of directiveDefinitions) {
buildDirectiveDefinitionInnerWithoutDirectiveApplications(directiveDefinitionNode, schema.directive(directiveDefinitionNode.name.value), errors);
}
for (const schemaDefinition of schemaDefinitions) {
buildSchemaDefinitionInner(schemaDefinition, schema.schemaDefinition, errors);
}
for (const schemaExtension of schemaExtensions) {
buildSchemaDefinitionInner(schemaExtension, schema.schemaDefinition, errors, schema.schemaDefinition.newExtension());
}
errors.push(...schema.blueprint.onDirectiveDefinitionAndSchemaParsed(schema));
for (const directiveDefinitionNode of directiveDefinitions) {
buildDirectiveApplicationsInDirectiveDefinition(directiveDefinitionNode, schema.directive(directiveDefinitionNode.name.value), errors);
}
for (const typeNode of typeDefinitions) {
buildNamedTypeInner(typeNode, schema.type(typeNode.name.value), schema.blueprint, errors);
}
for (const typeExtensionNode of typeExtensions) {
const toExtend = schema.type(typeExtensionNode.name.value);
const extension = toExtend.newExtension();
extension.sourceAST = typeExtensionNode;
buildNamedTypeInner(typeExtensionNode, toExtend, schema.blueprint, errors, extension);
}
if (errors.length > 0) {
throw (0, definitions_1.ErrGraphQLValidationFailed)(errors);
}
if ((_a = options === null || options === void 0 ? void 0 : options.validate) !== null && _a !== void 0 ? _a : true) {
schema.validate();
}
return schema;
}
exports.buildSchemaFromAST = buildSchemaFromAST;
function buildNamedTypeAndDirectivesShallow(documentNode, schema, errors) {
const directiveDefinitions = [];
const typeDefinitions = [];
const typeExtensions = [];
const schemaDefinitions = [];
const schemaExtensions = [];
for (const definitionNode of documentNode.definitions) {
switch (definitionNode.kind) {
case 'OperationDefinition':
case 'FragmentDefinition':
errors.push(error_1.ERRORS.INVALID_GRAPHQL.err("Invalid executable definition found while building schema", { nodes: definitionNode }));
continue;
case 'SchemaDefinition':
schemaDefinitions.push(definitionNode);
schema.schemaDefinition.preserveEmptyDefinition = true;
break;
case 'SchemaExtension':
schemaExtensions.push(definitionNode);
break;
case 'ScalarTypeDefinition':
case 'ObjectTypeDefinition':
case 'InterfaceTypeDefinition':
case 'UnionTypeDefinition':
case 'EnumTypeDefinition':
case 'InputObjectTypeDefinition':
if (introspection_1.introspectionTypeNames.includes(definitionNode.name.value)) {
continue;
}
typeDefinitions.push(definitionNode);
let type = schema.type(definitionNode.name.value);
if (!type || type.isBuiltIn) {
type = schema.addType((0, definitions_1.newNamedType)(withoutTrailingDefinition(definitionNode.kind), definitionNode.name.value));
}
else if (type.preserveEmptyDefinition) {
throw error_1.ERRORS.INVALID_GRAPHQL.err(`There can be only one type named "${definitionNode.name.value}"`);
}
type.preserveEmptyDefinition = true;
break;
case 'ScalarTypeExtension':
case 'ObjectTypeExtension':
case 'InterfaceTypeExtension':
case 'UnionTypeExtension':
case 'EnumTypeExtension':
case 'InputObjectTypeExtension':
if (introspection_1.introspectionTypeNames.includes(definitionNode.name.value)) {
continue;
}
typeExtensions.push(definitionNode);
const existing = schema.type(definitionNode.name.value);
if (!existing) {
schema.addType((0, definitions_1.newNamedType)(withoutTrailingDefinition(definitionNode.kind), definitionNode.name.value));
}
else if (existing.isBuiltIn) {
throw error_1.ERRORS.INVALID_GRAPHQL.err(`Cannot extend built-in type "${definitionNode.name.value}"`);
}
break;
case 'DirectiveDefinition':
directiveDefinitions.push(definitionNode);
schema.addDirectiveDefinition(definitionNode.name.value);
break;
}
}
return {
directiveDefinitions,
typeDefinitions,
typeExtensions,
schemaDefinitions,
schemaExtensions,
};
}
function withoutTrailingDefinition(str) {
const endString = str.endsWith('Definition') ? 'Definition' : 'Extension';
return str.slice(0, str.length - endString.length);
}
function getReferencedType(node, schema) {
const type = schema.type(node.name.value);
if (!type) {
throw error_1.ERRORS.INVALID_GRAPHQL.err(`Unknown type ${node.name.value}`, { nodes: node });
}
return type;
}
function withNodeAttachedToError(operation, node, errors) {
try {
operation();
}
catch (e) {
const causes = (0, error_1.errorCauses)(e);
if (causes) {
for (const cause of causes) {
const allNodes = cause.nodes ? [node, ...cause.nodes] : node;
errors.push((0, error_1.withModifiedErrorNodes)(cause, allNodes));
}
}
else {
throw e;
}
}
}
function buildSchemaDefinitionInner(schemaNode, schemaDefinition, errors, extension) {
var _a, _b;
for (const opTypeNode of (_a = schemaNode.operationTypes) !== null && _a !== void 0 ? _a : []) {
withNodeAttachedToError(() => schemaDefinition.setRoot(opTypeNode.operation, opTypeNode.type.name.value).setOfExtension(extension), opTypeNode, errors);
}
schemaDefinition.sourceAST = schemaNode;
if ('description' in schemaNode) {
schemaDefinition.description = (_b = schemaNode.description) === null || _b === void 0 ? void 0 : _b.value;
}
buildAppliedDirectives(schemaNode, schemaDefinition, errors, extension);
}
function buildAppliedDirectives(elementNode, element, errors, extension) {
var _a;
for (const directive of (_a = elementNode.directives) !== null && _a !== void 0 ? _a : []) {
withNodeAttachedToError(() => {
if (element !== element.schema().schemaDefinition || directive.name.value === 'link' || !element.schema().blueprint.applyDirectivesAfterParsing()) {
const d = element.applyDirective(directive.name.value, buildArgs(directive));
d.setOfExtension(extension);
d.sourceAST = directive;
}
else {
element.addUnappliedDirective({
extension,
directive,
args: buildArgs(directive),
nameOrDef: directive.name.value,
});
}
}, directive, errors);
}
}
function buildArgs(argumentsNode) {
var _a;
const args = Object.create(null);
for (const argNode of (_a = argumentsNode.arguments) !== null && _a !== void 0 ? _a : []) {
args[argNode.name.value] = buildValue(argNode.value);
}
return args;
}
function buildNamedTypeInner(definitionNode, type, blueprint, errors, extension) {
var _a, _b, _c, _d, _e;
switch (definitionNode.kind) {
case 'EnumTypeDefinition':
case 'EnumTypeExtension':
const enumType = type;
for (const enumVal of (_a = definitionNode.values) !== null && _a !== void 0 ? _a : []) {
buildAppliedDirectives(enumVal, enumType.value(enumVal.name.value), errors);
}
break;
case 'ObjectTypeDefinition':
case 'ObjectTypeExtension':
case 'InterfaceTypeDefinition':
case 'InterfaceTypeExtension':
const fieldBasedType = type;
for (const fieldNode of (_b = definitionNode.fields) !== null && _b !== void 0 ? _b : []) {
if (blueprint.ignoreParsedField(type, fieldNode.name.value)) {
continue;
}
const field = fieldBasedType.addField(fieldNode.name.value);
field.setOfExtension(extension);
buildFieldDefinitionInner(fieldNode, field, errors);
}
for (const itfNode of (_c = definitionNode.interfaces) !== null && _c !== void 0 ? _c : []) {
withNodeAttachedToError(() => {
const itfName = itfNode.name.value;
if (fieldBasedType.implementsInterface(itfName)) {
throw error_1.ERRORS.INVALID_GRAPHQL.err(`Type "${type}" can only implement "${itfName}" once.`);
}
fieldBasedType.addImplementedInterface(itfName).setOfExtension(extension);
}, itfNode, errors);
}
break;
case 'UnionTypeDefinition':
case 'UnionTypeExtension':
const unionType = type;
for (const namedType of (_d = definitionNode.types) !== null && _d !== void 0 ? _d : []) {
withNodeAttachedToError(() => {
const name = namedType.name.value;
if (unionType.hasTypeMember(name)) {
throw error_1.ERRORS.INVALID_GRAPHQL.err(`Union type "${unionType}" can only include type "${name}" once.`);
}
unionType.addType(name).setOfExtension(extension);
}, namedType, errors);
}
break;
case 'InputObjectTypeDefinition':
case 'InputObjectTypeExtension':
const inputObjectType = type;
for (const fieldNode of (_e = definitionNode.fields) !== null && _e !== void 0 ? _e : []) {
const field = inputObjectType.addField(fieldNode.name.value);
field.setOfExtension(extension);
buildInputFieldDefinitionInner(fieldNode, field, errors);
}
break;
}
buildAppliedDirectives(definitionNode, type, errors, extension);
buildDescriptionAndSourceAST(definitionNode, type);
}
function buildEnumTypeValuesWithoutDirectiveApplications(definitionNode, type, extension) {
var _a;
const enumType = type;
for (const enumVal of (_a = definitionNode.values) !== null && _a !== void 0 ? _a : []) {
const v = enumType.addValue(enumVal.name.value);
if (enumVal.description) {
v.description = enumVal.description.value;
}
v.setOfExtension(extension);
}
buildDescriptionAndSourceAST(definitionNode, type);
}
function buildDescriptionAndSourceAST(definitionNode, dest) {
if (definitionNode.description) {
dest.description = definitionNode.description.value;
}
dest.sourceAST = definitionNode;
}
function buildFieldDefinitionInner(fieldNode, field, errors) {
var _a, _b;
const type = buildTypeReferenceFromAST(fieldNode.type, field.schema());
field.type = validateOutputType(type, field.coordinate, fieldNode, errors);
for (const inputValueDef of (_a = fieldNode.arguments) !== null && _a !== void 0 ? _a : []) {
buildArgumentDefinitionInner(inputValueDef, field.addArgument(inputValueDef.name.value), errors, true);
}
buildAppliedDirectives(fieldNode, field, errors);
field.description = (_b = fieldNode.description) === null || _b === void 0 ? void 0 : _b.value;
field.sourceAST = fieldNode;
}
function validateOutputType(type, what, node, errors) {
if ((0, definitions_1.isOutputType)(type)) {
return type;
}
else {
errors.push(error_1.ERRORS.INVALID_GRAPHQL.err(`The type of "${what}" must be Output Type but got "${type}", a ${type.kind}.`, { nodes: node }));
return undefined;
}
}
function validateInputType(type, what, node, errors) {
if ((0, definitions_1.isInputType)(type)) {
return type;
}
else {
errors.push(error_1.ERRORS.INVALID_GRAPHQL.err(`The type of "${what}" must be Input Type but got "${type}", a ${type.kind}.`, { nodes: node }));
return undefined;
}
}
function builtTypeReference(encodedType, schema) {
return buildTypeReferenceFromAST((0, graphql_1.parseType)(encodedType), schema);
}
exports.builtTypeReference = builtTypeReference;
function buildTypeReferenceFromAST(typeNode, schema) {
switch (typeNode.kind) {
case graphql_1.Kind.LIST_TYPE:
return new definitions_1.ListType(buildTypeReferenceFromAST(typeNode.type, schema));
case graphql_1.Kind.NON_NULL_TYPE:
const wrapped = buildTypeReferenceFromAST(typeNode.type, schema);
if (wrapped.kind == graphql_1.Kind.NON_NULL_TYPE) {
throw error_1.ERRORS.INVALID_GRAPHQL.err(`Cannot apply the non-null operator (!) twice to the same type`, { nodes: typeNode });
}
return new definitions_1.NonNullType(wrapped);
default:
return getReferencedType(typeNode, schema);
}
}
function buildArgumentDefinitionInner(inputNode, arg, errors, includeDirectiveApplication) {
var _a;
const type = buildTypeReferenceFromAST(inputNode.type, arg.schema());
arg.type = validateInputType(type, arg.coordinate, inputNode, errors);
arg.defaultValue = buildValue(inputNode.defaultValue);
if (includeDirectiveApplication) {
buildAppliedDirectives(inputNode, arg, errors);
}
arg.description = (_a = inputNode.description) === null || _a === void 0 ? void 0 : _a.value;
arg.sourceAST = inputNode;
}
function buildInputFieldDefinitionInner(fieldNode, field, errors) {
var _a;
const type = buildTypeReferenceFromAST(fieldNode.type, field.schema());
field.type = validateInputType(type, field.coordinate, fieldNode, errors);
field.defaultValue = buildValue(fieldNode.defaultValue);
buildAppliedDirectives(fieldNode, field, errors);
field.description = (_a = fieldNode.description) === null || _a === void 0 ? void 0 : _a.value;
field.sourceAST = fieldNode;
}
function buildDirectiveDefinitionInnerWithoutDirectiveApplications(directiveNode, directive, errors) {
var _a;
for (const inputValueDef of (_a = directiveNode.arguments) !== null && _a !== void 0 ? _a : []) {
buildArgumentDefinitionInner(inputValueDef, directive.addArgument(inputValueDef.name.value), errors, false);
}
directive.repeatable = directiveNode.repeatable;
const locations = directiveNode.locations.map(({ value }) => value);
directive.addLocations(...locations);
buildDescriptionAndSourceAST(directiveNode, directive);
}
function buildDirectiveApplicationsInDirectiveDefinition(directiveNode, directive, errors) {
var _a;
for (const inputValueDef of (_a = directiveNode.arguments) !== null && _a !== void 0 ? _a : []) {
buildAppliedDirectives(inputValueDef, directive.argument(inputValueDef.name.value), errors);
}
}
//# sourceMappingURL=buildSchema.js.map