diff --git a/.changeset/cyan-countries-brush.md b/.changeset/cyan-countries-brush.md new file mode 100644 index 0000000000..cba8519534 --- /dev/null +++ b/.changeset/cyan-countries-brush.md @@ -0,0 +1,7 @@ +--- +"@medusajs/authentication": patch +"@medusajs/pricing": patch +"@medusajs/utils": patch +--- + +chore(utils): Update base repository to infer primary keys and support composite diff --git a/packages/authentication/src/repositories/auth-provider.ts b/packages/authentication/src/repositories/auth-provider.ts index a0d1d0f543..6445266d14 100644 --- a/packages/authentication/src/repositories/auth-provider.ts +++ b/packages/authentication/src/repositories/auth-provider.ts @@ -10,7 +10,12 @@ export class AuthProviderRepository extends DALUtils.mikroOrmBaseRepositoryFacto { create: RepositoryTypes.CreateAuthProviderDTO } ->(AuthProvider, "provider") { +>(AuthProvider) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async update( data: RepositoryTypes.UpdateAuthProviderDTO[], context: Context = {} diff --git a/packages/authentication/src/repositories/auth-user.ts b/packages/authentication/src/repositories/auth-user.ts index 2da807aa41..6e5fa10f9c 100644 --- a/packages/authentication/src/repositories/auth-user.ts +++ b/packages/authentication/src/repositories/auth-user.ts @@ -8,6 +8,11 @@ import { SqlEntityManager } from "@mikro-orm/postgresql" export class AuthUserRepository extends DALUtils.mikroOrmBaseRepositoryFactory( AuthUser ) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async create( data: RepositoryTypes.CreateAuthUserDTO[], context: Context = {} diff --git a/packages/cart/src/repositories/address.ts b/packages/cart/src/repositories/address.ts index 994ff44221..53c375821e 100644 --- a/packages/cart/src/repositories/address.ts +++ b/packages/cart/src/repositories/address.ts @@ -10,6 +10,11 @@ export class AddressRepository extends DALUtils.mikroOrmBaseRepositoryFactory< create: CreateAddressDTO } >(Address) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async update( data: { address: Address; update: UpdateAddressDTO }[], context: Context = {} diff --git a/packages/cart/src/repositories/cart.ts b/packages/cart/src/repositories/cart.ts index 4aeddee7f3..952fc9fd1b 100644 --- a/packages/cart/src/repositories/cart.ts +++ b/packages/cart/src/repositories/cart.ts @@ -10,6 +10,11 @@ export class CartRepository extends DALUtils.mikroOrmBaseRepositoryFactory< create: CreateCartDTO } >(Cart) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async update( data: { cart: Cart; update: UpdateCartDTO }[], context: Context = {} diff --git a/packages/pricing/src/repositories/currency.ts b/packages/pricing/src/repositories/currency.ts index 1aa87a5319..7a15ded16b 100644 --- a/packages/pricing/src/repositories/currency.ts +++ b/packages/pricing/src/repositories/currency.ts @@ -9,4 +9,9 @@ export class CurrencyRepository extends DALUtils.mikroOrmBaseRepositoryFactory< create: RepositoryTypes.CreateCurrencyDTO update: RepositoryTypes.UpdateCurrencyDTO } ->(Currency, "code") {} +>(Currency) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/pricing/src/repositories/money-amount.ts b/packages/pricing/src/repositories/money-amount.ts index 66595fc592..c23c7fa070 100644 --- a/packages/pricing/src/repositories/money-amount.ts +++ b/packages/pricing/src/repositories/money-amount.ts @@ -9,4 +9,9 @@ export class MoneyAmountRepository extends DALUtils.mikroOrmBaseRepositoryFactor create: RepositoryTypes.CreateMoneyAmountDTO update: RepositoryTypes.UpdateMoneyAmountDTO } ->(MoneyAmount) {} +>(MoneyAmount) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/pricing/src/repositories/price-list-rule-value.ts b/packages/pricing/src/repositories/price-list-rule-value.ts index 6829876056..d1512834c0 100644 --- a/packages/pricing/src/repositories/price-list-rule-value.ts +++ b/packages/pricing/src/repositories/price-list-rule-value.ts @@ -10,6 +10,11 @@ export class PriceListRuleValueRepository extends DALUtils.mikroOrmBaseRepositor update: RepositoryTypes.UpdatePriceListRuleValueDTO } >(PriceListRuleValue) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async create( data: RepositoryTypes.CreatePriceListRuleValueDTO[], context: Context = {} diff --git a/packages/pricing/src/repositories/price-list-rule.ts b/packages/pricing/src/repositories/price-list-rule.ts index 2e0664fc78..481549f7ab 100644 --- a/packages/pricing/src/repositories/price-list-rule.ts +++ b/packages/pricing/src/repositories/price-list-rule.ts @@ -7,6 +7,11 @@ import { RepositoryTypes } from "@types" export class PriceListRuleRepository extends DALUtils.mikroOrmBaseRepositoryFactory( PriceListRule ) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async create( data: RepositoryTypes.CreatePriceListRuleDTO[], context: Context = {} diff --git a/packages/pricing/src/repositories/price-list.ts b/packages/pricing/src/repositories/price-list.ts index 6245fbb752..e1db5946df 100644 --- a/packages/pricing/src/repositories/price-list.ts +++ b/packages/pricing/src/repositories/price-list.ts @@ -7,6 +7,11 @@ import { RepositoryTypes } from "@types" export class PriceListRepository extends DALUtils.mikroOrmBaseRepositoryFactory( PriceList ) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async create( data: RepositoryTypes.CreatePriceListDTO[], context: Context = {} diff --git a/packages/pricing/src/repositories/price-rule.ts b/packages/pricing/src/repositories/price-rule.ts index 73ffe0a994..ce6d41ee30 100644 --- a/packages/pricing/src/repositories/price-rule.ts +++ b/packages/pricing/src/repositories/price-rule.ts @@ -10,6 +10,11 @@ export class PriceRuleRepository extends DALUtils.mikroOrmBaseRepositoryFactory< update: RepositoryTypes.UpdatePriceRuleDTO } >(PriceRule) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async create( data: RepositoryTypes.CreatePriceRuleDTO[], context: Context = {} diff --git a/packages/pricing/src/repositories/price-set-money-amount-rules.ts b/packages/pricing/src/repositories/price-set-money-amount-rules.ts index 1cec6d2c9a..8577254b9f 100644 --- a/packages/pricing/src/repositories/price-set-money-amount-rules.ts +++ b/packages/pricing/src/repositories/price-set-money-amount-rules.ts @@ -9,4 +9,9 @@ export class PriceSetMoneyAmountRulesRepository extends DALUtils.mikroOrmBaseRep create: RepositoryTypes.CreatePriceSetMoneyAmountRulesDTO update: RepositoryTypes.UpdatePriceSetMoneyAmountRulesDTO } ->(PriceSetMoneyAmountRules) {} +>(PriceSetMoneyAmountRules) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/pricing/src/repositories/price-set-money-amount.ts b/packages/pricing/src/repositories/price-set-money-amount.ts index 2c9a017d6e..dde772edc6 100644 --- a/packages/pricing/src/repositories/price-set-money-amount.ts +++ b/packages/pricing/src/repositories/price-set-money-amount.ts @@ -9,4 +9,9 @@ export class PriceSetMoneyAmountRepository extends DALUtils.mikroOrmBaseReposito create: RepositoryTypes.CreatePriceSetMoneyAmountDTO update: RepositoryTypes.UpdatePriceSetMoneyAmountDTO } ->(PriceSetMoneyAmount) {} +>(PriceSetMoneyAmount) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/pricing/src/repositories/price-set-rule-type.ts b/packages/pricing/src/repositories/price-set-rule-type.ts index 1dea5f21ad..9cffb54d7e 100644 --- a/packages/pricing/src/repositories/price-set-rule-type.ts +++ b/packages/pricing/src/repositories/price-set-rule-type.ts @@ -9,4 +9,9 @@ export class PriceSetRuleTypeRepository extends DALUtils.mikroOrmBaseRepositoryF create: RepositoryTypes.CreatePriceSetRuleTypeDTO update: RepositoryTypes.UpdatePriceSetRuleTypeDTO } ->(PriceSetRuleType) {} +>(PriceSetRuleType) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/pricing/src/repositories/price-set.ts b/packages/pricing/src/repositories/price-set.ts index 03984ca0e0..94cfe23065 100644 --- a/packages/pricing/src/repositories/price-set.ts +++ b/packages/pricing/src/repositories/price-set.ts @@ -9,4 +9,9 @@ export class PriceSetRepository extends DALUtils.mikroOrmBaseRepositoryFactory< create: RepositoryTypes.CreatePriceSetDTO update: RepositoryTypes.UpdatePriceSetDTO } ->(PriceSet) {} +>(PriceSet) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/pricing/src/repositories/rule-type.ts b/packages/pricing/src/repositories/rule-type.ts index 1873ad675d..21d9f3d4f1 100644 --- a/packages/pricing/src/repositories/rule-type.ts +++ b/packages/pricing/src/repositories/rule-type.ts @@ -8,4 +8,9 @@ export class RuleTypeRepository extends DALUtils.mikroOrmBaseRepositoryFactory< create: RepositoryTypes.CreateRuleTypeDTO update: RepositoryTypes.UpdateRuleTypeDTO } ->(RuleType) {} +>(RuleType) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/product/src/repositories/product-collection.ts b/packages/product/src/repositories/product-collection.ts index 9fcdb62d19..6a8310e0f4 100644 --- a/packages/product/src/repositories/product-collection.ts +++ b/packages/product/src/repositories/product-collection.ts @@ -14,6 +14,11 @@ type CreateProductCollection = ProductTypes.CreateProductCollectionDTO & { export class ProductCollectionRepository extends DALUtils.mikroOrmBaseRepositoryFactory( ProductCollection ) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async create( data: CreateProductCollection[], context: Context = {} diff --git a/packages/product/src/repositories/product-image.ts b/packages/product/src/repositories/product-image.ts index de12c58c04..5dc1799b1c 100644 --- a/packages/product/src/repositories/product-image.ts +++ b/packages/product/src/repositories/product-image.ts @@ -7,6 +7,11 @@ import { DALUtils } from "@medusajs/utils" export class ProductImageRepository extends DALUtils.mikroOrmBaseRepositoryFactory( Image ) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async upsert(urls: string[], context: Context = {}): Promise { const manager = this.getActiveManager(context) diff --git a/packages/product/src/repositories/product-option-value.ts b/packages/product/src/repositories/product-option-value.ts index 219764cd35..2b29297430 100644 --- a/packages/product/src/repositories/product-option-value.ts +++ b/packages/product/src/repositories/product-option-value.ts @@ -11,6 +11,11 @@ import { SqlEntityManager } from "@mikro-orm/postgresql" export class ProductOptionValueRepository extends DALUtils.mikroOrmBaseRepositoryFactory( ProductOptionValue ) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async upsert( optionValues: (UpdateProductOptionValueDTO | CreateProductOptionValueDTO)[], context: Context = {} diff --git a/packages/product/src/repositories/product-option.ts b/packages/product/src/repositories/product-option.ts index a51f9260fb..fc4616acd0 100644 --- a/packages/product/src/repositories/product-option.ts +++ b/packages/product/src/repositories/product-option.ts @@ -10,6 +10,11 @@ export class ProductOptionRepository extends DALUtils.mikroOrmBaseRepositoryFact update: ProductTypes.UpdateProductOptionDTO } >(ProductOption) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async create( data: ProductTypes.CreateProductOptionDTO[], context: Context = {} diff --git a/packages/product/src/repositories/product-tag.ts b/packages/product/src/repositories/product-tag.ts index 69dfb03ea7..e3eae1543c 100644 --- a/packages/product/src/repositories/product-tag.ts +++ b/packages/product/src/repositories/product-tag.ts @@ -15,6 +15,11 @@ export class ProductTagRepository extends DALUtils.mikroOrmBaseRepositoryFactory update: UpdateProductTagDTO } >(ProductTag) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async upsert( tags: UpsertProductTagDTO[], context: Context = {} diff --git a/packages/product/src/repositories/product-type.ts b/packages/product/src/repositories/product-type.ts index 082a926bcf..352ff1ace8 100644 --- a/packages/product/src/repositories/product-type.ts +++ b/packages/product/src/repositories/product-type.ts @@ -14,6 +14,11 @@ export class ProductTypeRepository extends DALUtils.mikroOrmBaseRepositoryFactor update: UpdateProductTypeDTO } >(ProductType) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async upsert( types: CreateProductTypeDTO[], context: Context = {} diff --git a/packages/product/src/repositories/product-variant.ts b/packages/product/src/repositories/product-variant.ts index 3e7824e9c6..f792a8e3be 100644 --- a/packages/product/src/repositories/product-variant.ts +++ b/packages/product/src/repositories/product-variant.ts @@ -14,4 +14,9 @@ export class ProductVariantRepository extends DALUtils.mikroOrmBaseRepositoryFac "id" > } ->(ProductVariant) {} +>(ProductVariant) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/product/src/repositories/product.ts b/packages/product/src/repositories/product.ts index 855477db87..833c61e9d5 100644 --- a/packages/product/src/repositories/product.ts +++ b/packages/product/src/repositories/product.ts @@ -24,6 +24,11 @@ export class ProductRepository extends DALUtils.mikroOrmBaseRepositoryFactory< create: WithRequiredProperty } >(Product) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + async find( findOptions: DAL.FindOptions = { where: {} }, context: Context = {} diff --git a/packages/promotion/src/repositories/application-method.ts b/packages/promotion/src/repositories/application-method.ts index f2d7a60517..fd666d995b 100644 --- a/packages/promotion/src/repositories/application-method.ts +++ b/packages/promotion/src/repositories/application-method.ts @@ -8,4 +8,9 @@ export class ApplicationMethodRepository extends DALUtils.mikroOrmBaseRepository create: CreateApplicationMethodDTO update: UpdateApplicationMethodDTO } ->(ApplicationMethod) {} +>(ApplicationMethod) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/promotion/src/repositories/promotion-rule-value.ts b/packages/promotion/src/repositories/promotion-rule-value.ts index aa3868e5bf..7587fa295c 100644 --- a/packages/promotion/src/repositories/promotion-rule-value.ts +++ b/packages/promotion/src/repositories/promotion-rule-value.ts @@ -11,4 +11,9 @@ export class PromotionRuleValueRepository extends DALUtils.mikroOrmBaseRepositor create: CreatePromotionRuleValueDTO update: UpdatePromotionRuleValueDTO } ->(PromotionRuleValue) {} +>(PromotionRuleValue) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/promotion/src/repositories/promotion-rule.ts b/packages/promotion/src/repositories/promotion-rule.ts index d80c92c9f0..aceb5d33dd 100644 --- a/packages/promotion/src/repositories/promotion-rule.ts +++ b/packages/promotion/src/repositories/promotion-rule.ts @@ -8,4 +8,9 @@ export class PromotionRuleRepository extends DALUtils.mikroOrmBaseRepositoryFact create: CreatePromotionRuleDTO update: UpdatePromotionRuleDTO } ->(PromotionRule) {} +>(PromotionRule) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/promotion/src/repositories/promotion.ts b/packages/promotion/src/repositories/promotion.ts index 0df0002f02..c6732c74ba 100644 --- a/packages/promotion/src/repositories/promotion.ts +++ b/packages/promotion/src/repositories/promotion.ts @@ -8,4 +8,9 @@ export class PromotionRepository extends DALUtils.mikroOrmBaseRepositoryFactory< create: CreatePromotionDTO Update: UpdatePromotionDTO } ->(Promotion) {} +>(Promotion) { + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/utils/src/dal/mikro-orm/mikro-orm-repository.ts b/packages/utils/src/dal/mikro-orm/mikro-orm-repository.ts index 84c8b437f2..da3f18819c 100644 --- a/packages/utils/src/dal/mikro-orm/mikro-orm-repository.ts +++ b/packages/utils/src/dal/mikro-orm/mikro-orm-repository.ts @@ -7,7 +7,6 @@ import { import { EntityManager, EntitySchema, - FilterQuery, LoadStrategy, RequiredEntityData, } from "@mikro-orm/core" @@ -17,9 +16,9 @@ import { EntityName, FilterQuery as MikroFilterQuery, } from "@mikro-orm/core/typings" -import { MedusaError, arrayDifference, isString } from "../../common" +import { isString, MedusaError } from "../../common" import { MedusaContext } from "../../decorators" -import { InjectTransactionManager, buildQuery } from "../../modules-sdk" +import { buildQuery, InjectTransactionManager } from "../../modules-sdk" import { getSoftDeletedCascadedEntitiesIdsMappedBy, transactionWrapper, @@ -77,10 +76,11 @@ export class MikroOrmBase { export class MikroOrmBaseRepository< T extends object = object > extends MikroOrmBase { - constructor() { + constructor(...args: any[]) { // @ts-ignore super(...arguments) } + create(data: unknown[], context?: Context): Promise { throw new Error("Method not implemented.") } @@ -89,7 +89,7 @@ export class MikroOrmBaseRepository< throw new Error("Method not implemented.") } - delete(ids: string[], context?: Context): Promise { + delete(ids: string[] | object[], context?: Context): Promise { throw new Error("Method not implemented.") } @@ -227,11 +227,25 @@ export function mikroOrmBaseRepositoryFactory< TDTos extends { [K in DtoBasedMutationMethods]?: any } = { [K in DtoBasedMutationMethods]?: any } ->( - entity: EntityClass | EntitySchema | string, - primaryKey: string = "id" -) { +>(entity: EntityClass | EntitySchema) { class MikroOrmAbstractBaseRepository_ extends MikroOrmBaseRepository { + // @ts-ignore + constructor(...args: any[]) { + // @ts-ignore + super(...arguments) + } + + static buildUniqueCompositeKeyValue(keys: string[], data: object) { + return keys.map((k) => data[k]).join("_") + } + + static retrievePrimaryKeys(entity: EntityClass | EntitySchema) { + return ( + (entity as EntitySchema).meta?.primaryKeys ?? + (entity as EntityClass).prototype.__meta.primaryKeys + ) + } + async create(data: TDTos["create"][], context?: Context): Promise { const manager = this.getActiveManager(context) @@ -250,42 +264,71 @@ export function mikroOrmBaseRepositoryFactory< async update(data: TDTos["update"][], context?: Context): Promise { const manager = this.getActiveManager(context) - const primaryKeyValues: string[] = data.map((data_) => data_[primaryKey]) - const existingEntities = await this.find( - { - where: { - [primaryKey]: { - $in: primaryKeyValues, - }, - }, - } as DAL.FindOptions, - context + const primaryKeys = + MikroOrmAbstractBaseRepository_.retrievePrimaryKeys(entity) + + let primaryKeysCriteria: { [key: string]: any }[] = [] + if (primaryKeys.length === 1) { + primaryKeysCriteria.push({ + [primaryKeys[0]]: data.map((d) => d[primaryKeys[0]]), + }) + } else { + primaryKeysCriteria = data.map((d) => ({ + $and: primaryKeys.map((key) => ({ [key]: d[key] })), + })) + } + + const allEntities = await Promise.all( + primaryKeysCriteria.map( + async (criteria) => + await this.find({ where: criteria } as DAL.FindOptions, context) + ) ) - const missingEntities = arrayDifference( - data.map((d) => d[primaryKey]), - existingEntities.map((d: any) => d[primaryKey]) - ) + const existingEntities = allEntities.flat() + + const existingEntitiesMap = new Map() + existingEntities.forEach((entity) => { + if (entity) { + const key = + MikroOrmAbstractBaseRepository_.buildUniqueCompositeKeyValue( + primaryKeys, + entity + ) + existingEntitiesMap.set(key, entity) + } + }) + + const missingEntities = data.filter((data_) => { + const key = + MikroOrmAbstractBaseRepository_.buildUniqueCompositeKeyValue( + primaryKeys, + data_ + ) + return !existingEntitiesMap.has(key) + }) if (missingEntities.length) { const entityName = (entity as EntityClass).name ?? entity + const missingEntitiesKeys = data.map((data_) => + primaryKeys.map((key) => data_[key]).join(", ") + ) throw new MedusaError( MedusaError.Types.NOT_FOUND, - `${entityName} with ${[primaryKey]} "${missingEntities.join( + `${entityName} with ${primaryKeys.join( ", " - )}" not found` + )} "${missingEntitiesKeys.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])! + const key = + MikroOrmAbstractBaseRepository_.buildUniqueCompositeKeyValue( + primaryKeys, + data_ + ) + const existingEntity = existingEntitiesMap.get(key)! + return manager.assign(existingEntity, data_ as RequiredEntityData) }) @@ -294,13 +337,41 @@ export function mikroOrmBaseRepositoryFactory< return entities } - async delete(primaryKeyValues: string[], context?: Context): Promise { + async delete( + primaryKeyValues: string[] | object[], + context?: Context + ): Promise { const manager = this.getActiveManager(context) - await manager.nativeDelete( - entity as EntityName, - { [primaryKey]: { $in: primaryKeyValues } } as unknown as FilterQuery - ) + const primaryKeys = + MikroOrmAbstractBaseRepository_.retrievePrimaryKeys(entity) + + let deletionCriteria + if (primaryKeys.length > 1) { + deletionCriteria = { + $or: primaryKeyValues.map((compositeKeyValue) => { + const keys = Object.keys(compositeKeyValue) + if (!primaryKeys.every((k) => keys.includes(k))) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Composite key must contain all primary key fields: ${primaryKeys.join( + ", " + )}. Found: ${keys}` + ) + } + + const criteria: { [key: string]: any } = {} + for (const key of primaryKeys) { + criteria[key] = compositeKeyValue[key] + } + return criteria + }), + } + } else { + deletionCriteria = { [primaryKeys[0]]: { $in: primaryKeyValues } } + } + + await manager.nativeDelete(entity as EntityName, deletionCriteria) } async find(options?: DAL.FindOptions, context?: Context): Promise {