feat(link-modules,modules-sdk, utils, types, products) - Remote Link and Link modules (#4695)
What:
- Definition of all Modules links
- `link-modules` package to manage the creation of all pre-defined link or custom ones
```typescript
import { initialize as iniInventory } from "@medusajs/inventory";
import { initialize as iniProduct } from "@medusajs/product";
import {
initialize as iniLinks,
runMigrations as migrateLinks
} from "@medusajs/link-modules";
await Promise.all([iniInventory(), iniProduct()]);
await migrateLinks(); // create tables based on previous loaded modules
await iniLinks(); // load link based on previous loaded modules
await iniLinks(undefined, [
{
serviceName: "product_custom_translation_service_link",
isLink: true,
databaseConfig: {
tableName: "product_transalations",
},
alias: [
{
name: "translations",
},
],
primaryKeys: ["id", "product_id", "translation_id"],
relationships: [
{
serviceName: Modules.PRODUCT,
primaryKey: "id",
foreignKey: "product_id",
alias: "product",
},
{
serviceName: "custom_translation_service",
primaryKey: "id",
foreignKey: "translation_id",
alias: "transalation",
deleteCascade: true,
},
],
extends: [
{
serviceName: Modules.PRODUCT,
relationship: {
serviceName: "product_custom_translation_service_link",
primaryKey: "product_id",
foreignKey: "id",
alias: "translations",
isList: true,
},
},
{
serviceName: "custom_translation_service",
relationship: {
serviceName: "product_custom_translation_service_link",
primaryKey: "product_id",
foreignKey: "id",
alias: "product_link",
},
},
],
},
]); // custom links
```
Remote Link
```typescript
import { RemoteLink, Modules } from "@medusajs/modules-sdk";
// [...] initialize modules and links
const remoteLink = new RemoteLink();
// upsert the relationship
await remoteLink.create({ // one (object) or many (array)
[Modules.PRODUCT]: {
variant_id: "var_abc",
},
[Modules.INVENTORY]: {
inventory_item_id: "iitem_abc",
},
data: { // optional additional fields
required_quantity: 5
}
});
// dismiss (doesn't cascade)
await remoteLink.dismiss({ // one (object) or many (array)
[Modules.PRODUCT]: {
variant_id: "var_abc",
},
[Modules.INVENTORY]: {
inventory_item_id: "iitem_abc",
},
});
// delete
await remoteLink.delete({
// every key is a module
[Modules.PRODUCT]: {
// every key is a linkable field
variant_id: "var_abc", // single or multiple values
},
});
// restore
await remoteLink.restore({
// every key is a module
[Modules.PRODUCT]: {
// every key is a linkable field
variant_id: "var_abc", // single or multiple values
},
});
```
Co-authored-by: Riqwan Thamir <5105988+riqwan@users.noreply.github.com>
This commit is contained in:
co-authored by
Riqwan Thamir
parent
bc4c9e0d32
commit
4d16acf5f0
@@ -1,13 +1,14 @@
|
||||
import {
|
||||
ExternalModuleDeclaration,
|
||||
InternalModuleDeclaration,
|
||||
MedusaModule,
|
||||
MODULE_PACKAGE_NAMES,
|
||||
MedusaModule,
|
||||
Modules,
|
||||
} from "@medusajs/modules-sdk"
|
||||
import { IProductModuleService, ModulesSdkTypes } from "@medusajs/types"
|
||||
import { moduleDefinition } from "../module-definition"
|
||||
|
||||
import { InitializeModuleInjectableDependencies } from "../types"
|
||||
import { moduleDefinition } from "../module-definition"
|
||||
|
||||
export const initialize = async (
|
||||
options?:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { JoinerServiceConfig } from "@medusajs/types"
|
||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||
import { MapToConfig } from "@medusajs/utils"
|
||||
import {
|
||||
Product,
|
||||
ProductCategory,
|
||||
@@ -10,10 +11,9 @@ import {
|
||||
ProductVariant,
|
||||
} from "@models"
|
||||
import ProductImage from "./models/product-image"
|
||||
import { MapToConfig } from "@medusajs/utils"
|
||||
|
||||
export enum LinkableKeys {
|
||||
PRODUCT_ID = "product_id",
|
||||
PRODUCT_ID = "product_id", // Main service ID must the first
|
||||
PRODUCT_HANDLE = "product_handle",
|
||||
VARIANT_ID = "variant_id",
|
||||
VARIANT_SKU = "variant_sku",
|
||||
@@ -55,9 +55,10 @@ export const entityNameToLinkableKeysMap: MapToConfig = {
|
||||
],
|
||||
}
|
||||
|
||||
export const joinerConfig: JoinerServiceConfig = {
|
||||
export const joinerConfig: ModuleJoinerConfig = {
|
||||
serviceName: Modules.PRODUCT,
|
||||
primaryKeys: ["id", "handle"],
|
||||
linkableKeys: Object.values(LinkableKeys),
|
||||
alias: [
|
||||
{
|
||||
name: "product",
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import {
|
||||
ProductCategoryService,
|
||||
ProductCollectionService,
|
||||
ProductOptionService,
|
||||
ProductService,
|
||||
ProductTagService,
|
||||
ProductTypeService,
|
||||
ProductVariantService,
|
||||
} from "@services"
|
||||
Context,
|
||||
CreateProductOnlyDTO,
|
||||
DAL,
|
||||
FindConfig,
|
||||
IEventBusModuleService,
|
||||
InternalModuleDeclaration,
|
||||
ModuleJoinerConfig,
|
||||
ProductTypes,
|
||||
RestoreReturn,
|
||||
SoftDeleteReturn,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
Image,
|
||||
Product,
|
||||
@@ -18,15 +21,14 @@ import {
|
||||
ProductVariant,
|
||||
} from "@models"
|
||||
import {
|
||||
Context,
|
||||
CreateProductOnlyDTO,
|
||||
DAL,
|
||||
FindConfig,
|
||||
IEventBusModuleService,
|
||||
InternalModuleDeclaration,
|
||||
JoinerServiceConfig,
|
||||
ProductTypes,
|
||||
} from "@medusajs/types"
|
||||
ProductCategoryService,
|
||||
ProductCollectionService,
|
||||
ProductOptionService,
|
||||
ProductService,
|
||||
ProductTagService,
|
||||
ProductTypeService,
|
||||
ProductVariantService,
|
||||
} from "@services"
|
||||
|
||||
import ProductImageService from "./product-image"
|
||||
|
||||
@@ -136,7 +138,7 @@ export default class ProductModuleService<
|
||||
this.eventBusModuleService_ = eventBusModuleService
|
||||
}
|
||||
|
||||
__joinerConfig(): JoinerServiceConfig {
|
||||
__joinerConfig(): ModuleJoinerConfig {
|
||||
return joinerConfig
|
||||
}
|
||||
|
||||
@@ -1071,11 +1073,7 @@ export default class ProductModuleService<
|
||||
>
|
||||
>(
|
||||
productIds: string[],
|
||||
{
|
||||
returnLinkableKeys,
|
||||
}: { returnLinkableKeys?: TReturnableLinkableKeys[] } = {
|
||||
returnLinkableKeys: [],
|
||||
},
|
||||
{ returnLinkableKeys }: SoftDeleteReturn<TReturnableLinkableKeys> = {},
|
||||
sharedContext: Context = {}
|
||||
): Promise<Record<Lowercase<keyof typeof LinkableKeys>, string[]> | void> {
|
||||
const [products, cascadedEntitiesMap] = await this.softDelete_(
|
||||
@@ -1098,10 +1096,12 @@ export default class ProductModuleService<
|
||||
|
||||
let mappedCascadedEntitiesMap
|
||||
if (returnLinkableKeys) {
|
||||
// Map internal table/column names to their respective external linkable keys
|
||||
// eg: product.id = product_id, variant.id = variant_id
|
||||
mappedCascadedEntitiesMap = mapObjectTo<
|
||||
Record<Lowercase<keyof typeof LinkableKeys>, string[]>
|
||||
>(cascadedEntitiesMap, entityNameToLinkableKeysMap, {
|
||||
pick: returnLinkableKeys as string[],
|
||||
pick: returnLinkableKeys,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1116,22 +1116,39 @@ export default class ProductModuleService<
|
||||
return await this.productService_.softDelete(productIds, sharedContext)
|
||||
}
|
||||
|
||||
async restore(
|
||||
async restore<
|
||||
TReturnableLinkableKeys extends string = Lowercase<
|
||||
keyof typeof LinkableKeys
|
||||
>
|
||||
>(
|
||||
productIds: string[],
|
||||
{ returnLinkableKeys }: RestoreReturn<TReturnableLinkableKeys> = {},
|
||||
sharedContext: Context = {}
|
||||
): Promise<ProductTypes.ProductDTO[]> {
|
||||
const products = await this.restore_(productIds, sharedContext)
|
||||
): Promise<Record<Lowercase<keyof typeof LinkableKeys>, string[]> | void> {
|
||||
const [_, cascadedEntitiesMap] = await this.restore_(
|
||||
productIds,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return this.baseRepository_.serialize<ProductTypes.ProductDTO[]>(products, {
|
||||
populate: true,
|
||||
})
|
||||
let mappedCascadedEntitiesMap
|
||||
if (returnLinkableKeys) {
|
||||
// Map internal table/column names to their respective external linkable keys
|
||||
// eg: product.id = product_id, variant.id = variant_id
|
||||
mappedCascadedEntitiesMap = mapObjectTo<
|
||||
Record<Lowercase<keyof typeof LinkableKeys>, string[]>
|
||||
>(cascadedEntitiesMap, entityNameToLinkableKeysMap, {
|
||||
pick: returnLinkableKeys,
|
||||
})
|
||||
}
|
||||
|
||||
return mappedCascadedEntitiesMap ? mappedCascadedEntitiesMap : void 0
|
||||
}
|
||||
|
||||
@InjectTransactionManager(shouldForceTransaction, "baseRepository_")
|
||||
async restore_(
|
||||
productIds: string[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TProduct[]> {
|
||||
): Promise<[TProduct[], Record<string, unknown[]>]> {
|
||||
return await this.productService_.restore(productIds, sharedContext)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Product } from "@models"
|
||||
import {
|
||||
Context,
|
||||
DAL,
|
||||
@@ -9,12 +8,13 @@ import {
|
||||
import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
isDefined,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
ModulesSdkUtils,
|
||||
ProductUtils,
|
||||
isDefined,
|
||||
} from "@medusajs/utils"
|
||||
import { Product } from "@models"
|
||||
import { ProductRepository } from "@repositories"
|
||||
|
||||
import { ProductServiceTypes } from "../types/services"
|
||||
@@ -178,7 +178,7 @@ export default class ProductService<TEntity extends Product = Product> {
|
||||
async restore(
|
||||
productIds: string[],
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity[]> {
|
||||
): Promise<[TEntity[], Record<string, unknown[]>]> {
|
||||
return await this.productRepository_.restore(productIds, {
|
||||
transactionManager: sharedContext.transactionManager,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user