|
|
@@ -1,5 +1,11 @@
|
|
|
-import { Injectable } from '@nestjs/common';
|
|
|
-import { GqlModuleOptions, GqlOptionsFactory, GraphQLTypesLoader } from '@nestjs/graphql';
|
|
|
+import { DynamicModule, Injectable } from '@nestjs/common';
|
|
|
+import {
|
|
|
+ GqlModuleAsyncOptions,
|
|
|
+ GqlModuleOptions,
|
|
|
+ GqlOptionsFactory,
|
|
|
+ GraphQLModule,
|
|
|
+ GraphQLTypesLoader,
|
|
|
+} from '@nestjs/graphql';
|
|
|
import { GraphQLUpload } from 'apollo-server-core';
|
|
|
import { extendSchema, printSchema } from 'graphql';
|
|
|
import { GraphQLDateTime } from 'graphql-iso-date';
|
|
|
@@ -7,54 +13,75 @@ import GraphQLJSON from 'graphql-type-json';
|
|
|
import path from 'path';
|
|
|
|
|
|
import { notNullOrUndefined } from '../../../../shared/shared-utils';
|
|
|
+import { ConfigModule } from '../../config/config.module';
|
|
|
import { ConfigService } from '../../config/config.service';
|
|
|
+import { I18nModule } from '../../i18n/i18n.module';
|
|
|
import { I18nService } from '../../i18n/i18n.service';
|
|
|
import { TranslateErrorExtension } from '../middleware/translate-errors-extension';
|
|
|
|
|
|
import { addGraphQLCustomFields } from './graphql-custom-fields';
|
|
|
|
|
|
-@Injectable()
|
|
|
-export class GraphqlConfigService implements GqlOptionsFactory {
|
|
|
- readonly typePaths = ['type', 'admin-api', 'common'].map(p =>
|
|
|
- path.join(__dirname, '../schema', p, '**/*.graphql'),
|
|
|
- );
|
|
|
+export interface GraphQLApiOptions {
|
|
|
+ typePaths: string[];
|
|
|
+ apiPath: string;
|
|
|
+ // tslint:disable-next-line:ban-types
|
|
|
+ resolverModule: Function;
|
|
|
+}
|
|
|
|
|
|
- constructor(
|
|
|
- private i18nService: I18nService,
|
|
|
- private configService: ConfigService,
|
|
|
- private typesLoader: GraphQLTypesLoader,
|
|
|
- ) {}
|
|
|
+/**
|
|
|
+ * Dynamically generates a GraphQLModule according to the given config options.
|
|
|
+ */
|
|
|
+export function configureGraphQLModule(
|
|
|
+ getOptions: (configService: ConfigService) => GraphQLApiOptions,
|
|
|
+): DynamicModule {
|
|
|
+ return GraphQLModule.forRootAsync({
|
|
|
+ useFactory: (
|
|
|
+ configService: ConfigService,
|
|
|
+ i18nService: I18nService,
|
|
|
+ typesLoader: GraphQLTypesLoader,
|
|
|
+ ) => {
|
|
|
+ return createGraphQLOptions(i18nService, configService, typesLoader, getOptions(configService));
|
|
|
+ },
|
|
|
+ inject: [ConfigService, I18nService, GraphQLTypesLoader],
|
|
|
+ imports: [ConfigModule, I18nModule],
|
|
|
+ });
|
|
|
+}
|
|
|
|
|
|
- createGqlOptions(): GqlModuleOptions {
|
|
|
- // Prevent `Type "Node" is missing a "resolveType" resolver.` warnings.
|
|
|
- // See https://github.com/apollographql/apollo-server/issues/1075
|
|
|
- const dummyResolveType = {
|
|
|
- __resolveType() {
|
|
|
- return null;
|
|
|
- },
|
|
|
- };
|
|
|
+function createGraphQLOptions(
|
|
|
+ i18nService: I18nService,
|
|
|
+ configService: ConfigService,
|
|
|
+ typesLoader: GraphQLTypesLoader,
|
|
|
+ options: GraphQLApiOptions,
|
|
|
+): GqlModuleOptions {
|
|
|
+ // Prevent `Type "Node" is missing a "resolveType" resolver.` warnings.
|
|
|
+ // See https://github.com/apollographql/apollo-server/issues/1075
|
|
|
+ const dummyResolveType = {
|
|
|
+ __resolveType() {
|
|
|
+ return null;
|
|
|
+ },
|
|
|
+ };
|
|
|
|
|
|
- return {
|
|
|
- path: '/' + this.configService.apiPath,
|
|
|
- typeDefs: this.createTypeDefs(),
|
|
|
- resolvers: {
|
|
|
- JSON: GraphQLJSON,
|
|
|
- DateTime: GraphQLDateTime,
|
|
|
- Node: dummyResolveType,
|
|
|
- PaginatedList: dummyResolveType,
|
|
|
- Upload: GraphQLUpload || dummyResolveType,
|
|
|
- },
|
|
|
- uploads: {
|
|
|
- maxFileSize: this.configService.assetOptions.uploadMaxFileSize,
|
|
|
- },
|
|
|
- playground: true,
|
|
|
- debug: true,
|
|
|
- context: req => req,
|
|
|
- extensions: [() => new TranslateErrorExtension(this.i18nService)],
|
|
|
- // This is handled by the Express cors plugin
|
|
|
- cors: false,
|
|
|
- };
|
|
|
- }
|
|
|
+ return {
|
|
|
+ path: '/' + options.apiPath,
|
|
|
+ typeDefs: createTypeDefs(),
|
|
|
+ include: [options.resolverModule],
|
|
|
+ resolvers: {
|
|
|
+ JSON: GraphQLJSON,
|
|
|
+ DateTime: GraphQLDateTime,
|
|
|
+ Node: dummyResolveType,
|
|
|
+ PaginatedList: dummyResolveType,
|
|
|
+ Upload: GraphQLUpload || dummyResolveType,
|
|
|
+ },
|
|
|
+ uploads: {
|
|
|
+ maxFileSize: configService.assetOptions.uploadMaxFileSize,
|
|
|
+ },
|
|
|
+ playground: true,
|
|
|
+ debug: true,
|
|
|
+ context: req => req,
|
|
|
+ extensions: [() => new TranslateErrorExtension(i18nService)],
|
|
|
+ // This is handled by the Express cors plugin
|
|
|
+ cors: false,
|
|
|
+ };
|
|
|
|
|
|
/**
|
|
|
* Generates the server's GraphQL schema by combining:
|
|
|
@@ -62,11 +89,11 @@ export class GraphqlConfigService implements GqlOptionsFactory {
|
|
|
* 2. any custom fields defined in the config
|
|
|
* 3. any schema extensions defined by plugins
|
|
|
*/
|
|
|
- private createTypeDefs(): string {
|
|
|
- const customFields = this.configService.customFields;
|
|
|
- const typeDefs = this.typesLoader.mergeTypesByPaths(...this.typePaths);
|
|
|
+ function createTypeDefs(): string {
|
|
|
+ const customFields = configService.customFields;
|
|
|
+ const typeDefs = typesLoader.mergeTypesByPaths(...options.typePaths);
|
|
|
let schema = addGraphQLCustomFields(typeDefs, customFields);
|
|
|
- const pluginTypes = this.configService.plugins
|
|
|
+ const pluginTypes = configService.plugins
|
|
|
.map(p => (p.defineGraphQlTypes ? p.defineGraphQlTypes() : undefined))
|
|
|
.filter(notNullOrUndefined);
|
|
|
for (const types of pluginTypes) {
|