feat(types, product, utils, medusa): Include shared connection for modules (#4626)
This commit is contained in:
1 parent
a2d7540e40
commit
3f3a84262c
26 files changed
+476
-168
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { Context } from "@medusajs/types"
|
||||
import { DALUtils } from "@medusajs/utils"
|
||||
|
||||
class CustomRepository extends DALUtils.MikroOrmBaseRepository {
|
||||
@@ -5,12 +6,44 @@ class CustomRepository extends DALUtils.MikroOrmBaseRepository {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
}
|
||||
}
|
||||
|
||||
CustomRepository.prototype.find = jest.fn().mockImplementation(async () => [])
|
||||
CustomRepository.prototype.findAndCount = jest
|
||||
.fn()
|
||||
.mockImplementation(async () => [])
|
||||
find = jest.fn().mockImplementation(async () => [])
|
||||
findAndCount = jest.fn().mockImplementation(async () => [])
|
||||
create = jest.fn()
|
||||
update = jest.fn()
|
||||
delete = jest.fn()
|
||||
softDelete = jest.fn()
|
||||
restore = jest.fn()
|
||||
|
||||
async transaction<TManager = unknown>(
|
||||
task: (transactionManager: TManager) => Promise<any>,
|
||||
options: {
|
||||
isolationLevel?: string
|
||||
enableNestedTransactions?: boolean
|
||||
transaction?: TManager
|
||||
} = {}
|
||||
): Promise<any> {
|
||||
return super.transaction(task, options)
|
||||
}
|
||||
|
||||
getActiveManager<TManager = unknown>({
|
||||
transactionManager,
|
||||
manager,
|
||||
}: Context = {}): TManager {
|
||||
return super.getActiveManager({ transactionManager, manager })
|
||||
}
|
||||
|
||||
getFreshManager<TManager = unknown>(): TManager {
|
||||
return super.getFreshManager()
|
||||
}
|
||||
|
||||
async serialize<TOutput extends object | object[]>(
|
||||
data: any,
|
||||
options?: any
|
||||
): Promise<TOutput> {
|
||||
return super.serialize(data, options)
|
||||
}
|
||||
}
|
||||
|
||||
export class ProductRepository extends CustomRepository {}
|
||||
export class ProductTagRepository extends CustomRepository {}
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
import { MedusaModule } from "@medusajs/modules-sdk"
|
||||
import { initialize } from "../../src"
|
||||
import * as CustomRepositories from "../__fixtures__/module"
|
||||
import { ProductRepository } from "../__fixtures__/module"
|
||||
import { createProductAndTags } from "../__fixtures__/product"
|
||||
import {
|
||||
buildProductAndRelationsData,
|
||||
createProductAndTags,
|
||||
} from "../__fixtures__/product"
|
||||
import { productsData } from "../__fixtures__/product/data"
|
||||
import { DB_URL, TestDatabase } from "../utils"
|
||||
import { buildProductAndRelationsData } from "../__fixtures__/product/data/create-product"
|
||||
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
import { kebabCase } from "@medusajs/utils"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
import { knex } from "knex"
|
||||
import { EventBusService } from "../__fixtures__/event-bus"
|
||||
|
||||
const sharedPgConnection = knex<any, any>({
|
||||
client: "pg",
|
||||
searchPath: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
connection: {
|
||||
connectionString: DB_URL,
|
||||
},
|
||||
})
|
||||
|
||||
const beforeEach_ = async () => {
|
||||
await TestDatabase.setupDatabase()
|
||||
@@ -20,6 +30,8 @@ const afterEach_ = async () => {
|
||||
}
|
||||
|
||||
describe("Product module", function () {
|
||||
const eventBus = new EventBusService()
|
||||
|
||||
describe("Using built-in data access layer", function () {
|
||||
let module: IProductModuleService
|
||||
|
||||
@@ -27,12 +39,17 @@ describe("Product module", function () {
|
||||
const testManager = await beforeEach_()
|
||||
await createProductAndTags(testManager, productsData)
|
||||
|
||||
module = await initialize({
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
module = await initialize(
|
||||
{
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
},
|
||||
},
|
||||
})
|
||||
{
|
||||
eventBusModuleService: eventBus,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(afterEach_)
|
||||
@@ -41,6 +58,12 @@ describe("Product module", function () {
|
||||
expect(module).toBeDefined()
|
||||
})
|
||||
|
||||
it("should have a connection that is not the shared connection", async () => {
|
||||
expect(
|
||||
(module as any).baseRepository_.manager_.getConnection().client
|
||||
).not.toEqual(sharedPgConnection)
|
||||
})
|
||||
|
||||
it("should return a list of product", async () => {
|
||||
const products = await module.list()
|
||||
expect(products).toHaveLength(2)
|
||||
@@ -48,20 +71,25 @@ describe("Product module", function () {
|
||||
})
|
||||
|
||||
describe("Using custom data access layer", function () {
|
||||
let module: IProductModuleService
|
||||
let module
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await beforeEach_()
|
||||
|
||||
await createProductAndTags(testManager, productsData)
|
||||
|
||||
module = await initialize({
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
module = await initialize(
|
||||
{
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
},
|
||||
repositories: CustomRepositories,
|
||||
},
|
||||
repositories: CustomRepositories,
|
||||
})
|
||||
{
|
||||
eventBusModuleService: eventBus,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(afterEach_)
|
||||
@@ -70,16 +98,22 @@ describe("Product module", function () {
|
||||
expect(module).toBeDefined()
|
||||
})
|
||||
|
||||
it("should have a connection that is not the shared connection", async () => {
|
||||
expect(
|
||||
(module as any).baseRepository_.manager_.getConnection().client
|
||||
).not.toEqual(sharedPgConnection)
|
||||
})
|
||||
|
||||
it("should return a list of product", async () => {
|
||||
const products = await module.list()
|
||||
|
||||
expect(ProductRepository.prototype.find).toHaveBeenCalled()
|
||||
expect(module.productService_.productRepository_.find).toHaveBeenCalled()
|
||||
expect(products).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Using custom data access layer and connection", function () {
|
||||
let module: IProductModuleService
|
||||
describe("Using custom data access layer and manager", function () {
|
||||
let module
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await beforeEach_()
|
||||
@@ -87,10 +121,15 @@ describe("Product module", function () {
|
||||
|
||||
MedusaModule.clearInstances()
|
||||
|
||||
module = await initialize({
|
||||
manager: testManager,
|
||||
repositories: CustomRepositories,
|
||||
})
|
||||
module = await initialize(
|
||||
{
|
||||
manager: testManager,
|
||||
repositories: CustomRepositories,
|
||||
},
|
||||
{
|
||||
eventBusModuleService: eventBus,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(afterEach_)
|
||||
@@ -99,14 +138,54 @@ describe("Product module", function () {
|
||||
expect(module).toBeDefined()
|
||||
})
|
||||
|
||||
it("should have a connection that is not the shared connection", async () => {
|
||||
expect(
|
||||
(module as any).baseRepository_.manager_.getConnection().client
|
||||
).not.toEqual(sharedPgConnection)
|
||||
})
|
||||
|
||||
it("should return a list of product", async () => {
|
||||
const products = await module.list()
|
||||
|
||||
expect(ProductRepository.prototype.find).toHaveBeenCalled()
|
||||
expect(module.productService_.productRepository_.find).toHaveBeenCalled()
|
||||
expect(products).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Using an existing connection", function () {
|
||||
let module: IProductModuleService
|
||||
|
||||
beforeEach(async () => {
|
||||
const testManager = await beforeEach_()
|
||||
await createProductAndTags(testManager, productsData)
|
||||
|
||||
MedusaModule.clearInstances()
|
||||
|
||||
module = await initialize(
|
||||
{
|
||||
database: {
|
||||
connection: sharedPgConnection,
|
||||
},
|
||||
},
|
||||
{
|
||||
eventBusModuleService: eventBus,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(afterEach_)
|
||||
|
||||
it("should initialize and return a list of product", async () => {
|
||||
expect(module).toBeDefined()
|
||||
})
|
||||
|
||||
it("should have a connection that is the shared connection", async () => {
|
||||
expect(
|
||||
(module as any).baseRepository_.manager_.getConnection().client
|
||||
).toEqual(sharedPgConnection)
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", function () {
|
||||
let module: IProductModuleService
|
||||
let images = ["image-1"]
|
||||
@@ -116,12 +195,17 @@ describe("Product module", function () {
|
||||
|
||||
MedusaModule.clearInstances()
|
||||
|
||||
module = await initialize({
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
module = await initialize(
|
||||
{
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
},
|
||||
},
|
||||
})
|
||||
{
|
||||
eventBusModuleService: eventBus,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(afterEach_)
|
||||
@@ -212,12 +296,17 @@ describe("Product module", function () {
|
||||
|
||||
MedusaModule.clearInstances()
|
||||
|
||||
module = await initialize({
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
module = await initialize(
|
||||
{
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
},
|
||||
},
|
||||
})
|
||||
{
|
||||
eventBusModuleService: eventBus,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(afterEach_)
|
||||
@@ -282,12 +371,17 @@ describe("Product module", function () {
|
||||
|
||||
MedusaModule.clearInstances()
|
||||
|
||||
module = await initialize({
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
module = await initialize(
|
||||
{
|
||||
database: {
|
||||
clientUrl: DB_URL,
|
||||
schema: process.env.MEDUSA_PRODUCT_DB_SCHEMA,
|
||||
},
|
||||
},
|
||||
})
|
||||
{
|
||||
eventBusModuleService: eventBus,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(afterEach_)
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
"@mikro-orm/postgresql": "5.7.12",
|
||||
"awilix": "^8.0.0",
|
||||
"dotenv": "^16.1.4",
|
||||
"lodash": "^4.17.21"
|
||||
"lodash": "^4.17.21",
|
||||
"knex": "2.4.2"
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,8 @@ export const initialize = async (
|
||||
options?:
|
||||
| ModulesSdkTypes.ModuleServiceInitializeOptions
|
||||
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
||||
| ExternalModuleDeclaration,
|
||||
| ExternalModuleDeclaration
|
||||
| InternalModuleDeclaration,
|
||||
injectedDependencies?: InitializeModuleInjectableDependencies
|
||||
): Promise<IProductModuleService> => {
|
||||
const serviceKey = Modules.PRODUCT
|
||||
|
||||
@@ -1,68 +1,27 @@
|
||||
import { asValue } from "awilix"
|
||||
|
||||
import {
|
||||
InternalModuleDeclaration,
|
||||
LoaderOptions,
|
||||
MODULE_RESOURCE_TYPE,
|
||||
MODULE_SCOPE,
|
||||
} from "@medusajs/modules-sdk"
|
||||
import { DALUtils, MedusaError, ModulesSdkUtils } from "@medusajs/utils"
|
||||
|
||||
import { InternalModuleDeclaration, LoaderOptions } from "@medusajs/modules-sdk"
|
||||
import { ModulesSdkTypes } from "@medusajs/types"
|
||||
import { ModulesSdkUtils } from "@medusajs/utils"
|
||||
import * as ProductModels from "../models"
|
||||
import { EntitySchema } from "@mikro-orm/core"
|
||||
|
||||
import * as ProductModels from "@models"
|
||||
import { ConfigModule, ModulesSdkTypes } from "@medusajs/types"
|
||||
|
||||
export default async (
|
||||
{
|
||||
options,
|
||||
container,
|
||||
logger,
|
||||
}: LoaderOptions<
|
||||
| ModulesSdkTypes.ModuleServiceInitializeOptions
|
||||
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
||||
>,
|
||||
moduleDeclaration?: InternalModuleDeclaration
|
||||
): Promise<void> => {
|
||||
if (
|
||||
moduleDeclaration?.scope === MODULE_SCOPE.INTERNAL &&
|
||||
moduleDeclaration.resources === MODULE_RESOURCE_TYPE.SHARED
|
||||
) {
|
||||
const { projectConfig } = container.resolve("configModule") as ConfigModule
|
||||
options = {
|
||||
database: {
|
||||
clientUrl: projectConfig.database_url!,
|
||||
driverOptions: projectConfig.database_extra!,
|
||||
schema: projectConfig.database_schema!,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const customManager = (
|
||||
options as ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
||||
)?.manager
|
||||
|
||||
if (!customManager) {
|
||||
const dbData = ModulesSdkUtils.loadDatabaseConfig("product", options)
|
||||
await loadDefault({ database: dbData, container })
|
||||
} else {
|
||||
container.register({
|
||||
manager: asValue(customManager),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDefault({ database, container }) {
|
||||
if (!database) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
`Database config is not present at module config "options.database"`
|
||||
)
|
||||
}
|
||||
|
||||
const entities = Object.values(ProductModels) as unknown as EntitySchema[]
|
||||
const orm = await DALUtils.mikroOrmCreateConnection(database, entities)
|
||||
|
||||
container.register({
|
||||
manager: asValue(orm.em.fork()),
|
||||
await ModulesSdkUtils.mikroOrmConnectionLoader({
|
||||
entities,
|
||||
container,
|
||||
options,
|
||||
moduleDeclaration,
|
||||
logger,
|
||||
})
|
||||
}
|
||||
@@ -51,9 +51,9 @@ export default async ({
|
||||
})
|
||||
|
||||
if (customRepositories) {
|
||||
await loadCustomRepositories({ customRepositories, container })
|
||||
loadCustomRepositories({ customRepositories, container })
|
||||
} else {
|
||||
await loadDefaultRepositories({ container })
|
||||
loadDefaultRepositories({ container })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,15 +16,12 @@ export async function revertMigration({
|
||||
options,
|
||||
logger,
|
||||
}: Pick<
|
||||
LoaderOptions<
|
||||
| ModulesSdkTypes.ModuleServiceInitializeOptions
|
||||
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
||||
>,
|
||||
LoaderOptions<ModulesSdkTypes.ModuleServiceInitializeOptions>,
|
||||
"options" | "logger"
|
||||
> = {}) {
|
||||
logger ??= console as unknown as Logger
|
||||
|
||||
const dbData = ModulesSdkUtils.loadDatabaseConfig("product", options)
|
||||
const dbData = ModulesSdkUtils.loadDatabaseConfig("product", options)!
|
||||
const entities = Object.values(ProductModels) as unknown as EntitySchema[]
|
||||
|
||||
const orm = await DALUtils.mikroOrmCreateConnection(dbData, entities)
|
||||
|
||||
@@ -14,15 +14,12 @@ export async function runMigrations({
|
||||
options,
|
||||
logger,
|
||||
}: Pick<
|
||||
LoaderOptions<
|
||||
| ModulesSdkTypes.ModuleServiceInitializeOptions
|
||||
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
||||
>,
|
||||
LoaderOptions<ModulesSdkTypes.ModuleServiceInitializeOptions>,
|
||||
"options" | "logger"
|
||||
> = {}) {
|
||||
logger ??= console as unknown as Logger
|
||||
|
||||
const dbData = ModulesSdkUtils.loadDatabaseConfig("product", options)
|
||||
const dbData = ModulesSdkUtils.loadDatabaseConfig("product", options)!
|
||||
const entities = Object.values(ProductModels) as unknown as EntitySchema[]
|
||||
|
||||
const orm = await DALUtils.mikroOrmCreateConnection(dbData, entities)
|
||||
|
||||
@@ -13,10 +13,7 @@ export async function run({
|
||||
path,
|
||||
}: Partial<
|
||||
Pick<
|
||||
LoaderOptions<
|
||||
| ModulesSdkTypes.ModuleServiceInitializeOptions
|
||||
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
||||
>,
|
||||
LoaderOptions<ModulesSdkTypes.ModuleServiceInitializeOptions>,
|
||||
"options" | "logger"
|
||||
>
|
||||
> & {
|
||||
@@ -34,7 +31,7 @@ export async function run({
|
||||
|
||||
logger ??= console as unknown as Logger
|
||||
|
||||
const dbData = ModulesSdkUtils.loadDatabaseConfig("product", options)
|
||||
const dbData = ModulesSdkUtils.loadDatabaseConfig("product", options)!
|
||||
const entities = Object.values(ProductModels) as unknown as EntitySchema[]
|
||||
|
||||
const orm = await DALUtils.mikroOrmCreateConnection(dbData, entities)
|
||||
|
||||
@@ -22,10 +22,10 @@ import {
|
||||
CreateProductOnlyDTO,
|
||||
DAL,
|
||||
FindConfig,
|
||||
IEventBusModuleService,
|
||||
InternalModuleDeclaration,
|
||||
JoinerServiceConfig,
|
||||
ProductTypes,
|
||||
IEventBusModuleService,
|
||||
} from "@medusajs/types"
|
||||
|
||||
import ProductImageService from "./product-image"
|
||||
@@ -739,12 +739,6 @@ export default class ProductModuleService<
|
||||
productVariantsMap.set(productData.handle!, variants ?? [])
|
||||
productOptionsMap.set(productData.handle!, options ?? [])
|
||||
|
||||
if (!productData.thumbnail && productData.images?.length) {
|
||||
productData.thumbnail = isString(productData.images[0])
|
||||
? (productData.images[0] as string)
|
||||
: (productData.images[0] as { url: string }).url
|
||||
}
|
||||
|
||||
if (productData.is_giftcard) {
|
||||
productData.discountable = false
|
||||
}
|
||||
@@ -1084,7 +1078,7 @@ export default class ProductModuleService<
|
||||
},
|
||||
sharedContext: Context = {}
|
||||
): Promise<Record<Lowercase<keyof typeof LinkableKeys>, string[]> | void> {
|
||||
let [products, cascadedEntitiesMap] = await this.softDelete_(
|
||||
const [products, cascadedEntitiesMap] = await this.softDelete_(
|
||||
productIds,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
export * from "./services"
|
||||
|
||||
import { IEventBusModuleService } from "@medusajs/types"
|
||||
import { IEventBusModuleService, Logger } from "@medusajs/types"
|
||||
|
||||
export type InitializeModuleInjectableDependencies = {
|
||||
logger?: Logger
|
||||
eventBusModuleService?: IEventBusModuleService
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user