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