From 30dc63957a149d7175935cb31047cd46016b73bc Mon Sep 17 00:00:00 2001 From: Michael Bromley Date: Fri, 2 Oct 2020 16:18:06 +0200 Subject: [PATCH] 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. --- packages/core/src/app.module.ts | 11 +++- packages/core/src/config/default-config.ts | 3 +- packages/core/src/config/index.ts | 1 + .../src/config/order/order-code-strategy.ts | 52 +++++++++++++++++++ packages/core/src/config/vendure-config.ts | 5 +- .../src/service/services/order.service.ts | 2 +- 6 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 packages/core/src/config/order/order-code-strategy.ts diff --git a/packages/core/src/app.module.ts b/packages/core/src/app.module.ts index 02cc57afc7..300d58cba5 100644 --- a/packages/core/src/app.module.ts +++ b/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, diff --git a/packages/core/src/config/default-config.ts b/packages/core/src/config/default-config.ts index 929bd2dcfc..925607883f 100644 --- a/packages/core/src/config/default-config.ts +++ b/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: [], diff --git a/packages/core/src/config/index.ts b/packages/core/src/config/index.ts index 005521269e..ebfabb9d5a 100644 --- a/packages/core/src/config/index.ts +++ b/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'; diff --git a/packages/core/src/config/order/order-code-strategy.ts b/packages/core/src/config/order/order-code-strategy.ts new file mode 100644 index 0000000000..b8dffcf9cf --- /dev/null +++ b/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; +} + +/** + * @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(); + } +} diff --git a/packages/core/src/config/vendure-config.ts b/packages/core/src/config/vendure-config.ts index f6e8488041..43df1f0290 100644 --- a/packages/core/src/config/vendure-config.ts +++ b/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; + orderCodeStrategy?: OrderCodeStrategy; } /** diff --git a/packages/core/src/service/services/order.service.ts b/packages/core/src/service/services/order.service.ts index a2db121f41..fc66cd8730 100644 --- a/packages/core/src/service/services/order.service.ts +++ b/packages/core/src/service/services/order.service.ts @@ -276,7 +276,7 @@ export class OrderService { async create(ctx: RequestContext, userId?: ID): Promise { 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: [],