@apollo/subgraph
Version:
Apollo Subgraph Utilities
264 lines • 10.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildSchemaFromSDL = exports.addResolversToSchema = exports.modulesFromSDL = exports.isDocumentNode = exports.isNode = void 0;
const graphql_1 = require("graphql");
const validation_1 = require("graphql/validation");
const validate_1 = require("graphql/validation/validate");
const specifiedRules_1 = require("graphql/validation/specifiedRules");
const error_1 = require("./error");
function isNotNullOrUndefined(value) {
return value !== null && typeof value !== 'undefined';
}
function isNode(maybeNode) {
return maybeNode && typeof maybeNode.kind === "string";
}
exports.isNode = isNode;
function isDocumentNode(node) {
return isNode(node) && node.kind === graphql_1.Kind.DOCUMENT;
}
exports.isDocumentNode = isDocumentNode;
function mapValues(object, callback) {
const result = Object.create(null);
for (const [key, value] of Object.entries(object)) {
result[key] = callback(value);
}
return result;
}
const skippedSDLRules = [
validation_1.KnownTypeNamesRule,
validation_1.UniqueDirectivesPerLocationRule,
validation_1.PossibleTypeExtensionsRule,
];
const sdlRules = specifiedRules_1.specifiedSDLRules.filter(rule => !skippedSDLRules.includes(rule));
const extKindToDefKind = {
[graphql_1.Kind.SCALAR_TYPE_EXTENSION]: graphql_1.Kind.SCALAR_TYPE_DEFINITION,
[graphql_1.Kind.OBJECT_TYPE_EXTENSION]: graphql_1.Kind.OBJECT_TYPE_DEFINITION,
[graphql_1.Kind.INTERFACE_TYPE_EXTENSION]: graphql_1.Kind.INTERFACE_TYPE_DEFINITION,
[graphql_1.Kind.UNION_TYPE_EXTENSION]: graphql_1.Kind.UNION_TYPE_DEFINITION,
[graphql_1.Kind.ENUM_TYPE_EXTENSION]: graphql_1.Kind.ENUM_TYPE_DEFINITION,
[graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION]: graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION
};
function modulesFromSDL(modulesOrSDL) {
if (Array.isArray(modulesOrSDL)) {
return modulesOrSDL.map(moduleOrSDL => {
if (isNode(moduleOrSDL) && isDocumentNode(moduleOrSDL)) {
return { typeDefs: moduleOrSDL };
}
else {
return moduleOrSDL;
}
});
}
else {
return [{ typeDefs: modulesOrSDL }];
}
}
exports.modulesFromSDL = modulesFromSDL;
function addResolversToSchema(schema, resolvers) {
var _a, _b;
for (const [typeName, fieldConfigs] of Object.entries(resolvers)) {
const type = schema.getType(typeName);
if ((0, graphql_1.isAbstractType)(type)) {
const existingExtensions = type.extensions;
for (const [fieldName, fieldConfig] of Object.entries(fieldConfigs)) {
if (fieldName === '__resolveReference') {
type.extensions = {
...existingExtensions,
apollo: {
...existingExtensions.apollo,
subgraph: {
...(_a = existingExtensions.apollo) === null || _a === void 0 ? void 0 : _a.subgraph,
resolveReference: fieldConfig,
},
},
};
}
else if (fieldName === '__resolveType') {
type.resolveType = fieldConfig;
}
}
}
if ((0, graphql_1.isScalarType)(type)) {
for (const fn in fieldConfigs) {
const fnValue = fieldConfigs[fn];
if (fnValue !== undefined) {
type[fn] = fnValue;
}
}
}
if ((0, graphql_1.isEnumType)(type)) {
const values = type.getValues();
const newValues = {};
values.forEach(value => {
let newValue = fieldConfigs[value.name];
if (newValue === undefined) {
newValue = value.name;
}
newValues[value.name] = {
value: newValue,
deprecationReason: value.deprecationReason,
description: value.description,
astNode: value.astNode,
extensions: undefined
};
});
Object.assign(type, new graphql_1.GraphQLEnumType({
...type.toConfig(),
values: newValues
}));
}
if (!(0, graphql_1.isObjectType)(type))
continue;
const fieldMap = type.getFields();
const existingExtensions = type.extensions;
for (const [fieldName, fieldConfig] of Object.entries(fieldConfigs)) {
if (fieldName === '__resolveReference') {
type.extensions = {
...existingExtensions,
apollo: {
...existingExtensions.apollo,
subgraph: {
...(_b = existingExtensions.apollo) === null || _b === void 0 ? void 0 : _b.subgraph,
resolveReference: fieldConfig,
},
},
};
continue;
}
else if (fieldName === '__isTypeOf') {
type.isTypeOf = fieldConfig;
continue;
}
const field = fieldMap[fieldName];
if (!field)
continue;
if (typeof fieldConfig === "function") {
field.resolve = fieldConfig;
}
else {
field.resolve = fieldConfig.resolve;
field.subscribe = fieldConfig.subscribe;
}
}
}
}
exports.addResolversToSchema = addResolversToSchema;
function buildSchemaFromSDL(modulesOrSDL, schemaToExtend) {
const modules = modulesFromSDL(modulesOrSDL);
const documentAST = (0, graphql_1.concatAST)(modules.map(module => module.typeDefs));
const errors = (0, validate_1.validateSDL)(documentAST, schemaToExtend, sdlRules);
if (errors.length > 0) {
throw new error_1.GraphQLSchemaValidationError(errors);
}
const definitionsMap = Object.create(null);
const extensionsMap = Object.create(null);
const directiveDefinitions = [];
const schemaDefinitions = [];
const schemaExtensions = [];
const schemaDirectives = [];
let description;
for (const definition of documentAST.definitions) {
if ((0, graphql_1.isTypeDefinitionNode)(definition)) {
const typeName = definition.name.value;
if (definitionsMap[typeName]) {
definitionsMap[typeName].push(definition);
}
else {
definitionsMap[typeName] = [definition];
}
}
else if ((0, graphql_1.isTypeExtensionNode)(definition)) {
const typeName = definition.name.value;
if (extensionsMap[typeName]) {
extensionsMap[typeName].push(definition);
}
else {
extensionsMap[typeName] = [definition];
}
}
else if (definition.kind === graphql_1.Kind.DIRECTIVE_DEFINITION) {
directiveDefinitions.push(definition);
}
else if (definition.kind === graphql_1.Kind.SCHEMA_DEFINITION) {
schemaDefinitions.push(definition);
schemaDirectives.push(...(definition.directives ? definition.directives : []));
description = definition.description;
}
else if (definition.kind === graphql_1.Kind.SCHEMA_EXTENSION) {
schemaExtensions.push(definition);
schemaDirectives.push(...(definition.directives ? definition.directives : []));
}
}
let schema = schemaToExtend
? schemaToExtend
: new graphql_1.GraphQLSchema({
query: undefined
});
const missingTypeDefinitions = [];
for (const [extendedTypeName, extensions] of Object.entries(extensionsMap)) {
if (!definitionsMap[extendedTypeName]) {
const extension = extensions[0];
const kind = extension.kind;
const definition = {
kind: extKindToDefKind[kind],
name: extension.name
};
missingTypeDefinitions.push(definition);
}
}
schema = (0, graphql_1.extendSchema)(schema, {
kind: graphql_1.Kind.DOCUMENT,
definitions: [
...Object.values(definitionsMap).flat(),
...missingTypeDefinitions,
...directiveDefinitions
]
}, {
assumeValidSDL: true
});
schema = (0, graphql_1.extendSchema)(schema, {
kind: graphql_1.Kind.DOCUMENT,
definitions: Object.values(extensionsMap).flat(),
}, {
assumeValidSDL: true
});
let operationTypeMap;
const operationTypes = [...schemaDefinitions, ...schemaExtensions]
.map(node => node.operationTypes)
.filter(isNotNullOrUndefined)
.flat();
if (operationTypes.length > 0) {
operationTypeMap = {};
for (const { operation, type } of operationTypes) {
operationTypeMap[operation] = type.name.value;
}
}
else {
operationTypeMap = {
query: "Query",
mutation: "Mutation",
subscription: "Subscription"
};
}
schema = new graphql_1.GraphQLSchema({
...schema.toConfig(),
...mapValues(operationTypeMap, typeName => typeName
? schema.getType(typeName)
: undefined),
description: description === null || description === void 0 ? void 0 : description.value,
astNode: {
kind: graphql_1.Kind.SCHEMA_DEFINITION,
description,
directives: schemaDirectives,
operationTypes: []
}
});
for (const module of modules) {
if (!module.resolvers)
continue;
addResolversToSchema(schema, module.resolvers);
}
return schema;
}
exports.buildSchemaFromSDL = buildSchemaFromSDL;
//# sourceMappingURL=buildSchemaFromSDL.js.map