feat(medusa): Improve buildQuery as well as refactor the cart service as an example
This commit is contained in:
1 parent
e7e715ac17
commit
b90291b18d
6 files changed
+84
-67
No files matched your search
@@ -1,6 +1,5 @@
|
|||||||
import _ from "lodash"
|
import _ from "lodash"
|
||||||
import { MedusaError, Validator } from "medusa-core-utils"
|
import { MedusaError, Validator } from "medusa-core-utils"
|
||||||
import { BaseService } from "medusa-interfaces"
|
|
||||||
import { DeepPartial, EntityManager, In } from "typeorm"
|
import { DeepPartial, EntityManager, In } from "typeorm"
|
||||||
import { IPriceSelectionStrategy } from "../interfaces/price-selection-strategy"
|
import { IPriceSelectionStrategy } from "../interfaces/price-selection-strategy"
|
||||||
import { Address } from "../models/address"
|
import { Address } from "../models/address"
|
||||||
@@ -37,6 +36,8 @@ import InventoryService from "./inventory"
|
|||||||
import CustomShippingOptionService from "./custom-shipping-option"
|
import CustomShippingOptionService from "./custom-shipping-option"
|
||||||
import LineItemAdjustmentService from "./line-item-adjustment"
|
import LineItemAdjustmentService from "./line-item-adjustment"
|
||||||
import { LineItemRepository } from "../repositories/line-item"
|
import { LineItemRepository } from "../repositories/line-item"
|
||||||
|
import { TransactionBaseService } from "../interfaces"
|
||||||
|
import { buildQuery, setMetadata, validateId } from "../utils"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
manager: EntityManager
|
manager: EntityManager
|
||||||
@@ -70,14 +71,16 @@ type TotalsConfig = {
|
|||||||
/* Provides layer to manipulate carts.
|
/* Provides layer to manipulate carts.
|
||||||
* @implements BaseService
|
* @implements BaseService
|
||||||
*/
|
*/
|
||||||
class CartService extends BaseService {
|
class CartService extends TransactionBaseService<CartService> {
|
||||||
static Events = {
|
static readonly Events = {
|
||||||
CUSTOMER_UPDATED: "cart.customer_updated",
|
CUSTOMER_UPDATED: "cart.customer_updated",
|
||||||
CREATED: "cart.created",
|
CREATED: "cart.created",
|
||||||
UPDATED: "cart.updated",
|
UPDATED: "cart.updated",
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly manager_: EntityManager
|
protected manager_: EntityManager
|
||||||
|
protected transactionManager_: EntityManager | undefined
|
||||||
|
|
||||||
protected readonly shippingMethodRepository_: typeof ShippingMethodRepository
|
protected readonly shippingMethodRepository_: typeof ShippingMethodRepository
|
||||||
protected readonly cartRepository_: typeof CartRepository
|
protected readonly cartRepository_: typeof CartRepository
|
||||||
protected readonly addressRepository_: typeof AddressRepository
|
protected readonly addressRepository_: typeof AddressRepository
|
||||||
@@ -124,7 +127,30 @@ class CartService extends BaseService {
|
|||||||
lineItemAdjustmentService,
|
lineItemAdjustmentService,
|
||||||
priceSelectionStrategy,
|
priceSelectionStrategy,
|
||||||
}: InjectedDependencies) {
|
}: InjectedDependencies) {
|
||||||
super()
|
super({
|
||||||
|
manager,
|
||||||
|
cartRepository,
|
||||||
|
shippingMethodRepository,
|
||||||
|
lineItemRepository,
|
||||||
|
eventBusService,
|
||||||
|
paymentProviderService,
|
||||||
|
productService,
|
||||||
|
productVariantService,
|
||||||
|
taxProviderService,
|
||||||
|
regionService,
|
||||||
|
lineItemService,
|
||||||
|
shippingOptionService,
|
||||||
|
customerService,
|
||||||
|
discountService,
|
||||||
|
giftCardService,
|
||||||
|
totalsService,
|
||||||
|
addressRepository,
|
||||||
|
paymentSessionRepository,
|
||||||
|
inventoryService,
|
||||||
|
customShippingOptionService,
|
||||||
|
lineItemAdjustmentService,
|
||||||
|
priceSelectionStrategy,
|
||||||
|
})
|
||||||
|
|
||||||
this.manager_ = manager
|
this.manager_ = manager
|
||||||
this.shippingMethodRepository_ = shippingMethodRepository
|
this.shippingMethodRepository_ = shippingMethodRepository
|
||||||
@@ -150,41 +176,6 @@ class CartService extends BaseService {
|
|||||||
this.priceSelectionStrategy_ = priceSelectionStrategy
|
this.priceSelectionStrategy_ = priceSelectionStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
withTransaction(transactionManager: EntityManager): CartService {
|
|
||||||
if (!transactionManager) {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
const cloned = new CartService({
|
|
||||||
manager: transactionManager,
|
|
||||||
taxProviderService: this.taxProviderService_,
|
|
||||||
cartRepository: this.cartRepository_,
|
|
||||||
lineItemRepository: this.lineItemRepository_,
|
|
||||||
eventBusService: this.eventBus_,
|
|
||||||
paymentProviderService: this.paymentProviderService_,
|
|
||||||
paymentSessionRepository: this.paymentSessionRepository_,
|
|
||||||
shippingMethodRepository: this.shippingMethodRepository_,
|
|
||||||
productService: this.productService_,
|
|
||||||
productVariantService: this.productVariantService_,
|
|
||||||
regionService: this.regionService_,
|
|
||||||
lineItemService: this.lineItemService_,
|
|
||||||
shippingOptionService: this.shippingOptionService_,
|
|
||||||
customerService: this.customerService_,
|
|
||||||
discountService: this.discountService_,
|
|
||||||
totalsService: this.totalsService_,
|
|
||||||
addressRepository: this.addressRepository_,
|
|
||||||
giftCardService: this.giftCardService_,
|
|
||||||
inventoryService: this.inventoryService_,
|
|
||||||
customShippingOptionService: this.customShippingOptionService_,
|
|
||||||
lineItemAdjustmentService: this.lineItemAdjustmentService_,
|
|
||||||
priceSelectionStrategy: this.priceSelectionStrategy_,
|
|
||||||
})
|
|
||||||
|
|
||||||
cloned.transactionManager_ = transactionManager
|
|
||||||
|
|
||||||
return cloned
|
|
||||||
}
|
|
||||||
|
|
||||||
protected transformQueryForTotals_(
|
protected transformQueryForTotals_(
|
||||||
config: FindConfig<Cart>
|
config: FindConfig<Cart>
|
||||||
): FindConfig<Cart> & { totalsToSelect: TotalField[] } {
|
): FindConfig<Cart> & { totalsToSelect: TotalField[] } {
|
||||||
@@ -292,7 +283,7 @@ class CartService extends BaseService {
|
|||||||
this.cartRepository_
|
this.cartRepository_
|
||||||
)
|
)
|
||||||
|
|
||||||
const query = this.buildQuery_(selector, config)
|
const query = buildQuery<Cart>(selector, config)
|
||||||
return await cartRepo.find(query)
|
return await cartRepo.find(query)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -315,12 +306,12 @@ class CartService extends BaseService {
|
|||||||
const cartRepo = transactionManager.getCustomRepository(
|
const cartRepo = transactionManager.getCustomRepository(
|
||||||
this.cartRepository_
|
this.cartRepository_
|
||||||
)
|
)
|
||||||
const validatedId = this.validateId_(cartId)
|
const validatedId = validateId(cartId)
|
||||||
|
|
||||||
const { select, relations, totalsToSelect } =
|
const { select, relations, totalsToSelect } =
|
||||||
this.transformQueryForTotals_(options)
|
this.transformQueryForTotals_(options)
|
||||||
|
|
||||||
const query = this.buildQuery_(
|
const query = buildQuery<Cart>(
|
||||||
{ id: validatedId },
|
{ id: validatedId },
|
||||||
{ ...options, select, relations }
|
{ ...options, select, relations }
|
||||||
)
|
)
|
||||||
@@ -714,16 +705,20 @@ class CartService extends BaseService {
|
|||||||
* @param shouldAdd - flag to indicate, if we should add or remove
|
* @param shouldAdd - flag to indicate, if we should add or remove
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
async adjustFreeShipping_(cart: Cart, shouldAdd: boolean): Promise<void> {
|
protected async adjustFreeShipping_(
|
||||||
|
cart: Cart,
|
||||||
|
shouldAdd: boolean
|
||||||
|
): Promise<void> {
|
||||||
|
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||||
|
|
||||||
if (cart.shipping_methods?.length) {
|
if (cart.shipping_methods?.length) {
|
||||||
const shippingMethodRepository =
|
const shippingMethodRepository = transactionManager.getCustomRepository(
|
||||||
this.transactionManager_.getCustomRepository(
|
|
||||||
this.shippingMethodRepository_
|
this.shippingMethodRepository_
|
||||||
)
|
)
|
||||||
|
|
||||||
// if any free shipping discounts, we ensure to update shipping method amount
|
// if any free shipping discounts, we ensure to update shipping method amount
|
||||||
if (shouldAdd) {
|
if (shouldAdd) {
|
||||||
return shippingMethodRepository.update(
|
await shippingMethodRepository.update(
|
||||||
{
|
{
|
||||||
id: In(
|
id: In(
|
||||||
cart.shipping_methods.map((shippingMethod) => shippingMethod.id)
|
cart.shipping_methods.map((shippingMethod) => shippingMethod.id)
|
||||||
@@ -855,8 +850,8 @@ class CartService extends BaseService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("metadata" in data) {
|
if (data?.metadata) {
|
||||||
cart.metadata = this.setMetadata_(cart, data.metadata)
|
cart.metadata = setMetadata(cart, data.metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("context" in data) {
|
if ("context" in data) {
|
||||||
@@ -1352,7 +1347,7 @@ class CartService extends BaseService {
|
|||||||
* @param cartOrCartId - the id of the cart to set payment session for
|
* @param cartOrCartId - the id of the cart to set payment session for
|
||||||
* @return the result of the update operation.
|
* @return the result of the update operation.
|
||||||
*/
|
*/
|
||||||
async setPaymentSessions(cartOrCartId: Cart | string): Promise<Cart> {
|
async setPaymentSessions(cartOrCartId: Cart | string): Promise<void> {
|
||||||
return await this.atomicPhase_(
|
return await this.atomicPhase_(
|
||||||
async (transactionManager: EntityManager) => {
|
async (transactionManager: EntityManager) => {
|
||||||
const psRepo = transactionManager.getCustomRepository(
|
const psRepo = transactionManager.getCustomRepository(
|
||||||
@@ -1679,6 +1674,8 @@ class CartService extends BaseService {
|
|||||||
regionId?: string,
|
regionId?: string,
|
||||||
customer_id?: string
|
customer_id?: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||||
|
|
||||||
// If the cart contains items, we update the price of the items
|
// If the cart contains items, we update the price of the items
|
||||||
// to match the updated region or customer id (keeping the old
|
// to match the updated region or customer id (keeping the old
|
||||||
// value if it exists)
|
// value if it exists)
|
||||||
@@ -1693,7 +1690,7 @@ class CartService extends BaseService {
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
cart.items.map(async (item) => {
|
cart.items.map(async (item) => {
|
||||||
const availablePrice = await this.priceSelectionStrategy_
|
const availablePrice = await this.priceSelectionStrategy_
|
||||||
.withTransaction(this.transactionManager_)
|
.withTransaction(transactionManager)
|
||||||
.calculateVariantPrice(item.variant_id, {
|
.calculateVariantPrice(item.variant_id, {
|
||||||
region_id: region.id,
|
region_id: region.id,
|
||||||
currency_code: region.currency_code,
|
currency_code: region.currency_code,
|
||||||
@@ -1708,14 +1705,14 @@ class CartService extends BaseService {
|
|||||||
availablePrice.calculatedPrice !== null
|
availablePrice.calculatedPrice !== null
|
||||||
) {
|
) {
|
||||||
return this.lineItemService_
|
return this.lineItemService_
|
||||||
.withTransaction(this.transactionManager_)
|
.withTransaction(transactionManager)
|
||||||
.update(item.id, {
|
.update(item.id, {
|
||||||
has_shipping: false,
|
has_shipping: false,
|
||||||
unit_price: availablePrice.calculatedPrice,
|
unit_price: availablePrice.calculatedPrice,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
await this.lineItemService_
|
await this.lineItemService_
|
||||||
.withTransaction(this.transactionManager_)
|
.withTransaction(transactionManager)
|
||||||
.delete(item.id)
|
.delete(item.id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1737,6 +1734,8 @@ class CartService extends BaseService {
|
|||||||
regionId: string,
|
regionId: string,
|
||||||
countryCode: string | null
|
countryCode: string | null
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||||
|
|
||||||
if (cart.completed_at || cart.payment_authorized_at) {
|
if (cart.completed_at || cart.payment_authorized_at) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.NOT_ALLOWED,
|
MedusaError.Types.NOT_ALLOWED,
|
||||||
@@ -1750,7 +1749,7 @@ class CartService extends BaseService {
|
|||||||
.retrieve(regionId, {
|
.retrieve(regionId, {
|
||||||
relations: ["countries"],
|
relations: ["countries"],
|
||||||
})
|
})
|
||||||
const addrRepo = this.transactionManager_.getCustomRepository(
|
const addrRepo = transactionManager.getCustomRepository(
|
||||||
this.addressRepository_
|
this.addressRepository_
|
||||||
)
|
)
|
||||||
cart.region = region
|
cart.region = region
|
||||||
@@ -1828,7 +1827,7 @@ class CartService extends BaseService {
|
|||||||
// new shipping method
|
// new shipping method
|
||||||
if (cart.shipping_methods && cart.shipping_methods.length) {
|
if (cart.shipping_methods && cart.shipping_methods.length) {
|
||||||
await this.shippingOptionService_
|
await this.shippingOptionService_
|
||||||
.withTransaction(this.transactionManager_)
|
.withTransaction(transactionManager)
|
||||||
.deleteShippingMethods(cart.shipping_methods)
|
.deleteShippingMethods(cart.shipping_methods)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1841,7 +1840,7 @@ class CartService extends BaseService {
|
|||||||
cart.gift_cards = []
|
cart.gift_cards = []
|
||||||
|
|
||||||
if (cart.payment_sessions && cart.payment_sessions.length) {
|
if (cart.payment_sessions && cart.payment_sessions.length) {
|
||||||
const paymentSessionRepo = this.transactionManager_.getCustomRepository(
|
const paymentSessionRepo = transactionManager.getCustomRepository(
|
||||||
this.paymentSessionRepository_
|
this.paymentSessionRepository_
|
||||||
)
|
)
|
||||||
await paymentSessionRepo.delete({
|
await paymentSessionRepo.delete({
|
||||||
@@ -1859,7 +1858,7 @@ class CartService extends BaseService {
|
|||||||
* @param cartId - the id of the cart to delete
|
* @param cartId - the id of the cart to delete
|
||||||
* @return the deleted cart or undefined if the cart was not found.
|
* @return the deleted cart or undefined if the cart was not found.
|
||||||
*/
|
*/
|
||||||
async delete(cartId: string): Promise<string> {
|
async delete(cartId: string): Promise<Cart> {
|
||||||
return await this.atomicPhase_(
|
return await this.atomicPhase_(
|
||||||
async (transactionManager: EntityManager) => {
|
async (transactionManager: EntityManager) => {
|
||||||
const cart = await this.retrieve(cartId, {
|
const cart = await this.retrieve(cartId, {
|
||||||
@@ -1913,7 +1912,7 @@ class CartService extends BaseService {
|
|||||||
this.cartRepository_
|
this.cartRepository_
|
||||||
)
|
)
|
||||||
|
|
||||||
const validatedId = this.validateId_(cartId)
|
const validatedId = validateId(cartId)
|
||||||
if (typeof key !== "string") {
|
if (typeof key !== "string") {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_ARGUMENT,
|
MedusaError.Types.INVALID_ARGUMENT,
|
||||||
@@ -1972,20 +1971,22 @@ class CartService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async refreshAdjustments_(cart: Cart): Promise<void> {
|
protected async refreshAdjustments_(cart: Cart): Promise<void> {
|
||||||
|
const transactionManager = this.transactionManager_ ?? this.manager_
|
||||||
|
|
||||||
const nonReturnLineIDs = cart.items
|
const nonReturnLineIDs = cart.items
|
||||||
.filter((item) => !item.is_return)
|
.filter((item) => !item.is_return)
|
||||||
.map((i) => i.id)
|
.map((i) => i.id)
|
||||||
|
|
||||||
// delete all old non return line item adjustments
|
// delete all old non return line item adjustments
|
||||||
await this.lineItemAdjustmentService_
|
await this.lineItemAdjustmentService_
|
||||||
.withTransaction(this.transactionManager_)
|
.withTransaction(transactionManager)
|
||||||
.delete({
|
.delete({
|
||||||
item_id: nonReturnLineIDs,
|
item_id: nonReturnLineIDs,
|
||||||
})
|
})
|
||||||
|
|
||||||
// potentially create/update line item adjustments
|
// potentially create/update line item adjustments
|
||||||
await this.lineItemAdjustmentService_
|
await this.lineItemAdjustmentService_
|
||||||
.withTransaction(this.transactionManager_)
|
.withTransaction(transactionManager)
|
||||||
.createAdjustments(cart)
|
.createAdjustments(cart)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2001,7 +2002,7 @@ class CartService extends BaseService {
|
|||||||
const cartRepo = transactionManager.getCustomRepository(
|
const cartRepo = transactionManager.getCustomRepository(
|
||||||
this.cartRepository_
|
this.cartRepository_
|
||||||
)
|
)
|
||||||
const validatedId = this.validateId_(cartId)
|
const validatedId = validateId(cartId)
|
||||||
|
|
||||||
if (typeof key !== "string") {
|
if (typeof key !== "string") {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
|
|||||||
@@ -71,5 +71,5 @@ export type CartUpdateProps = {
|
|||||||
discounts?: Discount[]
|
discounts?: Discount[]
|
||||||
customer_id?: string
|
customer_id?: string
|
||||||
context?: object
|
context?: object
|
||||||
metadata?: object
|
metadata?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,20 @@
|
|||||||
import { FindConfig, Writable } from "../types/common"
|
import {
|
||||||
|
DateComparisonOperator,
|
||||||
|
FindConfig,
|
||||||
|
NumericalComparisonOperator,
|
||||||
|
StringComparisonOperator,
|
||||||
|
Writable,
|
||||||
|
} from "../types/common"
|
||||||
import { FindOperator, In, Raw } from "typeorm"
|
import { FindOperator, In, Raw } from "typeorm"
|
||||||
|
|
||||||
type Selector<TEntity> = { [key in keyof TEntity]?: unknown }
|
type Selector<TEntity> = {
|
||||||
|
[key in keyof TEntity]?: TEntity[key]
|
||||||
|
| TEntity[key][]
|
||||||
|
| DateComparisonOperator
|
||||||
|
| StringComparisonOperator
|
||||||
|
| NumericalComparisonOperator
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to build TypeORM queries.
|
* Used to build TypeORM queries.
|
||||||
* @param selector The selector
|
* @param selector The selector
|
||||||
@@ -16,7 +29,7 @@ export function buildQuery<TEntity = unknown>(
|
|||||||
withDeleted?: boolean
|
withDeleted?: boolean
|
||||||
} {
|
} {
|
||||||
const build = (
|
const build = (
|
||||||
obj: Record<string, unknown>
|
obj: Selector<TEntity>
|
||||||
): Partial<Writable<TEntity>> => {
|
): Partial<Writable<TEntity>> => {
|
||||||
return Object.entries(obj).reduce((acc, [key, value]: any) => {
|
return Object.entries(obj).reduce((acc, [key, value]: any) => {
|
||||||
// Undefined values indicate that they have no significance to the query.
|
// Undefined values indicate that they have no significance to the query.
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export * from './build-query'
|
||||||
|
export * from './set-metadata'
|
||||||
|
export * from './validate-id'
|
||||||
@@ -6,7 +6,7 @@ import { MedusaError } from "medusa-core-utils/dist"
|
|||||||
* @param metadata - the metadata to set
|
* @param metadata - the metadata to set
|
||||||
* @return resolves to the updated result.
|
* @return resolves to the updated result.
|
||||||
*/
|
*/
|
||||||
export function setMetadata_(
|
export function setMetadata(
|
||||||
obj: { metadata: Record<string, unknown> },
|
obj: { metadata: Record<string, unknown> },
|
||||||
metadata: Record<string, unknown>
|
metadata: Record<string, unknown>
|
||||||
): Record<string, unknown> {
|
): Record<string, unknown> {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
import { MedusaError } from "medusa-core-utils/dist"
|
import { MedusaError } from "medusa-core-utils/dist"
|
||||||
|
|
||||||
export function validateId_(
|
export function validateId(
|
||||||
rawId: string,
|
rawId: string,
|
||||||
config: { prefix?: string; length?: number } = {}
|
config: { prefix?: string; length?: number } = {}
|
||||||
): string {
|
): string {
|
||||||
|
|||||||
Reference in new issue
Block a user