chore: abstract the modules repository (#6035)
**What** Reduce the work effort to create repositories when building new modules by abstracting the most common cases into the base class default implementation returned by a factory - [x] Migrate all modules Co-authored-by: Riqwan Thamir <5105988+riqwan@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
ef5024980d
commit
b6ac768698
@@ -1,10 +1,10 @@
|
||||
import {
|
||||
Context,
|
||||
DAL,
|
||||
FilterQuery,
|
||||
FilterQuery as InternalFilerQuery,
|
||||
RepositoryTransformOptions,
|
||||
} from "@medusajs/types"
|
||||
import { isString } from "../../common"
|
||||
import { arrayDifference, isString, MedusaError } from "../../common"
|
||||
import { MedusaContext } from "../../decorators"
|
||||
import { buildQuery, InjectTransactionManager } from "../../modules-sdk"
|
||||
import {
|
||||
@@ -12,9 +12,22 @@ import {
|
||||
transactionWrapper,
|
||||
} from "../utils"
|
||||
import { mikroOrmSerializer, mikroOrmUpdateDeletedAtRecursively } from "./utils"
|
||||
import {
|
||||
EntityManager,
|
||||
EntitySchema,
|
||||
FilterQuery,
|
||||
LoadStrategy,
|
||||
RequiredEntityData,
|
||||
} from "@mikro-orm/core"
|
||||
import {
|
||||
EntityClass,
|
||||
EntityName,
|
||||
FilterQuery as MikroFilterQuery,
|
||||
} from "@mikro-orm/core/typings"
|
||||
import { FindOptions as MikroOptions } from "@mikro-orm/core/drivers/IDatabaseDriver"
|
||||
|
||||
export class MikroOrmBase<T = any> {
|
||||
protected readonly manager_: any
|
||||
readonly manager_: any
|
||||
|
||||
protected constructor({ manager }) {
|
||||
this.manager_ = manager
|
||||
@@ -53,28 +66,47 @@ export class MikroOrmBase<T = any> {
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class MikroOrmAbstractBaseRepository<T = any>
|
||||
extends MikroOrmBase
|
||||
implements DAL.RepositoryService<T>
|
||||
{
|
||||
abstract find(options?: DAL.FindOptions<T>, context?: Context)
|
||||
/**
|
||||
* Privileged extends of the abstract classes unless most of the methods can't be implemented
|
||||
* in your repository. This base repository is also used to provide a base repository
|
||||
* injection if needed to be able to use the common methods without being related to an entity.
|
||||
* In this case, none of the method will be implemented except the manager and transaction
|
||||
* related ones.
|
||||
*/
|
||||
|
||||
abstract findAndCount(
|
||||
options?: DAL.FindOptions<T>,
|
||||
context?: Context
|
||||
): Promise<[T[], number]>
|
||||
|
||||
abstract create(data: unknown[], context?: Context): Promise<T[]>
|
||||
export class MikroOrmBaseRepository<
|
||||
T extends object = object
|
||||
> extends MikroOrmBase<T> {
|
||||
constructor() {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
}
|
||||
create(data: unknown[], context?: Context): Promise<T[]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
update(data: unknown[], context?: Context): Promise<T[]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
abstract delete(ids: string[], context?: Context): Promise<void>
|
||||
delete(ids: string[], context?: Context): Promise<void> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
find(options?: DAL.FindOptions<T>, context?: Context): Promise<T[]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
findAndCount(
|
||||
options?: DAL.FindOptions<T>,
|
||||
context?: Context
|
||||
): Promise<[T[], number]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
@InjectTransactionManager()
|
||||
async softDelete(
|
||||
idsOrFilter: string[] | FilterQuery,
|
||||
idsOrFilter: string[] | InternalFilerQuery,
|
||||
@MedusaContext()
|
||||
{ transactionManager: manager }: Context = {}
|
||||
): Promise<[T[], Record<string, unknown[]>]> {
|
||||
@@ -91,7 +123,11 @@ export abstract class MikroOrmAbstractBaseRepository<T = any>
|
||||
const entities = await this.find({ where: filter as any })
|
||||
const date = new Date()
|
||||
|
||||
await mikroOrmUpdateDeletedAtRecursively(manager, entities, date)
|
||||
await mikroOrmUpdateDeletedAtRecursively<T>(
|
||||
manager,
|
||||
entities as any[],
|
||||
date
|
||||
)
|
||||
|
||||
const softDeletedEntitiesMap = getSoftDeletedCascadedEntitiesIdsMappedBy({
|
||||
entities,
|
||||
@@ -102,7 +138,7 @@ export abstract class MikroOrmAbstractBaseRepository<T = any>
|
||||
|
||||
@InjectTransactionManager()
|
||||
async restore(
|
||||
idsOrFilter: string[] | FilterQuery,
|
||||
idsOrFilter: string[] | InternalFilerQuery,
|
||||
@MedusaContext()
|
||||
{ transactionManager: manager }: Context = {}
|
||||
): Promise<[T[], Record<string, unknown[]>]> {
|
||||
@@ -122,7 +158,7 @@ export abstract class MikroOrmAbstractBaseRepository<T = any>
|
||||
|
||||
const entities = await this.find(query)
|
||||
|
||||
await mikroOrmUpdateDeletedAtRecursively(manager, entities, null)
|
||||
await mikroOrmUpdateDeletedAtRecursively(manager, entities as any[], null)
|
||||
|
||||
const softDeletedEntitiesMap = getSoftDeletedCascadedEntitiesIdsMappedBy({
|
||||
entities,
|
||||
@@ -151,72 +187,10 @@ export abstract class MikroOrmAbstractBaseRepository<T = any>
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class MikroOrmAbstractTreeRepositoryBase<T = any>
|
||||
extends MikroOrmBase<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>
|
||||
}
|
||||
|
||||
/**
|
||||
* Privileged extends of the abstract classes unless most of the methods can't be implemented
|
||||
* in your repository. This base repository is also used to provide a base repository
|
||||
* injection if needed to be able to use the common methods without being related to an entity.
|
||||
* In this case, none of the method will be implemented except the manager and transaction
|
||||
* related ones.
|
||||
*/
|
||||
|
||||
export class MikroOrmBaseRepository extends MikroOrmAbstractBaseRepository {
|
||||
constructor({ manager }) {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
}
|
||||
|
||||
create(data: unknown[], context?: Context): Promise<any[]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
update(data: unknown[], context?: Context): Promise<any[]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
delete(ids: string[], context?: Context): Promise<void> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
find(options?: DAL.FindOptions, context?: Context): Promise<any[]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
findAndCount(
|
||||
options?: DAL.FindOptions,
|
||||
context?: Context
|
||||
): Promise<[any[], number]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
}
|
||||
|
||||
export class MikroOrmBaseTreeRepository extends MikroOrmAbstractTreeRepositoryBase {
|
||||
constructor({ manager }) {
|
||||
export class MikroOrmBaseTreeRepository<
|
||||
T extends object = object
|
||||
> extends MikroOrmBase<T> {
|
||||
constructor() {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
}
|
||||
@@ -225,7 +199,7 @@ export class MikroOrmBaseTreeRepository extends MikroOrmAbstractTreeRepositoryBa
|
||||
options?: DAL.FindOptions,
|
||||
transformOptions?: RepositoryTransformOptions,
|
||||
context?: Context
|
||||
): Promise<any[]> {
|
||||
): Promise<T[]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
@@ -233,11 +207,11 @@ export class MikroOrmBaseTreeRepository extends MikroOrmAbstractTreeRepositoryBa
|
||||
options?: DAL.FindOptions,
|
||||
transformOptions?: RepositoryTransformOptions,
|
||||
context?: Context
|
||||
): Promise<[any[], number]> {
|
||||
): Promise<[T[], number]> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
create(data: unknown, context?: Context): Promise<any> {
|
||||
create(data: unknown, context?: Context): Promise<T> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
@@ -245,3 +219,127 @@ export class MikroOrmBaseTreeRepository extends MikroOrmAbstractTreeRepositoryBa
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
}
|
||||
|
||||
type DtoBasedMutationMethods = "create" | "update"
|
||||
|
||||
export function mikroOrmBaseRepositoryFactory<
|
||||
T extends object = object,
|
||||
TDTos extends { [K in DtoBasedMutationMethods]?: any } = {
|
||||
[K in DtoBasedMutationMethods]?: any
|
||||
}
|
||||
>(
|
||||
entity: EntityClass<T> | EntitySchema<T> | string,
|
||||
primaryKey: string = "id"
|
||||
) {
|
||||
class MikroOrmAbstractBaseRepository_ extends MikroOrmBaseRepository<T> {
|
||||
async create(data: TDTos["create"][], context?: Context): Promise<T[]> {
|
||||
const manager = this.getActiveManager<EntityManager>(context)
|
||||
|
||||
const entities = data.map((data_) => {
|
||||
return manager.create(
|
||||
entity as EntityName<T>,
|
||||
data_ as RequiredEntityData<T>
|
||||
)
|
||||
})
|
||||
|
||||
manager.persist(entities)
|
||||
|
||||
return entities
|
||||
}
|
||||
|
||||
async update(data: TDTos["update"][], context?: Context): Promise<T[]> {
|
||||
const manager = this.getActiveManager<EntityManager>(context)
|
||||
|
||||
const primaryKeyValues: string[] = data.map((data_) => data_[primaryKey])
|
||||
const existingEntities = await this.find(
|
||||
{
|
||||
where: {
|
||||
[primaryKey]: {
|
||||
$in: primaryKeyValues,
|
||||
},
|
||||
},
|
||||
} as DAL.FindOptions<T>,
|
||||
context
|
||||
)
|
||||
|
||||
const missingEntities = arrayDifference(
|
||||
data.map((d) => d[primaryKey]),
|
||||
existingEntities.map((d: any) => d[primaryKey])
|
||||
)
|
||||
|
||||
if (missingEntities.length) {
|
||||
const entityName = (entity as EntityClass<T>).name ?? entity
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`${entityName} with ${[primaryKey]} "${missingEntities.join(
|
||||
", "
|
||||
)}" not found`
|
||||
)
|
||||
}
|
||||
|
||||
const existingEntitiesMap = new Map(
|
||||
existingEntities.map<[string, T]>((entity_: any) => [
|
||||
entity_[primaryKey],
|
||||
entity_,
|
||||
])
|
||||
)
|
||||
|
||||
const entities = data.map((data_) => {
|
||||
const existingEntity = existingEntitiesMap.get(data_[primaryKey])!
|
||||
return manager.assign(existingEntity, data_ as RequiredEntityData<T>)
|
||||
})
|
||||
|
||||
manager.persist(entities)
|
||||
|
||||
return entities
|
||||
}
|
||||
|
||||
async delete(primaryKeyValues: string[], context?: Context): Promise<void> {
|
||||
const manager = this.getActiveManager<EntityManager>(context)
|
||||
|
||||
await manager.nativeDelete<T>(
|
||||
entity as EntityName<T>,
|
||||
{ [primaryKey]: { $in: primaryKeyValues } } as unknown as FilterQuery<T>
|
||||
)
|
||||
}
|
||||
|
||||
async find(options?: DAL.FindOptions<T>, context?: Context): Promise<T[]> {
|
||||
const manager = this.getActiveManager<EntityManager>(context)
|
||||
|
||||
const findOptions_ = { ...options }
|
||||
findOptions_.options ??= {}
|
||||
|
||||
Object.assign(findOptions_.options, {
|
||||
strategy: LoadStrategy.SELECT_IN,
|
||||
})
|
||||
|
||||
return await manager.find(
|
||||
entity as EntityName<T>,
|
||||
findOptions_.where as MikroFilterQuery<T>,
|
||||
findOptions_.options as MikroOptions<T>
|
||||
)
|
||||
}
|
||||
|
||||
async findAndCount(
|
||||
findOptions: DAL.FindOptions<T> = { where: {} },
|
||||
context: Context = {}
|
||||
): Promise<[T[], number]> {
|
||||
const manager = this.getActiveManager<EntityManager>(context)
|
||||
|
||||
const findOptions_ = { ...findOptions }
|
||||
findOptions_.options ??= {}
|
||||
|
||||
Object.assign(findOptions_.options, {
|
||||
strategy: LoadStrategy.SELECT_IN,
|
||||
})
|
||||
|
||||
return await manager.findAndCount(
|
||||
entity as EntityName<T>,
|
||||
findOptions_.where as MikroFilterQuery<T>,
|
||||
findOptions_.options as MikroOptions<T>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return MikroOrmAbstractBaseRepository_
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MedusaError } from "../common"
|
||||
|
||||
type RuleAttributeInput = string | undefined
|
||||
|
||||
export const ReservedPricingRuleAttributes = [
|
||||
|
||||
Reference in New Issue
Block a user