feat(product): Move mikro orm utils to the utils package (#4631)
Move utils to the utils package as much as possible Co-authored-by: Shahed Nasser <27354907+shahednasser@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
648eb106d6
commit
4073b73130
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@medusajs/product": patch
|
||||
"@medusajs/utils": patch
|
||||
"@medusajs/types": patch
|
||||
---
|
||||
|
||||
feat(product): Move mikro orm utils to the utils package
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BaseRepository } from "../../src/repositories/base"
|
||||
import { DALUtils } from "@medusajs/utils"
|
||||
|
||||
class CustomRepository extends BaseRepository {
|
||||
class CustomRepository extends DALUtils.MikroOrmBaseRepository {
|
||||
constructor({ manager }) {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
|
||||
+121
-85
@@ -1,7 +1,6 @@
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
import { IProductModuleService, ProductTypes } from "@medusajs/types"
|
||||
import { Product, ProductCategory } from "@models"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
|
||||
import { initialize } from "../../../../src"
|
||||
import { DB_URL, TestDatabase } from "../../../utils"
|
||||
@@ -43,15 +42,18 @@ describe("ProductModuleService product categories", () => {
|
||||
status: ProductTypes.ProductStatus.PUBLISHED,
|
||||
})
|
||||
|
||||
const productCategoriesData = [{
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [productOne],
|
||||
},{
|
||||
id: "test-2",
|
||||
name: "category",
|
||||
products: [productTwo],
|
||||
}]
|
||||
const productCategoriesData = [
|
||||
{
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [productOne],
|
||||
},
|
||||
{
|
||||
id: "test-2",
|
||||
name: "category",
|
||||
products: [productTwo],
|
||||
},
|
||||
]
|
||||
|
||||
productCategories = await createProductCategories(
|
||||
testManager,
|
||||
@@ -121,10 +123,12 @@ describe("ProductModuleService product categories", () => {
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
})],
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
@@ -191,10 +195,12 @@ describe("ProductModuleService product categories", () => {
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
})],
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
])
|
||||
})
|
||||
@@ -203,35 +209,34 @@ describe("ProductModuleService product categories", () => {
|
||||
describe("retrieveCategory", () => {
|
||||
it("should return the requested category", async () => {
|
||||
const result = await service.retrieveCategory(productCategoryOne.id, {
|
||||
select: ["id", "name"]
|
||||
select: ["id", "name"],
|
||||
})
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should return requested attributes when requested through config", async () => {
|
||||
const result = await service.retrieveCategory(
|
||||
productCategoryOne.id,
|
||||
{
|
||||
select: ["id", "name", "products.title"],
|
||||
relations: ["products"],
|
||||
}
|
||||
)
|
||||
const result = await service.retrieveCategory(productCategoryOne.id, {
|
||||
select: ["id", "name", "products.title"],
|
||||
relations: ["products"],
|
||||
})
|
||||
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-1",
|
||||
name: "category 1",
|
||||
products: [expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
})],
|
||||
}),
|
||||
products: [
|
||||
expect.objectContaining({
|
||||
id: "product-1",
|
||||
title: "product 1",
|
||||
}),
|
||||
],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
@@ -244,7 +249,9 @@ describe("ProductModuleService product categories", () => {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual("ProductCategory with id: does-not-exist was not found")
|
||||
expect(error.message).toEqual(
|
||||
"ProductCategory with id: does-not-exist was not found"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -255,11 +262,14 @@ describe("ProductModuleService product categories", () => {
|
||||
parent_category_id: productCategoryOne.id,
|
||||
})
|
||||
|
||||
const [productCategory] = await service.listCategories({
|
||||
name: "New Category"
|
||||
}, {
|
||||
select: ["name", "rank"]
|
||||
})
|
||||
const [productCategory] = await service.listCategories(
|
||||
{
|
||||
name: "New Category",
|
||||
},
|
||||
{
|
||||
select: ["name", "rank"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategory).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -273,7 +283,7 @@ describe("ProductModuleService product categories", () => {
|
||||
await service.createCategory({
|
||||
name: "New Category",
|
||||
parent_category_id: productCategoryOne.id,
|
||||
rank: 0
|
||||
rank: 0,
|
||||
})
|
||||
|
||||
await service.createCategory({
|
||||
@@ -281,11 +291,14 @@ describe("ProductModuleService product categories", () => {
|
||||
parent_category_id: productCategoryOne.id,
|
||||
})
|
||||
|
||||
const [productCategoryNew] = await service.listCategories({
|
||||
name: "New Category 2"
|
||||
}, {
|
||||
select: ["name", "rank"]
|
||||
})
|
||||
const [productCategoryNew] = await service.listCategories(
|
||||
{
|
||||
name: "New Category 2",
|
||||
},
|
||||
{
|
||||
select: ["name", "rank"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategoryNew).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -299,11 +312,14 @@ describe("ProductModuleService product categories", () => {
|
||||
parent_category_id: productCategoryNew.id,
|
||||
})
|
||||
|
||||
const [productCategoryWithParent] = await service.listCategories({
|
||||
name: "New Category 2.1"
|
||||
}, {
|
||||
select: ["name", "rank", "parent_category_id"]
|
||||
})
|
||||
const [productCategoryWithParent] = await service.listCategories(
|
||||
{
|
||||
name: "New Category 2.1",
|
||||
},
|
||||
{
|
||||
select: ["name", "rank", "parent_category_id"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategoryWithParent).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -342,12 +358,15 @@ describe("ProductModuleService product categories", () => {
|
||||
|
||||
it("should update the name of the category successfully", async () => {
|
||||
await service.updateCategory(productCategoryZero.id, {
|
||||
name: "New Category"
|
||||
name: "New Category",
|
||||
})
|
||||
|
||||
const productCategory = await service.retrieveCategory(productCategoryZero.id, {
|
||||
select: ["name"]
|
||||
})
|
||||
const productCategory = await service.retrieveCategory(
|
||||
productCategoryZero.id,
|
||||
{
|
||||
select: ["name"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategory.name).toEqual("New Category")
|
||||
})
|
||||
@@ -357,13 +376,15 @@ describe("ProductModuleService product categories", () => {
|
||||
|
||||
try {
|
||||
await service.updateCategory("does-not-exist", {
|
||||
name: "New Category"
|
||||
name: "New Category",
|
||||
})
|
||||
} catch (e) {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(`ProductCategory not found ({ id: 'does-not-exist' })`)
|
||||
expect(error.message).toEqual(
|
||||
`ProductCategory not found ({ id: 'does-not-exist' })`
|
||||
)
|
||||
})
|
||||
|
||||
it("should reorder rank successfully in the same parent", async () => {
|
||||
@@ -371,11 +392,14 @@ describe("ProductModuleService product categories", () => {
|
||||
rank: 0,
|
||||
})
|
||||
|
||||
const productCategories = await service.listCategories({
|
||||
parent_category_id: null
|
||||
}, {
|
||||
select: ["name", "rank"]
|
||||
})
|
||||
const productCategories = await service.listCategories(
|
||||
{
|
||||
parent_category_id: null,
|
||||
},
|
||||
{
|
||||
select: ["name", "rank"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategories).toEqual(
|
||||
expect.arrayContaining([
|
||||
@@ -390,7 +414,7 @@ describe("ProductModuleService product categories", () => {
|
||||
expect.objectContaining({
|
||||
id: productCategoryOne.id,
|
||||
rank: "2",
|
||||
})
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
@@ -398,14 +422,17 @@ describe("ProductModuleService product categories", () => {
|
||||
it("should reorder rank successfully when changing parent", async () => {
|
||||
await service.updateCategory(productCategoryTwo.id, {
|
||||
rank: 0,
|
||||
parent_category_id: productCategoryZero.id
|
||||
parent_category_id: productCategoryZero.id,
|
||||
})
|
||||
|
||||
const productCategories = await service.listCategories({
|
||||
parent_category_id: productCategoryZero.id
|
||||
}, {
|
||||
select: ["name", "rank"]
|
||||
})
|
||||
const productCategories = await service.listCategories(
|
||||
{
|
||||
parent_category_id: productCategoryZero.id,
|
||||
},
|
||||
{
|
||||
select: ["name", "rank"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategories).toEqual(
|
||||
expect.arrayContaining([
|
||||
@@ -424,7 +451,7 @@ describe("ProductModuleService product categories", () => {
|
||||
expect.objectContaining({
|
||||
id: productCategoryZeroTwo.id,
|
||||
rank: "3",
|
||||
})
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
@@ -432,14 +459,17 @@ describe("ProductModuleService product categories", () => {
|
||||
it("should reorder rank successfully when changing parent and in first position", async () => {
|
||||
await service.updateCategory(productCategoryTwo.id, {
|
||||
rank: 0,
|
||||
parent_category_id: productCategoryZero.id
|
||||
parent_category_id: productCategoryZero.id,
|
||||
})
|
||||
|
||||
const productCategories = await service.listCategories({
|
||||
parent_category_id: productCategoryZero.id
|
||||
}, {
|
||||
select: ["name", "rank"]
|
||||
})
|
||||
const productCategories = await service.listCategories(
|
||||
{
|
||||
parent_category_id: productCategoryZero.id,
|
||||
},
|
||||
{
|
||||
select: ["name", "rank"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategories).toEqual(
|
||||
expect.arrayContaining([
|
||||
@@ -458,7 +488,7 @@ describe("ProductModuleService product categories", () => {
|
||||
expect.objectContaining({
|
||||
id: productCategoryZeroTwo.id,
|
||||
rank: "3",
|
||||
})
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
@@ -492,7 +522,9 @@ describe("ProductModuleService product categories", () => {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(`ProductCategory not found ({ id: 'does-not-exist' })`)
|
||||
expect(error.message).toEqual(
|
||||
`ProductCategory not found ({ id: 'does-not-exist' })`
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw an error when it has children", async () => {
|
||||
@@ -504,17 +536,22 @@ describe("ProductModuleService product categories", () => {
|
||||
error = e
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(`Deleting ProductCategory (category-0-0) with category children is not allowed`)
|
||||
expect(error.message).toEqual(
|
||||
`Deleting ProductCategory (category-0-0) with category children is not allowed`
|
||||
)
|
||||
})
|
||||
|
||||
it("should reorder siblings rank successfully on deleting", async () => {
|
||||
await service.deleteCategory(productCategoryOne.id)
|
||||
|
||||
const productCategories = await service.listCategories({
|
||||
parent_category_id: null
|
||||
}, {
|
||||
select: ["id", "rank"]
|
||||
})
|
||||
const productCategories = await service.listCategories(
|
||||
{
|
||||
parent_category_id: null,
|
||||
},
|
||||
{
|
||||
select: ["id", "rank"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(productCategories).toEqual(
|
||||
expect.arrayContaining([
|
||||
@@ -525,10 +562,9 @@ describe("ProductModuleService product categories", () => {
|
||||
expect.objectContaining({
|
||||
id: productCategoryTwo.id,
|
||||
rank: "1",
|
||||
})
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -6,12 +6,11 @@ import {
|
||||
MODULE_RESOURCE_TYPE,
|
||||
MODULE_SCOPE,
|
||||
} from "@medusajs/modules-sdk"
|
||||
import { MedusaError, ModulesSdkUtils } from "@medusajs/utils"
|
||||
import { DALUtils, MedusaError, ModulesSdkUtils } from "@medusajs/utils"
|
||||
|
||||
import { EntitySchema } from "@mikro-orm/core"
|
||||
|
||||
import * as ProductModels from "@models"
|
||||
import { createConnection } from "../utils"
|
||||
import { ConfigModule, ModulesSdkTypes } from "@medusajs/types"
|
||||
|
||||
export default async (
|
||||
@@ -61,7 +60,7 @@ async function loadDefault({ database, container }) {
|
||||
}
|
||||
|
||||
const entities = Object.values(ProductModels) as unknown as EntitySchema[]
|
||||
const orm = await createConnection(database, entities)
|
||||
const orm = await DALUtils.mikroOrmCreateConnection(database, entities)
|
||||
|
||||
container.register({
|
||||
manager: asValue(orm.em.fork()),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as DefaultRepositories from "@repositories"
|
||||
|
||||
import {
|
||||
BaseRepository,
|
||||
ProductCategoryRepository,
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
BeforeCreate,
|
||||
Collection,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
OneToMany,
|
||||
OptionalProps,
|
||||
@@ -10,14 +11,13 @@ import {
|
||||
Unique,
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import { generateEntityId, kebabCase } from "@medusajs/utils"
|
||||
import { DALUtils, generateEntityId, kebabCase } from "@medusajs/utils"
|
||||
import Product from "./product"
|
||||
import { SoftDeletable } from "../utils"
|
||||
|
||||
type OptionalRelations = "products"
|
||||
|
||||
@Entity({ tableName: "product_collection" })
|
||||
@SoftDeletable()
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductCollection {
|
||||
[OptionalProps]?: OptionalRelations
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
BeforeCreate,
|
||||
Collection,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToMany,
|
||||
OptionalProps,
|
||||
@@ -9,14 +10,13 @@ import {
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import { generateEntityId } from "@medusajs/utils"
|
||||
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
||||
import Product from "./product"
|
||||
import { SoftDeletable } from "../utils"
|
||||
|
||||
type OptionalRelations = "products"
|
||||
|
||||
@Entity({ tableName: "image" })
|
||||
@SoftDeletable()
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductImage {
|
||||
[OptionalProps]?: OptionalRelations
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
BeforeCreate,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToOne,
|
||||
OptionalProps,
|
||||
@@ -8,9 +9,7 @@ import {
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import { ProductOption, ProductVariant } from "./index"
|
||||
|
||||
import { SoftDeletable } from "../utils"
|
||||
import { generateEntityId } from "@medusajs/utils"
|
||||
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
||||
|
||||
type OptionalFields =
|
||||
| "created_at"
|
||||
@@ -22,7 +21,7 @@ type OptionalFields =
|
||||
type OptionalRelations = "product" | "option" | "variant"
|
||||
|
||||
@Entity({ tableName: "product_option_value" })
|
||||
@SoftDeletable()
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductOptionValue {
|
||||
[OptionalProps]?: OptionalFields | OptionalRelations
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { generateEntityId } from "@medusajs/utils"
|
||||
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
Cascade,
|
||||
Collection,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
@@ -13,13 +14,12 @@ import {
|
||||
} from "@mikro-orm/core"
|
||||
import { Product } from "./index"
|
||||
import ProductOptionValue from "./product-option-value"
|
||||
import { SoftDeletable } from "../utils"
|
||||
|
||||
type OptionalRelations = "values" | "product"
|
||||
type OptionalFields = "product_id"
|
||||
|
||||
@Entity({ tableName: "product_option" })
|
||||
@SoftDeletable()
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductOption {
|
||||
[OptionalProps]?: OptionalRelations | OptionalFields
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
BeforeCreate,
|
||||
Collection,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToMany,
|
||||
OptionalProps,
|
||||
@@ -9,14 +10,13 @@ import {
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import { generateEntityId } from "@medusajs/utils"
|
||||
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
||||
import Product from "./product"
|
||||
import { SoftDeletable } from "../utils"
|
||||
|
||||
type OptionalRelations = "products"
|
||||
|
||||
@Entity({ tableName: "product_tag" })
|
||||
@SoftDeletable()
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductTag {
|
||||
[OptionalProps]?: OptionalRelations
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import {
|
||||
BeforeCreate,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import { generateEntityId } from "@medusajs/utils"
|
||||
import { SoftDeletable } from "../utils"
|
||||
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
||||
|
||||
@Entity({ tableName: "product_type" })
|
||||
@SoftDeletable()
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductType {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { generateEntityId } from "@medusajs/utils"
|
||||
import { DALUtils, generateEntityId } from "@medusajs/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
Cascade,
|
||||
Collection,
|
||||
Entity,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
@@ -14,7 +15,6 @@ import {
|
||||
} from "@mikro-orm/core"
|
||||
import { Product } from "@models"
|
||||
import ProductOptionValue from "./product-option-value"
|
||||
import { SoftDeletable } from "../utils"
|
||||
|
||||
type OptionalFields =
|
||||
| "created_at"
|
||||
@@ -25,7 +25,7 @@ type OptionalFields =
|
||||
| "product_id"
|
||||
|
||||
@Entity({ tableName: "product_variant" })
|
||||
@SoftDeletable()
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class ProductVariant {
|
||||
[OptionalProps]?: OptionalFields
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
Collection,
|
||||
Entity,
|
||||
Enum,
|
||||
Filter,
|
||||
Index,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
@@ -14,7 +15,7 @@ import {
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import { ProductTypes } from "@medusajs/types"
|
||||
import { generateEntityId, kebabCase } from "@medusajs/utils"
|
||||
import { DALUtils, generateEntityId, kebabCase } from "@medusajs/utils"
|
||||
import ProductCategory from "./product-category"
|
||||
import ProductCollection from "./product-collection"
|
||||
import ProductOption from "./product-option"
|
||||
@@ -22,7 +23,6 @@ import ProductTag from "./product-tag"
|
||||
import ProductType from "./product-type"
|
||||
import ProductVariant from "./product-variant"
|
||||
import ProductImage from "./product-image"
|
||||
import { SoftDeletable } from "../utils"
|
||||
|
||||
type OptionalRelations = "collection" | "type"
|
||||
type OptionalFields =
|
||||
@@ -34,7 +34,7 @@ type OptionalFields =
|
||||
| "updated_at"
|
||||
|
||||
@Entity({ tableName: "product" })
|
||||
@SoftDeletable()
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
class Product {
|
||||
[OptionalProps]?: OptionalRelations | OptionalFields
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export { BaseRepository } from "./base"
|
||||
export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils"
|
||||
export { ProductRepository } from "./product"
|
||||
export { ProductTagRepository } from "./product-tag"
|
||||
export { ProductVariantRepository } from "./product-variant"
|
||||
|
||||
@@ -3,12 +3,17 @@ import {
|
||||
FindOptions as MikroOptions,
|
||||
LoadStrategy,
|
||||
} from "@mikro-orm/core"
|
||||
import { Product, ProductCategory } from "@models"
|
||||
import { ProductCategory } from "@models"
|
||||
import { Context, DAL, ProductCategoryTransformOptions } from "@medusajs/types"
|
||||
import groupBy from "lodash/groupBy"
|
||||
import { BaseTreeRepository } from "./base"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { InjectTransactionManager, MedusaContext, isDefined, MedusaError } from "@medusajs/utils"
|
||||
import {
|
||||
DALUtils,
|
||||
InjectTransactionManager,
|
||||
isDefined,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import { ProductCategoryServiceTypes } from "../types"
|
||||
|
||||
@@ -25,7 +30,7 @@ export type ReorderConditions = {
|
||||
}
|
||||
|
||||
export const tempReorderRank = 99999
|
||||
export class ProductCategoryRepository extends BaseTreeRepository {
|
||||
export class ProductCategoryRepository extends DALUtils.MikroOrmBaseTreeRepository {
|
||||
protected readonly manager_: SqlEntityManager
|
||||
|
||||
constructor({ manager }: { manager: SqlEntityManager }) {
|
||||
@@ -209,12 +214,9 @@ export class ProductCategoryRepository extends BaseTreeRepository {
|
||||
): Promise<ProductCategory> {
|
||||
const categoryData = { ...data }
|
||||
const manager = this.getActiveManager<SqlEntityManager>(sharedContext)
|
||||
const siblings = await manager.find(
|
||||
ProductCategory,
|
||||
{
|
||||
parent_category_id: categoryData?.parent_category_id || null
|
||||
},
|
||||
)
|
||||
const siblings = await manager.find(ProductCategory, {
|
||||
parent_category_id: categoryData?.parent_category_id || null,
|
||||
})
|
||||
|
||||
if (!isDefined(categoryData.rank)) {
|
||||
categoryData.rank = siblings.length
|
||||
@@ -343,25 +345,19 @@ export class ProductCategoryRepository extends BaseTreeRepository {
|
||||
|
||||
// The current sibling count will replace targetRank if
|
||||
// targetRank is greater than the count of siblings.
|
||||
const siblingCount = await manager.count(
|
||||
ProductCategory,
|
||||
{
|
||||
parent_category_id: targetParentId || null,
|
||||
id: { $ne: targetCategoryId },
|
||||
}
|
||||
)
|
||||
const siblingCount = await manager.count(ProductCategory, {
|
||||
parent_category_id: targetParentId || null,
|
||||
id: { $ne: targetCategoryId },
|
||||
})
|
||||
|
||||
// The category record that will be placed at the requested rank
|
||||
// We've temporarily placed it at a temporary rank that is
|
||||
// beyond a reasonable value (tempReorderRank)
|
||||
const targetCategory = await manager.findOne(
|
||||
ProductCategory,
|
||||
{
|
||||
id: targetCategoryId,
|
||||
parent_category_id: targetParentId || null,
|
||||
rank: tempReorderRank,
|
||||
}
|
||||
)
|
||||
const targetCategory = await manager.findOne(ProductCategory, {
|
||||
id: targetCategoryId,
|
||||
parent_category_id: targetParentId || null,
|
||||
rank: tempReorderRank,
|
||||
})
|
||||
|
||||
// If the targetRank is not present, or if targetRank is beyond the
|
||||
// rank of the last category, we set the rank as the last rank
|
||||
|
||||
@@ -7,14 +7,13 @@ import {
|
||||
import { Context, DAL, ProductTypes } from "@medusajs/types"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import {
|
||||
DALUtils,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import { BaseRepository } from "./base"
|
||||
|
||||
export class ProductCollectionRepository extends BaseRepository {
|
||||
export class ProductCollectionRepository extends DALUtils.MikroOrmBaseRepository {
|
||||
protected readonly manager_: SqlEntityManager
|
||||
|
||||
constructor({ manager }: { manager: SqlEntityManager }) {
|
||||
|
||||
@@ -5,11 +5,14 @@ import {
|
||||
} from "@mikro-orm/core"
|
||||
import { Context, DAL } from "@medusajs/types"
|
||||
import { Image, Product } from "@models"
|
||||
import { AbstractBaseRepository } from "./base"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { InjectTransactionManager, MedusaContext } from "@medusajs/utils"
|
||||
import {
|
||||
DALUtils,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
export class ProductImageRepository extends AbstractBaseRepository<Image> {
|
||||
export class ProductImageRepository extends DALUtils.MikroOrmAbstractBaseRepository<Image> {
|
||||
protected readonly manager_: SqlEntityManager
|
||||
|
||||
constructor({ manager }: { manager: SqlEntityManager }) {
|
||||
|
||||
@@ -5,15 +5,15 @@ import {
|
||||
} from "@mikro-orm/core"
|
||||
import { Product, ProductOption } from "@models"
|
||||
import { Context, DAL, ProductTypes } from "@medusajs/types"
|
||||
import { AbstractBaseRepository } from "./base"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import {
|
||||
DALUtils,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
export class ProductOptionRepository extends AbstractBaseRepository<ProductOption> {
|
||||
export class ProductOptionRepository extends DALUtils.MikroOrmAbstractBaseRepository<ProductOption> {
|
||||
protected readonly manager_: SqlEntityManager
|
||||
|
||||
constructor({ manager }: { manager: SqlEntityManager }) {
|
||||
@@ -88,13 +88,15 @@ export class ProductOptionRepository extends AbstractBaseRepository<ProductOptio
|
||||
|
||||
data.forEach((d) => d.product_id && productIds.push(d.product_id))
|
||||
|
||||
const existingProducts = await manager.find(
|
||||
Product,
|
||||
{ id: { $in: productIds } },
|
||||
)
|
||||
const existingProducts = await manager.find(Product, {
|
||||
id: { $in: productIds },
|
||||
})
|
||||
|
||||
const existingProductsMap = new Map(
|
||||
existingProducts.map<[string, Product]>((product) => [product.id, product])
|
||||
existingProducts.map<[string, Product]>((product) => [
|
||||
product.id,
|
||||
product,
|
||||
])
|
||||
)
|
||||
|
||||
const productOptions = data.map((optionData) => {
|
||||
@@ -136,7 +138,10 @@ export class ProductOptionRepository extends AbstractBaseRepository<ProductOptio
|
||||
)
|
||||
|
||||
const existingOptionsMap = new Map(
|
||||
existingOptions.map<[string, ProductOption]>((option) => [option.id, option])
|
||||
existingOptions.map<[string, ProductOption]>((option) => [
|
||||
option.id,
|
||||
option,
|
||||
])
|
||||
)
|
||||
|
||||
const productOptions = data.map((optionData) => {
|
||||
|
||||
@@ -12,15 +12,15 @@ import {
|
||||
UpdateProductTagDTO,
|
||||
UpsertProductTagDTO,
|
||||
} from "@medusajs/types"
|
||||
import { BaseRepository } from "./base"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import {
|
||||
DALUtils,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
export class ProductTagRepository extends BaseRepository {
|
||||
export class ProductTagRepository extends DALUtils.MikroOrmBaseRepository {
|
||||
protected readonly manager_: SqlEntityManager
|
||||
|
||||
constructor({ manager }: { manager: SqlEntityManager }) {
|
||||
|
||||
@@ -13,14 +13,13 @@ import {
|
||||
} from "@medusajs/types"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import {
|
||||
DALUtils,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import { BaseRepository } from "./base"
|
||||
|
||||
export class ProductTypeRepository extends BaseRepository {
|
||||
export class ProductTypeRepository extends DALUtils.MikroOrmBaseRepository {
|
||||
protected readonly manager_: SqlEntityManager
|
||||
|
||||
constructor({ manager }: { manager: SqlEntityManager }) {
|
||||
|
||||
@@ -8,17 +8,15 @@ import { ProductVariant } from "@models"
|
||||
import { Context, DAL, WithRequiredProperty } from "@medusajs/types"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import {
|
||||
MedusaError,
|
||||
isDefined,
|
||||
DALUtils,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import { ProductVariantServiceTypes } from "../types/services"
|
||||
import { AbstractBaseRepository } from "./base"
|
||||
import { doNotForceTransaction } from "../utils"
|
||||
|
||||
export class ProductVariantRepository extends AbstractBaseRepository<ProductVariant> {
|
||||
export class ProductVariantRepository extends DALUtils.MikroOrmAbstractBaseRepository<ProductVariant> {
|
||||
protected readonly manager_: SqlEntityManager
|
||||
|
||||
constructor({ manager }: { manager: SqlEntityManager }) {
|
||||
@@ -96,14 +94,17 @@ export class ProductVariantRepository extends AbstractBaseRepository<ProductVari
|
||||
}
|
||||
|
||||
async update(
|
||||
data: WithRequiredProperty<ProductVariantServiceTypes.UpdateProductVariantDTO, "id">[],
|
||||
data: WithRequiredProperty<
|
||||
ProductVariantServiceTypes.UpdateProductVariantDTO,
|
||||
"id"
|
||||
>[],
|
||||
context: Context = {}
|
||||
): Promise<ProductVariant[]> {
|
||||
const manager = (context.transactionManager ??
|
||||
this.manager_) as SqlEntityManager
|
||||
|
||||
const productVariantsToUpdate = await manager.find(ProductVariant, {
|
||||
id: data.map((updateData) => updateData.id)
|
||||
id: data.map((updateData) => updateData.id),
|
||||
})
|
||||
|
||||
const productVariantsToUpdateMap = new Map<string, ProductVariant>(
|
||||
|
||||
@@ -2,15 +2,14 @@ import {
|
||||
Product,
|
||||
ProductCategory,
|
||||
ProductCollection,
|
||||
ProductType,
|
||||
ProductTag,
|
||||
ProductType,
|
||||
} from "@models"
|
||||
|
||||
import {
|
||||
FilterQuery as MikroFilterQuery,
|
||||
FindOptions as MikroOptions,
|
||||
LoadStrategy,
|
||||
wrap
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
import {
|
||||
@@ -20,12 +19,17 @@ import {
|
||||
WithRequiredProperty,
|
||||
} from "@medusajs/types"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { MedusaError, isDefined, InjectTransactionManager, MedusaContext } from "@medusajs/utils"
|
||||
import {
|
||||
DALUtils,
|
||||
InjectTransactionManager,
|
||||
isDefined,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import { AbstractBaseRepository } from "./base"
|
||||
import { ProductServiceTypes } from "../types/services"
|
||||
|
||||
export class ProductRepository extends AbstractBaseRepository<Product> {
|
||||
export class ProductRepository extends DALUtils.MikroOrmAbstractBaseRepository<Product> {
|
||||
protected readonly manager_: SqlEntityManager
|
||||
|
||||
constructor({ manager }: { manager: SqlEntityManager }) {
|
||||
@@ -157,12 +161,10 @@ export class ProductRepository extends AbstractBaseRepository<Product> {
|
||||
|
||||
data.forEach((productData) => {
|
||||
categoryIds = categoryIds.concat(
|
||||
productData?.categories?.map(c => c.id) || []
|
||||
productData?.categories?.map((c) => c.id) || []
|
||||
)
|
||||
|
||||
tagIds = tagIds.concat(
|
||||
productData?.tags?.map(c => c.id) || []
|
||||
)
|
||||
tagIds = tagIds.concat(productData?.tags?.map((c) => c.id) || [])
|
||||
|
||||
if (productData.collection_id) {
|
||||
collectionIds.push(productData.collection_id)
|
||||
@@ -173,27 +175,39 @@ export class ProductRepository extends AbstractBaseRepository<Product> {
|
||||
}
|
||||
})
|
||||
|
||||
const productsToUpdate = await manager.find(Product, {
|
||||
id: data.map((updateData) => updateData.id)
|
||||
}, {
|
||||
populate: ["tags", "categories"]
|
||||
})
|
||||
const productsToUpdate = await manager.find(
|
||||
Product,
|
||||
{
|
||||
id: data.map((updateData) => updateData.id),
|
||||
},
|
||||
{
|
||||
populate: ["tags", "categories"],
|
||||
}
|
||||
)
|
||||
|
||||
const collectionsToAssign = collectionIds.length ? await manager.find(ProductCollection, {
|
||||
id: collectionIds
|
||||
}) : []
|
||||
const collectionsToAssign = collectionIds.length
|
||||
? await manager.find(ProductCollection, {
|
||||
id: collectionIds,
|
||||
})
|
||||
: []
|
||||
|
||||
const typesToAssign = typeIds.length ? await manager.find(ProductType, {
|
||||
id: typeIds
|
||||
}) : []
|
||||
const typesToAssign = typeIds.length
|
||||
? await manager.find(ProductType, {
|
||||
id: typeIds,
|
||||
})
|
||||
: []
|
||||
|
||||
const categoriesToAssign = categoryIds.length ? await manager.find(ProductCategory, {
|
||||
id: categoryIds
|
||||
}) : []
|
||||
const categoriesToAssign = categoryIds.length
|
||||
? await manager.find(ProductCategory, {
|
||||
id: categoryIds,
|
||||
})
|
||||
: []
|
||||
|
||||
const tagsToAssign = tagIds.length ? await manager.find(ProductTag, {
|
||||
id: tagIds
|
||||
}) : []
|
||||
const tagsToAssign = tagIds.length
|
||||
? await manager.find(ProductTag, {
|
||||
id: tagIds,
|
||||
})
|
||||
: []
|
||||
|
||||
const categoriesToAssignMap = new Map<string, ProductCategory>(
|
||||
categoriesToAssign.map((category) => [category.id, category])
|
||||
@@ -227,8 +241,8 @@ export class ProductRepository extends AbstractBaseRepository<Product> {
|
||||
}
|
||||
|
||||
const {
|
||||
categories: categoriesData,
|
||||
tags: tagsData,
|
||||
categories: categoriesData = [],
|
||||
tags: tagsData = [],
|
||||
collection_id: collectionId,
|
||||
type_id: typeId,
|
||||
} = updateData
|
||||
@@ -249,10 +263,15 @@ export class ProductRepository extends AbstractBaseRepository<Product> {
|
||||
}
|
||||
}
|
||||
|
||||
const categoryIdsToAssignSet = new Set(categoriesData.map(cd => cd.id))
|
||||
const categoriesToDelete = product.categories.getItems().filter(
|
||||
(existingCategory) => !categoryIdsToAssignSet.has(existingCategory.id)
|
||||
const categoryIdsToAssignSet = new Set(
|
||||
categoriesData.map((cd) => cd.id)
|
||||
)
|
||||
const categoriesToDelete = product.categories
|
||||
.getItems()
|
||||
.filter(
|
||||
(existingCategory) =>
|
||||
!categoryIdsToAssignSet.has(existingCategory.id)
|
||||
)
|
||||
|
||||
await product.categories.remove(categoriesToDelete)
|
||||
}
|
||||
@@ -272,22 +291,22 @@ export class ProductRepository extends AbstractBaseRepository<Product> {
|
||||
}
|
||||
}
|
||||
|
||||
const tagIdsToAssignSet = new Set(tagsData.map(cd => cd.id))
|
||||
const tagsToDelete = product.tags.getItems().filter(
|
||||
(existingTag) => !tagIdsToAssignSet.has(existingTag.id)
|
||||
)
|
||||
const tagIdsToAssignSet = new Set(tagsData.map((cd) => cd.id))
|
||||
const tagsToDelete = product.tags
|
||||
.getItems()
|
||||
.filter((existingTag) => !tagIdsToAssignSet.has(existingTag.id))
|
||||
|
||||
await product.tags.remove(tagsToDelete)
|
||||
}
|
||||
|
||||
if (isDefined(collectionId)) {
|
||||
const collection = collectionsToAssignMap.get(collectionId)
|
||||
const collection = collectionsToAssignMap.get(collectionId!)
|
||||
|
||||
product.collection = collection || null
|
||||
}
|
||||
|
||||
if (isDefined(typeId)) {
|
||||
const type = typesToAssignMap.get(typeId)
|
||||
const type = typesToAssignMap.get(typeId!)
|
||||
|
||||
if (type) {
|
||||
product.type = type
|
||||
|
||||
@@ -3,8 +3,7 @@ import * as ProductModels from "@models"
|
||||
import { LoaderOptions, Logger, ModulesSdkTypes } from "@medusajs/types"
|
||||
|
||||
import { EntitySchema } from "@mikro-orm/core"
|
||||
import { ModulesSdkUtils } from "@medusajs/utils"
|
||||
import { createConnection } from "../utils"
|
||||
import { DALUtils, ModulesSdkUtils } from "@medusajs/utils"
|
||||
|
||||
/**
|
||||
* This script is only valid for mikro orm managers. If a user provide a custom manager
|
||||
@@ -28,7 +27,7 @@ export async function revertMigration({
|
||||
const dbData = ModulesSdkUtils.loadDatabaseConfig("product", options)
|
||||
const entities = Object.values(ProductModels) as unknown as EntitySchema[]
|
||||
|
||||
const orm = await createConnection(dbData, entities)
|
||||
const orm = await DALUtils.mikroOrmCreateConnection(dbData, entities)
|
||||
|
||||
try {
|
||||
const migrator = orm.getMigrator()
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { LoaderOptions, Logger, ModulesSdkTypes } from "@medusajs/types"
|
||||
import { createConnection } from "../utils"
|
||||
import * as ProductModels from "@models"
|
||||
import { EntitySchema } from "@mikro-orm/core"
|
||||
import { ModulesSdkUtils } from "@medusajs/utils"
|
||||
import { DALUtils, ModulesSdkUtils } from "@medusajs/utils"
|
||||
|
||||
/**
|
||||
* This script is only valid for mikro orm managers. If a user provide a custom manager
|
||||
@@ -26,7 +25,7 @@ export async function runMigrations({
|
||||
const dbData = ModulesSdkUtils.loadDatabaseConfig("product", options)
|
||||
const entities = Object.values(ProductModels) as unknown as EntitySchema[]
|
||||
|
||||
const orm = await createConnection(dbData, entities)
|
||||
const orm = await DALUtils.mikroOrmCreateConnection(dbData, entities)
|
||||
|
||||
try {
|
||||
const migrator = orm.getMigrator()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { createConnection } from "../utils"
|
||||
import * as ProductModels from "@models"
|
||||
import { Product, ProductCategory, ProductVariant } from "@models"
|
||||
import { EntitySchema } from "@mikro-orm/core"
|
||||
@@ -6,7 +5,7 @@ import { LoaderOptions, Logger, ModulesSdkTypes } from "@medusajs/types"
|
||||
import { EOL } from "os"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { resolve } from "path"
|
||||
import { ModulesSdkUtils } from "@medusajs/utils"
|
||||
import { DALUtils, ModulesSdkUtils } from "@medusajs/utils"
|
||||
|
||||
export async function run({
|
||||
options,
|
||||
@@ -38,7 +37,7 @@ export async function run({
|
||||
const dbData = ModulesSdkUtils.loadDatabaseConfig("product", options)
|
||||
const entities = Object.values(ProductModels) as unknown as EntitySchema[]
|
||||
|
||||
const orm = await createConnection(dbData, entities)
|
||||
const orm = await DALUtils.mikroOrmCreateConnection(dbData, entities)
|
||||
const manager = orm.em.fork()
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { MODULE_RESOURCE_TYPE } from "@medusajs/types"
|
||||
|
||||
export * from "./create-connection"
|
||||
export * from "./soft-deletable"
|
||||
|
||||
export function shouldForceTransaction(target: any): boolean {
|
||||
return target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
// TODO: Should we create a mikro orm specific package for this and the base repository?
|
||||
|
||||
import { Filter } from "@mikro-orm/core"
|
||||
import { SoftDeletableFilterKey } from "@medusajs/utils"
|
||||
|
||||
interface FilterArguments {
|
||||
withDeleted?: boolean
|
||||
}
|
||||
|
||||
export const SoftDeletable = (): ClassDecorator => {
|
||||
return Filter({
|
||||
name: SoftDeletableFilterKey,
|
||||
cond: ({ withDeleted }: FilterArguments = {}) => {
|
||||
if (withDeleted) {
|
||||
return {}
|
||||
}
|
||||
return {
|
||||
deleted_at: null,
|
||||
}
|
||||
},
|
||||
default: true,
|
||||
})
|
||||
}
|
||||
@@ -35,13 +35,9 @@ export interface RepositoryService<T = any> extends BaseRepositoryService<T> {
|
||||
context?: Context
|
||||
): Promise<[T[], number]>
|
||||
|
||||
// Only required for some repositories
|
||||
upsert?(data: any, context?: Context): Promise<T[]>
|
||||
|
||||
create(data: unknown[], context?: Context): Promise<T[]>
|
||||
|
||||
// TODO: remove optionality when all the other repositories have an update
|
||||
update?(data: unknown[], context?: Context): Promise<T[]>
|
||||
update(data: unknown[], context?: Context): Promise<T[]>
|
||||
|
||||
delete(ids: string[], context?: Context): Promise<void>
|
||||
|
||||
@@ -50,7 +46,8 @@ export interface RepositoryService<T = any> extends BaseRepositoryService<T> {
|
||||
restore(ids: string[], context?: Context): Promise<T[]>
|
||||
}
|
||||
|
||||
export interface TreeRepositoryService<T = any> extends BaseRepositoryService<T> {
|
||||
export interface TreeRepositoryService<T = any>
|
||||
extends BaseRepositoryService<T> {
|
||||
find(
|
||||
options?: FindOptions<T>,
|
||||
transformOptions?: RepositoryTransformOptions,
|
||||
|
||||
@@ -2,3 +2,4 @@ export * as DecoratorUtils from "./decorators"
|
||||
export * as EventBusUtils from "./event-bus"
|
||||
export * as SearchUtils from "./search"
|
||||
export * as ModulesSdkUtils from "./modules-sdk"
|
||||
export * as DALUtils from "./dal"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export const SoftDeletableFilterKey = "softDeletable"
|
||||
@@ -22,4 +22,3 @@ export * from "./stringify-circular"
|
||||
export * from "./to-kebab-case"
|
||||
export * from "./to-pascal-case"
|
||||
export * from "./wrap-handler"
|
||||
export * from "./dal"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from "./mikro-orm/mikro-orm-repository"
|
||||
export * from "./repository"
|
||||
export * from "./utils"
|
||||
export * from "./mikro-orm/mikro-orm-create-connection"
|
||||
export * from "./mikro-orm/mikro-orm-soft-deletable-filter"
|
||||
+4
-3
@@ -1,12 +1,13 @@
|
||||
import { MikroORM, PostgreSqlDriver } from "@mikro-orm/postgresql"
|
||||
import { ModuleServiceInitializeOptions } from "@medusajs/types"
|
||||
|
||||
export async function createConnection(
|
||||
export async function mikroOrmCreateConnection(
|
||||
database: ModuleServiceInitializeOptions["database"],
|
||||
entities: any[]
|
||||
) {
|
||||
const { MikroORM } = await import("@mikro-orm/postgresql")
|
||||
|
||||
const schema = database.schema || "public"
|
||||
const orm = await MikroORM.init<PostgreSqlDriver>({
|
||||
const orm = await MikroORM.init({
|
||||
discovery: { disableDynamicFileAccess: true },
|
||||
entities,
|
||||
debug: database.debug ?? process.env.NODE_ENV?.startsWith("dev") ?? false,
|
||||
+34
-123
@@ -1,100 +1,14 @@
|
||||
import { Context, DAL, RepositoryTransformOptions } from "@medusajs/types"
|
||||
import { SoftDeletableFilterKey } from "@medusajs/utils"
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { MedusaContext } from "../../decorators"
|
||||
import { buildQuery, InjectTransactionManager } from "../../modules-sdk"
|
||||
import {
|
||||
buildQuery,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
} from "@medusajs/utils"
|
||||
import { serialize } from "@mikro-orm/core"
|
||||
mikroOrmSerializer,
|
||||
mikroOrmUpdateDeletedAtRecursively,
|
||||
transactionWrapper,
|
||||
} from "../utils"
|
||||
|
||||
// TODO: move to utils package
|
||||
|
||||
async function transactionWrapper<TManager = unknown>(
|
||||
this: any,
|
||||
task: (transactionManager: unknown) => Promise<any>,
|
||||
{
|
||||
transaction,
|
||||
isolationLevel,
|
||||
enableNestedTransactions = false,
|
||||
}: {
|
||||
isolationLevel?: string
|
||||
transaction?: TManager
|
||||
enableNestedTransactions?: boolean
|
||||
} = {}
|
||||
): Promise<any> {
|
||||
// Reuse the same transaction if it is already provided and nested transactions are disabled
|
||||
if (!enableNestedTransactions && transaction) {
|
||||
return await task(transaction)
|
||||
}
|
||||
|
||||
const options = {}
|
||||
|
||||
if (transaction) {
|
||||
Object.assign(options, { ctx: transaction })
|
||||
}
|
||||
|
||||
if (isolationLevel) {
|
||||
Object.assign(options, { isolationLevel })
|
||||
}
|
||||
|
||||
return await (this.manager_ as SqlEntityManager).transactional(task, options)
|
||||
}
|
||||
|
||||
// TODO: move to utils package
|
||||
const mikroOrmUpdateDeletedAtRecursively = async <T extends object = any>(
|
||||
manager: any,
|
||||
entities: T[],
|
||||
value: Date | null
|
||||
) => {
|
||||
for (const entity of entities) {
|
||||
if (!("deleted_at" in entity)) continue
|
||||
|
||||
;(entity as any).deleted_at = value
|
||||
|
||||
const relations = manager
|
||||
.getDriver()
|
||||
.getMetadata()
|
||||
.get(entity.constructor.name).relations
|
||||
|
||||
const relationsToCascade = relations.filter((relation) =>
|
||||
relation.cascade.includes("soft-remove" as any)
|
||||
)
|
||||
|
||||
for (const relation of relationsToCascade) {
|
||||
let collectionRelation = entity[relation.name]
|
||||
|
||||
if (!collectionRelation.isInitialized()) {
|
||||
await collectionRelation.init()
|
||||
}
|
||||
|
||||
const relationEntities = await collectionRelation.getItems({
|
||||
filters: {
|
||||
[SoftDeletableFilterKey]: {
|
||||
withDeleted: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await mikroOrmUpdateDeletedAtRecursively(manager, relationEntities, value)
|
||||
}
|
||||
|
||||
await manager.persist(entity)
|
||||
}
|
||||
}
|
||||
|
||||
const serializer = <TOutput extends object>(
|
||||
data: any,
|
||||
options?: any
|
||||
): Promise<TOutput> => {
|
||||
options ??= {}
|
||||
const result = serialize(data, options)
|
||||
return result as unknown as Promise<TOutput>
|
||||
}
|
||||
|
||||
// TODO: move to utils package
|
||||
class AbstractBase<T = any> {
|
||||
protected readonly manager_: SqlEntityManager
|
||||
class MikroOrmBase<T = any> {
|
||||
protected readonly manager_: any
|
||||
|
||||
protected constructor({ manager }) {
|
||||
this.manager_ = manager
|
||||
@@ -115,29 +29,26 @@ class AbstractBase<T = any> {
|
||||
|
||||
async transaction<TManager = unknown>(
|
||||
task: (transactionManager: TManager) => Promise<any>,
|
||||
{
|
||||
transaction,
|
||||
isolationLevel,
|
||||
enableNestedTransactions = false,
|
||||
}: {
|
||||
options: {
|
||||
isolationLevel?: string
|
||||
enableNestedTransactions?: boolean
|
||||
transaction?: TManager
|
||||
} = {}
|
||||
): Promise<any> {
|
||||
return await transactionWrapper.apply(this, arguments)
|
||||
// @ts-ignore
|
||||
return await transactionWrapper.bind(this)(task, options)
|
||||
}
|
||||
|
||||
async serialize<TOutput extends object | object[]>(
|
||||
data: any,
|
||||
options?: any
|
||||
): Promise<TOutput> {
|
||||
return await serializer<TOutput>(data, options)
|
||||
return await mikroOrmSerializer<TOutput>(data, options)
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class AbstractBaseRepository<T = any>
|
||||
extends AbstractBase
|
||||
export abstract class MikroOrmAbstractBaseRepository<T = any>
|
||||
extends MikroOrmBase
|
||||
implements DAL.RepositoryService<T>
|
||||
{
|
||||
abstract find(options?: DAL.FindOptions<T>, context?: Context)
|
||||
@@ -149,6 +60,10 @@ export abstract class AbstractBaseRepository<T = any>
|
||||
|
||||
abstract create(data: unknown[], context?: Context): Promise<T[]>
|
||||
|
||||
update(data: unknown[], context?: Context): Promise<T[]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
abstract delete(ids: string[], context?: Context): Promise<void>
|
||||
|
||||
@InjectTransactionManager()
|
||||
@@ -160,11 +75,7 @@ export abstract class AbstractBaseRepository<T = any>
|
||||
const entities = await this.find({ where: { id: { $in: ids } } as any })
|
||||
const date = new Date()
|
||||
|
||||
await mikroOrmUpdateDeletedAtRecursively(
|
||||
manager as SqlEntityManager,
|
||||
entities,
|
||||
date
|
||||
)
|
||||
await mikroOrmUpdateDeletedAtRecursively(manager, entities, date)
|
||||
|
||||
return entities
|
||||
}
|
||||
@@ -173,7 +84,7 @@ export abstract class AbstractBaseRepository<T = any>
|
||||
async restore(
|
||||
ids: string[],
|
||||
@MedusaContext()
|
||||
{ transactionManager: manager }: Context<SqlEntityManager> = {}
|
||||
{ transactionManager: manager }: Context = {}
|
||||
): Promise<T[]> {
|
||||
const query = buildQuery(
|
||||
{ id: { $in: ids } },
|
||||
@@ -184,19 +95,14 @@ export abstract class AbstractBaseRepository<T = any>
|
||||
|
||||
const entities = await this.find(query)
|
||||
|
||||
await mikroOrmUpdateDeletedAtRecursively(
|
||||
manager as SqlEntityManager,
|
||||
entities,
|
||||
null
|
||||
)
|
||||
await mikroOrmUpdateDeletedAtRecursively(manager, entities, null)
|
||||
|
||||
return entities
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: move to utils package
|
||||
export abstract class AbstractTreeRepositoryBase<T = any>
|
||||
extends AbstractBase<T>
|
||||
export abstract class MikroOrmAbstractTreeRepositoryBase<T = any>
|
||||
extends MikroOrmBase<T>
|
||||
implements DAL.TreeRepositoryService<T>
|
||||
{
|
||||
protected constructor({ manager }) {
|
||||
@@ -221,13 +127,15 @@ export abstract class AbstractTreeRepositoryBase<T = any>
|
||||
abstract delete(id: string, context?: Context): Promise<void>
|
||||
}
|
||||
|
||||
// TODO: move to utils package
|
||||
/**
|
||||
* Only used internally in order to be able to wrap in transaction from a
|
||||
* non identified repository
|
||||
* Priviliged extends of the abstract classes unless most of the methods can't be implemented
|
||||
* in your repository. This base repository is also used to provide a base repository
|
||||
* injection if needed to be able to use the common methods without being related to an entity.
|
||||
* In this case, none of the method will be implemented except the manager and transaction
|
||||
* related ones.
|
||||
*/
|
||||
|
||||
export class BaseRepository extends AbstractBaseRepository {
|
||||
export class MikroOrmBaseRepository extends MikroOrmAbstractBaseRepository {
|
||||
constructor({ manager }) {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
@@ -237,6 +145,10 @@ export class BaseRepository extends AbstractBaseRepository {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
update(data: unknown[], context?: Context): Promise<any[]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
delete(ids: string[], context?: Context): Promise<void> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
@@ -253,8 +165,7 @@ export class BaseRepository extends AbstractBaseRepository {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class BaseTreeRepository extends AbstractTreeRepositoryBase {
|
||||
export class MikroOrmBaseTreeRepository extends MikroOrmAbstractTreeRepositoryBase {
|
||||
constructor({ manager }) {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
@@ -0,0 +1,19 @@
|
||||
export const SoftDeletableFilterKey = "softDeletable"
|
||||
|
||||
interface FilterArguments {
|
||||
withDeleted?: boolean
|
||||
}
|
||||
|
||||
export const mikroOrmSoftDeletableFilterOptions = {
|
||||
name: SoftDeletableFilterKey,
|
||||
cond: ({ withDeleted }: FilterArguments = {}) => {
|
||||
if (withDeleted) {
|
||||
return {}
|
||||
}
|
||||
return {
|
||||
deleted_at: null,
|
||||
}
|
||||
},
|
||||
default: true,
|
||||
args: false,
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import { Context, DAL, RepositoryTransformOptions } from "@medusajs/types"
|
||||
import { MedusaContext } from "../decorators"
|
||||
|
||||
class AbstractBase<T = any> {
|
||||
protected readonly manager_: any
|
||||
|
||||
protected constructor({ manager }) {
|
||||
this.manager_ = manager
|
||||
}
|
||||
|
||||
getActiveManager<TManager = unknown>(
|
||||
@MedusaContext()
|
||||
{ transactionManager, manager }: Context = {}
|
||||
): TManager {
|
||||
return (transactionManager ?? manager ?? this.manager_) as TManager
|
||||
}
|
||||
|
||||
async transaction<TManager = unknown>(
|
||||
task: (transactionManager: TManager) => Promise<any>,
|
||||
{
|
||||
transaction,
|
||||
isolationLevel,
|
||||
enableNestedTransactions = false,
|
||||
}: {
|
||||
isolationLevel?: string
|
||||
enableNestedTransactions?: boolean
|
||||
transaction?: TManager
|
||||
} = {}
|
||||
): Promise<any> {
|
||||
// @ts-ignore
|
||||
return await transactionWrapper.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class AbstractBaseRepository<T = any>
|
||||
extends AbstractBase
|
||||
implements DAL.RepositoryService<T>
|
||||
{
|
||||
abstract find(options?: DAL.FindOptions<T>, context?: Context)
|
||||
|
||||
abstract findAndCount(
|
||||
options?: DAL.FindOptions<T>,
|
||||
context?: Context
|
||||
): Promise<[T[], number]>
|
||||
|
||||
abstract create(data: unknown[], context?: Context): Promise<T[]>
|
||||
|
||||
abstract update(data: unknown[], context?: Context): Promise<T[]>
|
||||
|
||||
abstract delete(ids: string[], context?: Context): Promise<void>
|
||||
|
||||
abstract softDelete(ids: string[], context?: Context): Promise<T[]>
|
||||
|
||||
abstract restore(ids: string[], context?: Context): Promise<T[]>
|
||||
|
||||
abstract getFreshManager<TManager = unknown>(): TManager
|
||||
|
||||
abstract serialize<TOutput extends object | object[]>(
|
||||
data: any,
|
||||
options?: any
|
||||
): Promise<TOutput>
|
||||
}
|
||||
|
||||
export abstract class AbstractTreeRepositoryBase<T = any>
|
||||
extends AbstractBase<T>
|
||||
implements DAL.TreeRepositoryService<T>
|
||||
{
|
||||
protected constructor({ manager }) {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
}
|
||||
|
||||
abstract find(
|
||||
options?: DAL.FindOptions<T>,
|
||||
transformOptions?: RepositoryTransformOptions,
|
||||
context?: Context
|
||||
)
|
||||
|
||||
abstract findAndCount(
|
||||
options?: DAL.FindOptions<T>,
|
||||
transformOptions?: RepositoryTransformOptions,
|
||||
context?: Context
|
||||
): Promise<[T[], number]>
|
||||
|
||||
abstract create(data: unknown, context?: Context): Promise<T>
|
||||
|
||||
abstract delete(id: string, context?: Context): Promise<void>
|
||||
|
||||
abstract getFreshManager<TManager = unknown>(): TManager
|
||||
|
||||
abstract serialize<TOutput extends object | object[]>(
|
||||
data: any,
|
||||
options?: any
|
||||
): Promise<TOutput>
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { SoftDeletableFilterKey } from "../dal"
|
||||
|
||||
export async function transactionWrapper<TManager = unknown>(
|
||||
this: any,
|
||||
task: (transactionManager: unknown) => Promise<any>,
|
||||
{
|
||||
transaction,
|
||||
isolationLevel,
|
||||
enableNestedTransactions = false,
|
||||
}: {
|
||||
isolationLevel?: string
|
||||
transaction?: TManager
|
||||
enableNestedTransactions?: boolean
|
||||
} = {}
|
||||
): Promise<any> {
|
||||
// Reuse the same transaction if it is already provided and nested transactions are disabled
|
||||
if (!enableNestedTransactions && transaction) {
|
||||
return await task(transaction)
|
||||
}
|
||||
|
||||
const options = {}
|
||||
|
||||
if (transaction) {
|
||||
Object.assign(options, { ctx: transaction })
|
||||
}
|
||||
|
||||
if (isolationLevel) {
|
||||
Object.assign(options, { isolationLevel })
|
||||
}
|
||||
|
||||
const transactionMethod =
|
||||
this.manager_.transaction ?? this.manager_.transactional
|
||||
return await transactionMethod.bind(this.manager_)(task, options)
|
||||
}
|
||||
|
||||
export const mikroOrmUpdateDeletedAtRecursively = async <
|
||||
T extends object = any
|
||||
>(
|
||||
manager: any,
|
||||
entities: T[],
|
||||
value: Date | null
|
||||
) => {
|
||||
for (const entity of entities) {
|
||||
if (!("deleted_at" in entity)) continue
|
||||
;(entity as any).deleted_at = value
|
||||
|
||||
const relations = manager
|
||||
.getDriver()
|
||||
.getMetadata()
|
||||
.get(entity.constructor.name).relations
|
||||
|
||||
const relationsToCascade = relations.filter((relation) =>
|
||||
relation.cascade.includes("soft-remove" as any)
|
||||
)
|
||||
|
||||
for (const relation of relationsToCascade) {
|
||||
let collectionRelation = entity[relation.name]
|
||||
|
||||
if (!collectionRelation.isInitialized()) {
|
||||
await collectionRelation.init()
|
||||
}
|
||||
|
||||
const relationEntities = await collectionRelation.getItems({
|
||||
filters: {
|
||||
[SoftDeletableFilterKey]: {
|
||||
withDeleted: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await mikroOrmUpdateDeletedAtRecursively(manager, relationEntities, value)
|
||||
}
|
||||
|
||||
await manager.persist(entity)
|
||||
}
|
||||
}
|
||||
|
||||
export const mikroOrmSerializer = async <TOutput extends object>(
|
||||
data: any,
|
||||
options?: any
|
||||
): Promise<TOutput> => {
|
||||
options ??= {}
|
||||
const { serialize } = await import("@mikro-orm/core")
|
||||
const result = serialize(data, options)
|
||||
return result as unknown as Promise<TOutput>
|
||||
}
|
||||
@@ -5,3 +5,4 @@ export * from "./decorators"
|
||||
export * from "./event-bus"
|
||||
export * from "./search"
|
||||
export * from "./modules-sdk"
|
||||
export * from "./dal"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { DAL, FindConfig } from "@medusajs/types"
|
||||
|
||||
import { deduplicate, isObject, SoftDeletableFilterKey } from "../common"
|
||||
import { deduplicate, isObject } from "../common"
|
||||
import { SoftDeletableFilterKey } from "../dal"
|
||||
|
||||
export function buildQuery<T = any, TDto = any>(
|
||||
filters: Record<string, any> = {},
|
||||
|
||||
Reference in New Issue
Block a user