Browse Source

feat(core): Create OrderCodeStrategy for more control over order codes

Closes #452

BREAKING CHANGE: The `orderOptions.generateOrderCode` config option has been replaced with `orderOptions.orderCodeStrategy`. This change allows order code generation to take advantage of the `InjectableStrategy` interface, i.e. to be able to inject Vendure services and other providers (e.g. the database connection). See the `OrderCodeStrategy` documentation for guidance on how to use the new API.
Michael Bromley 5 years ago
parent
commit
30dc63957a

+ 9 - 2
packages/core/src/app.module.ts

@@ -121,9 +121,14 @@ export class AppModule implements NestModule, OnApplicationBootstrap, OnApplicat
         const { adminAuthenticationStrategy, shopAuthenticationStrategy } = this.configService.authOptions;
         const { taxCalculationStrategy, taxZoneStrategy } = this.configService.taxOptions;
         const { jobQueueStrategy } = this.configService.jobQueueOptions;
-        const { mergeStrategy, priceCalculationStrategy } = this.configService.orderOptions;
+        const {
+            mergeStrategy,
+            checkoutMergeStrategy,
+            priceCalculationStrategy,
+            process,
+            orderCodeStrategy,
+        } = this.configService.orderOptions;
         const { entityIdStrategy } = this.configService;
-        const { process } = this.configService.orderOptions;
         return [
             ...adminAuthenticationStrategy,
             ...shopAuthenticationStrategy,
@@ -134,6 +139,8 @@ export class AppModule implements NestModule, OnApplicationBootstrap, OnApplicat
             taxZoneStrategy,
             jobQueueStrategy,
             mergeStrategy,
+            checkoutMergeStrategy,
+            orderCodeStrategy,
             entityIdStrategy,
             priceCalculationStrategy,
             ...process,

+ 2 - 1
packages/core/src/config/default-config.ts

@@ -19,6 +19,7 @@ import { DefaultLogger } from './logger/default-logger';
 import { TypeOrmLogger } from './logger/typeorm-logger';
 import { DefaultPriceCalculationStrategy } from './order/default-price-calculation-strategy';
 import { MergeOrdersStrategy } from './order/merge-orders-strategy';
+import { DefaultOrderCodeStrategy } from './order/order-code-strategy';
 import { UseGuestStrategy } from './order/use-guest-strategy';
 import { defaultPromotionActions, defaultPromotionConditions } from './promotion';
 import { InMemorySessionCacheStrategy } from './session-cache/in-memory-session-cache-strategy';
@@ -105,7 +106,7 @@ export const defaultConfig: RuntimeVendureConfig = {
         mergeStrategy: new MergeOrdersStrategy(),
         checkoutMergeStrategy: new UseGuestStrategy(),
         process: [],
-        generateOrderCode: () => generatePublicId(),
+        orderCodeStrategy: new DefaultOrderCodeStrategy(),
     },
     paymentOptions: {
         paymentMethodHandlers: [],

+ 1 - 0
packages/core/src/config/index.ts

@@ -18,6 +18,7 @@ export * from './logger/noop-logger';
 export * from './logger/vendure-logger';
 export * from './merge-config';
 export * from './order/custom-order-process';
+export * from './order/order-code-strategy';
 export * from './order/order-merge-strategy';
 export * from './order/price-calculation-strategy';
 export * from './payment-method/example-payment-method-handler';

+ 52 - 0
packages/core/src/config/order/order-code-strategy.ts

@@ -0,0 +1,52 @@
+import { RequestContext } from '../../api/common/request-context';
+import { generatePublicId } from '../../common/generate-public-id';
+import { InjectableStrategy } from '../../common/types/injectable-strategy';
+
+/**
+ * @description
+ * The OrderCodeStrategy determines how Order codes are generated.
+ * A custom strategy can be defined which e.g. integrates with an
+ * existing system to generate a code:
+ *
+ * @example
+ * ```TypeScript
+ * class MyOrderCodeStrategy implements OrderCodeStrategy {
+ *   // Some imaginary service which calls out to an existing external
+ *   // order management system.
+ *   private mgmtService: ExternalOrderManagementService;
+ *
+ *   init(injector: Injector) {
+ *     this.mgmtService = injector.get(ExternalOrderManagementService);
+ *   }
+ *
+ *   async generate(ctx: RequestContext) {
+ *     const result = await this.mgmtService.getAvailableOrderParams();
+ *     return result.code;
+ *   }
+ * }
+ * ```
+ *
+ * @docsCategory orders
+ * @docsPage OrderCodeStrategy
+ */
+export interface OrderCodeStrategy extends InjectableStrategy {
+    /**
+     * @description
+     * Generates the order code.
+     */
+    generate(ctx: RequestContext): string | Promise<string>;
+}
+
+/**
+ * @description
+ * The default OrderCodeStrategy generates a random string consisting
+ * of 16 uppercase letters and numbers.
+ *
+ * @docsCategory orders
+ * @docsPage OrderCodeStrategy
+ */
+export class DefaultOrderCodeStrategy implements OrderCodeStrategy {
+    generate(ctx: RequestContext): string {
+        return generatePublicId();
+    }
+}

+ 4 - 1
packages/core/src/config/vendure-config.ts

@@ -23,6 +23,7 @@ import { CustomFulfillmentProcess } from './fulfillment/custom-fulfillment-proce
 import { JobQueueStrategy } from './job-queue/job-queue-strategy';
 import { VendureLogger } from './logger/vendure-logger';
 import { CustomOrderProcess } from './order/custom-order-process';
+import { OrderCodeStrategy } from './order/order-code-strategy';
 import { OrderMergeStrategy } from './order/order-merge-strategy';
 import { PriceCalculationStrategy } from './order/price-calculation-strategy';
 import { PaymentMethodHandler } from './payment-method/payment-method-handler';
@@ -413,8 +414,10 @@ export interface OrderOptions {
      * Note: when using a custom function for Order codes, bear in mind the database limit
      * for string types (e.g. 255 chars for a varchar field in MySQL), and also the need
      * for codes to be unique.
+     *
+     * @default DefaultOrderCodeStrategy
      */
-    generateOrderCode?: (ctx: RequestContext) => string | Promise<string>;
+    orderCodeStrategy?: OrderCodeStrategy;
 }
 
 /**

+ 1 - 1
packages/core/src/service/services/order.service.ts

@@ -276,7 +276,7 @@ export class OrderService {
 
     async create(ctx: RequestContext, userId?: ID): Promise<Order> {
         const newOrder = new Order({
-            code: await this.configService.orderOptions.generateOrderCode(ctx),
+            code: await this.configService.orderOptions.orderCodeStrategy.generate(ctx),
             state: this.orderStateMachine.getInitialState(),
             lines: [],
             couponCodes: [],