feat(prduct, utils, types): Create soft delete pattern for link module (#4649)
* feat(prouct, utils, types): Create soft delete pattern for link module * add comment * add comment * finalise * remove linkable keys * cleanup and tests * cleanup * add some comments and renaming * re work * fix tests --------- Co-authored-by: Riqwan Thamir <rmthamir@gmail.com>
This commit is contained in:
co-authored by
Riqwan Thamir
parent
fc6c9df035
commit
ce3326c5fb
@@ -1,5 +1,59 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { JoinerServiceConfig } from "@medusajs/types"
|
||||
import {
|
||||
Product,
|
||||
ProductCategory,
|
||||
ProductCollection,
|
||||
ProductOption,
|
||||
ProductTag,
|
||||
ProductType,
|
||||
ProductVariant,
|
||||
} from "@models"
|
||||
import ProductImage from "./models/product-image"
|
||||
import { MapToConfig } from "@medusajs/utils"
|
||||
|
||||
export enum LinkableKeys {
|
||||
PRODUCT_ID = "product_id",
|
||||
PRODUCT_HANDLE = "product_handle",
|
||||
VARIANT_ID = "variant_id",
|
||||
VARIANT_SKU = "variant_sku",
|
||||
PRODUCT_OPTION_ID = "product_option_id",
|
||||
PRODUCT_TYPE_ID = "product_type_id",
|
||||
PRODUCT_CATEGORY_ID = "product_category_id",
|
||||
PRODUCT_COLLECTION_ID = "product_collection_id",
|
||||
PRODUCT_TAG_ID = "product_tag_id",
|
||||
PRODUCT_IMAGE_ID = "product_image_id",
|
||||
}
|
||||
|
||||
export const entityNameToLinkableKeysMap: MapToConfig = {
|
||||
[Product.name]: [
|
||||
{ mapTo: LinkableKeys.PRODUCT_ID, valueFrom: "id" },
|
||||
{
|
||||
mapTo: LinkableKeys.PRODUCT_HANDLE,
|
||||
valueFrom: "handle",
|
||||
},
|
||||
],
|
||||
[ProductVariant.name]: [
|
||||
{ mapTo: LinkableKeys.VARIANT_ID, valueFrom: "id" },
|
||||
{ mapTo: LinkableKeys.VARIANT_SKU, valueFrom: "sku" },
|
||||
],
|
||||
[ProductOption.name]: [
|
||||
{ mapTo: LinkableKeys.PRODUCT_OPTION_ID, valueFrom: "id" },
|
||||
],
|
||||
[ProductType.name]: [
|
||||
{ mapTo: LinkableKeys.PRODUCT_TYPE_ID, valueFrom: "id" },
|
||||
],
|
||||
[ProductCategory.name]: [
|
||||
{ mapTo: LinkableKeys.PRODUCT_CATEGORY_ID, valueFrom: "id" },
|
||||
],
|
||||
[ProductCollection.name]: [
|
||||
{ mapTo: LinkableKeys.PRODUCT_COLLECTION_ID, valueFrom: "id" },
|
||||
],
|
||||
[ProductTag.name]: [{ mapTo: LinkableKeys.PRODUCT_TAG_ID, valueFrom: "id" }],
|
||||
[ProductImage.name]: [
|
||||
{ mapTo: LinkableKeys.PRODUCT_IMAGE_ID, valueFrom: "id" },
|
||||
],
|
||||
}
|
||||
|
||||
export const joinerConfig: JoinerServiceConfig = {
|
||||
serviceName: Modules.PRODUCT,
|
||||
|
||||
@@ -2,12 +2,12 @@ import { ProductCategory } from "@models"
|
||||
import { ProductCategoryRepository } from "@repositories"
|
||||
import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
ModulesSdkUtils,
|
||||
MedusaError,
|
||||
isDefined,
|
||||
InjectTransactionManager,
|
||||
InjectManager,
|
||||
MedusaContext
|
||||
InjectTransactionManager,
|
||||
isDefined,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
ModulesSdkUtils,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import { shouldForceTransaction } from "../utils"
|
||||
@@ -39,9 +39,12 @@ export default class ProductCategoryService<
|
||||
)
|
||||
}
|
||||
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<ProductCategory>({
|
||||
id: productCategoryId,
|
||||
}, config)
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<ProductCategory>(
|
||||
{
|
||||
id: productCategoryId,
|
||||
},
|
||||
config
|
||||
)
|
||||
|
||||
const transformOptions = {
|
||||
includeDescendantsTree: true,
|
||||
@@ -111,31 +114,37 @@ export default class ProductCategoryService<
|
||||
)) as [TEntity[], number]
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "productCategoryRepository_")
|
||||
@InjectTransactionManager(
|
||||
shouldForceTransaction,
|
||||
"productCategoryRepository_"
|
||||
)
|
||||
async create(
|
||||
data: ProductCategoryServiceTypes.CreateProductCategoryDTO,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity> {
|
||||
return (await (this.productCategoryRepository_ as ProductCategoryRepository).create(
|
||||
data,
|
||||
sharedContext
|
||||
)) as TEntity
|
||||
return (await (
|
||||
this.productCategoryRepository_ as unknown as ProductCategoryRepository
|
||||
).create(data, sharedContext)) as TEntity
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "productCategoryRepository_")
|
||||
@InjectTransactionManager(
|
||||
shouldForceTransaction,
|
||||
"productCategoryRepository_"
|
||||
)
|
||||
async update(
|
||||
id: string,
|
||||
data: ProductCategoryServiceTypes.UpdateProductCategoryDTO,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity> {
|
||||
return (await (this.productCategoryRepository_ as ProductCategoryRepository).update(
|
||||
id,
|
||||
data,
|
||||
sharedContext
|
||||
)) as TEntity
|
||||
return (await (
|
||||
this.productCategoryRepository_ as unknown as ProductCategoryRepository
|
||||
).update(id, data, sharedContext)) as TEntity
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "productCategoryRepository_")
|
||||
@InjectTransactionManager(
|
||||
shouldForceTransaction,
|
||||
"productCategoryRepository_"
|
||||
)
|
||||
async delete(
|
||||
id: string,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types"
|
||||
import {
|
||||
ModulesSdkUtils,
|
||||
retrieveEntity,
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
InjectManager,
|
||||
ModulesSdkUtils,
|
||||
retrieveEntity,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import { shouldForceTransaction } from "../utils"
|
||||
@@ -85,29 +85,36 @@ export default class ProductCollectionService<
|
||||
return queryOptions
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "productCollectionRepository_")
|
||||
@InjectTransactionManager(
|
||||
shouldForceTransaction,
|
||||
"productCollectionRepository_"
|
||||
)
|
||||
async create(
|
||||
data: ProductTypes.CreateProductCollectionDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
return (await (this.productCollectionRepository_ as ProductCollectionRepository).create(
|
||||
data,
|
||||
sharedContext
|
||||
)) as TEntity[]
|
||||
return (await (
|
||||
this.productCollectionRepository_ as ProductCollectionRepository
|
||||
).create(data, sharedContext)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "productCollectionRepository_")
|
||||
@InjectTransactionManager(
|
||||
shouldForceTransaction,
|
||||
"productCollectionRepository_"
|
||||
)
|
||||
async update(
|
||||
data: ProductTypes.UpdateProductCollectionDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
return (await (this.productCollectionRepository_ as ProductCollectionRepository).update(
|
||||
data,
|
||||
sharedContext
|
||||
)) as TEntity[]
|
||||
return (await (
|
||||
this.productCollectionRepository_ as ProductCollectionRepository
|
||||
).update(data, sharedContext)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "productCollectionRepository_")
|
||||
@InjectTransactionManager(
|
||||
shouldForceTransaction,
|
||||
"productCollectionRepository_"
|
||||
)
|
||||
async delete(
|
||||
ids: string[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
|
||||
@@ -38,12 +38,17 @@ import {
|
||||
isDefined,
|
||||
isString,
|
||||
kebabCase,
|
||||
mapObjectTo,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import { shouldForceTransaction } from "../utils"
|
||||
import { joinerConfig } from "./../joiner-config"
|
||||
import {
|
||||
entityNameToLinkableKeysMap,
|
||||
joinerConfig,
|
||||
LinkableKeys,
|
||||
} from "./../joiner-config"
|
||||
import { ProductCategoryServiceTypes } from "../types"
|
||||
|
||||
type InjectedDependencies = {
|
||||
@@ -958,7 +963,7 @@ export default class ProductModuleService<
|
||||
) {
|
||||
if (isDefined(productData.type)) {
|
||||
const productType = await this.productTypeService_.upsert(
|
||||
[productData.type],
|
||||
[productData.type!],
|
||||
sharedContext
|
||||
)
|
||||
|
||||
@@ -974,22 +979,41 @@ export default class ProductModuleService<
|
||||
await this.productService_.delete(productIds, sharedContext)
|
||||
}
|
||||
|
||||
async softDelete(
|
||||
async softDelete<
|
||||
TReturnableLinkableKeys extends string = Lowercase<
|
||||
keyof typeof LinkableKeys
|
||||
>
|
||||
>(
|
||||
productIds: string[],
|
||||
{
|
||||
returnLinkableKeys,
|
||||
}: { returnLinkableKeys?: TReturnableLinkableKeys[] } = {
|
||||
returnLinkableKeys: [],
|
||||
},
|
||||
sharedContext: Context = {}
|
||||
): Promise<ProductTypes.ProductDTO[]> {
|
||||
const products = await this.softDelete_(productIds, sharedContext)
|
||||
): Promise<Record<Lowercase<keyof typeof LinkableKeys>, string[]> | void> {
|
||||
let [, cascadedEntitiesMap] = await this.softDelete_(
|
||||
productIds,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return this.baseRepository_.serialize<ProductTypes.ProductDTO[]>(products, {
|
||||
populate: true,
|
||||
})
|
||||
let mappedCascadedEntitiesMap
|
||||
if (returnLinkableKeys) {
|
||||
mappedCascadedEntitiesMap = mapObjectTo<
|
||||
Record<Lowercase<keyof typeof LinkableKeys>, string[]>
|
||||
>(cascadedEntitiesMap, entityNameToLinkableKeysMap, {
|
||||
pick: returnLinkableKeys as string[],
|
||||
})
|
||||
}
|
||||
|
||||
return mappedCascadedEntitiesMap ? mappedCascadedEntitiesMap : void 0
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
||||
protected async softDelete_(
|
||||
productIds: string[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TProduct[]> {
|
||||
): Promise<[TProduct[], Record<string, unknown[]>]> {
|
||||
return await this.productService_.softDelete(productIds, sharedContext)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ import {
|
||||
import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
isDefined,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
ModulesSdkUtils,
|
||||
isDefined,
|
||||
} from "@medusajs/utils"
|
||||
import { ProductRepository } from "@repositories"
|
||||
|
||||
@@ -44,9 +44,12 @@ export default class ProductService<TEntity extends Product = Product> {
|
||||
)
|
||||
}
|
||||
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<Product>({
|
||||
id: productId,
|
||||
}, config)
|
||||
const queryOptions = ModulesSdkUtils.buildQuery<Product>(
|
||||
{
|
||||
id: productId,
|
||||
},
|
||||
config
|
||||
)
|
||||
|
||||
const product = await this.productRepository_.find(
|
||||
queryOptions,
|
||||
@@ -140,7 +143,7 @@ export default class ProductService<TEntity extends Product = Product> {
|
||||
data: ProductServiceTypes.UpdateProductDTO[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
return await (this.productRepository_ as ProductRepository).update(
|
||||
return (await (this.productRepository_ as ProductRepository).update(
|
||||
data as WithRequiredProperty<
|
||||
ProductServiceTypes.UpdateProductDTO,
|
||||
"id"
|
||||
@@ -148,7 +151,7 @@ export default class ProductService<TEntity extends Product = Product> {
|
||||
{
|
||||
transactionManager: sharedContext.transactionManager,
|
||||
}
|
||||
) as TEntity[]
|
||||
)) as TEntity[]
|
||||
}
|
||||
|
||||
@InjectTransactionManager(doNotForceTransaction, "productRepository_")
|
||||
@@ -165,7 +168,7 @@ export default class ProductService<TEntity extends Product = Product> {
|
||||
async softDelete(
|
||||
productIds: string[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
): Promise<[TEntity[], Record<string, unknown[]>]> {
|
||||
return await this.productRepository_.softDelete(productIds, {
|
||||
transactionManager: sharedContext.transactionManager,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user