@nestjs/graphql
Version:
Nest - modern, fast, powerful node.js web framework (@graphql)
256 lines (255 loc) • 12.6 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.GraphQLFederationFactory = void 0;
const tslib_1 = require("tslib");
const schema_1 = require("@graphql-tools/schema");
const utils_1 = require("@graphql-tools/utils");
const common_1 = require("@nestjs/common");
const load_package_util_1 = require("@nestjs/common/utils/load-package.util");
const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
const graphql_1 = require("graphql");
const graphql_tag_1 = require("graphql-tag");
const lodash_1 = require("lodash");
const graphql_schema_builder_1 = require("../graphql-schema.builder");
const services_1 = require("../services");
const utils_2 = require("../utils");
const transform_schema_util_1 = require("../utils/transform-schema.util");
const type_defs_decorator_factory_1 = require("./type-defs-decorator.factory");
const DEFAULT_FEDERATION_VERSION = 1;
let GraphQLFederationFactory = class GraphQLFederationFactory {
constructor(resolversExplorerService, scalarsExplorerService, gqlSchemaBuilder, typeDefsDecoratorFactory) {
this.resolversExplorerService = resolversExplorerService;
this.scalarsExplorerService = scalarsExplorerService;
this.gqlSchemaBuilder = gqlSchemaBuilder;
this.typeDefsDecoratorFactory = typeDefsDecoratorFactory;
}
async generateSchema(options = {}, buildFederatedSchema) {
const transformSchema = options.transformSchema ?? ((schema) => schema);
let schema;
if (options.autoSchemaFile) {
schema = await this.generateSchemaFromCodeFirst(options, buildFederatedSchema);
}
else if ((0, lodash_1.isEmpty)(options.typeDefs)) {
schema = options.schema;
}
else {
schema = this.buildSchemaFromTypeDefs(options);
}
return await transformSchema(schema);
}
buildSchemaFromTypeDefs(options) {
const { buildSubgraphSchema } = (0, load_package_util_1.loadPackage)('@apollo/subgraph', 'ApolloFederation', () => require('@apollo/subgraph'));
const resolvers = this.getResolvers(options.resolvers);
return (0, schema_1.addResolversToSchema)({
resolverValidationOptions: options.resolverValidationOptions,
inheritResolversFromInterfaces: options.inheritResolversFromInterfaces,
resolvers,
schema: buildSubgraphSchema([
{
typeDefs: (0, graphql_tag_1.gql) `
${options.typeDefs}
`,
resolvers,
},
]),
});
}
async generateSchemaFromCodeFirst(options, buildFederatedSchema) {
const apolloSubgraph = (0, load_package_util_1.loadPackage)('@apollo/subgraph', 'ApolloFederation', () => require('@apollo/subgraph'));
const apolloSubgraphVersion = (await Promise.resolve().then(() => require('@apollo/subgraph/package.json'))).version;
const apolloSubgraphMajorVersion = Number(apolloSubgraphVersion.split('.')[0]);
const printSubgraphSchema = apolloSubgraph.printSubgraphSchema;
if (!buildFederatedSchema) {
buildFederatedSchema = apolloSubgraph.buildSubgraphSchema;
}
const autoGeneratedSchema = await this.buildFederatedSchema(options.autoSchemaFile, options, this.resolversExplorerService.getAllCtors());
let typeDefs = apolloSubgraphMajorVersion >= 2
? (0, utils_1.printSchemaWithDirectives)(autoGeneratedSchema)
: printSubgraphSchema(autoGeneratedSchema);
const [federationVersion, federationOptions] = this.getFederationVersionAndConfig(options.autoSchemaFile);
const typeDefsDecorator = this.typeDefsDecoratorFactory.create(federationVersion, apolloSubgraphMajorVersion);
if (typeDefsDecorator) {
typeDefs = typeDefsDecorator.decorate(typeDefs, federationOptions);
}
let executableSchema = buildFederatedSchema({
typeDefs: (0, graphql_tag_1.gql)(typeDefs),
resolvers: this.getResolvers(options.resolvers),
});
executableSchema = this.overrideOrExtendResolvers(executableSchema, autoGeneratedSchema, printSubgraphSchema);
const schema = options.schema
? (0, schema_1.mergeSchemas)({
schemas: [options.schema, executableSchema],
})
: executableSchema;
return schema;
}
getResolvers(optionResolvers) {
optionResolvers = Array.isArray(optionResolvers)
? optionResolvers
: [optionResolvers];
return this.extendResolvers([
this.resolversExplorerService.explore(),
...this.scalarsExplorerService.explore(),
...optionResolvers,
]);
}
extendResolvers(resolvers) {
return resolvers.reduce((prev, curr) => (0, utils_2.extend)(prev, curr), {});
}
overrideOrExtendResolvers(executableSchema, autoGeneratedSchema, printSchema) {
return (0, transform_schema_util_1.transformSchema)(executableSchema, (type) => {
if ((0, graphql_1.isUnionType)(type) && type.name !== '_Entity') {
return this.overrideFederatedResolveType(type, autoGeneratedSchema);
}
else if ((0, graphql_1.isInterfaceType)(type)) {
return this.overrideFederatedResolveType(type, autoGeneratedSchema);
}
else if ((0, graphql_1.isEnumType)(type)) {
return autoGeneratedSchema.getType(type.name);
}
else if ((0, graphql_1.isInputObjectType)(type)) {
const autoGeneratedInputType = autoGeneratedSchema.getType(type.name);
if (!autoGeneratedInputType) {
return type;
}
const fields = type.getFields();
(0, lodash_1.forEach)(fields, (value, key) => {
const field = autoGeneratedInputType.getFields()[key];
if (!field) {
return;
}
value.extensions = field.extensions;
value.astNode = field.astNode;
});
type.extensions = autoGeneratedInputType.extensions;
return type;
}
else if ((0, graphql_1.isObjectType)(type)) {
const autoGeneratedObjectType = autoGeneratedSchema.getType(type.name);
if (!autoGeneratedObjectType) {
return type;
}
const fields = type.getFields();
(0, lodash_1.forEach)(fields, (value, key) => {
const field = autoGeneratedObjectType.getFields()[key];
if (!field) {
return;
}
value.extensions = field.extensions;
value.astNode = field.astNode;
if (!value.resolve) {
value.resolve = field.resolve;
}
});
if (autoGeneratedObjectType.astNode) {
type.astNode = {
...type.astNode,
...autoGeneratedObjectType.astNode,
};
}
type.extensions = {
...type.extensions,
...autoGeneratedObjectType.extensions,
};
return type;
}
else if ((0, graphql_1.isScalarType)(type) && type.name === 'DateTime') {
const autoGeneratedScalar = autoGeneratedSchema.getType(type.name);
if (!autoGeneratedScalar) {
return type;
}
type.parseLiteral = autoGeneratedScalar.parseLiteral;
type.parseValue = autoGeneratedScalar.parseValue;
return type;
}
return type;
});
}
/**
* Ensures that the resolveType method for unions and interfaces in the federated schema
* is properly set from the one in the autoGeneratedSchema.
*/
overrideFederatedResolveType(typeInFederatedSchema, autoGeneratedSchema) {
// Get the matching type from the auto generated schema
const autoGeneratedType = autoGeneratedSchema.getType(typeInFederatedSchema.name);
// Bail if inconsistent with original schema
if (!autoGeneratedType ||
!(autoGeneratedType instanceof graphql_1.GraphQLUnionType ||
autoGeneratedType instanceof graphql_1.GraphQLInterfaceType) ||
!autoGeneratedType.resolveType) {
return typeInFederatedSchema;
}
typeInFederatedSchema.resolveType = async (value, context, info, abstractType) => {
const resultFromAutogenSchema = await autoGeneratedType.resolveType(value, context, info, abstractType);
// If the result is not a GraphQLObjectType we're fine
if (!resultFromAutogenSchema || (0, shared_utils_1.isString)(resultFromAutogenSchema)) {
return resultFromAutogenSchema;
}
// We now have a GraphQLObjectType from the original union in the autogenerated schema.
// But we can't return that without the additional federation property apollo adds to object
// types (see node_modules/@apollo/federation/src/composition/types.ts:47).
// Without that property, Apollo will ignore the returned type and the
// union value will resolve to null. So we need to return the type with
// the same name from the federated schema
const resultFromFederatedSchema = info.schema.getType(resultFromAutogenSchema.name);
if (resultFromFederatedSchema &&
resultFromFederatedSchema instanceof graphql_1.GraphQLObjectType) {
return resultFromFederatedSchema;
}
// If we couldn't find a match in the federated schema, return just the
// name of the type and hope apollo works it out
return resultFromAutogenSchema;
};
return typeInFederatedSchema;
}
async buildFederatedSchema(autoSchemaFile, options, resolvers) {
const scalarsMap = this.scalarsExplorerService.getScalarsMap();
try {
const buildSchemaOptions = options.buildSchemaOptions || {};
const directives = [...graphql_1.specifiedDirectives];
const [federationVersion] = this.getFederationVersionAndConfig(autoSchemaFile);
if (federationVersion < 2) {
directives.push(...this.loadFederationDirectives());
}
if (buildSchemaOptions?.directives) {
directives.push(...buildSchemaOptions.directives);
}
return await this.gqlSchemaBuilder.generateSchema(resolvers, autoSchemaFile, {
...buildSchemaOptions,
directives,
scalarsMap,
skipCheck: true,
}, options.sortSchema, options.transformAutoSchemaFile && options.transformSchema);
}
catch (err) {
if (err && err.details) {
console.error(err.details);
}
throw err;
}
}
getFederationVersionAndConfig(autoSchemaFile) {
if (!autoSchemaFile || typeof autoSchemaFile !== 'object') {
return [DEFAULT_FEDERATION_VERSION];
}
if (typeof autoSchemaFile.federation !== 'object') {
return [autoSchemaFile.federation ?? DEFAULT_FEDERATION_VERSION];
}
return [
autoSchemaFile.federation?.version ?? DEFAULT_FEDERATION_VERSION,
autoSchemaFile.federation,
];
}
loadFederationDirectives() {
const { federationDirectives, directivesWithNoDefinitionNeeded } = (0, load_package_util_1.loadPackage)('@apollo/subgraph/dist/directives', 'SchemaBuilder', () => require('@apollo/subgraph/dist/directives'));
return federationDirectives ?? directivesWithNoDefinitionNeeded;
}
};
exports.GraphQLFederationFactory = GraphQLFederationFactory;
exports.GraphQLFederationFactory = GraphQLFederationFactory = tslib_1.__decorate([
(0, common_1.Injectable)(),
tslib_1.__metadata("design:paramtypes", [services_1.ResolversExplorerService,
services_1.ScalarsExplorerService,
graphql_schema_builder_1.GraphQLSchemaBuilder,
type_defs_decorator_factory_1.TypeDefsDecoratorFactory])
], GraphQLFederationFactory);
;