Răsfoiți Sursa

refactor(core): Refactor internals to better separate exported symbols

Michael Bromley 10 luni în urmă
părinte
comite
46bf67ebf8

+ 1 - 8
packages/core/src/api/api.module.ts

@@ -1,6 +1,5 @@
 import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
 import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
-import path from 'path';
 
 import { ConfigService } from '../config/config.service';
 import { ConnectionModule } from '../connection/connection.module';
@@ -10,19 +9,13 @@ import { ServiceModule } from '../service/service.module';
 
 import { AdminApiModule, ApiSharedModule, ShopApiModule } from './api-internal-modules';
 import { configureGraphQLModule } from './config/configure-graphql-module';
+import { VENDURE_ADMIN_API_TYPE_PATHS, VENDURE_SHOP_API_TYPE_PATHS } from './constants';
 import { AuthGuard } from './middleware/auth-guard';
 import { ExceptionLoggerFilter } from './middleware/exception-logger.filter';
 import { IdInterceptor } from './middleware/id-interceptor';
 import { TranslateErrorResultInterceptor } from './middleware/translate-error-result-interceptor';
 import { ValidateCustomFieldsInterceptor } from './middleware/validate-custom-fields-interceptor';
 
-export const VENDURE_SHOP_API_TYPE_PATHS = ['shop-api', 'common'].map(p =>
-    path.join(__dirname, 'schema', p, '*.graphql'),
-);
-export const VENDURE_ADMIN_API_TYPE_PATHS = ['admin-api', 'common'].map(p =>
-    path.join(__dirname, 'schema', p, '*.graphql'),
-);
-
 /**
  * The ApiModule is responsible for the public API of the application. This is where requests
  * come in, are parsed and then handed over to the ServiceModule classes which take care

+ 2 - 116
packages/core/src/api/config/configure-graphql-module.ts

@@ -1,56 +1,22 @@
 import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
 import { DynamicModule } from '@nestjs/common';
 import { GraphQLModule, GraphQLTypesLoader } from '@nestjs/graphql';
-import { notNullOrUndefined } from '@vendure/common/lib/shared-utils';
-import {
-    buildClientSchema,
-    buildSchema,
-    extendSchema,
-    GraphQLSchema,
-    IntrospectionQuery,
-    IntrospectionSchema,
-    printIntrospectionSchema,
-    printSchema,
-    ValidationContext,
-} from 'graphql';
-import path from 'path';
+import { GraphQLSchema, printSchema, ValidationContext } from 'graphql';
 
 import { ConfigModule } from '../../config/config.module';
 import { ConfigService } from '../../config/config.service';
-import {
-    AutoIncrementIdStrategy,
-    EntityIdStrategy,
-    RuntimeVendureConfig,
-    UuidIdStrategy,
-} from '../../config/index';
 import { I18nModule } from '../../i18n/i18n.module';
 import { I18nService } from '../../i18n/i18n.service';
-import { getPluginAPIExtensions } from '../../plugin/plugin-metadata';
 import { ServiceModule } from '../../service/service.module';
 import { ApiSharedModule } from '../api-internal-modules';
 import { CustomFieldRelationResolverService } from '../common/custom-field-relation-resolver.service';
-import { ApiType } from '../common/get-api-type';
 import { IdCodecService } from '../common/id-codec.service';
 import { AssetInterceptorPlugin } from '../middleware/asset-interceptor-plugin';
 import { IdCodecPlugin } from '../middleware/id-codec-plugin';
 import { TranslateErrorsPlugin } from '../middleware/translate-errors-plugin';
 
-import { generateActiveOrderTypes } from './generate-active-order-types';
-import { generateAuthenticationTypes } from './generate-auth-types';
-import { generateErrorCodeEnum } from './generate-error-code-enum';
-import { generateListOptions } from './generate-list-options';
-import { generatePermissionEnum } from './generate-permissions';
 import { generateResolvers } from './generate-resolvers';
-import {
-    addActiveAdministratorCustomFields,
-    addGraphQLCustomFields,
-    addModifyOrderCustomFields,
-    addOrderLineCustomFieldsInput,
-    addPaymentMethodQuoteCustomFields,
-    addRegisterCustomerCustomFieldsInput,
-    addServerConfigCustomFields,
-    addShippingMethodQuoteCustomFields,
-} from './graphql-custom-fields';
+import { getFinalVendureSchema, isUsingDefaultEntityIdStrategy } from './get-final-vendure-schema';
 
 export interface GraphQLApiOptions {
     apiType: 'shop' | 'admin';
@@ -167,83 +133,3 @@ async function createGraphQLOptions(
         });
     }
 }
-
-type GetSchemaOptions = {
-    config: RuntimeVendureConfig;
-    typePaths: string[];
-    typesLoader: GraphQLTypesLoader;
-    apiType: 'shop' | 'admin';
-};
-type GetSchemaAsSDLOptions = GetSchemaOptions & { output: 'sdl' };
-
-export async function getFinalVendureSchema(options: GetSchemaOptions): Promise<GraphQLSchema>;
-export async function getFinalVendureSchema(options: GetSchemaAsSDLOptions): Promise<string>;
-export async function getFinalVendureSchema(
-    options: GetSchemaOptions & { output?: 'sdl' },
-): Promise<GraphQLSchema | string> {
-    const { config, typePaths, typesLoader, apiType } = options;
-    // Paths must be normalized to use forward-slash separators.
-    // See https://github.com/nestjs/graphql/issues/336
-    const normalizedPaths = typePaths.map(p => p.split(path.sep).join('/'));
-    const typeDefs = await typesLoader.mergeTypesByPaths(normalizedPaths);
-    let schema = buildSchema(typeDefs);
-    schema = buildSchemaFromVendureConfig(schema, config, apiType);
-    if (options.output === 'sdl') {
-        return printSchema(schema);
-    } else {
-        return schema;
-    }
-}
-
-export function buildSchemaFromVendureConfig(
-    schema: GraphQLSchema,
-    config: RuntimeVendureConfig,
-    apiType: 'shop' | 'admin',
-): GraphQLSchema {
-    const authStrategies =
-        apiType === 'shop'
-            ? config.authOptions.shopAuthenticationStrategy
-            : config.authOptions.adminAuthenticationStrategy;
-
-    const customFields = config.customFields;
-
-    schema = extendSchemaWithPluginApiExtensions(schema, config.plugins, apiType);
-    schema = generateListOptions(schema);
-    schema = addGraphQLCustomFields(schema, customFields, apiType === 'shop');
-    schema = addOrderLineCustomFieldsInput(schema, customFields.OrderLine || [], apiType === 'shop');
-    schema = addModifyOrderCustomFields(schema, customFields.Order || []);
-    schema = addShippingMethodQuoteCustomFields(schema, customFields.ShippingMethod || []);
-    schema = addPaymentMethodQuoteCustomFields(schema, customFields.PaymentMethod || []);
-    schema = generateAuthenticationTypes(schema, authStrategies);
-    schema = generateErrorCodeEnum(schema);
-    if (apiType === 'admin') {
-        schema = addServerConfigCustomFields(schema, customFields);
-        schema = addActiveAdministratorCustomFields(schema, customFields.Administrator);
-    }
-    if (apiType === 'shop') {
-        schema = addRegisterCustomerCustomFieldsInput(schema, customFields.Customer || []);
-        schema = generateActiveOrderTypes(schema, config.orderOptions.activeOrderStrategy);
-    }
-    schema = generatePermissionEnum(schema, config.authOptions.customPermissions);
-
-    return schema;
-}
-
-function extendSchemaWithPluginApiExtensions(
-    schema: GraphQLSchema,
-    plugins: RuntimeVendureConfig['plugins'],
-    apiType: 'admin' | 'shop',
-) {
-    getPluginAPIExtensions(plugins, apiType)
-        .map(e => (typeof e.schema === 'function' ? e.schema() : e.schema))
-        .filter(notNullOrUndefined)
-        .forEach(documentNode => (schema = extendSchema(schema, documentNode)));
-    return schema;
-}
-
-function isUsingDefaultEntityIdStrategy(entityIdStrategy: EntityIdStrategy<any>): boolean {
-    return (
-        entityIdStrategy.constructor === AutoIncrementIdStrategy ||
-        entityIdStrategy.constructor === UuidIdStrategy
-    );
-}

+ 138 - 0
packages/core/src/api/config/get-final-vendure-schema.ts

@@ -0,0 +1,138 @@
+import { GraphQLTypesLoader } from '@nestjs/graphql';
+import { notNullOrUndefined } from '@vendure/common/lib/shared-utils';
+import { buildSchema, extendSchema, GraphQLSchema, printSchema } from 'graphql/index';
+import path from 'path';
+
+import {
+    AutoIncrementIdStrategy,
+    EntityIdStrategy,
+    RuntimeVendureConfig,
+    UuidIdStrategy,
+} from '../../config/index';
+import { getPluginAPIExtensions } from '../../plugin/plugin-metadata';
+
+import { generateActiveOrderTypes } from './generate-active-order-types';
+import { generateAuthenticationTypes } from './generate-auth-types';
+import { generateErrorCodeEnum } from './generate-error-code-enum';
+import { generateListOptions } from './generate-list-options';
+import { generatePermissionEnum } from './generate-permissions';
+import {
+    addActiveAdministratorCustomFields,
+    addGraphQLCustomFields,
+    addModifyOrderCustomFields,
+    addOrderLineCustomFieldsInput,
+    addPaymentMethodQuoteCustomFields,
+    addRegisterCustomerCustomFieldsInput,
+    addServerConfigCustomFields,
+    addShippingMethodQuoteCustomFields,
+} from './graphql-custom-fields';
+
+type GetSchemaOptions = {
+    config: RuntimeVendureConfig;
+    typePaths: string[];
+    typesLoader: GraphQLTypesLoader;
+    apiType: 'shop' | 'admin';
+};
+type GetSchemaAsSDLOptions = GetSchemaOptions & { output: 'sdl' };
+
+/**
+ * @description
+ * Build the full Vendure GraphQL schema, including any custom fields, API extensions,
+ * custom scalars etc. defined in plugins.
+ *
+ * By setting the `output` option to `'sdl'`, the schema can be output as an SDL string.
+ * This is safe to use in scenarios where there might be multiple versions of
+ * the `graphql` package installed, where otherwise passing around the actual
+ * `GraphQLSchema` object might lead to conflicts.
+ *
+ * @example
+ * ```ts
+ * import { getFinalVendureSchema, VENDURE_ADMIN_API_TYPE_PATHS } from '\@vendure/core';
+ * import { GraphQLTypesLoader } from '@nestjs/graphql';
+ *
+ * import { config } from './vendure-config';
+ *
+ * const typesLoader = new GraphQLTypesLoader();
+ *
+ * const finalSchema = await getFinalVendureSchema({
+ *     config,
+ *     typePaths: VENDURE_ADMIN_API_TYPE_PATHS,
+ *     typesLoader,
+ *     apiType: 'admin',
+ *     output: 'sdl',
+ * });
+ * ```
+ *
+ * @param options
+ */
+export async function getFinalVendureSchema(options: GetSchemaOptions): Promise<GraphQLSchema>;
+export async function getFinalVendureSchema(options: GetSchemaAsSDLOptions): Promise<string>;
+export async function getFinalVendureSchema(
+    options: GetSchemaOptions & { output?: 'sdl' },
+): Promise<GraphQLSchema | string> {
+    const { config, typePaths, typesLoader, apiType } = options;
+    // Paths must be normalized to use forward-slash separators.
+    // See https://github.com/nestjs/graphql/issues/336
+    const normalizedPaths = typePaths.map(p => p.split(path.sep).join('/'));
+    const typeDefs = await typesLoader.mergeTypesByPaths(normalizedPaths);
+    let schema = buildSchema(typeDefs);
+    schema = buildSchemaFromVendureConfig(schema, config, apiType);
+    if (options.output === 'sdl') {
+        return printSchema(schema);
+    } else {
+        return schema;
+    }
+}
+
+export function buildSchemaFromVendureConfig(
+    schema: GraphQLSchema,
+    config: RuntimeVendureConfig,
+    apiType: 'shop' | 'admin',
+): GraphQLSchema {
+    const authStrategies =
+        apiType === 'shop'
+            ? config.authOptions.shopAuthenticationStrategy
+            : config.authOptions.adminAuthenticationStrategy;
+
+    const customFields = config.customFields;
+
+    schema = extendSchemaWithPluginApiExtensions(schema, config.plugins, apiType);
+    schema = generateListOptions(schema);
+    schema = addGraphQLCustomFields(schema, customFields, apiType === 'shop');
+    schema = addOrderLineCustomFieldsInput(schema, customFields.OrderLine || [], apiType === 'shop');
+    schema = addModifyOrderCustomFields(schema, customFields.Order || []);
+    schema = addShippingMethodQuoteCustomFields(schema, customFields.ShippingMethod || []);
+    schema = addPaymentMethodQuoteCustomFields(schema, customFields.PaymentMethod || []);
+    schema = generateAuthenticationTypes(schema, authStrategies);
+    schema = generateErrorCodeEnum(schema);
+    if (apiType === 'admin') {
+        schema = addServerConfigCustomFields(schema, customFields);
+        schema = addActiveAdministratorCustomFields(schema, customFields.Administrator);
+    }
+    if (apiType === 'shop') {
+        schema = addRegisterCustomerCustomFieldsInput(schema, customFields.Customer || []);
+        schema = generateActiveOrderTypes(schema, config.orderOptions.activeOrderStrategy);
+    }
+    schema = generatePermissionEnum(schema, config.authOptions.customPermissions);
+
+    return schema;
+}
+
+function extendSchemaWithPluginApiExtensions(
+    schema: GraphQLSchema,
+    plugins: RuntimeVendureConfig['plugins'],
+    apiType: 'admin' | 'shop',
+) {
+    getPluginAPIExtensions(plugins, apiType)
+        .map(e => (typeof e.schema === 'function' ? e.schema() : e.schema))
+        .filter(notNullOrUndefined)
+        .forEach(documentNode => (schema = extendSchema(schema, documentNode)));
+    return schema;
+}
+
+export function isUsingDefaultEntityIdStrategy(entityIdStrategy: EntityIdStrategy<any>): boolean {
+    return (
+        entityIdStrategy.constructor === AutoIncrementIdStrategy ||
+        entityIdStrategy.constructor === UuidIdStrategy
+    );
+}

+ 9 - 0
packages/core/src/api/constants.ts

@@ -0,0 +1,9 @@
+import path from 'path';
+
+export const VENDURE_SHOP_API_TYPE_PATHS = ['shop-api', 'common'].map(p =>
+    path.join(__dirname, 'schema', p, '*.graphql'),
+);
+
+export const VENDURE_ADMIN_API_TYPE_PATHS = ['admin-api', 'common'].map(p =>
+    path.join(__dirname, 'schema', p, '*.graphql'),
+);

+ 2 - 2
packages/core/src/api/index.ts

@@ -1,8 +1,8 @@
 export { ApiType } from './common/get-api-type';
-export * from './api.module';
+export * from './constants';
 export * from './common/request-context';
 export * from './common/extract-session-token';
-export * from './config/configure-graphql-module';
+export * from './config/get-final-vendure-schema';
 export * from './decorators/allow.decorator';
 export * from './decorators/transaction.decorator';
 export * from './decorators/api.decorator';

+ 0 - 15
packages/core/src/plugin/dynamic-plugin-api.module.ts

@@ -33,21 +33,6 @@ export function createDynamicGraphQlModulesForPlugins(apiType: 'shop' | 'admin')
         .filter(notNullOrUndefined);
 }
 
-/**
- * This function retrieves any dynamic modules which were created with createDynamicGraphQlModulesForPlugins.
- */
-export function getDynamicGraphQlModulesForPlugins(apiType: 'shop' | 'admin'): Array<Type<any>> {
-    return getConfig()
-        .plugins.map(plugin => {
-            const pluginModule = isDynamicModule(plugin) ? plugin.module : plugin;
-            const resolvers = graphQLResolversFor(plugin, apiType) || [];
-
-            const className = dynamicClassName(pluginModule, apiType);
-            return dynamicApiModuleClassMap[className];
-        })
-        .filter(notNullOrUndefined);
-}
-
 function dynamicClassName(module: Type<any>, apiType: 'shop' | 'admin'): string {
     return module.name + 'Dynamic' + (apiType === 'shop' ? 'Shop' : 'Admin') + 'Module';
 }