chore: Refactor and improve abstract module service factory (#7688)
* chore: Refactor and improve abstract module service factory * align naming * clean up some template args and tests * partially migrate modules * partially migrate modules * migrate more modules * migrate last modules * fix typings * rename interface * rename interface * fixes * fixes * rm local plain tests
This commit is contained in:
committed by
GitHub
parent
c57223a3a2
commit
d2a5201eeb
@@ -1,4 +1,4 @@
|
||||
import { internalModuleServiceFactory } from "../internal-module-service-factory"
|
||||
import { MedusaInternalService } from "../medusa-internal-service"
|
||||
import { lowerCaseFirst } from "../../common"
|
||||
|
||||
const defaultContext = { __type: "MedusaContext" }
|
||||
@@ -39,14 +39,14 @@ describe("Internal Module Service Factory", () => {
|
||||
},
|
||||
}
|
||||
|
||||
const internalModuleService = internalModuleServiceFactory<any>(Model)
|
||||
const IMedusaInternalService = MedusaInternalService<any>(Model)
|
||||
|
||||
describe("Internal Module Service Methods", () => {
|
||||
let instance
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
instance = new internalModuleService(containerMock)
|
||||
instance = new IMedusaInternalService(containerMock)
|
||||
})
|
||||
|
||||
it("should throw model id undefined error on retrieve if id is not defined", async () => {
|
||||
@@ -62,10 +62,10 @@ describe("Internal Module Service Factory", () => {
|
||||
static meta = { primaryKeys: ["id", "name"] }
|
||||
}
|
||||
|
||||
const compositeInternalModuleService =
|
||||
internalModuleServiceFactory<any>(CompositeModel)
|
||||
const compositeIMedusaInternalService =
|
||||
MedusaInternalService<any>(CompositeModel)
|
||||
|
||||
const instance = new compositeInternalModuleService(containerMock)
|
||||
const instance = new compositeIMedusaInternalService(containerMock)
|
||||
|
||||
const err = await instance.retrieve().catch((e) => e)
|
||||
expect(err.message).toBe("compositeModel - id, name must be defined")
|
||||
@@ -94,10 +94,10 @@ describe("Internal Module Service Factory", () => {
|
||||
static meta = { primaryKeys: ["id", "name"] }
|
||||
}
|
||||
|
||||
const compositeInternalModuleService =
|
||||
internalModuleServiceFactory<any>(CompositeModel)
|
||||
const compositeIMedusaInternalService =
|
||||
MedusaInternalService<any>(CompositeModel)
|
||||
|
||||
const instance = new compositeInternalModuleService(containerMock)
|
||||
const instance = new compositeIMedusaInternalService(containerMock)
|
||||
|
||||
const entity = { id: "1", name: "Item" }
|
||||
containerMock[
|
||||
@@ -1,4 +1,4 @@
|
||||
import { abstractModuleServiceFactory } from "../abstract-module-service-factory"
|
||||
import { MedusaService } from "../medusa-service"
|
||||
|
||||
const baseRepoMock = {
|
||||
serialize: jest.fn().mockImplementation((item) => item),
|
||||
@@ -41,13 +41,12 @@ describe("Abstract Module Service Factory", () => {
|
||||
},
|
||||
}
|
||||
|
||||
const mainModelMock = class MainModelMock {}
|
||||
const otherModelMock1 = class OtherModelMock1 {}
|
||||
const otherModelMock2 = class OtherModelMock2 {}
|
||||
class MainModelMock {}
|
||||
class OtherModelMock1 {}
|
||||
class OtherModelMock2 {}
|
||||
|
||||
const abstractModuleService = abstractModuleServiceFactory<
|
||||
any,
|
||||
any,
|
||||
const abstractModuleService = MedusaService<
|
||||
MainModelMock,
|
||||
{
|
||||
OtherModelMock1: {
|
||||
dto: any
|
||||
@@ -60,22 +59,10 @@ describe("Abstract Module Service Factory", () => {
|
||||
plural: "OtherModelMock2s"
|
||||
}
|
||||
}
|
||||
>(
|
||||
mainModelMock,
|
||||
[
|
||||
{
|
||||
model: otherModelMock1,
|
||||
plural: "otherModelMock1s",
|
||||
singular: "otherModelMock1",
|
||||
},
|
||||
{
|
||||
model: otherModelMock2,
|
||||
plural: "otherModelMock2s",
|
||||
singular: "otherModelMock2",
|
||||
},
|
||||
]
|
||||
// Add more parameters as needed
|
||||
)
|
||||
>(MainModelMock, {
|
||||
OtherModelMock1,
|
||||
OtherModelMock2,
|
||||
})
|
||||
|
||||
describe("Main Model Methods", () => {
|
||||
let instance
|
||||
@@ -6,7 +6,7 @@ export * from "./loaders/mikro-orm-connection-loader-factory"
|
||||
export * from "./loaders/container-loader-factory"
|
||||
export * from "./create-pg-connection"
|
||||
export * from "./migration-scripts"
|
||||
export * from "./internal-module-service-factory"
|
||||
export * from "./abstract-module-service-factory"
|
||||
export * from "./medusa-internal-service"
|
||||
export * from "./medusa-service"
|
||||
export * from "./definition"
|
||||
export * from "./event-builder-factory"
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from "@medusajs/types"
|
||||
|
||||
import { asClass } from "awilix"
|
||||
import { internalModuleServiceFactory } from "../internal-module-service-factory"
|
||||
import { MedusaInternalService } from "../medusa-internal-service"
|
||||
import { lowerCaseFirst } from "../../common"
|
||||
import {
|
||||
MikroOrmBaseRepository,
|
||||
@@ -100,10 +100,7 @@ export function loadModuleServices({
|
||||
const finalService = moduleServicesMap.get(mappedServiceName)
|
||||
|
||||
if (!finalService) {
|
||||
moduleServicesMap.set(
|
||||
mappedServiceName,
|
||||
internalModuleServiceFactory(Model)
|
||||
)
|
||||
moduleServicesMap.set(mappedServiceName, MedusaInternalService(Model))
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -33,20 +33,18 @@ type SelectorAndData = {
|
||||
data: any
|
||||
}
|
||||
|
||||
export function internalModuleServiceFactory<
|
||||
TContainer extends object = object
|
||||
>(
|
||||
export function MedusaInternalService<TContainer extends object = object>(
|
||||
model: any
|
||||
): {
|
||||
new <TEntity extends object = any>(
|
||||
container: TContainer
|
||||
): ModulesSdkTypes.InternalModuleService<TEntity, TContainer>
|
||||
): ModulesSdkTypes.IMedusaInternalService<TEntity, TContainer>
|
||||
} {
|
||||
const injectedRepositoryName = `${lowerCaseFirst(model.name)}Repository`
|
||||
const propertyRepositoryName = `__${injectedRepositoryName}__`
|
||||
|
||||
class AbstractService_<TEntity extends object>
|
||||
implements ModulesSdkTypes.InternalModuleService<TEntity, TContainer>
|
||||
implements ModulesSdkTypes.IMedusaInternalService<TEntity, TContainer>
|
||||
{
|
||||
readonly __container__: TContainer;
|
||||
[key: string]: any
|
||||
@@ -525,5 +523,5 @@ export function internalModuleServiceFactory<
|
||||
|
||||
return AbstractService_ as unknown as new <TEntity extends {}>(
|
||||
container: TContainer
|
||||
) => ModulesSdkTypes.InternalModuleService<TEntity, TContainer>
|
||||
) => ModulesSdkTypes.IMedusaInternalService<TEntity, TContainer>
|
||||
}
|
||||
@@ -12,11 +12,11 @@ import {
|
||||
SoftDeleteReturn,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
MapToConfig,
|
||||
isString,
|
||||
kebabCase,
|
||||
lowerCaseFirst,
|
||||
mapObjectTo,
|
||||
MapToConfig,
|
||||
pluralize,
|
||||
upperCaseFirst,
|
||||
} from "../common"
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
} from "./decorators"
|
||||
import { ModuleRegistrationName } from "./definition"
|
||||
|
||||
type BaseMethods =
|
||||
| "retrieve"
|
||||
@@ -49,68 +50,74 @@ const methods: BaseMethods[] = [...readMethods, ...writeMethods]
|
||||
|
||||
type ModelDTOConfig = {
|
||||
dto: object
|
||||
create?: object
|
||||
update?: object
|
||||
}
|
||||
type ModelDTOConfigRecord = Record<any, ModelDTOConfig>
|
||||
type ModelNamingConfig = {
|
||||
create?: any
|
||||
update?: any
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
singular?: string
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
plural?: string
|
||||
}
|
||||
|
||||
type ModelsConfigTemplate = {
|
||||
[ModelName: string]: ModelDTOConfig & ModelNamingConfig
|
||||
type EntitiesConfigTemplate = { [key: string]: ModelDTOConfig }
|
||||
|
||||
type ModelConfigurationToDto<T extends ModelConfiguration> =
|
||||
T extends abstract new (...args: any) => infer R
|
||||
? R
|
||||
: T extends { dto: infer DTO }
|
||||
? DTO
|
||||
: any
|
||||
|
||||
type ModelConfigurationsToConfigTemplate<
|
||||
T extends Record<string, ModelConfiguration>
|
||||
> = {
|
||||
[Key in keyof T as `${Capitalize<Key & string>}`]: {
|
||||
dto: ModelConfigurationToDto<T[Key]>
|
||||
create: any
|
||||
update: any
|
||||
}
|
||||
}
|
||||
|
||||
type ExtractSingularName<
|
||||
T extends Record<any, any>,
|
||||
K = keyof T
|
||||
> = T[K] extends { singular?: string } ? T[K]["singular"] : K
|
||||
|
||||
type CreateMethodName<
|
||||
TModelDTOConfig extends ModelDTOConfigRecord,
|
||||
TModelName = keyof TModelDTOConfig
|
||||
> = TModelDTOConfig[TModelName] extends { create?: object }
|
||||
? `create${ExtractPluralName<TModelDTOConfig, TModelName>}`
|
||||
: never
|
||||
|
||||
type UpdateMethodName<
|
||||
TModelDTOConfig extends ModelDTOConfigRecord,
|
||||
TModelName = keyof TModelDTOConfig
|
||||
> = TModelDTOConfig[TModelName] extends { update?: object }
|
||||
? `update${ExtractPluralName<TModelDTOConfig, TModelName>}`
|
||||
: never
|
||||
type ExtractSingularName<T extends Record<any, any>, K = keyof T> = Capitalize<
|
||||
T[K] extends { singular?: string } ? T[K]["singular"] & string : K & string
|
||||
>
|
||||
|
||||
type ExtractPluralName<T extends Record<any, any>, K = keyof T> = T[K] extends {
|
||||
plural?: string
|
||||
}
|
||||
? T[K]["plural"]
|
||||
? T[K]["plural"] & string
|
||||
: Pluralize<K & string>
|
||||
|
||||
type ModelConfiguration =
|
||||
| Constructor<any>
|
||||
| { singular?: string; plural?: string; model: Constructor<any> }
|
||||
// TODO: this will be removed in the follow up pr once the main entity concept will be removed
|
||||
type ModelConfiguration = Constructor<any> | ModelDTOConfig | any
|
||||
|
||||
export interface AbstractModuleServiceBase<TContainer, TMainModelDTO> {
|
||||
get __container__(): TContainer
|
||||
type ExtractMutationDtoOrAny<T> = T extends unknown ? any : T
|
||||
|
||||
export interface AbstractModuleServiceBase<TEntryEntityConfig> {
|
||||
new (container: Record<any, any>, ...args: any[]): this
|
||||
|
||||
get __container__(): Record<any, any>
|
||||
|
||||
retrieve(
|
||||
id: string,
|
||||
config?: FindConfig<any>,
|
||||
sharedContext?: Context
|
||||
): Promise<TMainModelDTO>
|
||||
): Promise<TEntryEntityConfig>
|
||||
|
||||
list(
|
||||
filters?: any,
|
||||
config?: FindConfig<any>,
|
||||
sharedContext?: Context
|
||||
): Promise<TMainModelDTO[]>
|
||||
): Promise<TEntryEntityConfig[]>
|
||||
|
||||
listAndCount(
|
||||
filters?: any,
|
||||
config?: FindConfig<any>,
|
||||
sharedContext?: Context
|
||||
): Promise<[TMainModelDTO[], number]>
|
||||
): Promise<[TEntryEntityConfig[], number]>
|
||||
|
||||
delete(
|
||||
primaryKeyValues: string | object | string[] | object[],
|
||||
@@ -131,56 +138,50 @@ export interface AbstractModuleServiceBase<TContainer, TMainModelDTO> {
|
||||
}
|
||||
|
||||
export type AbstractModuleService<
|
||||
TContainer,
|
||||
TMainModelDTO,
|
||||
TModelDTOConfig extends ModelsConfigTemplate
|
||||
> = AbstractModuleServiceBase<TContainer, TMainModelDTO> & {
|
||||
[TModelName in keyof TModelDTOConfig as `retrieve${ExtractSingularName<
|
||||
TModelDTOConfig,
|
||||
TModelName
|
||||
> &
|
||||
string}`]: (
|
||||
TEntryEntityConfig extends ModelConfiguration,
|
||||
TEntitiesDtoConfig extends EntitiesConfigTemplate
|
||||
> = AbstractModuleServiceBase<TEntryEntityConfig> & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `retrieve${ExtractSingularName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: (
|
||||
id: string,
|
||||
config?: FindConfig<any>,
|
||||
sharedContext?: Context
|
||||
) => Promise<TModelDTOConfig[TModelName & string]["dto"]>
|
||||
) => Promise<TEntitiesDtoConfig[TEntityName]["dto"]>
|
||||
} & {
|
||||
[TModelName in keyof TModelDTOConfig as `list${ExtractPluralName<
|
||||
TModelDTOConfig,
|
||||
TModelName
|
||||
> &
|
||||
string}`]: (
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `list${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: (
|
||||
filters?: any,
|
||||
config?: FindConfig<any>,
|
||||
sharedContext?: Context
|
||||
) => Promise<TModelDTOConfig[TModelName & string]["dto"][]>
|
||||
) => Promise<TEntitiesDtoConfig[TEntityName]["dto"][]>
|
||||
} & {
|
||||
[TModelName in keyof TModelDTOConfig as `listAndCount${ExtractPluralName<
|
||||
TModelDTOConfig,
|
||||
TModelName
|
||||
> &
|
||||
string}`]: {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `listAndCount${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(filters?: any, config?: FindConfig<any>, sharedContext?: Context): Promise<
|
||||
[TModelDTOConfig[TModelName & string]["dto"][], number]
|
||||
[TEntitiesDtoConfig[TEntityName]["dto"][], number]
|
||||
>
|
||||
}
|
||||
} & {
|
||||
[TModelName in keyof TModelDTOConfig as `delete${ExtractPluralName<
|
||||
TModelDTOConfig,
|
||||
TModelName
|
||||
> &
|
||||
string}`]: {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `delete${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(
|
||||
primaryKeyValues: string | object | string[] | object[],
|
||||
sharedContext?: Context
|
||||
): Promise<void>
|
||||
}
|
||||
} & {
|
||||
[TModelName in keyof TModelDTOConfig as `softDelete${ExtractPluralName<
|
||||
TModelDTOConfig,
|
||||
TModelName
|
||||
> &
|
||||
string}`]: {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `softDelete${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
<TReturnableLinkableKeys extends string>(
|
||||
primaryKeyValues: string | object | string[] | object[],
|
||||
config?: SoftDeleteReturn<TReturnableLinkableKeys>,
|
||||
@@ -188,11 +189,10 @@ export type AbstractModuleService<
|
||||
): Promise<Record<string, string[]> | void>
|
||||
}
|
||||
} & {
|
||||
[TModelName in keyof TModelDTOConfig as `restore${ExtractPluralName<
|
||||
TModelDTOConfig,
|
||||
TModelName
|
||||
> &
|
||||
string}`]: {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `restore${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
<TReturnableLinkableKeys extends string>(
|
||||
primaryKeyValues: string | object | string[] | object[],
|
||||
config?: RestoreReturn<TReturnableLinkableKeys>,
|
||||
@@ -200,45 +200,103 @@ export type AbstractModuleService<
|
||||
): Promise<Record<string, string[]> | void>
|
||||
}
|
||||
} & {
|
||||
[TModelName in keyof TModelDTOConfig as CreateMethodName<
|
||||
TModelDTOConfig,
|
||||
TModelName
|
||||
>]: {
|
||||
(
|
||||
data: TModelDTOConfig[TModelName]["create"][],
|
||||
sharedContext?: Context
|
||||
): Promise<TModelDTOConfig[TModelName]["dto"][]>
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(...args: any[]): Promise<any>
|
||||
}
|
||||
} & {
|
||||
[TModelName in keyof TModelDTOConfig as CreateMethodName<
|
||||
TModelDTOConfig,
|
||||
TModelName
|
||||
>]: {
|
||||
(
|
||||
data: TModelDTOConfig[TModelName]["create"],
|
||||
sharedContext?: Context
|
||||
): Promise<TModelDTOConfig[TModelName]["dto"]>
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(...args: any[]): Promise<any>
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Because of a bug, those methods were not made visible which now cause issues with the fix as our interface are not consistent with the expectations
|
||||
|
||||
// are not consistent accross modules
|
||||
/* & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(data: any[], sharedContext?: Context): Promise<
|
||||
TEntitiesDtoConfig[TEntityName]["dto"][]
|
||||
>
|
||||
}
|
||||
} & {
|
||||
[TModelName in keyof TModelDTOConfig as UpdateMethodName<
|
||||
TModelDTOConfig,
|
||||
TModelName
|
||||
>]: {
|
||||
(
|
||||
data: TModelDTOConfig[TModelName]["update"][],
|
||||
sharedContext?: Context
|
||||
): Promise<TModelDTOConfig[TModelName]["dto"][]>
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(data: any, sharedContext?: Context): Promise<
|
||||
TEntitiesDtoConfig[TEntityName]["dto"][]
|
||||
>
|
||||
}
|
||||
} & {
|
||||
[TModelName in keyof TModelDTOConfig as UpdateMethodName<
|
||||
TModelDTOConfig,
|
||||
TModelName
|
||||
>]: {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(
|
||||
data: TModelDTOConfig[TModelName]["update"],
|
||||
data: TEntitiesDtoConfig[TEntityName]["update"][],
|
||||
sharedContext?: Context
|
||||
): Promise<TModelDTOConfig[TModelName]["dto"]>
|
||||
): Promise<TEntitiesDtoConfig[TEntityName]["dto"][]>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(
|
||||
data: TEntitiesDtoConfig[TEntityName]["update"],
|
||||
sharedContext?: Context
|
||||
): Promise<TEntitiesDtoConfig[TEntityName]["dto"]>
|
||||
}
|
||||
} & {
|
||||
[TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName<
|
||||
TEntitiesDtoConfig,
|
||||
TEntityName
|
||||
>}`]: {
|
||||
(
|
||||
idOrdSelector: any,
|
||||
data: TEntitiesDtoConfig[TEntityName]["update"],
|
||||
sharedContext?: Context
|
||||
): Promise<TEntitiesDtoConfig[TEntityName]["dto"][]>
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
function buildMethodNamesFromModel(
|
||||
model: ModelConfiguration,
|
||||
suffixed: boolean = true
|
||||
): Record<string, string> {
|
||||
return methods.reduce((acc, method) => {
|
||||
let modelName: string = ""
|
||||
|
||||
if (method === "retrieve") {
|
||||
modelName =
|
||||
"singular" in model && model.singular
|
||||
? model.singular
|
||||
: (model as { name: string }).name
|
||||
} else {
|
||||
modelName =
|
||||
"plural" in model && model.plural
|
||||
? model.plural
|
||||
: pluralize((model as { name: string }).name)
|
||||
}
|
||||
|
||||
const methodName = suffixed
|
||||
? `${method}${upperCaseFirst(modelName)}`
|
||||
: method
|
||||
|
||||
return { ...acc, [method]: methodName }
|
||||
}, {})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,7 +304,7 @@ export type AbstractModuleService<
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* const otherModels = new Set([
|
||||
* const entities = {
|
||||
* Currency,
|
||||
* Price,
|
||||
* PriceList,
|
||||
@@ -255,12 +313,10 @@ export type AbstractModuleService<
|
||||
* PriceRule,
|
||||
* PriceSetRuleType,
|
||||
* RuleType,
|
||||
* ])
|
||||
* }
|
||||
*
|
||||
* const AbstractModuleService = ModulesSdkUtils.abstractModuleServiceFactory<
|
||||
* InjectedDependencies,
|
||||
* class MyService extends ModulesSdkUtils.MedusaService<
|
||||
* PricingTypes.PriceSetDTO,
|
||||
* // The configuration of each entity also accept singular/plural properties, if not provided then it is using english pluralization
|
||||
* {
|
||||
* Currency: { dto: PricingTypes.CurrencyDTO }
|
||||
* Price: { dto: PricingTypes.PriceDTO }
|
||||
@@ -269,61 +325,55 @@ export type AbstractModuleService<
|
||||
* PriceList: { dto: PricingTypes.PriceListDTO }
|
||||
* PriceListRule: { dto: PricingTypes.PriceListRuleDTO }
|
||||
* }
|
||||
* >(PriceSet, [...otherModels], entityNameToLinkableKeysMap)
|
||||
* >(PriceSet, entities, entityNameToLinkableKeysMap) {}
|
||||
*
|
||||
* @param mainModel
|
||||
* @param otherModels
|
||||
* @example
|
||||
*
|
||||
* // Here the DTO's and names will be inferred from the arguments
|
||||
*
|
||||
* const entities = {
|
||||
* Currency,
|
||||
* Price,
|
||||
* PriceList,
|
||||
* PriceListRule,
|
||||
* PriceListRuleValue,
|
||||
* PriceRule,
|
||||
* PriceSetRuleType,
|
||||
* RuleType,
|
||||
* }
|
||||
*
|
||||
* class MyService extends ModulesSdkUtils.MedusaService(PriceSet, entities, entityNameToLinkableKeysMap) {}
|
||||
*
|
||||
* @param entryEntity
|
||||
* @param entities
|
||||
* @param entityNameToLinkableKeysMap
|
||||
*/
|
||||
export function abstractModuleServiceFactory<
|
||||
TContainer,
|
||||
TMainModelDTO,
|
||||
ModelsConfig extends ModelsConfigTemplate
|
||||
export function MedusaService<
|
||||
TEntryEntityConfig extends ModelConfiguration = ModelConfiguration,
|
||||
EntitiesConfig extends EntitiesConfigTemplate = { __empty: any },
|
||||
TEntities extends Record<string, ModelConfiguration> = Record<
|
||||
string,
|
||||
ModelConfiguration
|
||||
>
|
||||
>(
|
||||
mainModel: Constructor<any>,
|
||||
otherModels: ModelConfiguration[],
|
||||
entryEntity: (TEntryEntityConfig & { name: string }) | Constructor<any>,
|
||||
entities: TEntities,
|
||||
entityNameToLinkableKeysMap: MapToConfig = {}
|
||||
): {
|
||||
new (container: TContainer): AbstractModuleService<
|
||||
TContainer,
|
||||
TMainModelDTO,
|
||||
ModelsConfig
|
||||
new (...args: any[]): AbstractModuleService<
|
||||
ModelConfigurationToDto<TEntryEntityConfig>,
|
||||
EntitiesConfig extends { __empty: any }
|
||||
? ModelConfigurationsToConfigTemplate<TEntities>
|
||||
: EntitiesConfig
|
||||
>
|
||||
} {
|
||||
const buildMethodNamesFromModel = (
|
||||
model: ModelConfiguration,
|
||||
suffixed: boolean = true
|
||||
): Record<string, string> => {
|
||||
return methods.reduce((acc, method) => {
|
||||
let modelName: string = ""
|
||||
|
||||
if (method === "retrieve") {
|
||||
modelName =
|
||||
"singular" in model && model.singular
|
||||
? model.singular
|
||||
: (model as Constructor<any>).name
|
||||
} else {
|
||||
modelName =
|
||||
"plural" in model && model.plural
|
||||
? model.plural
|
||||
: pluralize((model as Constructor<any>).name)
|
||||
}
|
||||
|
||||
const methodName = suffixed
|
||||
? `${method}${upperCaseFirst(modelName)}`
|
||||
: method
|
||||
|
||||
return { ...acc, [method]: methodName }
|
||||
}, {})
|
||||
}
|
||||
|
||||
const buildAndAssignMethodImpl = function (
|
||||
klassPrototype: any,
|
||||
method: string,
|
||||
methodName: string,
|
||||
model: Constructor<any>
|
||||
modelName: string
|
||||
): void {
|
||||
const serviceRegistrationName = `${lowerCaseFirst(model.name)}Service`
|
||||
const serviceRegistrationName = `${lowerCaseFirst(modelName)}Service`
|
||||
|
||||
const applyMethod = function (impl: Function, contextIndex) {
|
||||
klassPrototype[methodName] = impl
|
||||
@@ -465,7 +515,7 @@ export function abstractModuleServiceFactory<
|
||||
|
||||
await this.eventBusModuleService_?.emit(
|
||||
primaryKeyValues_.map((primaryKeyValue) => ({
|
||||
eventName: `${kebabCase(model.name)}.deleted`,
|
||||
eventName: `${kebabCase(modelName)}.deleted`,
|
||||
data: isString(primaryKeyValue)
|
||||
? { id: primaryKeyValue }
|
||||
: primaryKeyValue,
|
||||
@@ -500,7 +550,7 @@ export function abstractModuleServiceFactory<
|
||||
|
||||
await this.eventBusModuleService_?.emit(
|
||||
softDeletedEntities.map(({ id }) => ({
|
||||
eventName: `${kebabCase(model.name)}.deleted`,
|
||||
eventName: `${kebabCase(modelName)}.deleted`,
|
||||
metadata: { source: "", action: "", object: "" },
|
||||
data: { id },
|
||||
}))
|
||||
@@ -558,19 +608,19 @@ export function abstractModuleServiceFactory<
|
||||
}
|
||||
|
||||
class AbstractModuleService_ {
|
||||
readonly __container__: Record<string, any>
|
||||
readonly __container__: Record<any, any>
|
||||
readonly baseRepository_: RepositoryService
|
||||
readonly eventBusModuleService_: IEventBusModuleService;
|
||||
|
||||
[key: string]: any
|
||||
|
||||
constructor(container: Record<string, any>) {
|
||||
constructor(container: Record<any, any>) {
|
||||
this.__container__ = container
|
||||
this.baseRepository_ = container.baseRepository
|
||||
|
||||
const hasEventBusModuleService = Object.keys(this.__container__).find(
|
||||
// TODO: Should use ModuleRegistrationName.EVENT_BUS but it would require to move it to the utils package to prevent circular dependencies
|
||||
(key) => key === "eventBusModuleService"
|
||||
(key) => key === ModuleRegistrationName.EVENT_BUS
|
||||
)
|
||||
|
||||
this.eventBusModuleService_ = hasEventBusModuleService
|
||||
@@ -592,18 +642,18 @@ export function abstractModuleServiceFactory<
|
||||
}
|
||||
}
|
||||
|
||||
const mainModelMethods = buildMethodNamesFromModel(mainModel, false)
|
||||
const entryEntityMethods = buildMethodNamesFromModel(entryEntity, false)
|
||||
|
||||
/**
|
||||
* Build the main retrieve/list/listAndCount/delete/softDelete/restore methods for the main model
|
||||
*/
|
||||
|
||||
for (let [method, methodName] of Object.entries(mainModelMethods)) {
|
||||
for (let [method, methodName] of Object.entries(entryEntityMethods)) {
|
||||
buildAndAssignMethodImpl(
|
||||
AbstractModuleService_.prototype,
|
||||
method,
|
||||
methodName,
|
||||
mainModel
|
||||
entryEntity.name
|
||||
)
|
||||
}
|
||||
|
||||
@@ -611,22 +661,26 @@ export function abstractModuleServiceFactory<
|
||||
* Build the retrieve/list/listAndCount/delete/softDelete/restore methods for all the other models
|
||||
*/
|
||||
|
||||
const otherModelsMethods: [ModelConfiguration, Record<string, string>][] =
|
||||
otherModels.map((model) => [model, buildMethodNamesFromModel(model)])
|
||||
const entitiesMethods: [
|
||||
string,
|
||||
ModelConfiguration,
|
||||
Record<string, string>
|
||||
][] = Object.entries(entities).map(([name, config]) => [
|
||||
name,
|
||||
config,
|
||||
buildMethodNamesFromModel(config),
|
||||
])
|
||||
|
||||
for (let [model, modelsMethods] of otherModelsMethods) {
|
||||
for (let [modelName, model, modelsMethods] of entitiesMethods) {
|
||||
Object.entries(modelsMethods).forEach(([method, methodName]) => {
|
||||
model = "model" in model ? model.model : model
|
||||
buildAndAssignMethodImpl(
|
||||
AbstractModuleService_.prototype,
|
||||
method,
|
||||
methodName,
|
||||
model
|
||||
modelName
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return AbstractModuleService_ as unknown as new (
|
||||
container: TContainer
|
||||
) => AbstractModuleService<TContainer, TMainModelDTO, ModelsConfig>
|
||||
return AbstractModuleService_ as any
|
||||
}
|
||||
Reference in New Issue
Block a user