UNPKG

12.6 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.GraphQLFederationFactory = void 0;
4const tslib_1 = require("tslib");
5const schema_1 = require("@graphql-tools/schema");
6const utils_1 = require("@graphql-tools/utils");
7const common_1 = require("@nestjs/common");
8const load_package_util_1 = require("@nestjs/common/utils/load-package.util");
9const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
10const graphql_1 = require("graphql");
11const graphql_tag_1 = require("graphql-tag");
12const lodash_1 = require("lodash");
13const graphql_schema_builder_1 = require("../graphql-schema.builder");
14const services_1 = require("../services");
15const utils_2 = require("../utils");
16const transform_schema_util_1 = require("../utils/transform-schema.util");
17const type_defs_decorator_factory_1 = require("./type-defs-decorator.factory");
18const DEFAULT_FEDERATION_VERSION = 1;
19let GraphQLFederationFactory = class GraphQLFederationFactory {
20 constructor(resolversExplorerService, scalarsExplorerService, gqlSchemaBuilder, typeDefsDecoratorFactory) {
21 this.resolversExplorerService = resolversExplorerService;
22 this.scalarsExplorerService = scalarsExplorerService;
23 this.gqlSchemaBuilder = gqlSchemaBuilder;
24 this.typeDefsDecoratorFactory = typeDefsDecoratorFactory;
25 }
26 async generateSchema(options = {}, buildFederatedSchema) {
27 const transformSchema = options.transformSchema ?? ((schema) => schema);
28 let schema;
29 if (options.autoSchemaFile) {
30 schema = await this.generateSchemaFromCodeFirst(options, buildFederatedSchema);
31 }
32 else if ((0, lodash_1.isEmpty)(options.typeDefs)) {
33 schema = options.schema;
34 }
35 else {
36 schema = this.buildSchemaFromTypeDefs(options);
37 }
38 return await transformSchema(schema);
39 }
40 buildSchemaFromTypeDefs(options) {
41 const { buildSubgraphSchema } = (0, load_package_util_1.loadPackage)('@apollo/subgraph', 'ApolloFederation', () => require('@apollo/subgraph'));
42 const resolvers = this.getResolvers(options.resolvers);
43 return (0, schema_1.addResolversToSchema)({
44 resolverValidationOptions: options.resolverValidationOptions,
45 inheritResolversFromInterfaces: options.inheritResolversFromInterfaces,
46 resolvers,
47 schema: buildSubgraphSchema([
48 {
49 typeDefs: (0, graphql_tag_1.gql) `
50 ${options.typeDefs}
51 `,
52 resolvers,
53 },
54 ]),
55 });
56 }
57 async generateSchemaFromCodeFirst(options, buildFederatedSchema) {
58 const apolloSubgraph = (0, load_package_util_1.loadPackage)('@apollo/subgraph', 'ApolloFederation', () => require('@apollo/subgraph'));
59 const apolloSubgraphVersion = (await Promise.resolve().then(() => require('@apollo/subgraph/package.json'))).version;
60 const apolloSubgraphMajorVersion = Number(apolloSubgraphVersion.split('.')[0]);
61 const printSubgraphSchema = apolloSubgraph.printSubgraphSchema;
62 if (!buildFederatedSchema) {
63 buildFederatedSchema = apolloSubgraph.buildSubgraphSchema;
64 }
65 const autoGeneratedSchema = await this.buildFederatedSchema(options.autoSchemaFile, options, this.resolversExplorerService.getAllCtors());
66 let typeDefs = apolloSubgraphMajorVersion >= 2
67 ? (0, utils_1.printSchemaWithDirectives)(autoGeneratedSchema)
68 : printSubgraphSchema(autoGeneratedSchema);
69 const [federationVersion, federationOptions] = this.getFederationVersionAndConfig(options.autoSchemaFile);
70 const typeDefsDecorator = this.typeDefsDecoratorFactory.create(federationVersion, apolloSubgraphMajorVersion);
71 if (typeDefsDecorator) {
72 typeDefs = typeDefsDecorator.decorate(typeDefs, federationOptions);
73 }
74 let executableSchema = buildFederatedSchema({
75 typeDefs: (0, graphql_tag_1.gql)(typeDefs),
76 resolvers: this.getResolvers(options.resolvers),
77 });
78 executableSchema = this.overrideOrExtendResolvers(executableSchema, autoGeneratedSchema, printSubgraphSchema);
79 const schema = options.schema
80 ? (0, schema_1.mergeSchemas)({
81 schemas: [options.schema, executableSchema],
82 })
83 : executableSchema;
84 return schema;
85 }
86 getResolvers(optionResolvers) {
87 optionResolvers = Array.isArray(optionResolvers)
88 ? optionResolvers
89 : [optionResolvers];
90 return this.extendResolvers([
91 this.resolversExplorerService.explore(),
92 ...this.scalarsExplorerService.explore(),
93 ...optionResolvers,
94 ]);
95 }
96 extendResolvers(resolvers) {
97 return resolvers.reduce((prev, curr) => (0, utils_2.extend)(prev, curr), {});
98 }
99 overrideOrExtendResolvers(executableSchema, autoGeneratedSchema, printSchema) {
100 return (0, transform_schema_util_1.transformSchema)(executableSchema, (type) => {
101 if ((0, graphql_1.isUnionType)(type) && type.name !== '_Entity') {
102 return this.overrideFederatedResolveType(type, autoGeneratedSchema);
103 }
104 else if ((0, graphql_1.isInterfaceType)(type)) {
105 return this.overrideFederatedResolveType(type, autoGeneratedSchema);
106 }
107 else if ((0, graphql_1.isEnumType)(type)) {
108 return autoGeneratedSchema.getType(type.name);
109 }
110 else if ((0, graphql_1.isInputObjectType)(type)) {
111 const autoGeneratedInputType = autoGeneratedSchema.getType(type.name);
112 if (!autoGeneratedInputType) {
113 return type;
114 }
115 const fields = type.getFields();
116 (0, lodash_1.forEach)(fields, (value, key) => {
117 const field = autoGeneratedInputType.getFields()[key];
118 if (!field) {
119 return;
120 }
121 value.extensions = field.extensions;
122 value.astNode = field.astNode;
123 });
124 type.extensions = autoGeneratedInputType.extensions;
125 return type;
126 }
127 else if ((0, graphql_1.isObjectType)(type)) {
128 const autoGeneratedObjectType = autoGeneratedSchema.getType(type.name);
129 if (!autoGeneratedObjectType) {
130 return type;
131 }
132 const fields = type.getFields();
133 (0, lodash_1.forEach)(fields, (value, key) => {
134 const field = autoGeneratedObjectType.getFields()[key];
135 if (!field) {
136 return;
137 }
138 value.extensions = field.extensions;
139 value.astNode = field.astNode;
140 if (!value.resolve) {
141 value.resolve = field.resolve;
142 }
143 });
144 if (autoGeneratedObjectType.astNode) {
145 type.astNode = {
146 ...type.astNode,
147 ...autoGeneratedObjectType.astNode,
148 };
149 }
150 type.extensions = {
151 ...type.extensions,
152 ...autoGeneratedObjectType.extensions,
153 };
154 return type;
155 }
156 else if ((0, graphql_1.isScalarType)(type) && type.name === 'DateTime') {
157 const autoGeneratedScalar = autoGeneratedSchema.getType(type.name);
158 if (!autoGeneratedScalar) {
159 return type;
160 }
161 type.parseLiteral = autoGeneratedScalar.parseLiteral;
162 type.parseValue = autoGeneratedScalar.parseValue;
163 return type;
164 }
165 return type;
166 });
167 }
168 /**
169 * Ensures that the resolveType method for unions and interfaces in the federated schema
170 * is properly set from the one in the autoGeneratedSchema.
171 */
172 overrideFederatedResolveType(typeInFederatedSchema, autoGeneratedSchema) {
173 // Get the matching type from the auto generated schema
174 const autoGeneratedType = autoGeneratedSchema.getType(typeInFederatedSchema.name);
175 // Bail if inconsistent with original schema
176 if (!autoGeneratedType ||
177 !(autoGeneratedType instanceof graphql_1.GraphQLUnionType ||
178 autoGeneratedType instanceof graphql_1.GraphQLInterfaceType) ||
179 !autoGeneratedType.resolveType) {
180 return typeInFederatedSchema;
181 }
182 typeInFederatedSchema.resolveType = async (value, context, info, abstractType) => {
183 const resultFromAutogenSchema = await autoGeneratedType.resolveType(value, context, info, abstractType);
184 // If the result is not a GraphQLObjectType we're fine
185 if (!resultFromAutogenSchema || (0, shared_utils_1.isString)(resultFromAutogenSchema)) {
186 return resultFromAutogenSchema;
187 }
188 // We now have a GraphQLObjectType from the original union in the autogenerated schema.
189 // But we can't return that without the additional federation property apollo adds to object
190 // types (see node_modules/@apollo/federation/src/composition/types.ts:47).
191 // Without that property, Apollo will ignore the returned type and the
192 // union value will resolve to null. So we need to return the type with
193 // the same name from the federated schema
194 const resultFromFederatedSchema = info.schema.getType(resultFromAutogenSchema.name);
195 if (resultFromFederatedSchema &&
196 resultFromFederatedSchema instanceof graphql_1.GraphQLObjectType) {
197 return resultFromFederatedSchema;
198 }
199 // If we couldn't find a match in the federated schema, return just the
200 // name of the type and hope apollo works it out
201 return resultFromAutogenSchema;
202 };
203 return typeInFederatedSchema;
204 }
205 async buildFederatedSchema(autoSchemaFile, options, resolvers) {
206 const scalarsMap = this.scalarsExplorerService.getScalarsMap();
207 try {
208 const buildSchemaOptions = options.buildSchemaOptions || {};
209 const directives = [...graphql_1.specifiedDirectives];
210 const [federationVersion] = this.getFederationVersionAndConfig(autoSchemaFile);
211 if (federationVersion < 2) {
212 directives.push(...this.loadFederationDirectives());
213 }
214 if (buildSchemaOptions?.directives) {
215 directives.push(...buildSchemaOptions.directives);
216 }
217 return await this.gqlSchemaBuilder.generateSchema(resolvers, autoSchemaFile, {
218 ...buildSchemaOptions,
219 directives,
220 scalarsMap,
221 skipCheck: true,
222 }, options.sortSchema, options.transformAutoSchemaFile && options.transformSchema);
223 }
224 catch (err) {
225 if (err && err.details) {
226 console.error(err.details);
227 }
228 throw err;
229 }
230 }
231 getFederationVersionAndConfig(autoSchemaFile) {
232 if (!autoSchemaFile || typeof autoSchemaFile !== 'object') {
233 return [DEFAULT_FEDERATION_VERSION];
234 }
235 if (typeof autoSchemaFile.federation !== 'object') {
236 return [autoSchemaFile.federation ?? DEFAULT_FEDERATION_VERSION];
237 }
238 return [
239 autoSchemaFile.federation?.version ?? DEFAULT_FEDERATION_VERSION,
240 autoSchemaFile.federation,
241 ];
242 }
243 loadFederationDirectives() {
244 const { federationDirectives, directivesWithNoDefinitionNeeded } = (0, load_package_util_1.loadPackage)('@apollo/subgraph/dist/directives', 'SchemaBuilder', () => require('@apollo/subgraph/dist/directives'));
245 return federationDirectives ?? directivesWithNoDefinitionNeeded;
246 }
247};
248exports.GraphQLFederationFactory = GraphQLFederationFactory;
249exports.GraphQLFederationFactory = GraphQLFederationFactory = tslib_1.__decorate([
250 (0, common_1.Injectable)(),
251 tslib_1.__metadata("design:paramtypes", [services_1.ResolversExplorerService,
252 services_1.ScalarsExplorerService,
253 graphql_schema_builder_1.GraphQLSchemaBuilder,
254 type_defs_decorator_factory_1.TypeDefsDecoratorFactory])
255], GraphQLFederationFactory);