feat(cart): Cart + Address services (#6055)

Duplicate of #6008, but I messed up my git history big time :'(

**What**
- CartService and CartRepository + tests
- AddressService and AddressRepository + tests
- CartModuleService skeleton. Full implementation + tests will be added in a separate PR
This commit is contained in:
Oli Juhl
2024-01-11 15:40:13 +01:00
committed by GitHub
parent b6ac768698
commit ce81cade88
20 changed files with 847 additions and 33 deletions

View File

@@ -0,0 +1,5 @@
---
"@medusajs/types": patch
---
feat(cart): Add CartService and AddressService

View File

@@ -0,0 +1,23 @@
export const defaultCartsData = [
{
id: "cart-id-1",
region_id: "region-id-1",
customer_id: "customer-id-1",
email: "test@email.com",
currency_code: "usd",
},
{
id: "cart-id-2",
region_id: "region-id-1",
customer_id: "customer-id-1",
shipping_address: {
first_name: "Tony",
last_name: "Stark",
},
billing_address_id: {
address_1: "Stark Industries",
city: "New York",
},
currency_code: "usd",
},
]

View File

@@ -0,0 +1,21 @@
import { CreateCartDTO } from "@medusajs/types"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { Cart } from "../../../src/models"
import { defaultCartsData } from "./data"
export * from "./data"
export async function createCarts(
manager: SqlEntityManager,
cartsData: CreateCartDTO[] = defaultCartsData
): Promise<Cart[]> {
const carts: Cart[] = []
for (let cartData of cartsData) {
let cart = manager.create(Cart, cartData)
await manager.persistAndFlush(cart)
}
return carts
}

View File

@@ -0,0 +1,127 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { AddressRepository } from "../../../../src/repositories"
import { AddressService } from "../../../../src/services"
import { MikroOrmWrapper } from "../../../utils"
jest.setTimeout(30000)
describe("Address Service", () => {
let service: AddressService
let testManager: SqlEntityManager
let repositoryManager: SqlEntityManager
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
repositoryManager = await MikroOrmWrapper.forkManager()
testManager = await MikroOrmWrapper.forkManager()
const addressRepository = new AddressRepository({
manager: repositoryManager,
})
service = new AddressService({
addressRepository: addressRepository,
})
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})
describe("create", () => {
it("should create an address successfully", async () => {
const [createdAddress] = await service.create([
{
first_name: "John",
last_name: "Doe",
address_1: "Test street 1",
city: "Test city",
country_code: "US",
postal_code: "12345",
},
])
const [address] = await service.list({ id: [createdAddress.id] })
expect(address).toEqual(
expect.objectContaining({
id: createdAddress.id,
first_name: "John",
last_name: "Doe",
address_1: "Test street 1",
city: "Test city",
country_code: "US",
postal_code: "12345",
})
)
})
})
describe("update", () => {
it("should throw an error if address does not exist", async () => {
const error = await service
.update([
{
id: "none-existing",
},
])
.catch((e) => e)
expect(error.message).toContain(
"Address with id \"none-existing\" not found"
)
})
it("should update an address successfully", async () => {
const [createdAddress] = await service.create([
{
first_name: "Jane",
last_name: "Doe",
address_1: "Test street 1",
city: "Test city",
country_code: "US",
postal_code: "12345",
},
])
await service.update([
{
id: createdAddress.id,
address_1: "Test street 2",
city: "Test city 2",
},
])
const [address] = await service.list({ id: [createdAddress.id] })
expect(address).toEqual(
expect.objectContaining({
id: createdAddress.id,
first_name: "Jane",
last_name: "Doe",
address_1: "Test street 2",
city: "Test city 2",
country_code: "US",
postal_code: "12345",
})
)
})
})
describe("delete", () => {
it("should delete a cart successfully", async () => {
const [createdAddress] = await service.create([
{
first_name: "Jane",
last_name: "Doe",
},
])
await service.delete([createdAddress.id])
const carts = await service.list({ id: [createdAddress.id] })
expect(carts.length).toEqual(0)
})
})
})

View File

@@ -0,0 +1,124 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { CartRepository } from "../../../../src/repositories"
import { CartService } from "../../../../src/services"
import { createCarts } from "../../../__fixtures__/cart"
import { MikroOrmWrapper } from "../../../utils"
jest.setTimeout(30000)
describe("Cart Service", () => {
let service: CartService
let testManager: SqlEntityManager
let repositoryManager: SqlEntityManager
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
repositoryManager = await MikroOrmWrapper.forkManager()
testManager = await MikroOrmWrapper.forkManager()
const cartRepository = new CartRepository({
manager: repositoryManager,
})
service = new CartService({
cartRepository: cartRepository,
})
await createCarts(testManager)
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})
describe("create", () => {
it("should throw an error when required params are not passed", async () => {
const error = await service
.create([
{
email: "test@email.com",
} as any,
])
.catch((e) => e)
expect(error.message).toContain(
"Value for Cart.currency_code is required, 'undefined' found"
)
})
it("should create a cart successfully", async () => {
const [createdCart] = await service.create([
{
currency_code: "eur",
},
])
const [cart] = await service.list({ id: [createdCart.id] })
expect(cart).toEqual(
expect.objectContaining({
id: createdCart.id,
currency_code: "eur",
})
)
})
})
describe("update", () => {
it("should throw an error if cart does not exist", async () => {
const error = await service
.update([
{
id: "none-existing",
},
])
.catch((e) => e)
expect(error.message).toContain(
"Cart with id \"none-existing\" not found"
)
})
it("should update a cart successfully", async () => {
const [createdCart] = await service.create([
{
currency_code: "eur",
},
])
const [updatedCart] = await service.update([
{
id: createdCart.id,
email: "test@email.com",
},
])
const [cart] = await service.list({ id: [createdCart.id] })
expect(cart).toEqual(
expect.objectContaining({
id: createdCart.id,
currency_code: "eur",
email: updatedCart.email,
})
)
})
})
describe("delete", () => {
it("should delete a cart successfully", async () => {
const [createdCart] = await service.create([
{
currency_code: "eur",
},
])
await service.delete([createdCart.id])
const carts = await service.list({ id: [createdCart.id] })
expect(carts.length).toEqual(0)
})
})
})

View File

@@ -67,12 +67,13 @@ export default class Cart {
metadata?: Record<string, unknown> | null
@OneToMany(() => LineItem, (lineItem) => lineItem.cart, {
orphanRemoval: true,
cascade: [Cascade.REMOVE],
})
items = new Collection<LineItem>(this)
@OneToMany(() => ShippingMethod, (shippingMethod) => shippingMethod.cart, {
orphanRemoval: true,
cascade: [Cascade.REMOVE],
})
shipping_methods = new Collection<ShippingMethod>(this)

View File

@@ -0,0 +1,27 @@
import { Context } from "@medusajs/types"
import { DALUtils } from "@medusajs/utils"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { Address } from "@models"
import { CreateAddressDTO, UpdateAddressDTO } from "../types"
export class AddressRepository extends DALUtils.mikroOrmBaseRepositoryFactory<
Address,
{
create: CreateAddressDTO
}
>(Address) {
async update(
data: { address: Address; update: UpdateAddressDTO }[],
context: Context = {}
): Promise<Address[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const entities = data.map(({ address, update }) => {
return manager.assign(address, update)
})
manager.persist(entities)
return entities
}
}

View File

@@ -0,0 +1,27 @@
import { Context } from "@medusajs/types"
import { DALUtils } from "@medusajs/utils"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { Cart } from "@models"
import { CreateCartDTO, UpdateCartDTO } from "../types"
export class CartRepository extends DALUtils.mikroOrmBaseRepositoryFactory<
Cart,
{
create: CreateCartDTO
}
>(Cart) {
async update(
data: { cart: Cart; update: UpdateCartDTO }[],
context: Context = {}
): Promise<Cart[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
const entities = data.map(({ cart, update }) => {
return manager.assign(cart, update)
})
manager.persist(entities)
return entities
}
}

View File

@@ -1 +1,4 @@
export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils"
export * from "./address"
export * from "./cart"

View File

@@ -0,0 +1,128 @@
import {
AddressDTO,
Context,
DAL,
FilterableAddressProps,
FindConfig,
} from "@medusajs/types"
import {
InjectManager,
InjectTransactionManager,
MedusaContext,
MedusaError,
ModulesSdkUtils,
retrieveEntity,
} from "@medusajs/utils"
import { Address } from "@models"
import { AddressRepository } from "../repositories/address"
import { CreateAddressDTO, UpdateAddressDTO } from "../types"
type InjectedDependencies = {
addressRepository: DAL.RepositoryService
}
export default class AddressService<TEntity extends Address = Address> {
protected readonly addressRepository_: DAL.RepositoryService
constructor({ addressRepository }: InjectedDependencies) {
this.addressRepository_ = addressRepository
}
@InjectManager("addressRepository_")
async retrieve(
id: string,
config: FindConfig<AddressDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity> {
return (await retrieveEntity<Address, AddressDTO>({
id: id,
entityName: Address.name,
repository: this.addressRepository_,
config,
sharedContext,
})) as TEntity
}
@InjectManager("addressRepository_")
async list(
filters: FilterableAddressProps = {},
config: FindConfig<AddressDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
const queryOptions = ModulesSdkUtils.buildQuery<Address>(filters, config)
return (await this.addressRepository_.find(
queryOptions,
sharedContext
)) as TEntity[]
}
@InjectManager("addressRepository_")
async listAndCount(
filters: FilterableAddressProps = {},
config: FindConfig<AddressDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[TEntity[], number]> {
const queryOptions = ModulesSdkUtils.buildQuery<Address>(filters, config)
return (await this.addressRepository_.findAndCount(
queryOptions,
sharedContext
)) as [TEntity[], number]
}
@InjectTransactionManager("addressRepository_")
async create(
data: CreateAddressDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await (this.addressRepository_ as AddressRepository).create(
data,
sharedContext
)) as TEntity[]
}
@InjectTransactionManager("addressRepository_")
async update(
data: UpdateAddressDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
const existingAddresses = await this.list(
{ id: data.map(({ id }) => id) },
{},
sharedContext
)
const existingAddressesMap = new Map(
existingAddresses.map<[string, Address]>((addr) => [addr.id, addr])
)
const updates: { address: Address; update: UpdateAddressDTO }[] = []
for (const update of data) {
const address = existingAddressesMap.get(update.id)
if (!address) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Address with id "${update.id}" not found`
)
}
updates.push({ address, update })
}
return (await (this.addressRepository_ as AddressRepository).update(
updates,
sharedContext
)) as TEntity[]
}
@InjectTransactionManager("addressRepository_")
async delete(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.addressRepository_.delete(ids, sharedContext)
}
}

View File

@@ -1,20 +1,35 @@
import {
CartDTO,
Context,
CreateCartDTO,
DAL,
FilterableCartProps,
FindConfig,
ICartModuleService,
InternalModuleDeclaration,
ModuleJoinerConfig,
UpdateCartDTO,
} from "@medusajs/types"
import { Cart } from "@models"
import {
InjectManager,
InjectTransactionManager,
MedusaContext,
} from "@medusajs/utils"
import { joinerConfig } from "../joiner-config"
import AddressService from "./address"
import CartService from "./cart"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
cartService: CartService
addressService: AddressService
}
// TODO: implement ICartModuleService from @medusajs/types
export default class CartModuleService<TCart extends Cart = Cart> {
export default class CartModuleService implements ICartModuleService {
protected baseRepository_: DAL.RepositoryService
protected cartService_: CartService
protected addressService_: AddressService
constructor(
{ baseRepository }: InjectedDependencies,
@@ -26,4 +41,138 @@ export default class CartModuleService<TCart extends Cart = Cart> {
__joinerConfig(): ModuleJoinerConfig {
return joinerConfig
}
@InjectManager("baseRepository_")
async retrieve(
id: string,
config: FindConfig<CartDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<CartDTO> {
const cart = await this.cartService_.retrieve(id, config, sharedContext)
return await this.baseRepository_.serialize<CartDTO>(cart, {
populate: true,
})
}
@InjectManager("baseRepository_")
async list(
filters: FilterableCartProps = {},
config: FindConfig<CartDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<CartDTO[]> {
const carts = await this.cartService_.list(filters, config, sharedContext)
return await this.baseRepository_.serialize<CartDTO[]>(carts, {
populate: true,
})
}
@InjectManager("baseRepository_")
async listAndCount(
filters: FilterableCartProps = {},
config: FindConfig<CartDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[CartDTO[], number]> {
const [carts, count] = await this.cartService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<CartDTO[]>(carts, {
populate: true,
}),
count,
]
}
async create(
data: CreateCartDTO[],
sharedContext?: Context
): Promise<CartDTO[]>
async create(data: CreateCartDTO, sharedContext?: Context): Promise<CartDTO>
@InjectManager("baseRepository_")
async create(
data: CreateCartDTO[] | CreateCartDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<CartDTO[] | CartDTO> {
const input = Array.isArray(data) ? data : [data]
const carts = await this.create_(input, sharedContext)
const result = await this.list(
{ id: carts.map((p) => p!.id) },
{
relations: ["shipping_address", "billing_address"],
},
sharedContext
)
return (Array.isArray(data) ? result : result[0]) as CartDTO | CartDTO[]
}
@InjectTransactionManager("baseRepository_")
protected async create_(
data: CreateCartDTO[],
@MedusaContext() sharedContext: Context = {}
) {
return await this.cartService_.create(data, sharedContext)
}
async update(
data: UpdateCartDTO[],
sharedContext?: Context
): Promise<CartDTO[]>
async update(data: UpdateCartDTO, sharedContext?: Context): Promise<CartDTO>
@InjectManager("baseRepository_")
async update(
data: UpdateCartDTO[] | UpdateCartDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<CartDTO[] | CartDTO> {
const input = Array.isArray(data) ? data : [data]
const carts = await this.update_(input, sharedContext)
const result = await this.list(
{ id: carts.map((p) => p!.id) },
{
relations: ["shipping_address", "billing_address"],
},
sharedContext
)
return (Array.isArray(data) ? result : result[0]) as CartDTO | CartDTO[]
}
@InjectTransactionManager("baseRepository_")
protected async update_(
data: UpdateCartDTO[],
@MedusaContext() sharedContext: Context = {}
) {
return await this.cartService_.update(data, sharedContext)
}
async delete(
ids: string[],
sharedContext?: Context
): Promise<void>
async delete(
ids: string,
sharedContext?: Context
): Promise<void>
@InjectTransactionManager("baseRepository_")
async delete(
ids: string[] | string,
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
const cartIds = Array.isArray(ids) ? ids : [ids]
await this.cartService_.delete(cartIds, sharedContext)
}
}

View File

@@ -0,0 +1,128 @@
import {
CartDTO,
Context,
DAL,
FilterableCartProps,
FindConfig,
} from "@medusajs/types"
import {
InjectManager,
InjectTransactionManager,
MedusaContext,
MedusaError,
ModulesSdkUtils,
retrieveEntity,
} from "@medusajs/utils"
import { Cart } from "@models"
import { CartRepository } from "@repositories"
import { CreateCartDTO, UpdateCartDTO } from "@types"
type InjectedDependencies = {
cartRepository: DAL.RepositoryService
}
export default class CartService<TEntity extends Cart = Cart> {
protected readonly cartRepository_: DAL.RepositoryService
constructor({ cartRepository }: InjectedDependencies) {
this.cartRepository_ = cartRepository
}
@InjectManager("cartRepository_")
async retrieve(
id: string,
config: FindConfig<CartDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity> {
return (await retrieveEntity<Cart, CartDTO>({
id: id,
entityName: Cart.name,
repository: this.cartRepository_,
config,
sharedContext,
})) as TEntity
}
@InjectManager("cartRepository_")
async list(
filters: FilterableCartProps = {},
config: FindConfig<CartDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
const queryOptions = ModulesSdkUtils.buildQuery<Cart>(filters, config)
return (await this.cartRepository_.find(
queryOptions,
sharedContext
)) as TEntity[]
}
@InjectManager("cartRepository_")
async listAndCount(
filters: FilterableCartProps = {},
config: FindConfig<CartDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[TEntity[], number]> {
const queryOptions = ModulesSdkUtils.buildQuery<Cart>(filters, config)
return (await this.cartRepository_.findAndCount(
queryOptions,
sharedContext
)) as [TEntity[], number]
}
@InjectTransactionManager("cartRepository_")
async create(
data: CreateCartDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
return (await (this.cartRepository_ as CartRepository).create(
data,
sharedContext
)) as TEntity[]
}
@InjectTransactionManager("cartRepository_")
async update(
data: UpdateCartDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[]> {
const existingCarts = await this.list(
{ id: data.map(({ id }) => id) },
{},
sharedContext
)
const existingCartsMap = new Map(
existingCarts.map<[string, Cart]>((cart) => [cart.id, cart])
)
const updates: { cart: Cart; update: UpdateCartDTO }[] = []
for (const update of data) {
const cart = existingCartsMap.get(update.id)
if (!cart) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Cart with id "${update.id}" not found`
)
}
updates.push({ cart, update })
}
return (await (this.cartRepository_ as CartRepository).update(
updates,
sharedContext
)) as TEntity[]
}
@InjectTransactionManager("cartRepository_")
async delete(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.cartRepository_.delete(ids, sharedContext)
}
}

View File

@@ -1,2 +1,4 @@
export { default as CartModuleService } from "./cart-module";
export { default as AddressService } from "./address"
export { default as CartService } from "./cart"
export { default as CartModuleService } from "./cart-module"

View File

@@ -0,0 +1,20 @@
export interface UpsertAddressDTO {
customer_id?: string
company?: string
first_name?: string
last_name?: string
address_1?: string
address_2?: string
city?: string
country_code?: string
province?: string
postal_code?: string
phone?: string
metadata?: Record<string, unknown>
}
export interface UpdateAddressDTO extends UpsertAddressDTO {
id: string
}
export interface CreateAddressDTO extends UpsertAddressDTO {}

View File

@@ -0,0 +1,18 @@
export interface CreateCartDTO {
region_id?: string
customer_id?: string
sales_channel_id?: string
email?: string
currency_code: string
metadata?: Record<string, unknown>
}
export interface UpdateCartDTO {
id: string
region_id?: string
customer_id?: string
sales_channel_id?: string
email?: string
currency_code?: string
metadata?: Record<string, unknown>
}

View File

@@ -1,4 +1,6 @@
import { Logger } from "@medusajs/types"
export * from "./address"
export * from "./cart"
export type InitializeModuleInjectableDependencies = {
logger?: Logger

View File

@@ -22,7 +22,8 @@
"paths": {
"@models": ["./src/models"],
"@services": ["./src/services"],
"@repositories": ["./src/repositories"]
"@repositories": ["./src/repositories"],
"@types": ["./src/types"],
}
},
"include": ["src"],

View File

@@ -465,8 +465,13 @@ export interface FilterableCartProps
updated_at?: OperatorMap<string>
}
export interface FilterableAddressProps
extends BaseFilterable<FilterableAddressProps> {
id?: string | string[]
}
/**
* TODO: Remove this in favor of new__CartDTO, when module is released
* TODO: Remove this in favor of CartDTO, when module is released
* @deprecated Use CartDTO instead
*/

View File

@@ -2,7 +2,7 @@ import { FindConfig } from "../common"
import { IModuleService } from "../modules-sdk"
import { Context } from "../shared-context"
import { CartDTO, FilterableCartProps } from "./common"
import { AddLineItemsDTO, CreateCartDTO, UpdateCartDTO, UpdateLineItemsDTO } from "./mutations"
import { CreateCartDTO, UpdateCartDTO } from "./mutations"
export interface ICartModuleService extends IModuleService {
retrieve(
@@ -24,25 +24,28 @@ export interface ICartModuleService extends IModuleService {
): Promise<[CartDTO[], number]>
create(data: CreateCartDTO[], sharedContext?: Context): Promise<CartDTO[]>
create(data: CreateCartDTO, sharedContext?: Context): Promise<CartDTO>
update(data: UpdateCartDTO[], sharedContext?: Context): Promise<CartDTO[]>
update(data: UpdateCartDTO, sharedContext?: Context): Promise<CartDTO>
delete(cartIds: string[], sharedContext?: Context): Promise<void>
delete(cartId: string, sharedContext?: Context): Promise<void>
addLineItems(data: AddLineItemsDTO, sharedContext?: Context): Promise<CartDTO>
addLineItems(
data: AddLineItemsDTO[],
sharedContext?: Context
): Promise<CartDTO[]>
// addLineItems(data: AddLineItemsDTO, sharedContext?: Context): Promise<CartDTO>
// addLineItems(
// data: AddLineItemsDTO[],
// sharedContext?: Context
// ): Promise<CartDTO[]>
updateLineItems(
data: UpdateLineItemsDTO,
sharedContext?: Context
): Promise<CartDTO>
updateLineItems(
data: UpdateLineItemsDTO[],
sharedContext?: Context
): Promise<CartDTO[]>
// updateLineItems(
// data: UpdateLineItemsDTO,
// sharedContext?: Context
// ): Promise<CartDTO>
// updateLineItems(
// data: UpdateLineItemsDTO[],
// sharedContext?: Context
// ): Promise<CartDTO[]>
removeLineItems(lineItemIds: string[], sharedContext?: Context): Promise<void>
// removeLineItems(lineItemIds: string[], sharedContext?: Context): Promise<void>
}

View File

@@ -4,14 +4,6 @@ import {
FilterQuery as InternalFilerQuery,
RepositoryTransformOptions,
} from "@medusajs/types"
import { arrayDifference, isString, MedusaError } from "../../common"
import { MedusaContext } from "../../decorators"
import { buildQuery, InjectTransactionManager } from "../../modules-sdk"
import {
getSoftDeletedCascadedEntitiesIdsMappedBy,
transactionWrapper,
} from "../utils"
import { mikroOrmSerializer, mikroOrmUpdateDeletedAtRecursively } from "./utils"
import {
EntityManager,
EntitySchema,
@@ -19,12 +11,20 @@ import {
LoadStrategy,
RequiredEntityData,
} from "@mikro-orm/core"
import { FindOptions as MikroOptions } from "@mikro-orm/core/drivers/IDatabaseDriver"
import {
EntityClass,
EntityName,
FilterQuery as MikroFilterQuery,
} from "@mikro-orm/core/typings"
import { FindOptions as MikroOptions } from "@mikro-orm/core/drivers/IDatabaseDriver"
import { MedusaError, arrayDifference, isString } from "../../common"
import { MedusaContext } from "../../decorators"
import { InjectTransactionManager, buildQuery } from "../../modules-sdk"
import {
getSoftDeletedCascadedEntitiesIdsMappedBy,
transactionWrapper,
} from "../utils"
import { mikroOrmSerializer, mikroOrmUpdateDeletedAtRecursively } from "./utils"
export class MikroOrmBase<T = any> {
readonly manager_: any