vendure-ecommerce / vendure

The commerce platform with customization in its DNA.
https://www.vendure.io
Other
5.58k stars 989 forks source link

Order interceptor #2123

Open michaelbromley opened 1 year ago

michaelbromley commented 1 year ago

Is your feature request related to a problem? Please describe. In some more advanced use-cases a need has emerged to be able to intercept operations on an order and do some validation or take some other side-effectful action.

Use-case: Product bundles

Ref: https://github.com/vendure-ecommerce/vendure/issues/236

Let's say we want to create a plugin that allows us to group multiple product variants into a "bundle" that can get purchased at a special price. We want to ensure that the entire bundle is added or removed at once, and not allow the bundle to be added and then have individual variants from that bundle removed.

Allowing us to intercept the "add item", "adjust quantity", "remove item" operations would let us enforce this constraint.

Use-case: Min, max quantities

We want to enforce a rule that a certain product has a minimum and maximum quantity that may be purchased in a single order (this is independent of stock control). Using validation allows us to enforce these constraints when the customer attempts to update the order contents.

Describe the solution you'd like

We should extend the OrderOptions interface to allow an array of OrderInterceptor objects which would look roughly like this:

interface OrderInterceptor extends InjectableStrategy {

  // Resolving to a string would signify an error, which would prevent the action from
  // taking place
  willAddItemToOrder(
    ctx: RequestContext, 
    order: Order, 
    productVariantId: ID, 
    quantity: number, 
    customFields: Record<string, any>): Promise<void | string>;

  // Args omitted for brevity
  didAddItemToOrder(...): Promise<void>;

  willAdjustOrderLine(...): Promise<void | string>;
  didAdjustOrderLine(...): Promise<void>;

  willRemoveItemFromOrder(...): Promise<void | string>;
  didRemoveItemFromOrder(...): Promise<void>;
}

So taking the min, max use-case, we could define the willAddItemToOrder hook to check the variant's custom fields for min, max values, and if the quantity being added does not fall within this range, we can return an error message.

Describe alternatives you've considered An alternative is to re-implement the various graphql mutations that update order contents, but this is tedious, error-prone and does not handle instances where the OrderService APIs are used directly elsewhere.

michaelbromley commented 8 months ago

A generalized version of this that is also worth considering would be to allow interceptors for all GraphQL operations.

This would allow users to "decorate" any existing operation. Could an existing NestJS mechanism be used for this? This could be the low-level implementation upon which the order interceptor is built.

As part of this task we'd need a working demo of product bundling plugin as well as a docs guide.

michaelbromley commented 4 months ago

Another use-case:

mschipperheyn commented 1 month ago

Just a little tangent on the interface. What about wouldAddItemToOrder? We have a number of products that are age restricted or dependent on the previous purchase of other products. We have a plugin that processes the ability of a current user to add a product to his cart and display the ui accordingly. wouldAddItemToOrder would be a kind of dry run version where you throw a productVariant in there to know if it's addeable without requiring you to try and fail.

martijnvdbrug commented 1 month ago

A generalized version of this that is also worth considering would be to allow interceptors for all GraphQL operations.

This would allow users to "decorate" any existing operation. Could an existing NestJS mechanism be used for this? This could be the low-level implementation upon which the order interceptor is built.

As part of this task we'd need a working demo of product bundling plugin as well as a docs guide.

@michaelbromley Is there any timeline estimate on this feature?

We are currently in the midst of developing a product bundle plugin, and would like to contribute, as long as the release of this feature is somewhere in the coming months :-)

martijnvdbrug commented 1 month ago

Just a little tangent on the interface. What about wouldAddItemToOrder? We have a number of products that are age restricted or dependent on the previous purchase of other products. We have a plugin that processes the ability of a current user to add a product to his cart and display the ui accordingly. wouldAddItemToOrder would be a kind of dry run version where you throw a productVariant in there to know if it's addeable without requiring you to try and fail.

We do something similar in this plugins. If you throw an error, the transaction is rolled back, so no items are added. The benefit of this is that you don't have to do a dry run first, and repeat the action if the dry run succeeds

michaelbromley commented 1 month ago

Hi @martijnvdbrug

I can't give a better estimate than "it's on the list for v3.x". It certainly won't be within 1 month. Perhaps 2 for a pre-release, but maybe 3 or more. I'll coordinate with you when I have a better idea of feature priority for the next minor.