chore(core-utils): avoid overfetching to refresh cart (#11602)
What: * Not all Cart operations need a full refresh updating items. This PR introduces a flag to force the refresh for special ocasions, like updating the Cart's region, or transfering the Cart to another customer. For all other flows it will update only promotions, taxes and payment collection if needed.
This commit is contained in:
committed by
GitHub
parent
eeebb35758
commit
fa1793e8e9
5
.changeset/twenty-oranges-agree.md
Normal file
5
.changeset/twenty-oranges-agree.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/core-flows": patch
|
||||
---
|
||||
|
||||
chore(core-flows): avoid overfetching to refresh cart
|
||||
@@ -2068,7 +2068,7 @@ medusaIntegrationTestRunner({
|
||||
unit_price: 4000,
|
||||
is_custom_price: true,
|
||||
quantity: 2,
|
||||
title: "Test variant",
|
||||
title: "Test item",
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
@@ -35,6 +35,11 @@ export const cartFieldsForRefreshSteps = [
|
||||
"customer.*",
|
||||
"customer.groups.*",
|
||||
"promotions.code",
|
||||
"payment_collection.id",
|
||||
"payment_collection.raw_amount",
|
||||
"payment_collection.amount",
|
||||
"payment_collection.currency_code",
|
||||
"payment_collection.payment_sessions.id",
|
||||
]
|
||||
|
||||
export const completeCartFields = [
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
PromotionActions,
|
||||
} from "@medusajs/framework/utils"
|
||||
import {
|
||||
createHook,
|
||||
createWorkflow,
|
||||
transform,
|
||||
when,
|
||||
@@ -41,19 +40,23 @@ export type RefreshCartItemsWorkflowInput = {
|
||||
* These promotion codes will replace previously applied codes.
|
||||
*/
|
||||
promo_codes?: string[]
|
||||
/**
|
||||
* Force refresh the cart items
|
||||
*/
|
||||
force_refresh?: boolean
|
||||
}
|
||||
|
||||
export const refreshCartItemsWorkflowId = "refresh-cart-items"
|
||||
/**
|
||||
* This workflow refreshes a cart to ensure its prices, promotion codes, taxes, and other details are applied correctly. It's useful
|
||||
* after making a chnge to a cart, such as after adding an item to the cart or adding a promotion code.
|
||||
*
|
||||
*
|
||||
* This workflow is used by other cart-related workflows, such as the {@link addToCartWorkflow} after an item
|
||||
* is added to the cart.
|
||||
*
|
||||
*
|
||||
* You can use this workflow within your own customizations or custom workflows, allowing you to refresh the cart after making updates to it in your
|
||||
* custom flows.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* const { result } = await refreshCartItemsWorkflow(container)
|
||||
* .run({
|
||||
@@ -61,37 +64,34 @@ export const refreshCartItemsWorkflowId = "refresh-cart-items"
|
||||
* cart_id: "cart_123",
|
||||
* }
|
||||
* })
|
||||
*
|
||||
*
|
||||
* @summary
|
||||
*
|
||||
*
|
||||
* Refresh a cart's details after an update.
|
||||
*
|
||||
* @property hooks.validate - This hook is executed before all operations. You can consume this hook to perform any custom validation. If validation fails, you can throw an error to stop the workflow execution.
|
||||
*
|
||||
*/
|
||||
export const refreshCartItemsWorkflow = createWorkflow(
|
||||
refreshCartItemsWorkflowId,
|
||||
(
|
||||
input: WorkflowData<RefreshCartItemsWorkflowInput>
|
||||
) => {
|
||||
const cart = useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: cartFieldsForRefreshSteps,
|
||||
variables: { id: input.cart_id },
|
||||
list: false,
|
||||
})
|
||||
|
||||
const variantIds = transform({ cart }, (data) => {
|
||||
return (data.cart.items ?? []).map((i) => i.variant_id).filter(Boolean)
|
||||
})
|
||||
|
||||
const cartPricingContext = transform({ cart }, ({ cart }) => {
|
||||
return filterObjectByKeys(cart, cartFieldsForPricingContext)
|
||||
})
|
||||
|
||||
const variants = when({ variantIds }, ({ variantIds }) => {
|
||||
return !!variantIds.length
|
||||
(input: WorkflowData<RefreshCartItemsWorkflowInput>) => {
|
||||
when({ input }, ({ input }) => {
|
||||
return !!input.force_refresh
|
||||
}).then(() => {
|
||||
return useRemoteQueryStep({
|
||||
const cart = useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: cartFieldsForRefreshSteps,
|
||||
variables: { id: input.cart_id },
|
||||
list: false,
|
||||
})
|
||||
|
||||
const variantIds = transform({ cart }, (data) => {
|
||||
return (data.cart.items ?? []).map((i) => i.variant_id).filter(Boolean)
|
||||
})
|
||||
|
||||
const cartPricingContext = transform({ cart }, ({ cart }) => {
|
||||
return filterObjectByKeys(cart, cartFieldsForPricingContext)
|
||||
})
|
||||
|
||||
const variants = useRemoteQueryStep({
|
||||
entry_point: "variants",
|
||||
fields: productVariantsFields,
|
||||
variables: {
|
||||
@@ -101,62 +101,59 @@ export const refreshCartItemsWorkflow = createWorkflow(
|
||||
},
|
||||
},
|
||||
}).config({ name: "fetch-variants" })
|
||||
})
|
||||
|
||||
validateVariantPricesStep({ variants })
|
||||
validateVariantPricesStep({ variants })
|
||||
|
||||
const validate = createHook("validate", {
|
||||
input,
|
||||
cart,
|
||||
})
|
||||
const lineItems = transform({ cart, variants }, ({ cart, variants }) => {
|
||||
const items = cart.items.map((item) => {
|
||||
const variant = (variants ?? []).find(
|
||||
(v) => v.id === item.variant_id
|
||||
)!
|
||||
|
||||
const lineItems = transform({ cart, variants }, ({ cart, variants }) => {
|
||||
const items = cart.items.map((item) => {
|
||||
const variant = (variants ?? []).find((v) => v.id === item.variant_id)!
|
||||
const input: PrepareLineItemDataInput = {
|
||||
item,
|
||||
variant: variant,
|
||||
cartId: cart.id,
|
||||
unitPrice: item.unit_price,
|
||||
isTaxInclusive: item.is_tax_inclusive,
|
||||
}
|
||||
|
||||
const input: PrepareLineItemDataInput = {
|
||||
item,
|
||||
variant: variant,
|
||||
cartId: cart.id,
|
||||
unitPrice: item.unit_price,
|
||||
isTaxInclusive: item.is_tax_inclusive,
|
||||
}
|
||||
if (variant && !item.is_custom_price) {
|
||||
input.unitPrice = variant.calculated_price?.calculated_amount
|
||||
input.isTaxInclusive =
|
||||
variant.calculated_price?.is_calculated_price_tax_inclusive
|
||||
}
|
||||
|
||||
if (variant && !item.is_custom_price) {
|
||||
input.unitPrice = variant.calculated_price?.calculated_amount
|
||||
input.isTaxInclusive =
|
||||
variant.calculated_price?.is_calculated_price_tax_inclusive
|
||||
}
|
||||
const preparedItem = prepareLineItemData(input)
|
||||
|
||||
const preparedItem = prepareLineItemData(input)
|
||||
return {
|
||||
selector: { id: item.id },
|
||||
data: preparedItem,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
selector: { id: item.id },
|
||||
data: preparedItem,
|
||||
}
|
||||
return items
|
||||
})
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
updateLineItemsStep({
|
||||
id: cart.id,
|
||||
items: lineItems,
|
||||
updateLineItemsStep({
|
||||
id: cart.id,
|
||||
items: lineItems,
|
||||
})
|
||||
})
|
||||
|
||||
const refetchedCart = useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: cartFieldsForRefreshSteps,
|
||||
variables: { id: cart.id },
|
||||
variables: { id: input.cart_id },
|
||||
list: false,
|
||||
}).config({ name: "refetch–cart" })
|
||||
|
||||
refreshCartShippingMethodsWorkflow.runAsStep({
|
||||
input: { cart_id: cart.id },
|
||||
input: { cart_id: input.cart_id },
|
||||
})
|
||||
|
||||
updateTaxLinesWorkflow.runAsStep({
|
||||
input: { cart_id: cart.id },
|
||||
input: { cart_id: input.cart_id },
|
||||
})
|
||||
|
||||
const cartPromoCodes = transform(
|
||||
@@ -172,18 +169,16 @@ export const refreshCartItemsWorkflow = createWorkflow(
|
||||
|
||||
updateCartPromotionsWorkflow.runAsStep({
|
||||
input: {
|
||||
cart_id: cart.id,
|
||||
cart_id: input.cart_id,
|
||||
promo_codes: cartPromoCodes,
|
||||
action: PromotionActions.REPLACE,
|
||||
},
|
||||
})
|
||||
|
||||
refreshPaymentCollectionForCartWorkflow.runAsStep({
|
||||
input: { cart_id: cart.id },
|
||||
input: { cart: refetchedCart },
|
||||
})
|
||||
|
||||
return new WorkflowResponse(refetchedCart, {
|
||||
hooks: [validate],
|
||||
})
|
||||
return new WorkflowResponse(refetchedCart)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -19,22 +19,26 @@ export type RefreshPaymentCollectionForCartWorklowInput = {
|
||||
/**
|
||||
* The cart's ID.
|
||||
*/
|
||||
cart_id: string
|
||||
cart_id?: string
|
||||
/**
|
||||
* The Cart reference.
|
||||
*/
|
||||
cart?: any
|
||||
}
|
||||
|
||||
export const refreshPaymentCollectionForCartWorkflowId =
|
||||
"refresh-payment-collection-for-cart"
|
||||
/**
|
||||
* This workflow refreshes a cart's payment collection, which is useful once the cart is created or when its details
|
||||
* are updated. If the cart's total changes to the amount in its payment collection, the payment collection's payment sessions are
|
||||
* are updated. If the cart's total changes to the amount in its payment collection, the payment collection's payment sessions are
|
||||
* deleted. It also syncs the payment collection's amount, currency code, and other details with the details in the cart.
|
||||
*
|
||||
*
|
||||
* This workflow is used by other cart-related workflows, such as the {@link refreshCartItemsWorkflow} to refresh the cart's
|
||||
* payment collection after an update.
|
||||
*
|
||||
*
|
||||
* You can use this workflow within your own customizations or custom workflows, allowing you to refresh the cart's payment collection after making updates to it in your
|
||||
* custom flows.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* const { result } = await refreshPaymentCollectionForCartWorkflow(container)
|
||||
* .run({
|
||||
@@ -42,33 +46,41 @@ export const refreshPaymentCollectionForCartWorkflowId =
|
||||
* cart_id: "cart_123",
|
||||
* }
|
||||
* })
|
||||
*
|
||||
*
|
||||
* @summary
|
||||
*
|
||||
*
|
||||
* Refresh a cart's payment collection details.
|
||||
*
|
||||
*
|
||||
* @property hooks.validate - This hook is executed before all operations. You can consume this hook to perform any custom validation. If validation fails, you can throw an error to stop the workflow execution.
|
||||
*/
|
||||
export const refreshPaymentCollectionForCartWorkflow = createWorkflow(
|
||||
refreshPaymentCollectionForCartWorkflowId,
|
||||
(input: WorkflowData<RefreshPaymentCollectionForCartWorklowInput>) => {
|
||||
const cart = useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: [
|
||||
"id",
|
||||
"region_id",
|
||||
"currency_code",
|
||||
"total",
|
||||
"raw_total",
|
||||
"payment_collection.id",
|
||||
"payment_collection.raw_amount",
|
||||
"payment_collection.amount",
|
||||
"payment_collection.currency_code",
|
||||
"payment_collection.payment_sessions.id",
|
||||
],
|
||||
variables: { id: input.cart_id },
|
||||
throw_if_key_not_found: true,
|
||||
list: false,
|
||||
const fetchCart = when({ input }, ({ input }) => {
|
||||
return !input.cart
|
||||
}).then(() => {
|
||||
return useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: [
|
||||
"id",
|
||||
"region_id",
|
||||
"currency_code",
|
||||
"total",
|
||||
"raw_total",
|
||||
"payment_collection.id",
|
||||
"payment_collection.raw_amount",
|
||||
"payment_collection.amount",
|
||||
"payment_collection.currency_code",
|
||||
"payment_collection.payment_sessions.id",
|
||||
],
|
||||
variables: { id: input.cart_id },
|
||||
throw_if_key_not_found: true,
|
||||
list: false,
|
||||
})
|
||||
})
|
||||
|
||||
const cart = transform({ fetchCart, input }, ({ fetchCart, input }) => {
|
||||
return input.cart ?? fetchCart
|
||||
})
|
||||
|
||||
const validate = createHook("validate", {
|
||||
|
||||
@@ -13,11 +13,11 @@ import { refreshCartItemsWorkflow } from "./refresh-cart-items"
|
||||
/**
|
||||
* The cart ownership transfer details.
|
||||
*/
|
||||
export type TransferCartCustomerWorkflowInput = {
|
||||
export type TransferCartCustomerWorkflowInput = {
|
||||
/**
|
||||
* The cart's ID.
|
||||
*/
|
||||
id: string;
|
||||
id: string
|
||||
/**
|
||||
* The ID of the customer to transfer the cart to.
|
||||
*/
|
||||
@@ -29,9 +29,9 @@ export const transferCartCustomerWorkflowId = "transfer-cart-customer"
|
||||
* This workflow transfers a cart's customer ownership to another customer. It's useful if a customer logs in after
|
||||
* adding the items to their cart, allowing you to transfer the cart's ownership to the logged-in customer. This workflow is used
|
||||
* by the [Set Cart's Customer Store API Route](https://docs.medusajs.com/api/store#carts_postcartsidcustomer).
|
||||
*
|
||||
*
|
||||
* You can use this workflow within your own customizations or custom workflows, allowing you to set the cart's customer within your custom flows.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* const { result } = await transferCartCustomerWorkflow(container)
|
||||
* .run({
|
||||
@@ -40,11 +40,11 @@ export const transferCartCustomerWorkflowId = "transfer-cart-customer"
|
||||
* customer_id: "cus_123"
|
||||
* }
|
||||
* })
|
||||
*
|
||||
*
|
||||
* @summary
|
||||
*
|
||||
*
|
||||
* Refresh a cart's payment collection details.
|
||||
*
|
||||
*
|
||||
* @property hooks.validate - This hook is executed before all operations. You can consume this hook to perform any custom validation. If validation fails, you can throw an error to stop the workflow execution.
|
||||
*/
|
||||
export const transferCartCustomerWorkflow = createWorkflow(
|
||||
@@ -108,7 +108,7 @@ export const transferCartCustomerWorkflow = createWorkflow(
|
||||
updateCartsStep(cartInput)
|
||||
|
||||
refreshCartItemsWorkflow.runAsStep({
|
||||
input: { cart_id: input.id },
|
||||
input: { cart_id: input.id, force_refresh: true },
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
@@ -27,8 +27,8 @@ import {
|
||||
findSalesChannelStep,
|
||||
updateCartsStep,
|
||||
} from "../steps"
|
||||
import { refreshCartItemsWorkflow } from "./refresh-cart-items"
|
||||
import { validateSalesChannelStep } from "../steps/validate-sales-channel"
|
||||
import { refreshCartItemsWorkflow } from "./refresh-cart-items"
|
||||
|
||||
/**
|
||||
* The data to update the cart, along with custom data that's passed to the workflow's hooks.
|
||||
@@ -278,7 +278,11 @@ export const updateCartWorkflow = createWorkflow(
|
||||
})
|
||||
|
||||
const cart = refreshCartItemsWorkflow.runAsStep({
|
||||
input: { cart_id: cartInput.id, promo_codes: input.promo_codes },
|
||||
input: {
|
||||
cart_id: cartInput.id,
|
||||
promo_codes: input.promo_codes,
|
||||
force_refresh: !!newRegion,
|
||||
},
|
||||
})
|
||||
|
||||
const cartUpdated = createHook("cartUpdated", {
|
||||
|
||||
Reference in New Issue
Block a user