Bläddra i källkod

feat(core): Allow lazy evaluation of APIExtensionDefinitions

This change allows you to define extensions to the GraphQL APIs in plugins which may rely on runtime execution, e.g. to dynamically build up the DocumentNode data based on some config options.
Michael Bromley 6 år sedan
förälder
incheckning
69dad0b664

+ 20 - 0
packages/core/e2e/fixtures/test-plugins.ts

@@ -96,6 +96,26 @@ export class TestShopPluginResolver {
 })
 })
 export class TestAPIExtensionPlugin {}
 export class TestAPIExtensionPlugin {}
 
 
+@Resolver()
+export class TestLazyResolver {
+    @Query()
+    lazy() {
+        return 'sleeping';
+    }
+}
+
+@VendurePlugin({
+    shopApiExtensions: {
+        resolvers: () => [TestLazyResolver],
+        schema: () => gql`
+            extend type Query {
+                lazy: String!
+            }
+        `,
+    },
+})
+export class TestLazyExtensionPlugin {}
+
 @Injectable()
 @Injectable()
 export class NameService {
 export class NameService {
     getNames(): string[] {
     getNames(): string[] {

+ 11 - 0
packages/core/e2e/plugin.e2e-spec.ts

@@ -7,6 +7,7 @@ import { ConfigService } from '../src/config/config.service';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import {
 import {
     TestAPIExtensionPlugin,
     TestAPIExtensionPlugin,
+    TestLazyExtensionPlugin,
     TestPluginWithAllLifecycleHooks,
     TestPluginWithAllLifecycleHooks,
     TestPluginWithConfigAndBootstrap,
     TestPluginWithConfigAndBootstrap,
     TestPluginWithProvider,
     TestPluginWithProvider,
@@ -41,6 +42,7 @@ describe('Plugins', () => {
                     TestPluginWithConfigAndBootstrap.setup(bootstrapMockFn),
                     TestPluginWithConfigAndBootstrap.setup(bootstrapMockFn),
                     TestAPIExtensionPlugin,
                     TestAPIExtensionPlugin,
                     TestPluginWithProvider,
                     TestPluginWithProvider,
+                    TestLazyExtensionPlugin,
                 ],
                 ],
             },
             },
         );
         );
@@ -85,6 +87,15 @@ describe('Plugins', () => {
         expect(result.baz).toEqual(['quux']);
         expect(result.baz).toEqual(['quux']);
     });
     });
 
 
+    it('allows lazy evaluation of API extension', async () => {
+        const result = await shopClient.query(gql`
+            query {
+                lazy
+            }
+        `);
+        expect(result.lazy).toEqual('sleeping');
+    });
+
     it('generates ListQueryOptions for api extensions', async () => {
     it('generates ListQueryOptions for api extensions', async () => {
         const result = await adminClient.query(gql`
         const result = await adminClient.query(gql`
             query {
             query {

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

@@ -164,7 +164,7 @@ async function createGraphQLOptions(
         let schema = buildSchema(typeDefs);
         let schema = buildSchema(typeDefs);
 
 
         getPluginAPIExtensions(configService.plugins, apiType)
         getPluginAPIExtensions(configService.plugins, apiType)
-            .map(e => e.schema)
+            .map(e => (typeof e.schema === 'function' ? e.schema() : e.schema))
             .filter(notNullOrUndefined)
             .filter(notNullOrUndefined)
             .forEach(documentNode => (schema = extendSchema(schema, documentNode)));
             .forEach(documentNode => (schema = extendSchema(schema, documentNode)));
         schema = generateListOptions(schema);
         schema = generateListOptions(schema);

+ 6 - 2
packages/core/src/plugin/plugin-metadata.ts

@@ -69,12 +69,16 @@ export function graphQLResolversFor(
     plugin: Type<any> | DynamicModule,
     plugin: Type<any> | DynamicModule,
     apiType: 'shop' | 'admin',
     apiType: 'shop' | 'admin',
 ): Array<Type<any>> {
 ): Array<Type<any>> {
-    const apiExtensions =
+    const apiExtensions: APIExtensionDefinition =
         apiType === 'shop'
         apiType === 'shop'
             ? reflectMetadata(plugin, PLUGIN_METADATA.SHOP_API_EXTENSIONS)
             ? reflectMetadata(plugin, PLUGIN_METADATA.SHOP_API_EXTENSIONS)
             : reflectMetadata(plugin, PLUGIN_METADATA.ADMIN_API_EXTENSIONS);
             : reflectMetadata(plugin, PLUGIN_METADATA.ADMIN_API_EXTENSIONS);
 
 
-    return apiExtensions ? apiExtensions.resolvers : [];
+    return apiExtensions
+        ? typeof apiExtensions.resolvers === 'function'
+            ? apiExtensions.resolvers()
+            : apiExtensions.resolvers
+        : [];
 }
 }
 
 
 function reflectMetadata(metatype: Type<any> | DynamicModule, metadataKey: string) {
 function reflectMetadata(metatype: Type<any> | DynamicModule, metadataKey: string) {

+ 2 - 2
packages/core/src/plugin/vendure-plugin.ts

@@ -70,13 +70,13 @@ export interface APIExtensionDefinition {
      * }`;
      * }`;
      * ```
      * ```
      */
      */
-    schema?: DocumentNode;
+    schema?: DocumentNode | (() => DocumentNode);
     /**
     /**
      * @description
      * @description
      * An array of resolvers for the schema extensions. Should be defined as [Nestjs GraphQL resolver](https://docs.nestjs.com/graphql/resolvers-map)
      * An array of resolvers for the schema extensions. Should be defined as [Nestjs GraphQL resolver](https://docs.nestjs.com/graphql/resolvers-map)
      * classes, i.e. using the Nest `\@Resolver()` decorator etc.
      * classes, i.e. using the Nest `\@Resolver()` decorator etc.
      */
      */
-    resolvers: Array<Type<any>>;
+    resolvers: Array<Type<any>> | (() => Array<Type<any>>);
 }
 }
 
 
 /**
 /**