Browse Source

feat(core): Enable defining custom states in a type-safe manner (#1678)

Alexander Shitikov 3 years ago
parent
commit
4e2b4adf6e

+ 18 - 0
docs/content/developer-guide/customizing-the-order-process/index.md

@@ -49,6 +49,7 @@ export const customerValidationProcess: CustomOrderProcess<'ValidatingCustomer'>
   },
   },
 };
 };
 ```
 ```
+
 This object means:
 This object means:
 
 
 * the `AddingItems` state may _only_ transition to the `ValidatingCustomer` state (`mergeStrategy: 'replace'` tells Vendure to discard any existing transition targets and replace with this one). 
 * the `AddingItems` state may _only_ transition to the `ValidatingCustomer` state (`mergeStrategy: 'replace'` tells Vendure to discard any existing transition targets and replace with this one). 
@@ -122,6 +123,23 @@ const customerValidationProcess: CustomOrderProcess<'ValidatingCustomer'> = {
 
 
 ```
 ```
 
 
+## TypeScript Typings
+
+To make your custom states compatible with standard services you should declare your new states in the following way:
+
+```TypeScript
+// types.ts
+import { CustomOrderStates } from '@vendure/core';
+
+declare module '@vendure/core' {
+  interface CustomOrderStates {
+    ValidatingCustomer: never;
+  }
+}
+```
+
+This technique uses advanced TypeScript features - [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces) and  [ambient modules](https://www.typescriptlang.org/docs/handbook/modules.html#ambient-modules).
+
 ## Controlling custom states in the Admin UI
 ## Controlling custom states in the Admin UI
 
 
 If you have defined custom order states, the Admin UI will allow you to manually transition an 
 If you have defined custom order states, the Admin UI will allow you to manually transition an 

+ 12 - 0
docs/content/developer-guide/payment-integrations/index.md

@@ -151,6 +151,18 @@ If you need to support an entirely different payment flow than the above, it is
 Here's an example which adds a new "Validating" state to the Payment state machine, and combines it with a [CustomOrderProcess]({{< relref "custom-order-process" >}}), [PaymentMethodHandler]({{< relref "payment-method-handler" >}}) and [OrderPlacedStrategy]({{< relref "order-placed-strategy" >}}).
 Here's an example which adds a new "Validating" state to the Payment state machine, and combines it with a [CustomOrderProcess]({{< relref "custom-order-process" >}}), [PaymentMethodHandler]({{< relref "payment-method-handler" >}}) and [OrderPlacedStrategy]({{< relref "order-placed-strategy" >}}).
 
 
 ```TypeScript
 ```TypeScript
+// types.ts
+import { CustomOrderStates } from '@vendure/core';
+
+/**
+ * Declare your custom state in special interface to make it type-safe
+ */
+declare module '@vendure/core' {
+  interface CustomPaymentStates {
+    Validating: never;
+  }
+}
+
 /**
 /**
  * Define a new "Validating" Payment state, and set up the
  * Define a new "Validating" Payment state, and set up the
  * permitted transitions to/from it.
  * permitted transitions to/from it.

+ 2 - 1
packages/core/src/config/fulfillment/custom-fulfillment-process.ts

@@ -6,6 +6,7 @@ import {
 } from '../../common/finite-state-machine/types';
 } from '../../common/finite-state-machine/types';
 import { InjectableStrategy } from '../../common/types/injectable-strategy';
 import { InjectableStrategy } from '../../common/types/injectable-strategy';
 import {
 import {
+    CustomFulfillmentStates,
     FulfillmentState,
     FulfillmentState,
     FulfillmentTransitionData,
     FulfillmentTransitionData,
 } from '../../service/helpers/fulfillment-state-machine/fulfillment-state';
 } from '../../service/helpers/fulfillment-state-machine/fulfillment-state';
@@ -18,7 +19,7 @@ import {
  *
  *
  * @docsCategory fulfillment
  * @docsCategory fulfillment
  */
  */
-export interface CustomFulfillmentProcess<State extends string> extends InjectableStrategy {
+export interface CustomFulfillmentProcess<State extends keyof CustomFulfillmentStates | string> extends InjectableStrategy {
     transitions?: Transitions<State, State | FulfillmentState> &
     transitions?: Transitions<State, State | FulfillmentState> &
         Partial<Transitions<FulfillmentState | State>>;
         Partial<Transitions<FulfillmentState | State>>;
     onTransitionStart?: OnTransitionStartFn<State | FulfillmentState, FulfillmentTransitionData>;
     onTransitionStart?: OnTransitionStartFn<State | FulfillmentState, FulfillmentTransitionData>;

+ 2 - 2
packages/core/src/config/order/custom-order-process.ts

@@ -6,7 +6,7 @@ import {
     Transitions,
     Transitions,
 } from '../../common/finite-state-machine/types';
 } from '../../common/finite-state-machine/types';
 import { InjectableStrategy } from '../../common/types/injectable-strategy';
 import { InjectableStrategy } from '../../common/types/injectable-strategy';
-import { OrderState, OrderTransitionData } from '../../service/helpers/order-state-machine/order-state';
+import { CustomOrderStates, OrderState, OrderTransitionData } from '../../service/helpers/order-state-machine/order-state';
 
 
 /**
 /**
  * @description
  * @description
@@ -16,7 +16,7 @@ import { OrderState, OrderTransitionData } from '../../service/helpers/order-sta
  *
  *
  * @docsCategory orders
  * @docsCategory orders
  */
  */
-export interface CustomOrderProcess<State extends string> extends InjectableStrategy {
+export interface CustomOrderProcess<State extends keyof CustomOrderStates | string> extends InjectableStrategy {
     transitions?: Transitions<State, State | OrderState> & Partial<Transitions<OrderState | State>>;
     transitions?: Transitions<State, State | OrderState> & Partial<Transitions<OrderState | State>>;
     onTransitionStart?: OnTransitionStartFn<State | OrderState, OrderTransitionData>;
     onTransitionStart?: OnTransitionStartFn<State | OrderState, OrderTransitionData>;
     onTransitionEnd?: OnTransitionEndFn<State | OrderState, OrderTransitionData>;
     onTransitionEnd?: OnTransitionEndFn<State | OrderState, OrderTransitionData>;

+ 2 - 1
packages/core/src/config/payment/custom-payment-process.ts

@@ -6,6 +6,7 @@ import {
 } from '../../common/finite-state-machine/types';
 } from '../../common/finite-state-machine/types';
 import { InjectableStrategy } from '../../common/types/injectable-strategy';
 import { InjectableStrategy } from '../../common/types/injectable-strategy';
 import {
 import {
+    CustomPaymentStates,
     PaymentState,
     PaymentState,
     PaymentTransitionData,
     PaymentTransitionData,
 } from '../../service/helpers/payment-state-machine/payment-state';
 } from '../../service/helpers/payment-state-machine/payment-state';
@@ -18,7 +19,7 @@ import {
  *
  *
  * @docsCategory fulfillment
  * @docsCategory fulfillment
  */
  */
-export interface CustomPaymentProcess<State extends string> extends InjectableStrategy {
+export interface CustomPaymentProcess<State extends keyof CustomPaymentStates | string> extends InjectableStrategy {
     transitions?: Transitions<State, State | PaymentState> & Partial<Transitions<PaymentState | State>>;
     transitions?: Transitions<State, State | PaymentState> & Partial<Transitions<PaymentState | State>>;
     onTransitionStart?: OnTransitionStartFn<State | PaymentState, PaymentTransitionData>;
     onTransitionStart?: OnTransitionStartFn<State | PaymentState, PaymentTransitionData>;
     onTransitionEnd?: OnTransitionEndFn<State | PaymentState, PaymentTransitionData>;
     onTransitionEnd?: OnTransitionEndFn<State | PaymentState, PaymentTransitionData>;

+ 9 - 1
packages/core/src/service/helpers/fulfillment-state-machine/fulfillment-state.ts

@@ -3,13 +3,21 @@ import { Transitions } from '../../../common/finite-state-machine/types';
 import { Fulfillment } from '../../../entity/fulfillment/fulfillment.entity';
 import { Fulfillment } from '../../../entity/fulfillment/fulfillment.entity';
 import { Order } from '../../../entity/order/order.entity';
 import { Order } from '../../../entity/order/order.entity';
 
 
+/**
+ * @description
+ * An interface to extend standard {@link FulfillmentState}.
+ * 
+ * @docsCategory fulfillment
+ */
+export interface CustomFulfillmentStates {}
+
 /**
 /**
  * @description
  * @description
  * These are the default states of the fulfillment process.
  * These are the default states of the fulfillment process.
  *
  *
  * @docsCategory fulfillment
  * @docsCategory fulfillment
  */
  */
-export type FulfillmentState = 'Created' | 'Pending' | 'Shipped' | 'Delivered' | 'Cancelled';
+export type FulfillmentState = 'Created' | 'Pending' | 'Shipped' | 'Delivered' | 'Cancelled' | keyof CustomFulfillmentStates;
 
 
 export const fulfillmentStateTransitions: Transitions<FulfillmentState> = {
 export const fulfillmentStateTransitions: Transitions<FulfillmentState> = {
     Created: {
     Created: {

+ 10 - 1
packages/core/src/service/helpers/order-state-machine/order-state.ts

@@ -2,6 +2,14 @@ import { RequestContext } from '../../../api/common/request-context';
 import { Transitions } from '../../../common/finite-state-machine/types';
 import { Transitions } from '../../../common/finite-state-machine/types';
 import { Order } from '../../../entity/order/order.entity';
 import { Order } from '../../../entity/order/order.entity';
 
 
+/**
+ * @description
+ * An interface to extend standard {@link OrderState}.
+ * 
+ * @docsCategory orders
+ */
+ export interface CustomOrderStates {}
+
 /**
 /**
  * @description
  * @description
  * These are the default states of the Order process. They can be augmented and
  * These are the default states of the Order process. They can be augmented and
@@ -21,7 +29,8 @@ export type OrderState =
     | 'Delivered'
     | 'Delivered'
     | 'Modifying'
     | 'Modifying'
     | 'ArrangingAdditionalPayment'
     | 'ArrangingAdditionalPayment'
-    | 'Cancelled';
+    | 'Cancelled'
+    | keyof CustomOrderStates;
 
 
 export const orderStateTransitions: Transitions<OrderState> = {
 export const orderStateTransitions: Transitions<OrderState> = {
     Created: {
     Created: {

+ 9 - 1
packages/core/src/service/helpers/payment-state-machine/payment-state.ts

@@ -3,13 +3,21 @@ import { Transitions } from '../../../common/finite-state-machine/types';
 import { Order } from '../../../entity/order/order.entity';
 import { Order } from '../../../entity/order/order.entity';
 import { Payment } from '../../../entity/payment/payment.entity';
 import { Payment } from '../../../entity/payment/payment.entity';
 
 
+/**
+ * @description
+ * An interface to extend standard {@link PaymentState}.
+ * 
+ * @docsCategory payment
+ */
+ export interface CustomPaymentStates {}
+
 /**
 /**
  * @description
  * @description
  * These are the default states of the payment process.
  * These are the default states of the payment process.
  *
  *
  * @docsCategory payment
  * @docsCategory payment
  */
  */
-export type PaymentState = 'Created' | 'Authorized' | 'Settled' | 'Declined' | 'Error' | 'Cancelled';
+export type PaymentState = 'Created' | 'Authorized' | 'Settled' | 'Declined' | 'Error' | 'Cancelled' | keyof CustomPaymentStates;
 
 
 export const paymentStateTransitions: Transitions<PaymentState> = {
 export const paymentStateTransitions: Transitions<PaymentState> = {
     Created: {
     Created: {