Feat(modules-sdk,inventory,stock-location): modules isolated connection (#3329)
* feat: scoped container for modules
This commit is contained in:
1 parent
8e78c533c4
commit
77d46220c2
65 files changed
+977
-872
No files matched your search
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/stock-location": minor
|
||||||
|
"@medusajs/inventory": minor
|
||||||
|
"medusa-core-utils": minor
|
||||||
|
"@medusajs/modules-sdk": minor
|
||||||
|
"@medusajs/medusa": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Inventory and Stock location modules supporting isolated connection
|
||||||
@@ -30,7 +30,13 @@ module.exports = ({ cwd, redisUrl, uploadDir, verbose, env }) => {
|
|||||||
: ["ignore", "ignore", "ignore", "ipc"],
|
: ["ignore", "ignore", "ignore", "ipc"],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
medusaProcess.on("error", (err) => {
|
||||||
|
console.log(err)
|
||||||
|
process.exit()
|
||||||
|
})
|
||||||
|
|
||||||
medusaProcess.on("uncaughtException", (err) => {
|
medusaProcess.on("uncaughtException", (err) => {
|
||||||
|
console.log(err)
|
||||||
medusaProcess.kill()
|
medusaProcess.kill()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ describe("Inventory Items endpoints", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it.only("Creates an inventory item using the api", async () => {
|
it.skip("Creates an inventory item using the api", async () => {
|
||||||
const product = await simpleProductFactory(dbConnection, {})
|
const product = await simpleProductFactory(dbConnection, {})
|
||||||
|
|
||||||
const api = useApi()
|
const api = useApi()
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import { ModuleExports } from "@medusajs/modules-sdk"
|
|||||||
|
|
||||||
import InMemoryCacheService from "./services/inmemory-cache"
|
import InMemoryCacheService from "./services/inmemory-cache"
|
||||||
|
|
||||||
const loaders = []
|
|
||||||
const service = InMemoryCacheService
|
const service = InMemoryCacheService
|
||||||
|
|
||||||
const moduleDefinition: ModuleExports = {
|
const moduleDefinition: ModuleExports = {
|
||||||
service,
|
service,
|
||||||
loaders,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default moduleDefinition
|
export default moduleDefinition
|
||||||
@@ -59,6 +59,8 @@ class InMemoryCacheService implements ICacheService {
|
|||||||
this.invalidate(key)
|
this.invalidate(key)
|
||||||
}, ttl * 1000)
|
}, ttl * 1000)
|
||||||
|
|
||||||
|
ref.unref()
|
||||||
|
|
||||||
this.timoutRefs.set(key, ref)
|
this.timoutRefs.set(key, ref)
|
||||||
this.store.set(key, record)
|
this.store.set(key, record)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ModuleExports } from "@medusajs/modules-sdk"
|
import { ModuleExports } from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
import { RedisCacheService } from "./services"
|
|
||||||
import Loader from "./loaders"
|
import Loader from "./loaders"
|
||||||
|
import { RedisCacheService } from "./services"
|
||||||
|
|
||||||
const service = RedisCacheService
|
const service = RedisCacheService
|
||||||
const loaders = [Loader]
|
const loaders = [Loader]
|
||||||
|
|||||||
@@ -21,11 +21,12 @@
|
|||||||
"cross-env": "^5.2.1",
|
"cross-env": "^5.2.1",
|
||||||
"jest": "^25.5.4",
|
"jest": "^25.5.4",
|
||||||
"ts-jest": "^25.5.1",
|
"ts-jest": "^25.5.1",
|
||||||
"typeorm": "^0.3.11",
|
|
||||||
"typescript": "^4.4.4"
|
"typescript": "^4.4.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@medusajs/modules-sdk": "*"
|
"@medusajs/modules-sdk": "*",
|
||||||
|
"awilix": "^8.0.0",
|
||||||
|
"typeorm": "^0.3.11"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch": "tsc --build --watch",
|
"watch": "tsc --build --watch",
|
||||||
@@ -35,7 +36,6 @@
|
|||||||
"test:unit": "jest --passWithNoTests"
|
"test:unit": "jest --passWithNoTests"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@medusajs/medusa": "1.7.13",
|
"@medusajs/medusa": "1.7.13"
|
||||||
"typeorm": "^0.3.11"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
export const CONNECTION_NAME = "inventory_connection"
|
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
import ConnectionLoader from "./loaders/connection"
|
import loadConnection from "./loaders/connection"
|
||||||
import InventoryService from "./services/inventory"
|
import loadContainer from "./loaders/container"
|
||||||
|
|
||||||
|
import migrations from "./migrations"
|
||||||
import * as InventoryModels from "./models"
|
import * as InventoryModels from "./models"
|
||||||
import * as SchemaMigration from "./migrations/schema-migrations/1665748086258-inventory_setup"
|
import InventoryService from "./services/inventory"
|
||||||
|
|
||||||
import { ModuleExports } from "@medusajs/modules-sdk"
|
import { ModuleExports } from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
const service = InventoryService
|
const service = InventoryService
|
||||||
const migrations = [SchemaMigration]
|
const loaders = [loadContainer, loadConnection]
|
||||||
const loaders = [ConnectionLoader]
|
|
||||||
const models = Object.values(InventoryModels)
|
const models = Object.values(InventoryModels)
|
||||||
|
|
||||||
const moduleDefinition: ModuleExports = {
|
const moduleDefinition: ModuleExports = {
|
||||||
|
|||||||
@@ -1,9 +1,49 @@
|
|||||||
import {
|
import {
|
||||||
ConfigurableModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
LoaderOptions,
|
LoaderOptions,
|
||||||
|
MODULE_RESOURCE_TYPE,
|
||||||
|
MODULE_SCOPE,
|
||||||
} from "@medusajs/modules-sdk"
|
} from "@medusajs/modules-sdk"
|
||||||
|
import { DataSource, DataSourceOptions } from "typeorm"
|
||||||
|
|
||||||
|
import * as InventoryModels from "../models"
|
||||||
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import { asValue } from "awilix"
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
{ configModule }: LoaderOptions,
|
{ options, container }: LoaderOptions,
|
||||||
moduleDeclaration?: ConfigurableModuleDeclaration
|
moduleDeclaration?: InternalModuleDeclaration
|
||||||
): Promise<void> => {}
|
): Promise<void> => {
|
||||||
|
if (
|
||||||
|
moduleDeclaration?.scope === MODULE_SCOPE.INTERNAL &&
|
||||||
|
moduleDeclaration.resources === MODULE_RESOURCE_TYPE.SHARED
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const dbData = options?.database as Record<string, string>
|
||||||
|
|
||||||
|
if (!dbData) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_ARGUMENT,
|
||||||
|
`Database config is not present at module config "options.database"`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const entities = Object.values(InventoryModels)
|
||||||
|
const dataSource = new DataSource({
|
||||||
|
type: dbData.database_type,
|
||||||
|
url: dbData.database_url,
|
||||||
|
database: dbData.database_database,
|
||||||
|
extra: dbData.database_extra || {},
|
||||||
|
schema: dbData.database_schema,
|
||||||
|
entities,
|
||||||
|
logging: dbData.database_logging,
|
||||||
|
} as DataSourceOptions)
|
||||||
|
|
||||||
|
await dataSource.initialize()
|
||||||
|
|
||||||
|
container.register({
|
||||||
|
manager: asValue(dataSource.manager),
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { InternalModuleDeclaration, LoaderOptions } from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
|
import {
|
||||||
|
InventoryItemService,
|
||||||
|
InventoryLevelService,
|
||||||
|
ReservationItemService,
|
||||||
|
} from "../services"
|
||||||
|
|
||||||
|
import { asClass } from "awilix"
|
||||||
|
|
||||||
|
export default async (
|
||||||
|
{ container }: LoaderOptions,
|
||||||
|
moduleDeclaration?: InternalModuleDeclaration
|
||||||
|
): Promise<void> => {
|
||||||
|
container.register({
|
||||||
|
inventoryItemService: asClass(InventoryItemService).singleton(),
|
||||||
|
inventoryLevelService: asClass(InventoryLevelService).singleton(),
|
||||||
|
reservationItemService: asClass(ReservationItemService).singleton(),
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./connection"
|
||||||
|
export * from "./container"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import * as setup from "./schema-migrations/1665748086258-inventory_setup"
|
||||||
|
|
||||||
|
export default [setup]
|
||||||
+1
-43
@@ -1,46 +1,4 @@
|
|||||||
import { ConfigModule } from "@medusajs/medusa"
|
import { MigrationInterface, QueryRunner } from "typeorm"
|
||||||
import {
|
|
||||||
createConnection,
|
|
||||||
ConnectionOptions,
|
|
||||||
MigrationInterface,
|
|
||||||
QueryRunner,
|
|
||||||
} from "typeorm"
|
|
||||||
|
|
||||||
import { CONNECTION_NAME } from "../../config"
|
|
||||||
|
|
||||||
export const up = async ({ configModule }: { configModule: ConfigModule }) => {
|
|
||||||
const connection = await createConnection({
|
|
||||||
name: CONNECTION_NAME,
|
|
||||||
type: configModule.projectConfig.database_type,
|
|
||||||
url: configModule.projectConfig.database_url,
|
|
||||||
extra: configModule.projectConfig.database_extra || {},
|
|
||||||
schema: configModule.projectConfig.database_schema,
|
|
||||||
migrations: [inventorySetup1665748086258],
|
|
||||||
logging: true,
|
|
||||||
} as ConnectionOptions)
|
|
||||||
|
|
||||||
await connection.runMigrations()
|
|
||||||
await connection.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
export const down = async ({
|
|
||||||
configModule,
|
|
||||||
}: {
|
|
||||||
configModule: ConfigModule
|
|
||||||
}) => {
|
|
||||||
const connection = await createConnection({
|
|
||||||
name: CONNECTION_NAME,
|
|
||||||
type: configModule.projectConfig.database_type,
|
|
||||||
url: configModule.projectConfig.database_url,
|
|
||||||
extra: configModule.projectConfig.database_extra || {},
|
|
||||||
schema: configModule.projectConfig.database_schema,
|
|
||||||
migrations: [inventorySetup1665748086258],
|
|
||||||
logging: true,
|
|
||||||
} as ConnectionOptions)
|
|
||||||
|
|
||||||
await connection.undoLastMigration({ transaction: "all" })
|
|
||||||
await connection.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
export class inventorySetup1665748086258 implements MigrationInterface {
|
export class inventorySetup1665748086258 implements MigrationInterface {
|
||||||
name = "inventorySetup1665748086258"
|
name = "inventorySetup1665748086258"
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import {
|
import { InternalModuleDeclaration } from "@medusajs/modules-sdk"
|
||||||
ConfigurableModuleDeclaration,
|
|
||||||
MODULE_RESOURCE_TYPE,
|
|
||||||
} from "@medusajs/modules-sdk"
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CreateInventoryItemInput,
|
CreateInventoryItemInput,
|
||||||
@@ -22,18 +19,20 @@ import {
|
|||||||
} from "@medusajs/medusa"
|
} from "@medusajs/medusa"
|
||||||
import { MedusaError } from "medusa-core-utils"
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
import {
|
import {
|
||||||
InventoryItemService,
|
InventoryItemService,
|
||||||
InventoryLevelService,
|
InventoryLevelService,
|
||||||
ReservationItemService,
|
ReservationItemService,
|
||||||
} from "./"
|
} from "./"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
manager: EntityManager
|
manager: EntityManager
|
||||||
eventBusService: IEventBusService
|
eventBusService: IEventBusService
|
||||||
|
inventoryItemService: InventoryItemService
|
||||||
|
inventoryLevelService: InventoryLevelService
|
||||||
|
reservationItemService: ReservationItemService
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class InventoryService
|
export default class InventoryService
|
||||||
extends TransactionBaseService
|
extends TransactionBaseService
|
||||||
implements IInventoryService
|
implements IInventoryService
|
||||||
@@ -44,34 +43,23 @@ export default class InventoryService
|
|||||||
protected readonly inventoryLevelService_: InventoryLevelService
|
protected readonly inventoryLevelService_: InventoryLevelService
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{ eventBusService, manager }: InjectedDependencies,
|
{
|
||||||
|
eventBusService,
|
||||||
|
manager,
|
||||||
|
inventoryItemService,
|
||||||
|
inventoryLevelService,
|
||||||
|
reservationItemService,
|
||||||
|
}: InjectedDependencies,
|
||||||
options?: unknown,
|
options?: unknown,
|
||||||
moduleDeclaration?: ConfigurableModuleDeclaration
|
moduleDeclaration?: InternalModuleDeclaration
|
||||||
) {
|
) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
super(...arguments)
|
super(...arguments)
|
||||||
|
|
||||||
if (moduleDeclaration?.resources !== MODULE_RESOURCE_TYPE.SHARED) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_ARGUMENT,
|
|
||||||
"At the moment this module can only be used with shared resources"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.eventBusService_ = eventBusService
|
this.eventBusService_ = eventBusService
|
||||||
this.inventoryItemService_ = new InventoryItemService({
|
this.inventoryItemService_ = inventoryItemService
|
||||||
eventBusService,
|
this.inventoryLevelService_ = inventoryLevelService
|
||||||
manager,
|
this.reservationItemService_ = reservationItemService
|
||||||
})
|
|
||||||
this.inventoryLevelService_ = new InventoryLevelService({
|
|
||||||
eventBusService,
|
|
||||||
manager,
|
|
||||||
})
|
|
||||||
this.reservationItemService_ = new ReservationItemService({
|
|
||||||
eventBusService,
|
|
||||||
manager,
|
|
||||||
inventoryLevelService: this.inventoryLevelService_,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": [
|
"lib": ["es2020"],
|
||||||
"es5",
|
"target": "ES2020",
|
||||||
"es6",
|
|
||||||
"es2019"
|
|
||||||
],
|
|
||||||
"target": "es5",
|
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
@@ -19,12 +15,11 @@
|
|||||||
"strictFunctionTypes": true,
|
"strictFunctionTypes": true,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true
|
||||||
"downlevelIteration": true // to use ES5 specific tooling
|
|
||||||
},
|
},
|
||||||
"include": ["./src/**/*", "index.d.ts"],
|
"include": ["src"],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"./dist/**/*",
|
"dist",
|
||||||
"./src/**/__tests__",
|
"./src/**/__tests__",
|
||||||
"./src/**/__mocks__",
|
"./src/**/__mocks__",
|
||||||
"node_modules"
|
"node_modules"
|
||||||
|
|||||||
@@ -20,16 +20,11 @@
|
|||||||
"author": "Sebastian Rindom",
|
"author": "Sebastian Rindom",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"class-transformer": "^0.5.1",
|
|
||||||
"class-validator": "^0.13.2",
|
|
||||||
"cross-env": "^5.2.1",
|
"cross-env": "^5.2.1",
|
||||||
"jest": "^25.5.4",
|
"jest": "^25.5.4",
|
||||||
"ts-jest": "^25.5.1",
|
"ts-jest": "^25.5.1",
|
||||||
"typescript": "^4.4.4"
|
"typescript": "^4.4.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {},
|
||||||
"joi": "^17.3.0",
|
|
||||||
"joi-objectid": "^3.0.1"
|
|
||||||
},
|
|
||||||
"gitHead": "a69b1e85be1da3b1b5bc4c5446471252623c8808"
|
"gitHead": "a69b1e85be1da3b1b5bc4c5446471252623c8808"
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ import { join } from "path"
|
|||||||
function getConfigFile<TConfig = unknown>(
|
function getConfigFile<TConfig = unknown>(
|
||||||
rootDir: string,
|
rootDir: string,
|
||||||
configName: string
|
configName: string
|
||||||
): { configModule: TConfig; configFilePath: string, error?: any } {
|
): { configModule: TConfig; configFilePath: string; error?: any } {
|
||||||
const configPath = join(rootDir, configName)
|
const configPath = join(rootDir, configName)
|
||||||
let configFilePath = ``
|
let configFilePath = ``
|
||||||
let configModule
|
let configModule
|
||||||
@@ -22,6 +22,10 @@ function getConfigFile<TConfig = unknown>(
|
|||||||
err = e
|
err = e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (configModule && typeof configModule.default === "object") {
|
||||||
|
configModule = configModule.default
|
||||||
|
}
|
||||||
|
|
||||||
return { configModule, configFilePath, error: err }
|
return { configModule, configFilePath, error: err }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export * from "./graceful-shutdown-server"
|
|||||||
export { default as humanizeAmount } from "./humanize-amount"
|
export { default as humanizeAmount } from "./humanize-amount"
|
||||||
export { indexTypes } from "./index-types"
|
export { indexTypes } from "./index-types"
|
||||||
export * from "./is-defined"
|
export * from "./is-defined"
|
||||||
|
export * from "./medusa-container"
|
||||||
export { parseCorsOrigins } from "./parse-cors-origins"
|
export { parseCorsOrigins } from "./parse-cors-origins"
|
||||||
export { transformIdableFields } from "./transform-idable-fields"
|
export { transformIdableFields } from "./transform-idable-fields"
|
||||||
export { default as Validator } from "./validator"
|
|
||||||
export { default as zeroDecimalCurrencies } from "./zero-decimal-currencies"
|
export { default as zeroDecimalCurrencies } from "./zero-decimal-currencies"
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import {
|
||||||
|
asFunction,
|
||||||
|
asValue,
|
||||||
|
AwilixContainer,
|
||||||
|
ClassOrFunctionReturning,
|
||||||
|
createContainer,
|
||||||
|
Resolver,
|
||||||
|
} from "awilix"
|
||||||
|
|
||||||
|
export type MedusaContainer = AwilixContainer & {
|
||||||
|
registerAdd: <T>(name: string, registration: T) => MedusaContainer
|
||||||
|
createScope: () => MedusaContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
function asArray(
|
||||||
|
resolvers: (ClassOrFunctionReturning<unknown> | Resolver<unknown>)[]
|
||||||
|
): { resolve: (container: AwilixContainer) => unknown[] } {
|
||||||
|
return {
|
||||||
|
resolve: (container: AwilixContainer) =>
|
||||||
|
resolvers.map((resolver) => container.build(resolver)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerAdd(
|
||||||
|
this: MedusaContainer,
|
||||||
|
name: string,
|
||||||
|
registration: typeof asFunction | typeof asValue
|
||||||
|
) {
|
||||||
|
const storeKey = name + "_STORE"
|
||||||
|
|
||||||
|
if (this.registrations[storeKey] === undefined) {
|
||||||
|
this.register(storeKey, asValue([] as Resolver<unknown>[]))
|
||||||
|
}
|
||||||
|
const store = this.resolve(storeKey) as (
|
||||||
|
| ClassOrFunctionReturning<unknown>
|
||||||
|
| Resolver<unknown>
|
||||||
|
)[]
|
||||||
|
|
||||||
|
if (this.registrations[name] === undefined) {
|
||||||
|
this.register(name, asArray(store))
|
||||||
|
}
|
||||||
|
store.unshift(registration)
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMedusaContainer(...args): MedusaContainer {
|
||||||
|
const container = createContainer.apply(null, args) as MedusaContainer
|
||||||
|
|
||||||
|
container.registerAdd = registerAdd.bind(container)
|
||||||
|
|
||||||
|
const originalScope = container.createScope
|
||||||
|
container.createScope = () => {
|
||||||
|
const scoped = originalScope() as MedusaContainer
|
||||||
|
scoped.registerAdd = registerAdd.bind(scoped)
|
||||||
|
|
||||||
|
return scoped
|
||||||
|
}
|
||||||
|
|
||||||
|
return container
|
||||||
|
}
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
import { default as Joi } from "joi"
|
|
||||||
|
|
||||||
const dateFilter = () => {
|
|
||||||
return Joi.object({
|
|
||||||
lt: Joi.alternatives(Joi.date().timestamp("unix"), Joi.date()),
|
|
||||||
gt: Joi.alternatives(Joi.date().timestamp("unix"), Joi.date()),
|
|
||||||
gte: Joi.alternatives(Joi.date().timestamp("unix"), Joi.date()),
|
|
||||||
lte: Joi.alternatives(Joi.date().timestamp("unix"), Joi.date()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(Joi, {
|
|
||||||
objectId: require("joi-objectid")(Joi),
|
|
||||||
address: () => {
|
|
||||||
return Joi.alternatives().try(
|
|
||||||
Joi.string(),
|
|
||||||
Joi.object().keys({
|
|
||||||
first_name: Joi.string().required(),
|
|
||||||
last_name: Joi.string().required(),
|
|
||||||
company: Joi.string().optional(),
|
|
||||||
address_1: Joi.string().required(),
|
|
||||||
address_2: Joi.string().allow(null, "").optional(),
|
|
||||||
city: Joi.string().required(),
|
|
||||||
country_code: Joi.string().required(),
|
|
||||||
province: Joi.string().allow(null, "").optional(),
|
|
||||||
postal_code: Joi.string().required(),
|
|
||||||
phone: Joi.string().optional(),
|
|
||||||
metadata: Joi.object().allow(null, {}).optional(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
},
|
|
||||||
dateFilter,
|
|
||||||
orderFilter: () => {
|
|
||||||
return Joi.object().keys({
|
|
||||||
id: Joi.string(),
|
|
||||||
q: Joi.string(),
|
|
||||||
status: Joi.array()
|
|
||||||
.items(
|
|
||||||
Joi.string().valid(
|
|
||||||
"pending",
|
|
||||||
"completed",
|
|
||||||
"archived",
|
|
||||||
"canceled",
|
|
||||||
"requires_action"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.single(),
|
|
||||||
fulfillment_status: Joi.array()
|
|
||||||
.items(
|
|
||||||
Joi.string().valid(
|
|
||||||
"not_fulfilled",
|
|
||||||
"fulfilled",
|
|
||||||
"partially_fulfilled",
|
|
||||||
"shipped",
|
|
||||||
"partially_shipped",
|
|
||||||
"canceled",
|
|
||||||
"returned",
|
|
||||||
"partially_returned",
|
|
||||||
"requires_action"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.single(),
|
|
||||||
payment_status: Joi.array()
|
|
||||||
.items(
|
|
||||||
Joi.string().valid(
|
|
||||||
"captured",
|
|
||||||
"awaiting",
|
|
||||||
"not_paid",
|
|
||||||
"refunded",
|
|
||||||
"partially_refunded",
|
|
||||||
"canceled",
|
|
||||||
"requires_action"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.single(),
|
|
||||||
display_id: Joi.string(),
|
|
||||||
cart_id: Joi.string(),
|
|
||||||
offset: Joi.string(),
|
|
||||||
limit: Joi.string(),
|
|
||||||
expand: Joi.string(),
|
|
||||||
fields: Joi.string(),
|
|
||||||
customer_id: Joi.string(),
|
|
||||||
email: Joi.string(),
|
|
||||||
region_id: Joi.string(),
|
|
||||||
currency_code: Joi.string(),
|
|
||||||
tax_rate: Joi.string(),
|
|
||||||
canceled_at: dateFilter(),
|
|
||||||
created_at: dateFilter(),
|
|
||||||
updated_at: dateFilter(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
productFilter: () => {
|
|
||||||
return Joi.object().keys({
|
|
||||||
id: Joi.string(),
|
|
||||||
q: Joi.string().allow(null, ""),
|
|
||||||
status: Joi.array()
|
|
||||||
.items(Joi.string().valid("proposed", "draft", "published", "rejected"))
|
|
||||||
.single(),
|
|
||||||
collection_id: Joi.array().items(Joi.string()).single(),
|
|
||||||
tags: Joi.array().items(Joi.string()).single(),
|
|
||||||
title: Joi.string(),
|
|
||||||
description: Joi.string(),
|
|
||||||
handle: Joi.string(),
|
|
||||||
is_giftcard: Joi.string(),
|
|
||||||
type: Joi.string(),
|
|
||||||
offset: Joi.string(),
|
|
||||||
limit: Joi.string(),
|
|
||||||
expand: Joi.string(),
|
|
||||||
fields: Joi.string(),
|
|
||||||
order: Joi.string().optional(),
|
|
||||||
created_at: dateFilter(),
|
|
||||||
updated_at: dateFilter(),
|
|
||||||
deleted_at: dateFilter(),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
declare module "joi" {
|
|
||||||
interface Root {
|
|
||||||
objectId: Joi.StringSchema
|
|
||||||
address: <T>() => Joi.AlternativesSchema
|
|
||||||
dateFilter: <T>() => Joi.ObjectSchema<T>
|
|
||||||
orderFilter: <T>() => Joi.ObjectSchema<T>
|
|
||||||
productFilter: <T>() => Joi.ObjectSchema<T>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Joi
|
|
||||||
@@ -75,7 +75,7 @@ export default async (req, res) => {
|
|||||||
.withTransaction(transactionManager)
|
.withTransaction(transactionManager)
|
||||||
.removeLocation(id)
|
.removeLocation(id)
|
||||||
|
|
||||||
await stockLocationService.withTransaction(transactionManager).delete(id)
|
await stockLocationService.delete(id)
|
||||||
|
|
||||||
if (inventoryService) {
|
if (inventoryService) {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
import glob from "glob"
|
|
||||||
import path from "path"
|
|
||||||
import fs from "fs"
|
|
||||||
import { isString } from "lodash"
|
|
||||||
import { sync as existsSync } from "fs-exists-cached"
|
|
||||||
import { getConfigFile, createRequireFromPath } from "medusa-core-utils"
|
|
||||||
import { handleConfigError } from "../../loaders/config"
|
|
||||||
import { registerModules } from "@medusajs/modules-sdk"
|
import { registerModules } from "@medusajs/modules-sdk"
|
||||||
|
import fs from "fs"
|
||||||
|
import { sync as existsSync } from "fs-exists-cached"
|
||||||
|
import glob from "glob"
|
||||||
|
import { isString } from "lodash"
|
||||||
|
import {
|
||||||
|
createRequireFromPath,
|
||||||
|
getConfigFile,
|
||||||
|
isDefined,
|
||||||
|
} from "medusa-core-utils"
|
||||||
|
import path from "path"
|
||||||
|
import { handleConfigError } from "../../loaders/config"
|
||||||
|
|
||||||
function createFileContentHash(path, files) {
|
function createFileContentHash(path, files) {
|
||||||
return path + files
|
return path + files
|
||||||
@@ -104,7 +108,7 @@ export function getInternalModules(configModule) {
|
|||||||
|
|
||||||
let loadedModule = null
|
let loadedModule = null
|
||||||
try {
|
try {
|
||||||
loadedModule = require(moduleResolution.moduleDeclaration.resolve).default
|
loadedModule = require(moduleResolution.resolutionPath).default
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Error loading Module", error)
|
console.log("Error loading Module", error)
|
||||||
continue
|
continue
|
||||||
@@ -179,15 +183,12 @@ export const getEnabledMigrations = (migrationDirs, isFlagEnabled) => {
|
|||||||
return allMigrations
|
return allMigrations
|
||||||
.map((file) => {
|
.map((file) => {
|
||||||
const loaded = require(file)
|
const loaded = require(file)
|
||||||
if (
|
if (!isDefined(loaded.featureFlag) || isFlagEnabled(loaded.featureFlag)) {
|
||||||
typeof loaded.featureFlag === "undefined" ||
|
delete loaded.featureFlag
|
||||||
isFlagEnabled(loaded.featureFlag)
|
return Object.values(loaded)
|
||||||
) {
|
|
||||||
return file
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
})
|
||||||
|
.flat()
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,22 +202,17 @@ export const getModuleMigrations = (configModule, isFlagEnabled) => {
|
|||||||
|
|
||||||
const isolatedMigrations = {}
|
const isolatedMigrations = {}
|
||||||
const moduleMigrations = (mod.migrations ?? [])
|
const moduleMigrations = (mod.migrations ?? [])
|
||||||
.map((migrations) => {
|
.map((migration) => {
|
||||||
const all = []
|
if (
|
||||||
for (const migration of Object.values(migrations)) {
|
!isDefined(migration.featureFlag) ||
|
||||||
// TODO: revisit how Modules export their migration entrypoints up/down
|
|
||||||
if (["up", "down"].includes(migration.name)) {
|
|
||||||
isolatedMigrations[migration.name] = migration
|
|
||||||
} else if (
|
|
||||||
typeof migration.featureFlag === "undefined" ||
|
|
||||||
isFlagEnabled(migration.featureFlag)
|
isFlagEnabled(migration.featureFlag)
|
||||||
) {
|
) {
|
||||||
all.push(migration)
|
delete migration.featureFlag
|
||||||
|
return Object.values(migration)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return all
|
|
||||||
})
|
})
|
||||||
.flat()
|
.flat()
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
allModules.push({
|
allModules.push({
|
||||||
moduleDeclaration: loadedModule.moduleDeclaration,
|
moduleDeclaration: loadedModule.moduleDeclaration,
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ const config = {
|
|||||||
admin_cors: "",
|
admin_cors: "",
|
||||||
store_cors: "",
|
store_cors: "",
|
||||||
},
|
},
|
||||||
moduleResolutions,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const testApp = express()
|
const testApp = express()
|
||||||
@@ -70,7 +69,7 @@ featureFlagLoader(config)
|
|||||||
servicesLoader({ container, configModule: config })
|
servicesLoader({ container, configModule: config })
|
||||||
strategiesLoader({ container, configModule: config })
|
strategiesLoader({ container, configModule: config })
|
||||||
passportLoader({ app: testApp, container, configModule: config })
|
passportLoader({ app: testApp, container, configModule: config })
|
||||||
moduleLoader({ container, configModule: config })
|
moduleLoader({ container, moduleResolutions })
|
||||||
|
|
||||||
testApp.use((req, res, next) => {
|
testApp.use((req, res, next) => {
|
||||||
req.scope = container.createScope()
|
req.scope = container.createScope()
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
import { EntityManager } from "typeorm"
|
|
||||||
import { FindConfig } from "../../types/common"
|
import { FindConfig } from "../../types/common"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
StockLocationDTO,
|
|
||||||
FilterableStockLocationProps,
|
|
||||||
CreateStockLocationInput,
|
CreateStockLocationInput,
|
||||||
|
FilterableStockLocationProps,
|
||||||
|
StockLocationDTO,
|
||||||
UpdateStockLocationInput,
|
UpdateStockLocationInput,
|
||||||
} from "../../types/stock-location"
|
} from "../../types/stock-location"
|
||||||
|
|
||||||
export interface IStockLocationService {
|
export interface IStockLocationService {
|
||||||
withTransaction(transactionManager?: EntityManager): this
|
|
||||||
list(
|
list(
|
||||||
selector: FilterableStockLocationProps,
|
selector: FilterableStockLocationProps,
|
||||||
config?: FindConfig<StockLocationDTO>
|
config?: FindConfig<StockLocationDTO>
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
asFunction,
|
|
||||||
asValue,
|
asValue,
|
||||||
AwilixContainer,
|
AwilixContainer,
|
||||||
ClassOrFunctionReturning,
|
ClassOrFunctionReturning,
|
||||||
createContainer,
|
|
||||||
Resolver,
|
Resolver,
|
||||||
} from "awilix"
|
} from "awilix"
|
||||||
import { mkdirSync, rmSync, writeFileSync } from "fs"
|
import { mkdirSync, rmSync, writeFileSync } from "fs"
|
||||||
import { resolve } from "path"
|
import { resolve } from "path"
|
||||||
import Logger from "../logger"
|
import Logger from "../logger"
|
||||||
import { registerServices, registerStrategies } from "../plugins"
|
import { registerServices, registerStrategies } from "../plugins"
|
||||||
import { MedusaContainer } from "../../types/global"
|
import { DataSource, EntityManager } from "typeorm"
|
||||||
import { Connection, EntityManager } from "typeorm";
|
import { createMedusaContainer } from "medusa-core-utils"
|
||||||
|
|
||||||
// ***** TEMPLATES *****
|
// ***** TEMPLATES *****
|
||||||
const buildServiceTemplate = (name: string): string => {
|
const buildServiceTemplate = (name: string): string => {
|
||||||
@@ -111,32 +109,10 @@ function asArray(
|
|||||||
// ***** TESTS *****
|
// ***** TESTS *****
|
||||||
|
|
||||||
describe("plugins loader", () => {
|
describe("plugins loader", () => {
|
||||||
const container = createContainer() as MedusaContainer
|
const container = createMedusaContainer()
|
||||||
container.registerAdd = function (
|
|
||||||
this: MedusaContainer,
|
|
||||||
name: string,
|
|
||||||
registration: typeof asFunction | typeof asValue
|
|
||||||
): MedusaContainer {
|
|
||||||
const storeKey = name + "_STORE"
|
|
||||||
|
|
||||||
if (this.registrations[storeKey] === undefined) {
|
|
||||||
this.register(storeKey, asValue([] as Resolver<unknown>[]))
|
|
||||||
}
|
|
||||||
const store = this.resolve(storeKey) as (
|
|
||||||
| ClassOrFunctionReturning<unknown>
|
|
||||||
| Resolver<unknown>
|
|
||||||
)[]
|
|
||||||
|
|
||||||
if (this.registrations[name] === undefined) {
|
|
||||||
this.register(name, asArray(store))
|
|
||||||
}
|
|
||||||
store.unshift(registration)
|
|
||||||
|
|
||||||
return this
|
|
||||||
}.bind(container)
|
|
||||||
|
|
||||||
container.register("logger", asValue(Logger))
|
container.register("logger", asValue(Logger))
|
||||||
container.register("manager", asValue(new EntityManager({} as Connection)))
|
container.register("manager", asValue(new EntityManager({} as DataSource)))
|
||||||
|
|
||||||
const pluginsDetails = {
|
const pluginsDetails = {
|
||||||
resolve: resolve(__dirname, "__pluginsLoaderTest__"),
|
resolve: resolve(__dirname, "__pluginsLoaderTest__"),
|
||||||
@@ -207,8 +183,9 @@ describe("plugins loader", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("registers price selection strategy", () => {
|
it("registers price selection strategy", () => {
|
||||||
const priceSelectionStrategy =
|
const priceSelectionStrategy = container.resolve(
|
||||||
container.resolve("priceSelectionStrategy") as (...args: unknown[]) => any
|
"priceSelectionStrategy"
|
||||||
|
) as (...args: unknown[]) => any
|
||||||
|
|
||||||
expect(priceSelectionStrategy).toBeTruthy()
|
expect(priceSelectionStrategy).toBeTruthy()
|
||||||
expect(priceSelectionStrategy.constructor.name).toBe(
|
expect(priceSelectionStrategy.constructor.name).toBe(
|
||||||
@@ -217,8 +194,9 @@ describe("plugins loader", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("registers tax calculation strategy", () => {
|
it("registers tax calculation strategy", () => {
|
||||||
const taxCalculationStrategy =
|
const taxCalculationStrategy = container.resolve(
|
||||||
container.resolve("taxCalculationStrategy") as (...args: unknown[]) => any
|
"taxCalculationStrategy"
|
||||||
|
) as (...args: unknown[]) => any
|
||||||
|
|
||||||
expect(taxCalculationStrategy).toBeTruthy()
|
expect(taxCalculationStrategy).toBeTruthy()
|
||||||
expect(taxCalculationStrategy.constructor.name).toBe(
|
expect(taxCalculationStrategy.constructor.name).toBe(
|
||||||
@@ -227,8 +205,9 @@ describe("plugins loader", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("registers batch job strategies as single array", () => {
|
it("registers batch job strategies as single array", () => {
|
||||||
const batchJobStrategies =
|
const batchJobStrategies = container.resolve("batchJobStrategies") as (
|
||||||
container.resolve("batchJobStrategies") as (...args: unknown[]) => any
|
...args: unknown[]
|
||||||
|
) => any
|
||||||
|
|
||||||
expect(batchJobStrategies).toBeTruthy()
|
expect(batchJobStrategies).toBeTruthy()
|
||||||
expect(Array.isArray(batchJobStrategies)).toBeTruthy()
|
expect(Array.isArray(batchJobStrategies)).toBeTruthy()
|
||||||
@@ -236,8 +215,9 @@ describe("plugins loader", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("registers batch job strategies by type and only keep the last", () => {
|
it("registers batch job strategies by type and only keep the last", () => {
|
||||||
const batchJobStrategy =
|
const batchJobStrategy = container.resolve("batchType_type-1") as (
|
||||||
container.resolve("batchType_type-1") as (...args: unknown[]) => any
|
...args: unknown[]
|
||||||
|
) => any
|
||||||
|
|
||||||
expect(batchJobStrategy).toBeTruthy()
|
expect(batchJobStrategy).toBeTruthy()
|
||||||
expect(batchJobStrategy.constructor.name).toBe("testBatch2BatchStrategy")
|
expect(batchJobStrategy.constructor.name).toBe("testBatch2BatchStrategy")
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { getConfigFile } from "medusa-core-utils"
|
import { getConfigFile } from "medusa-core-utils"
|
||||||
import { ConfigModule } from "../types/global"
|
import { ConfigModule } from "../types/global"
|
||||||
import logger from "./logger"
|
import logger from "./logger"
|
||||||
import { registerModules } from "@medusajs/modules-sdk"
|
|
||||||
|
|
||||||
const isProduction = ["production", "prod"].includes(process.env.NODE_ENV || "")
|
const isProduction = ["production", "prod"].includes(process.env.NODE_ENV || "")
|
||||||
|
|
||||||
@@ -65,8 +64,6 @@ export default (rootDirectory: string): ConfigModule => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const moduleResolutions = registerModules(configModule)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
projectConfig: {
|
projectConfig: {
|
||||||
jwt_secret: jwt_secret ?? "supersecret",
|
jwt_secret: jwt_secret ?? "supersecret",
|
||||||
@@ -74,7 +71,6 @@ export default (rootDirectory: string): ConfigModule => {
|
|||||||
...configModule?.projectConfig,
|
...configModule?.projectConfig,
|
||||||
},
|
},
|
||||||
modules: configModule.modules ?? {},
|
modules: configModule.modules ?? {},
|
||||||
moduleResolutions,
|
|
||||||
featureFlags: configModule?.featureFlags ?? {},
|
featureFlags: configModule?.featureFlags ?? {},
|
||||||
plugins: configModule?.plugins ?? [],
|
plugins: configModule?.plugins ?? [],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
import {
|
import { asValue } from "awilix"
|
||||||
asFunction,
|
|
||||||
asValue,
|
|
||||||
AwilixContainer,
|
|
||||||
createContainer,
|
|
||||||
Resolver,
|
|
||||||
} from "awilix"
|
|
||||||
import { ClassOrFunctionReturning } from "awilix/lib/container"
|
|
||||||
import { Express, NextFunction, Request, Response } from "express"
|
import { Express, NextFunction, Request, Response } from "express"
|
||||||
import { track } from "medusa-telemetry"
|
import { track } from "medusa-telemetry"
|
||||||
import { EOL } from "os"
|
import { EOL } from "os"
|
||||||
@@ -30,7 +23,8 @@ import servicesLoader from "./services"
|
|||||||
import strategiesLoader from "./strategies"
|
import strategiesLoader from "./strategies"
|
||||||
import subscribersLoader from "./subscribers"
|
import subscribersLoader from "./subscribers"
|
||||||
|
|
||||||
import { moduleLoader } from "@medusajs/modules-sdk"
|
import { moduleLoader, registerModules } from "@medusajs/modules-sdk"
|
||||||
|
import { createMedusaContainer } from "medusa-core-utils"
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
directory: string
|
directory: string
|
||||||
@@ -49,32 +43,9 @@ export default async ({
|
|||||||
}> => {
|
}> => {
|
||||||
const configModule = loadConfig(rootDirectory)
|
const configModule = loadConfig(rootDirectory)
|
||||||
|
|
||||||
const container = createContainer() as MedusaContainer
|
const container = createMedusaContainer()
|
||||||
container.register("configModule", asValue(configModule))
|
container.register("configModule", asValue(configModule))
|
||||||
|
|
||||||
container.registerAdd = function (
|
|
||||||
this: MedusaContainer,
|
|
||||||
name: string,
|
|
||||||
registration: typeof asFunction | typeof asValue
|
|
||||||
) {
|
|
||||||
const storeKey = name + "_STORE"
|
|
||||||
|
|
||||||
if (this.registrations[storeKey] === undefined) {
|
|
||||||
this.register(storeKey, asValue([] as Resolver<unknown>[]))
|
|
||||||
}
|
|
||||||
const store = this.resolve(storeKey) as (
|
|
||||||
| ClassOrFunctionReturning<unknown>
|
|
||||||
| Resolver<unknown>
|
|
||||||
)[]
|
|
||||||
|
|
||||||
if (this.registrations[name] === undefined) {
|
|
||||||
this.register(name, asArray(store))
|
|
||||||
}
|
|
||||||
store.unshift(registration)
|
|
||||||
|
|
||||||
return this
|
|
||||||
}.bind(container)
|
|
||||||
|
|
||||||
// Add additional information to context of request
|
// Add additional information to context of request
|
||||||
expressApp.use((req: Request, res: Response, next: NextFunction) => {
|
expressApp.use((req: Request, res: Response, next: NextFunction) => {
|
||||||
const ipAddress = requestIp.getClientIp(req) as string
|
const ipAddress = requestIp.getClientIp(req) as string
|
||||||
@@ -118,7 +89,11 @@ export default async ({
|
|||||||
|
|
||||||
const modulesActivity = Logger.activity(`Initializing modules${EOL}`)
|
const modulesActivity = Logger.activity(`Initializing modules${EOL}`)
|
||||||
track("MODULES_INIT_STARTED")
|
track("MODULES_INIT_STARTED")
|
||||||
await moduleLoader({ container, configModule, logger: Logger })
|
await moduleLoader({
|
||||||
|
container,
|
||||||
|
moduleResolutions: registerModules(configModule),
|
||||||
|
logger: Logger,
|
||||||
|
})
|
||||||
const modAct = Logger.success(modulesActivity, "Modules initialized") || {}
|
const modAct = Logger.success(modulesActivity, "Modules initialized") || {}
|
||||||
track("MODULES_INIT_COMPLETED", { duration: modAct.duration })
|
track("MODULES_INIT_COMPLETED", { duration: modAct.duration })
|
||||||
|
|
||||||
@@ -200,12 +175,3 @@ export default async ({
|
|||||||
|
|
||||||
return { container, dbConnection, app: expressApp }
|
return { container, dbConnection, app: expressApp }
|
||||||
}
|
}
|
||||||
|
|
||||||
function asArray(
|
|
||||||
resolvers: (ClassOrFunctionReturning<unknown> | Resolver<unknown>)[]
|
|
||||||
): { resolve: (container: AwilixContainer) => unknown[] } {
|
|
||||||
return {
|
|
||||||
resolve: (container: AwilixContainer) =>
|
|
||||||
resolvers.map((resolver) => container.build(resolver)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
import {
|
import { InternalModuleDeclaration } from "@medusajs/modules-sdk"
|
||||||
ConfigurableModuleDeclaration,
|
import { MedusaContainer as coreMedusaContainer } from "medusa-core-utils"
|
||||||
ModuleResolution,
|
|
||||||
} from "@medusajs/modules-sdk"
|
|
||||||
import { AwilixContainer } from "awilix"
|
|
||||||
import { Request } from "express"
|
import { Request } from "express"
|
||||||
import { LoggerOptions } from "typeorm"
|
import { LoggerOptions } from "typeorm"
|
||||||
import { Logger as _Logger } from "winston"
|
import { Logger as _Logger } from "winston"
|
||||||
@@ -34,9 +31,7 @@ export type ClassConstructor<T> = {
|
|||||||
new (...args: unknown[]): T
|
new (...args: unknown[]): T
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MedusaContainer = AwilixContainer & {
|
export type MedusaContainer = coreMedusaContainer
|
||||||
registerAdd: <T>(name: string, registration: T) => MedusaContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Logger = _Logger & {
|
export type Logger = _Logger & {
|
||||||
progress: (activityId: string, msg: string) => void
|
progress: (activityId: string, msg: string) => void
|
||||||
@@ -96,11 +91,7 @@ export type ConfigModule = {
|
|||||||
admin_cors?: string
|
admin_cors?: string
|
||||||
}
|
}
|
||||||
featureFlags: Record<string, boolean | string>
|
featureFlags: Record<string, boolean | string>
|
||||||
modules?: Record<
|
modules?: Record<string, false | string | Partial<InternalModuleDeclaration>>
|
||||||
string,
|
|
||||||
false | string | Partial<ConfigurableModuleDeclaration>
|
|
||||||
>
|
|
||||||
moduleResolutions?: Record<string, ModuleResolution>
|
|
||||||
plugins: (
|
plugins: (
|
||||||
| {
|
| {
|
||||||
resolve: string
|
resolve: string
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Store, PaymentProvider, FulfillmentProvider } from "../models"
|
import { Store, PaymentProvider, FulfillmentProvider } from "../models"
|
||||||
import { FeatureFlagsResponse } from "./feature-flags"
|
import { FeatureFlagsResponse } from "./feature-flags"
|
||||||
import { ModulesResponse } from "@medusajs/modules-sdk"
|
import { ModulesResponse as sdkModulesResponse } from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
export type UpdateStoreInput = {
|
export type UpdateStoreInput = {
|
||||||
name?: string
|
name?: string
|
||||||
@@ -29,6 +29,7 @@ export type UpdateStoreInput = {
|
|||||||
* description: The resolution path of the module or false if module is not installed.
|
* description: The resolution path of the module or false if module is not installed.
|
||||||
* type: string
|
* type: string
|
||||||
*/
|
*/
|
||||||
|
export type ModulesResponse = sdkModulesResponse
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @schema ExtendedStoreDTO
|
* @schema ExtendedStoreDTO
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"awilix": "^8.0.0",
|
"awilix": "^8.0.0",
|
||||||
|
"glob": "7.1.6",
|
||||||
|
"medusa-core-utils": "^1.1.39",
|
||||||
"medusa-telemetry": "^0.0.16",
|
"medusa-telemetry": "^0.0.16",
|
||||||
"resolve-cwd": "^3.0.0"
|
"resolve-cwd": "^3.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export const MODULE_DEFINITIONS: ModuleDefinition[] = [
|
|||||||
label: "StockLocationService",
|
label: "StockLocationService",
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
canOverride: true,
|
canOverride: true,
|
||||||
|
dependencies: ["eventBusService"],
|
||||||
defaultModuleDeclaration: {
|
defaultModuleDeclaration: {
|
||||||
scope: MODULE_SCOPE.INTERNAL,
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
resources: MODULE_RESOURCE_TYPE.SHARED,
|
resources: MODULE_RESOURCE_TYPE.SHARED,
|
||||||
@@ -20,6 +21,7 @@ export const MODULE_DEFINITIONS: ModuleDefinition[] = [
|
|||||||
label: "InventoryService",
|
label: "InventoryService",
|
||||||
isRequired: false,
|
isRequired: false,
|
||||||
canOverride: true,
|
canOverride: true,
|
||||||
|
dependencies: ["eventBusService"],
|
||||||
defaultModuleDeclaration: {
|
defaultModuleDeclaration: {
|
||||||
scope: MODULE_SCOPE.INTERNAL,
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
resources: MODULE_RESOURCE_TYPE.SHARED,
|
resources: MODULE_RESOURCE_TYPE.SHARED,
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
export * from "./types"
|
|
||||||
export * from "./loaders"
|
|
||||||
|
|
||||||
export * from "./module-helper"
|
|
||||||
|
|
||||||
export * from "./definitions"
|
export * from "./definitions"
|
||||||
|
export * from "./loaders"
|
||||||
|
export * from "./module-helper"
|
||||||
|
export * from "./types"
|
||||||
@@ -1,14 +1,7 @@
|
|||||||
|
import { EOL } from "os"
|
||||||
|
import { AwilixContainer, ClassOrFunctionReturning, Resolver } from "awilix"
|
||||||
|
import { createMedusaContainer } from "medusa-core-utils"
|
||||||
import {
|
import {
|
||||||
asFunction,
|
|
||||||
asValue,
|
|
||||||
AwilixContainer,
|
|
||||||
ClassOrFunctionReturning,
|
|
||||||
createContainer,
|
|
||||||
Resolver,
|
|
||||||
} from "awilix"
|
|
||||||
import {
|
|
||||||
ConfigModule,
|
|
||||||
MedusaContainer,
|
|
||||||
ModuleResolution,
|
ModuleResolution,
|
||||||
MODULE_RESOURCE_TYPE,
|
MODULE_RESOURCE_TYPE,
|
||||||
MODULE_SCOPE,
|
MODULE_SCOPE,
|
||||||
@@ -28,46 +21,9 @@ function asArray(
|
|||||||
|
|
||||||
const logger = {
|
const logger = {
|
||||||
warn: jest.fn(),
|
warn: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
} as any
|
} as any
|
||||||
|
|
||||||
const buildConfigModule = (
|
|
||||||
configParts: Partial<ConfigModule>
|
|
||||||
): ConfigModule => {
|
|
||||||
return {
|
|
||||||
modules: {},
|
|
||||||
moduleResolutions: {},
|
|
||||||
...configParts,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const buildContainer = () => {
|
|
||||||
const container = createContainer() as MedusaContainer
|
|
||||||
|
|
||||||
container.registerAdd = function (
|
|
||||||
this: MedusaContainer,
|
|
||||||
name: string,
|
|
||||||
registration: typeof asFunction | typeof asValue
|
|
||||||
): MedusaContainer {
|
|
||||||
const storeKey = name + "_STORE"
|
|
||||||
|
|
||||||
if (this.registrations[storeKey] === undefined) {
|
|
||||||
this.register(storeKey, asValue([] as Resolver<unknown>[]))
|
|
||||||
}
|
|
||||||
const store = this.resolve(storeKey) as (
|
|
||||||
| ClassOrFunctionReturning<unknown>
|
|
||||||
| Resolver<unknown>
|
|
||||||
)[]
|
|
||||||
|
|
||||||
if (this.registrations[name] === undefined) {
|
|
||||||
this.register(name, asArray(store))
|
|
||||||
}
|
|
||||||
store.unshift(registration)
|
|
||||||
|
|
||||||
return this
|
|
||||||
}.bind(container)
|
|
||||||
|
|
||||||
return container
|
|
||||||
}
|
|
||||||
describe("modules loader", () => {
|
describe("modules loader", () => {
|
||||||
let container
|
let container
|
||||||
|
|
||||||
@@ -76,7 +32,7 @@ describe("modules loader", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
container = buildContainer()
|
container = createMedusaContainer()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("registers service as undefined in container when no resolution path is given", async () => {
|
it("registers service as undefined in container when no resolution path is given", async () => {
|
||||||
@@ -100,10 +56,7 @@ describe("modules loader", () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const configModule = buildConfigModule({
|
await moduleLoader({ container, moduleResolutions, logger })
|
||||||
moduleResolutions,
|
|
||||||
})
|
|
||||||
await moduleLoader({ container, configModule, logger })
|
|
||||||
|
|
||||||
const testService = container.resolve(
|
const testService = container.resolve(
|
||||||
moduleResolutions.testService.definition.key
|
moduleResolutions.testService.definition.key
|
||||||
@@ -132,11 +85,7 @@ describe("modules loader", () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const configModule = buildConfigModule({
|
await moduleLoader({ container, moduleResolutions, logger })
|
||||||
moduleResolutions,
|
|
||||||
})
|
|
||||||
|
|
||||||
await moduleLoader({ container, configModule, logger })
|
|
||||||
|
|
||||||
const testService = container.resolve(
|
const testService = container.resolve(
|
||||||
moduleResolutions.testService.definition.key,
|
moduleResolutions.testService.definition.key,
|
||||||
@@ -175,14 +124,10 @@ describe("modules loader", () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const configModule = buildConfigModule({
|
await moduleLoader({ container, moduleResolutions, logger })
|
||||||
moduleResolutions,
|
|
||||||
})
|
|
||||||
|
|
||||||
await moduleLoader({ container, configModule, logger })
|
|
||||||
|
|
||||||
expect(logger.warn).toHaveBeenCalledWith(
|
expect(logger.warn).toHaveBeenCalledWith(
|
||||||
"Could not resolve module: TestService. Error: Loaders for module TestService failed: loader"
|
`Could not resolve module: TestService. Error: Loaders for module TestService failed: loader${EOL}`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -207,14 +152,10 @@ describe("modules loader", () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const configModule = buildConfigModule({
|
await moduleLoader({ container, moduleResolutions, logger })
|
||||||
moduleResolutions,
|
|
||||||
})
|
|
||||||
|
|
||||||
await moduleLoader({ container, configModule, logger })
|
|
||||||
|
|
||||||
expect(logger.warn).toHaveBeenCalledWith(
|
expect(logger.warn).toHaveBeenCalledWith(
|
||||||
"Could not resolve module: TestService. Error: No service found in module. Make sure that your module exports a service."
|
`Could not resolve module: TestService. Error: No service found in module. Make sure your module exports at least one service.${EOL}`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -241,14 +182,11 @@ describe("modules loader", () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const configModule = buildConfigModule({
|
|
||||||
moduleResolutions,
|
|
||||||
})
|
|
||||||
try {
|
try {
|
||||||
await moduleLoader({ container, configModule, logger })
|
await moduleLoader({ container, moduleResolutions, logger })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err.message).toEqual(
|
expect(err.message).toEqual(
|
||||||
"No service found in module. Make sure that your module exports a service."
|
"No service found in module. Make sure your module exports at least one service."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -276,11 +214,8 @@ describe("modules loader", () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const configModule = buildConfigModule({
|
|
||||||
moduleResolutions,
|
|
||||||
})
|
|
||||||
try {
|
try {
|
||||||
await moduleLoader({ container, configModule, logger })
|
await moduleLoader({ container, moduleResolutions, logger })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err.message).toEqual(
|
expect(err.message).toEqual(
|
||||||
"The module TestService has to define its scope (internal | external)"
|
"The module TestService has to define its scope (internal | external)"
|
||||||
@@ -308,14 +243,11 @@ describe("modules loader", () => {
|
|||||||
moduleDeclaration: {
|
moduleDeclaration: {
|
||||||
scope: MODULE_SCOPE.INTERNAL,
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
},
|
},
|
||||||
},
|
} as any,
|
||||||
}
|
}
|
||||||
|
|
||||||
const configModule = buildConfigModule({
|
|
||||||
moduleResolutions,
|
|
||||||
})
|
|
||||||
try {
|
try {
|
||||||
await moduleLoader({ container, configModule, logger })
|
await moduleLoader({ container, moduleResolutions, logger })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err.message).toEqual(
|
expect(err.message).toEqual(
|
||||||
"The module TestService is missing its resources config"
|
"The module TestService is missing its resources config"
|
||||||
|
|||||||
+33
-18
@@ -1,10 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
ConfigModule,
|
InternalModuleDeclaration,
|
||||||
ModuleDefinition,
|
ModuleDefinition,
|
||||||
MODULE_RESOURCE_TYPE,
|
MODULE_RESOURCE_TYPE,
|
||||||
MODULE_SCOPE,
|
MODULE_SCOPE,
|
||||||
} from "../../types"
|
} from "../../types"
|
||||||
import { registerModules } from "../module-definition"
|
import { registerModules } from "../register-modules"
|
||||||
import MODULE_DEFINITIONS from "../../definitions"
|
import MODULE_DEFINITIONS from "../../definitions"
|
||||||
|
|
||||||
const RESOLVED_PACKAGE = "@medusajs/test-service-resolved"
|
const RESOLVED_PACKAGE = "@medusajs/test-service-resolved"
|
||||||
@@ -35,9 +35,10 @@ describe("module definitions loader", () => {
|
|||||||
it("Resolves module with default definition given empty config", () => {
|
it("Resolves module with default definition given empty config", () => {
|
||||||
MODULE_DEFINITIONS.push({ ...defaultDefinition })
|
MODULE_DEFINITIONS.push({ ...defaultDefinition })
|
||||||
|
|
||||||
const res = registerModules({ modules: {} } as ConfigModule)
|
const res = registerModules({ modules: {} })
|
||||||
|
|
||||||
expect(res[defaultDefinition.key]).toEqual({
|
expect(res[defaultDefinition.key]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
resolutionPath: defaultDefinition.defaultPackage,
|
resolutionPath: defaultDefinition.defaultPackage,
|
||||||
definition: defaultDefinition,
|
definition: defaultDefinition,
|
||||||
options: {},
|
options: {},
|
||||||
@@ -46,6 +47,7 @@ describe("module definitions loader", () => {
|
|||||||
resources: "shared",
|
resources: "shared",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("boolean config", () => {
|
describe("boolean config", () => {
|
||||||
@@ -54,13 +56,15 @@ describe("module definitions loader", () => {
|
|||||||
|
|
||||||
const res = registerModules({
|
const res = registerModules({
|
||||||
modules: { [defaultDefinition.key]: false },
|
modules: { [defaultDefinition.key]: false },
|
||||||
} as ConfigModule)
|
})
|
||||||
|
|
||||||
expect(res[defaultDefinition.key]).toEqual({
|
expect(res[defaultDefinition.key]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
resolutionPath: false,
|
resolutionPath: false,
|
||||||
definition: defaultDefinition,
|
definition: defaultDefinition,
|
||||||
options: {},
|
options: {},
|
||||||
})
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Fails to resolve module with no resolution path when given false for a required module", () => {
|
it("Fails to resolve module with no resolution path when given false for a required module", () => {
|
||||||
@@ -70,7 +74,7 @@ describe("module definitions loader", () => {
|
|||||||
try {
|
try {
|
||||||
registerModules({
|
registerModules({
|
||||||
modules: { [defaultDefinition.key]: false },
|
modules: { [defaultDefinition.key]: false },
|
||||||
} as ConfigModule)
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
expect(err.message).toEqual(
|
expect(err.message).toEqual(
|
||||||
`Module: ${defaultDefinition.label} is required`
|
`Module: ${defaultDefinition.label} is required`
|
||||||
@@ -88,9 +92,10 @@ describe("module definitions loader", () => {
|
|||||||
|
|
||||||
const res = registerModules({
|
const res = registerModules({
|
||||||
modules: {},
|
modules: {},
|
||||||
} as ConfigModule)
|
})
|
||||||
|
|
||||||
expect(res[defaultDefinition.key]).toEqual({
|
expect(res[defaultDefinition.key]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
resolutionPath: false,
|
resolutionPath: false,
|
||||||
definition: definition,
|
definition: definition,
|
||||||
options: {},
|
options: {},
|
||||||
@@ -99,6 +104,7 @@ describe("module definitions loader", () => {
|
|||||||
resources: "shared",
|
resources: "shared",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -110,9 +116,10 @@ describe("module definitions loader", () => {
|
|||||||
modules: {
|
modules: {
|
||||||
[defaultDefinition.key]: defaultDefinition.defaultPackage,
|
[defaultDefinition.key]: defaultDefinition.defaultPackage,
|
||||||
},
|
},
|
||||||
} as ConfigModule)
|
})
|
||||||
|
|
||||||
expect(res[defaultDefinition.key]).toEqual({
|
expect(res[defaultDefinition.key]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
resolutionPath: RESOLVED_PACKAGE,
|
resolutionPath: RESOLVED_PACKAGE,
|
||||||
definition: defaultDefinition,
|
definition: defaultDefinition,
|
||||||
options: {},
|
options: {},
|
||||||
@@ -121,6 +128,7 @@ describe("module definitions loader", () => {
|
|||||||
resources: "shared",
|
resources: "shared",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -131,13 +139,15 @@ describe("module definitions loader", () => {
|
|||||||
const res = registerModules({
|
const res = registerModules({
|
||||||
modules: {
|
modules: {
|
||||||
[defaultDefinition.key]: {
|
[defaultDefinition.key]: {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
resolve: defaultDefinition.defaultPackage,
|
resolve: defaultDefinition.defaultPackage,
|
||||||
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
resources: MODULE_RESOURCE_TYPE.ISOLATED,
|
||||||
|
} as InternalModuleDeclaration,
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
} as ConfigModule)
|
|
||||||
|
|
||||||
expect(res[defaultDefinition.key]).toEqual({
|
expect(res[defaultDefinition.key]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
resolutionPath: RESOLVED_PACKAGE,
|
resolutionPath: RESOLVED_PACKAGE,
|
||||||
definition: defaultDefinition,
|
definition: defaultDefinition,
|
||||||
options: {},
|
options: {},
|
||||||
@@ -147,6 +157,7 @@ describe("module definitions loader", () => {
|
|||||||
resolve: defaultDefinition.defaultPackage,
|
resolve: defaultDefinition.defaultPackage,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Resolves default resolution path and provides options when only options are provided", () => {
|
it("Resolves default resolution path and provides options when only options are provided", () => {
|
||||||
@@ -158,9 +169,10 @@ describe("module definitions loader", () => {
|
|||||||
options: { test: 123 },
|
options: { test: 123 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as unknown as ConfigModule)
|
} as any)
|
||||||
|
|
||||||
expect(res[defaultDefinition.key]).toEqual({
|
expect(res[defaultDefinition.key]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
resolutionPath: defaultDefinition.defaultPackage,
|
resolutionPath: defaultDefinition.defaultPackage,
|
||||||
definition: defaultDefinition,
|
definition: defaultDefinition,
|
||||||
options: { test: 123 },
|
options: { test: 123 },
|
||||||
@@ -170,6 +182,7 @@ describe("module definitions loader", () => {
|
|||||||
options: { test: 123 },
|
options: { test: 123 },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("Resolves resolution path and provides options when only options are provided", () => {
|
it("Resolves resolution path and provides options when only options are provided", () => {
|
||||||
@@ -184,9 +197,10 @@ describe("module definitions loader", () => {
|
|||||||
resources: "isolated",
|
resources: "isolated",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as unknown as ConfigModule)
|
} as any)
|
||||||
|
|
||||||
expect(res[defaultDefinition.key]).toEqual({
|
expect(res[defaultDefinition.key]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
resolutionPath: RESOLVED_PACKAGE,
|
resolutionPath: RESOLVED_PACKAGE,
|
||||||
definition: defaultDefinition,
|
definition: defaultDefinition,
|
||||||
options: { test: 123 },
|
options: { test: 123 },
|
||||||
@@ -197,6 +211,7 @@ describe("module definitions loader", () => {
|
|||||||
options: { test: 123 },
|
options: { test: 123 },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from "./module-loader"
|
export * from "./module-loader"
|
||||||
|
export * from "./register-modules"
|
||||||
export * from "./module-definition"
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import resolveCwd from "resolve-cwd"
|
|
||||||
|
|
||||||
import { ConfigModule, ModuleResolution } from "../types"
|
|
||||||
import MODULE_DEFINITIONS from "../definitions"
|
|
||||||
|
|
||||||
export const registerModules = ({ modules }: ConfigModule) => {
|
|
||||||
const moduleResolutions = {} as Record<string, ModuleResolution>
|
|
||||||
const projectModules = modules ?? {}
|
|
||||||
|
|
||||||
for (const definition of MODULE_DEFINITIONS) {
|
|
||||||
let resolutionPath = definition.defaultPackage
|
|
||||||
|
|
||||||
const moduleConfiguration = projectModules[definition.key]
|
|
||||||
|
|
||||||
if (typeof moduleConfiguration === "boolean") {
|
|
||||||
if (!moduleConfiguration && definition.isRequired) {
|
|
||||||
throw new Error(`Module: ${definition.label} is required`)
|
|
||||||
}
|
|
||||||
if (!moduleConfiguration) {
|
|
||||||
moduleResolutions[definition.key] = {
|
|
||||||
resolutionPath: false,
|
|
||||||
definition,
|
|
||||||
options: {},
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If user added a module and it's overridable, we resolve that instead
|
|
||||||
if (
|
|
||||||
definition.canOverride &&
|
|
||||||
(typeof moduleConfiguration === "string" ||
|
|
||||||
(typeof moduleConfiguration === "object" &&
|
|
||||||
moduleConfiguration.resolve))
|
|
||||||
) {
|
|
||||||
resolutionPath = resolveCwd(
|
|
||||||
typeof moduleConfiguration === "string"
|
|
||||||
? moduleConfiguration
|
|
||||||
: (moduleConfiguration.resolve as string)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const moduleDeclaration =
|
|
||||||
typeof moduleConfiguration === "object" ? moduleConfiguration : {}
|
|
||||||
|
|
||||||
moduleResolutions[definition.key] = {
|
|
||||||
resolutionPath,
|
|
||||||
definition,
|
|
||||||
moduleDeclaration: {
|
|
||||||
...definition.defaultModuleDeclaration,
|
|
||||||
...moduleDeclaration,
|
|
||||||
},
|
|
||||||
options:
|
|
||||||
typeof moduleConfiguration === "object"
|
|
||||||
? moduleConfiguration.options ?? {}
|
|
||||||
: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return moduleResolutions
|
|
||||||
}
|
|
||||||
@@ -1,38 +1,41 @@
|
|||||||
import { asFunction, asValue } from "awilix"
|
import { asValue } from "awilix"
|
||||||
import { trackInstallation } from "medusa-telemetry"
|
import { EOL } from "os"
|
||||||
|
import { loadInternalModule } from "./utils"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ClassConstructor,
|
|
||||||
ConfigModule,
|
|
||||||
LoaderOptions,
|
|
||||||
Logger,
|
Logger,
|
||||||
MedusaContainer,
|
MedusaContainer,
|
||||||
ModuleExports,
|
|
||||||
ModuleResolution,
|
ModuleResolution,
|
||||||
MODULE_RESOURCE_TYPE,
|
|
||||||
MODULE_SCOPE,
|
MODULE_SCOPE,
|
||||||
} from "../types/module"
|
} from "../types"
|
||||||
|
|
||||||
import { ModulesHelper } from "../module-helper"
|
import { ModulesHelper } from "../module-helper"
|
||||||
|
|
||||||
export const moduleHelper = new ModulesHelper()
|
export const moduleHelper = new ModulesHelper()
|
||||||
|
|
||||||
const registerModule = async (
|
async function loadModule(
|
||||||
container: MedusaContainer,
|
container: MedusaContainer,
|
||||||
resolution: ModuleResolution,
|
resolution: ModuleResolution,
|
||||||
configModule: ConfigModule,
|
|
||||||
logger: Logger
|
logger: Logger
|
||||||
): Promise<{ error?: Error } | void> => {
|
): Promise<{ error?: Error } | void> {
|
||||||
const constainerName = resolution.definition.registrationName
|
const registrationName = resolution.definition.registrationName
|
||||||
|
|
||||||
|
const { scope, resources } = resolution.moduleDeclaration ?? ({} as any)
|
||||||
|
|
||||||
|
if (scope === MODULE_SCOPE.EXTERNAL) {
|
||||||
|
// TODO: implement external Resolvers
|
||||||
|
// return loadExternalModule(...)
|
||||||
|
throw new Error("External Modules are not supported yet.")
|
||||||
|
}
|
||||||
|
|
||||||
const { scope, resources } = resolution.moduleDeclaration ?? {}
|
|
||||||
if (!scope || (scope === MODULE_SCOPE.INTERNAL && !resources)) {
|
if (!scope || (scope === MODULE_SCOPE.INTERNAL && !resources)) {
|
||||||
let message = `The module ${resolution.definition.label} has to define its scope (internal | external)`
|
let message = `The module ${resolution.definition.label} has to define its scope (internal | external)`
|
||||||
if (scope && !resources) {
|
if (scope === MODULE_SCOPE.INTERNAL && !resources) {
|
||||||
message = `The module ${resolution.definition.label} is missing its resources config`
|
message = `The module ${resolution.definition.label} is missing its resources config`
|
||||||
}
|
}
|
||||||
|
|
||||||
container.register({
|
container.register({
|
||||||
[constainerName]: asValue(undefined),
|
[registrationName]: asValue(undefined),
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -42,107 +45,38 @@ const registerModule = async (
|
|||||||
|
|
||||||
if (!resolution.resolutionPath) {
|
if (!resolution.resolutionPath) {
|
||||||
container.register({
|
container.register({
|
||||||
[constainerName]: asValue(undefined),
|
[registrationName]: asValue(undefined),
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let loadedModule: ModuleExports
|
return await loadInternalModule(container, resolution, logger)
|
||||||
try {
|
|
||||||
loadedModule = (await import(resolution.resolutionPath!)).default
|
|
||||||
} catch (error) {
|
|
||||||
return { error }
|
|
||||||
}
|
|
||||||
|
|
||||||
const moduleService = loadedModule?.service || null
|
|
||||||
|
|
||||||
if (!moduleService) {
|
|
||||||
return {
|
|
||||||
error: new Error(
|
|
||||||
"No service found in module. Make sure that your module exports a service."
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
scope === MODULE_SCOPE.INTERNAL &&
|
|
||||||
resources === MODULE_RESOURCE_TYPE.SHARED
|
|
||||||
) {
|
|
||||||
const moduleModels = loadedModule?.models || null
|
|
||||||
if (moduleModels) {
|
|
||||||
moduleModels.map((val: ClassConstructor<unknown>) => {
|
|
||||||
container.registerAdd("db_entities", asValue(val))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: "cradle" should only contain dependent Modules and the EntityManager if module scope is shared
|
|
||||||
container.register({
|
|
||||||
[constainerName]: asFunction((cradle) => {
|
|
||||||
return new moduleService(
|
|
||||||
cradle,
|
|
||||||
resolution.options,
|
|
||||||
resolution.moduleDeclaration
|
|
||||||
)
|
|
||||||
}).singleton(),
|
|
||||||
})
|
|
||||||
|
|
||||||
const moduleLoaders = loadedModule?.loaders || []
|
|
||||||
try {
|
|
||||||
for (const loader of moduleLoaders) {
|
|
||||||
await loader(
|
|
||||||
{
|
|
||||||
container,
|
|
||||||
configModule,
|
|
||||||
logger,
|
|
||||||
options: resolution.options,
|
|
||||||
},
|
|
||||||
resolution.moduleDeclaration
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
return {
|
|
||||||
error: new Error(
|
|
||||||
`Loaders for module ${resolution.definition.label} failed: ${err.message}`
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trackInstallation(
|
|
||||||
{
|
|
||||||
module: resolution.definition.key,
|
|
||||||
resolution: resolution.resolutionPath,
|
|
||||||
},
|
|
||||||
"module"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const moduleLoader = async ({
|
export const moduleLoader = async ({
|
||||||
container,
|
container,
|
||||||
configModule,
|
moduleResolutions,
|
||||||
logger,
|
logger,
|
||||||
}: LoaderOptions): Promise<void> => {
|
}: {
|
||||||
const moduleResolutions = configModule?.moduleResolutions ?? {}
|
container: MedusaContainer
|
||||||
|
moduleResolutions: Record<string, ModuleResolution>
|
||||||
|
logger: Logger
|
||||||
|
}): Promise<void> => {
|
||||||
|
for (const resolution of Object.values(moduleResolutions ?? {})) {
|
||||||
|
const registrationResult = await loadModule(container, resolution, logger!)
|
||||||
|
|
||||||
for (const resolution of Object.values(moduleResolutions)) {
|
|
||||||
const registrationResult = await registerModule(
|
|
||||||
container,
|
|
||||||
resolution,
|
|
||||||
configModule,
|
|
||||||
logger!
|
|
||||||
)
|
|
||||||
if (registrationResult?.error) {
|
if (registrationResult?.error) {
|
||||||
const { error } = registrationResult
|
const { error } = registrationResult
|
||||||
if (resolution.definition.isRequired) {
|
if (resolution.definition.isRequired) {
|
||||||
logger?.warn(
|
logger?.error(
|
||||||
`Could not resolve required module: ${resolution.definition.label}. Error: ${error.message}`
|
`Could not resolve required module: ${resolution.definition.label}. Error: ${error.message}${EOL}`
|
||||||
)
|
)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
logger?.warn(
|
logger?.warn(
|
||||||
`Could not resolve module: ${resolution.definition.label}. Error: ${error.message}`
|
`Could not resolve module: ${resolution.definition.label}. Error: ${error.message}${EOL}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import resolveCwd from "resolve-cwd"
|
||||||
|
|
||||||
|
import {
|
||||||
|
MedusaModuleConfig,
|
||||||
|
InternalModuleDeclaration,
|
||||||
|
ModuleDefinition,
|
||||||
|
ModuleResolution,
|
||||||
|
MODULE_SCOPE,
|
||||||
|
} from "../types"
|
||||||
|
import MODULE_DEFINITIONS from "../definitions"
|
||||||
|
|
||||||
|
export const registerModules = ({
|
||||||
|
modules,
|
||||||
|
}: MedusaModuleConfig): Record<string, ModuleResolution> => {
|
||||||
|
const moduleResolutions = {} as Record<string, ModuleResolution>
|
||||||
|
const projectModules = modules ?? {}
|
||||||
|
|
||||||
|
for (const definition of MODULE_DEFINITIONS) {
|
||||||
|
const customConfig = projectModules[definition.key]
|
||||||
|
const isObj = typeof customConfig === "object"
|
||||||
|
|
||||||
|
if (isObj && customConfig.scope === MODULE_SCOPE.EXTERNAL) {
|
||||||
|
// TODO: getExternalModuleResolution(...)
|
||||||
|
throw new Error("External Modules are not supported yet.")
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleResolutions[definition.key] = getInternalModuleResolution(
|
||||||
|
definition,
|
||||||
|
projectModules[definition.key] as
|
||||||
|
| InternalModuleDeclaration
|
||||||
|
| false
|
||||||
|
| string
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return moduleResolutions
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInternalModuleResolution(
|
||||||
|
definition: ModuleDefinition,
|
||||||
|
moduleConfig: InternalModuleDeclaration | false | string
|
||||||
|
): ModuleResolution {
|
||||||
|
if (typeof moduleConfig === "boolean") {
|
||||||
|
if (!moduleConfig && definition.isRequired) {
|
||||||
|
throw new Error(`Module: ${definition.label} is required`)
|
||||||
|
}
|
||||||
|
if (!moduleConfig) {
|
||||||
|
return {
|
||||||
|
resolutionPath: false,
|
||||||
|
definition,
|
||||||
|
dependencies: [],
|
||||||
|
options: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isObj = typeof moduleConfig === "object"
|
||||||
|
let resolutionPath = definition.defaultPackage
|
||||||
|
|
||||||
|
// If user added a module and it's overridable, we resolve that instead
|
||||||
|
const isString = typeof moduleConfig === "string"
|
||||||
|
if (definition.canOverride && (isString || (isObj && moduleConfig.resolve))) {
|
||||||
|
resolutionPath = resolveCwd(
|
||||||
|
isString ? moduleConfig : (moduleConfig.resolve as string)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleDeclaration = isObj ? moduleConfig : {}
|
||||||
|
const additionalDependencies = isObj ? moduleConfig.dependencies || [] : []
|
||||||
|
|
||||||
|
return {
|
||||||
|
resolutionPath,
|
||||||
|
definition,
|
||||||
|
dependencies: [
|
||||||
|
...new Set(
|
||||||
|
(definition.dependencies || []).concat(additionalDependencies)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
moduleDeclaration: {
|
||||||
|
...definition.defaultModuleDeclaration,
|
||||||
|
...moduleDeclaration,
|
||||||
|
},
|
||||||
|
options: isObj ? moduleConfig.options ?? {} : {},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./load-internal"
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import { asFunction, asValue } from "awilix"
|
||||||
|
import { createMedusaContainer } from "medusa-core-utils"
|
||||||
|
import { trackInstallation } from "medusa-telemetry"
|
||||||
|
import {
|
||||||
|
Constructor,
|
||||||
|
InternalModuleDeclaration,
|
||||||
|
Logger,
|
||||||
|
MedusaContainer,
|
||||||
|
ModuleExports,
|
||||||
|
ModuleResolution,
|
||||||
|
MODULE_RESOURCE_TYPE,
|
||||||
|
MODULE_SCOPE,
|
||||||
|
} from "../../types"
|
||||||
|
|
||||||
|
export async function loadInternalModule(
|
||||||
|
container: MedusaContainer,
|
||||||
|
resolution: ModuleResolution,
|
||||||
|
logger: Logger
|
||||||
|
): Promise<{ error?: Error } | void> {
|
||||||
|
const registrationName = resolution.definition.registrationName
|
||||||
|
|
||||||
|
const { scope, resources } =
|
||||||
|
resolution.moduleDeclaration as InternalModuleDeclaration
|
||||||
|
|
||||||
|
let loadedModule: ModuleExports
|
||||||
|
try {
|
||||||
|
loadedModule = (await import(resolution.resolutionPath as string)).default
|
||||||
|
} catch (error) {
|
||||||
|
return { error }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadedModule?.service) {
|
||||||
|
container.register({
|
||||||
|
[registrationName]: asValue(undefined),
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
error: new Error(
|
||||||
|
"No service found in module. Make sure your module exports at least one service."
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
scope === MODULE_SCOPE.INTERNAL &&
|
||||||
|
resources === MODULE_RESOURCE_TYPE.SHARED
|
||||||
|
) {
|
||||||
|
const moduleModels = loadedModule?.models || null
|
||||||
|
if (moduleModels) {
|
||||||
|
moduleModels.map((val: Constructor<unknown>) => {
|
||||||
|
container.registerAdd("db_entities", asValue(val))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const localContainer =
|
||||||
|
resources === MODULE_RESOURCE_TYPE.ISOLATED
|
||||||
|
? createMedusaContainer()
|
||||||
|
: (container.createScope() as MedusaContainer)
|
||||||
|
|
||||||
|
if (resources === MODULE_RESOURCE_TYPE.ISOLATED) {
|
||||||
|
const moduleDependencies = resolution?.dependencies ?? []
|
||||||
|
|
||||||
|
for (const dependency of moduleDependencies) {
|
||||||
|
localContainer.register(
|
||||||
|
dependency,
|
||||||
|
asFunction(() => container.resolve(dependency))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleLoaders = loadedModule?.loaders ?? []
|
||||||
|
try {
|
||||||
|
for (const loader of moduleLoaders) {
|
||||||
|
await loader(
|
||||||
|
{
|
||||||
|
container: localContainer,
|
||||||
|
logger,
|
||||||
|
options: resolution.options,
|
||||||
|
},
|
||||||
|
resolution.moduleDeclaration as InternalModuleDeclaration
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
container.register({
|
||||||
|
[registrationName]: asValue(undefined),
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
error: new Error(
|
||||||
|
`Loaders for module ${resolution.definition.label} failed: ${err.message}`
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleService = loadedModule.service
|
||||||
|
container.register({
|
||||||
|
[registrationName]: asFunction((cradle) => {
|
||||||
|
return new moduleService(
|
||||||
|
localContainer.cradle,
|
||||||
|
resolution.options,
|
||||||
|
resolution.moduleDeclaration
|
||||||
|
)
|
||||||
|
}).singleton(),
|
||||||
|
})
|
||||||
|
|
||||||
|
trackInstallation(
|
||||||
|
{
|
||||||
|
module: resolution.definition.key,
|
||||||
|
resolution: resolution.resolutionPath,
|
||||||
|
},
|
||||||
|
"module"
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ModuleResolution, ModulesResponse } from "./types/module"
|
import { ModuleResolution, ModulesResponse } from "./types"
|
||||||
|
|
||||||
export class ModulesHelper {
|
export class ModulesHelper {
|
||||||
private modules_: Record<string, ModuleResolution> = {}
|
private modules_: Record<string, ModuleResolution> = {}
|
||||||
|
|||||||
@@ -1 +1,101 @@
|
|||||||
export * from "./module"
|
import { MedusaContainer as coreMedusaContainer } from "medusa-core-utils"
|
||||||
|
import { Logger as _Logger } from "winston"
|
||||||
|
|
||||||
|
export type MedusaContainer = coreMedusaContainer
|
||||||
|
export type Constructor<T> = new (...args: any[]) => T
|
||||||
|
|
||||||
|
export type LogLevel =
|
||||||
|
| "query"
|
||||||
|
| "schema"
|
||||||
|
| "error"
|
||||||
|
| "warn"
|
||||||
|
| "info"
|
||||||
|
| "log"
|
||||||
|
| "migration"
|
||||||
|
export type LoggerOptions = boolean | "all" | LogLevel[]
|
||||||
|
|
||||||
|
export type Logger = _Logger & {
|
||||||
|
progress: (activityId: string, msg: string) => void
|
||||||
|
info: (msg: string) => void
|
||||||
|
warn: (msg: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MODULE_SCOPE {
|
||||||
|
INTERNAL = "internal",
|
||||||
|
EXTERNAL = "external",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MODULE_RESOURCE_TYPE {
|
||||||
|
SHARED = "shared",
|
||||||
|
ISOLATED = "isolated",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InternalModuleDeclaration = {
|
||||||
|
scope: MODULE_SCOPE.INTERNAL
|
||||||
|
resources: MODULE_RESOURCE_TYPE
|
||||||
|
dependencies?: string[]
|
||||||
|
resolve?: string
|
||||||
|
options?: Record<string, unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExternalModuleDeclaration = {
|
||||||
|
scope: MODULE_SCOPE.EXTERNAL
|
||||||
|
server: {
|
||||||
|
type: "http"
|
||||||
|
url: string
|
||||||
|
keepAlive: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModuleResolution = {
|
||||||
|
resolutionPath: string | false
|
||||||
|
definition: ModuleDefinition
|
||||||
|
options?: Record<string, unknown>
|
||||||
|
dependencies?: string[]
|
||||||
|
moduleDeclaration?: InternalModuleDeclaration | ExternalModuleDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModuleDefinition = {
|
||||||
|
key: string
|
||||||
|
registrationName: string
|
||||||
|
defaultPackage: string | false
|
||||||
|
label: string
|
||||||
|
canOverride?: boolean
|
||||||
|
isRequired?: boolean
|
||||||
|
dependencies?: string[]
|
||||||
|
defaultModuleDeclaration:
|
||||||
|
| InternalModuleDeclaration
|
||||||
|
| ExternalModuleDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LoaderOptions = {
|
||||||
|
container: MedusaContainer
|
||||||
|
options?: Record<string, unknown>
|
||||||
|
logger?: Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModuleLoaderFunction = (
|
||||||
|
options: LoaderOptions,
|
||||||
|
moduleDeclaration?: InternalModuleDeclaration
|
||||||
|
) => Promise<void>
|
||||||
|
|
||||||
|
export type ModulesResponse = {
|
||||||
|
module: string
|
||||||
|
resolution: string | false
|
||||||
|
}[]
|
||||||
|
|
||||||
|
export type ModuleExports = {
|
||||||
|
service: Constructor<any>
|
||||||
|
loaders?: ModuleLoaderFunction[]
|
||||||
|
migrations?: any[]
|
||||||
|
models?: Constructor<any>[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MedusaModuleConfig = {
|
||||||
|
modules?: Record<
|
||||||
|
string,
|
||||||
|
| false
|
||||||
|
| string
|
||||||
|
| Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
|
||||||
|
>
|
||||||
|
}
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
import { AwilixContainer } from "awilix"
|
|
||||||
import { Logger as _Logger } from "winston"
|
|
||||||
|
|
||||||
export type LogLevel =
|
|
||||||
| "query"
|
|
||||||
| "schema"
|
|
||||||
| "error"
|
|
||||||
| "warn"
|
|
||||||
| "info"
|
|
||||||
| "log"
|
|
||||||
| "migration"
|
|
||||||
export type LoggerOptions = boolean | "all" | LogLevel[]
|
|
||||||
|
|
||||||
export type ClassConstructor<T> = {
|
|
||||||
new (...args: unknown[]): T
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MedusaContainer = AwilixContainer & {
|
|
||||||
registerAdd: <T>(name: string, registration: T) => MedusaContainer
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Logger = _Logger & {
|
|
||||||
progress: (activityId: string, msg: string) => void
|
|
||||||
info: (msg: string) => void
|
|
||||||
warn: (msg: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum MODULE_SCOPE {
|
|
||||||
INTERNAL = "internal",
|
|
||||||
EXTERNAL = "external",
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum MODULE_RESOURCE_TYPE {
|
|
||||||
SHARED = "shared",
|
|
||||||
ISOLATED = "isolated",
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ConfigurableModuleDeclaration = {
|
|
||||||
scope: MODULE_SCOPE.INTERNAL
|
|
||||||
resources: MODULE_RESOURCE_TYPE
|
|
||||||
resolve?: string
|
|
||||||
options?: Record<string, unknown>
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
| {
|
|
||||||
scope: MODULE_SCOPE.external
|
|
||||||
server: {
|
|
||||||
type: "built-in" | "rest" | "tsrpc" | "grpc" | "gql"
|
|
||||||
url: string
|
|
||||||
options?: Record<string, unknown>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
export type ModuleResolution = {
|
|
||||||
resolutionPath: string | false
|
|
||||||
definition: ModuleDefinition
|
|
||||||
options?: Record<string, unknown>
|
|
||||||
moduleDeclaration?: ConfigurableModuleDeclaration
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ModuleDefinition = {
|
|
||||||
key: string
|
|
||||||
registrationName: string
|
|
||||||
defaultPackage: string | false
|
|
||||||
label: string
|
|
||||||
canOverride?: boolean
|
|
||||||
isRequired?: boolean
|
|
||||||
defaultModuleDeclaration: ConfigurableModuleDeclaration
|
|
||||||
}
|
|
||||||
|
|
||||||
export type LoaderOptions = {
|
|
||||||
container: MedusaContainer
|
|
||||||
configModule: ConfigModule
|
|
||||||
options?: Record<string, unknown>
|
|
||||||
logger?: Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Constructor<T> = new (...args: any[]) => T
|
|
||||||
|
|
||||||
export type ModuleExports = {
|
|
||||||
loaders: ((
|
|
||||||
options: LoaderOptions,
|
|
||||||
moduleDeclaration?: ConfigurableModuleDeclaration
|
|
||||||
) => Promise<void>)[]
|
|
||||||
service: Constructor<any>
|
|
||||||
migrations?: any[]
|
|
||||||
models?: Constructor<any>[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ConfigModule = {
|
|
||||||
options?: Record<string, any>
|
|
||||||
modules?: Record<
|
|
||||||
string,
|
|
||||||
false | string | Partial<ConfigurableModuleDeclaration>
|
|
||||||
>
|
|
||||||
moduleResolutions?: Record<string, ModuleResolution>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ModulesResponse = {
|
|
||||||
module: string
|
|
||||||
resolution: string | false
|
|
||||||
}[]
|
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": [
|
"lib": ["es2020"],
|
||||||
"es5",
|
"target": "es2020",
|
||||||
"es6",
|
|
||||||
"es2019"
|
|
||||||
],
|
|
||||||
"target": "es5",
|
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
@@ -19,12 +15,11 @@
|
|||||||
"strictFunctionTypes": true,
|
"strictFunctionTypes": true,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true
|
||||||
"downlevelIteration": true // to use ES5 specific tooling
|
|
||||||
},
|
},
|
||||||
"include": ["./src/**/*", "index.d.ts"],
|
"include": ["src"],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"./dist/**/*",
|
"dist",
|
||||||
"./src/**/__tests__",
|
"./src/**/__tests__",
|
||||||
"./src/**/__mocks__",
|
"./src/**/__mocks__",
|
||||||
"node_modules"
|
"node_modules"
|
||||||
|
|||||||
@@ -18,14 +18,17 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@medusajs/medusa": "^1.7.7",
|
"@medusajs/medusa": "^1.7.7",
|
||||||
|
"@medusajs/types": "*",
|
||||||
"cross-env": "^5.2.1",
|
"cross-env": "^5.2.1",
|
||||||
"jest": "^25.5.4",
|
"jest": "^25.5.4",
|
||||||
"ts-jest": "^25.5.1",
|
"ts-jest": "^25.5.1",
|
||||||
"typeorm": "^0.3.11",
|
|
||||||
"typescript": "^4.4.4"
|
"typescript": "^4.4.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@medusajs/modules-sdk": "*"
|
"@medusajs/modules-sdk": "*",
|
||||||
|
"@medusajs/utils": "^0.0.1",
|
||||||
|
"awilix": "^8.0.0",
|
||||||
|
"typeorm": "^0.3.11"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"watch": "tsc --build --watch",
|
"watch": "tsc --build --watch",
|
||||||
@@ -36,6 +39,6 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@medusajs/medusa": "^1.7.7",
|
"@medusajs/medusa": "^1.7.7",
|
||||||
"typeorm": "^0.3.11"
|
"@medusajs/types": "^0.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
export const CONNECTION_NAME = "stock_location_connection"
|
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import ConnectionLoader from "./loaders/connection"
|
import loadConnection from "./loaders/connection"
|
||||||
import StockLocationService from "./services/stock-location"
|
|
||||||
import * as SchemaMigration from "./migrations/schema-migrations/1665749860179-setup"
|
import migrations from "./migrations"
|
||||||
import * as StockLocationModels from "./models"
|
import * as StockLocationModels from "./models"
|
||||||
|
import StockLocationService from "./services/stock-location"
|
||||||
|
|
||||||
import { ModuleExports } from "@medusajs/modules-sdk"
|
import { ModuleExports } from "@medusajs/modules-sdk"
|
||||||
|
|
||||||
const service = StockLocationService
|
const service = StockLocationService
|
||||||
const migrations = [SchemaMigration]
|
const loaders = [loadConnection]
|
||||||
const loaders = [ConnectionLoader]
|
|
||||||
|
|
||||||
const models = Object.values(StockLocationModels)
|
const models = Object.values(StockLocationModels)
|
||||||
|
|
||||||
const moduleDefinition: ModuleExports = {
|
const moduleDefinition: ModuleExports = {
|
||||||
|
|||||||
@@ -1,9 +1,49 @@
|
|||||||
import {
|
import {
|
||||||
ConfigurableModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
LoaderOptions,
|
LoaderOptions,
|
||||||
|
MODULE_RESOURCE_TYPE,
|
||||||
|
MODULE_SCOPE,
|
||||||
} from "@medusajs/modules-sdk"
|
} from "@medusajs/modules-sdk"
|
||||||
|
import { DataSource, DataSourceOptions } from "typeorm"
|
||||||
|
|
||||||
|
import * as StockLocationModels from "../models"
|
||||||
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import { asValue } from "awilix"
|
||||||
|
|
||||||
export default async (
|
export default async (
|
||||||
{ configModule }: LoaderOptions,
|
{ options, container }: LoaderOptions,
|
||||||
moduleDeclaration?: ConfigurableModuleDeclaration
|
moduleDeclaration?: InternalModuleDeclaration
|
||||||
): Promise<void> => {}
|
): Promise<void> => {
|
||||||
|
if (
|
||||||
|
moduleDeclaration?.scope === MODULE_SCOPE.INTERNAL &&
|
||||||
|
moduleDeclaration.resources === MODULE_RESOURCE_TYPE.SHARED
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const dbData = options?.database as Record<string, string>
|
||||||
|
|
||||||
|
if (!dbData) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_ARGUMENT,
|
||||||
|
`Database config is not present at module config "options.database"`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const entities = Object.values(StockLocationModels)
|
||||||
|
const dataSource = new DataSource({
|
||||||
|
type: dbData.database_type,
|
||||||
|
url: dbData.database_url,
|
||||||
|
database: dbData.database_database,
|
||||||
|
extra: dbData.database_extra || {},
|
||||||
|
schema: dbData.database_schema,
|
||||||
|
entities,
|
||||||
|
logging: dbData.database_logging,
|
||||||
|
} as DataSourceOptions)
|
||||||
|
|
||||||
|
await dataSource.initialize()
|
||||||
|
|
||||||
|
container.register({
|
||||||
|
manager: asValue(dataSource.manager),
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import * as setup from "./schema-migrations/1665749860179-setup"
|
||||||
|
|
||||||
|
export default [setup]
|
||||||
@@ -1,46 +1,4 @@
|
|||||||
import { ConfigModule } from "@medusajs/medusa"
|
import { MigrationInterface, QueryRunner } from "typeorm"
|
||||||
import {
|
|
||||||
createConnection,
|
|
||||||
ConnectionOptions,
|
|
||||||
MigrationInterface,
|
|
||||||
QueryRunner,
|
|
||||||
} from "typeorm"
|
|
||||||
|
|
||||||
import { CONNECTION_NAME } from "../../config"
|
|
||||||
|
|
||||||
export const up = async ({ configModule }: { configModule: ConfigModule }) => {
|
|
||||||
const connection = await createConnection({
|
|
||||||
name: CONNECTION_NAME,
|
|
||||||
type: configModule.projectConfig.database_type,
|
|
||||||
url: configModule.projectConfig.database_url,
|
|
||||||
database: configModule.projectConfig.database_database,
|
|
||||||
schema: configModule.projectConfig.database_schema,
|
|
||||||
extra: configModule.projectConfig.database_extra || {},
|
|
||||||
migrations: [setup1665749860179],
|
|
||||||
logging: true,
|
|
||||||
} as ConnectionOptions)
|
|
||||||
|
|
||||||
await connection.runMigrations()
|
|
||||||
}
|
|
||||||
|
|
||||||
export const down = async ({
|
|
||||||
configModule,
|
|
||||||
}: {
|
|
||||||
configModule: ConfigModule
|
|
||||||
}) => {
|
|
||||||
const connection = await createConnection({
|
|
||||||
name: CONNECTION_NAME,
|
|
||||||
type: configModule.projectConfig.database_type,
|
|
||||||
url: configModule.projectConfig.database_url,
|
|
||||||
database: configModule.projectConfig.database_database,
|
|
||||||
schema: configModule.projectConfig.database_schema,
|
|
||||||
extra: configModule.projectConfig.database_extra || {},
|
|
||||||
migrations: [setup1665749860179],
|
|
||||||
logging: true,
|
|
||||||
} as ConnectionOptions)
|
|
||||||
|
|
||||||
await connection.undoLastMigration({ transaction: "all" })
|
|
||||||
}
|
|
||||||
|
|
||||||
export class setup1665749860179 implements MigrationInterface {
|
export class setup1665749860179 implements MigrationInterface {
|
||||||
name = "setup1665749860179"
|
name = "setup1665749860179"
|
||||||
|
|||||||
@@ -6,18 +6,13 @@ import {
|
|||||||
IEventBusService,
|
IEventBusService,
|
||||||
setMetadata,
|
setMetadata,
|
||||||
StockLocationAddressInput,
|
StockLocationAddressInput,
|
||||||
TransactionBaseService,
|
|
||||||
UpdateStockLocationInput,
|
UpdateStockLocationInput,
|
||||||
} from "@medusajs/medusa"
|
} from "@medusajs/medusa"
|
||||||
|
import { InternalModuleDeclaration } from "@medusajs/modules-sdk"
|
||||||
import {
|
import { SharedContext } from "@medusajs/types"
|
||||||
ConfigurableModuleDeclaration,
|
import { InjectEntityManager, MedusaContext } from "@medusajs/utils"
|
||||||
MODULE_RESOURCE_TYPE,
|
|
||||||
} from "@medusajs/modules-sdk"
|
|
||||||
|
|
||||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
|
|
||||||
import { StockLocation, StockLocationAddress } from "../models"
|
import { StockLocation, StockLocationAddress } from "../models"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
@@ -29,30 +24,22 @@ type InjectedDependencies = {
|
|||||||
* Service for managing stock locations.
|
* Service for managing stock locations.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default class StockLocationService extends TransactionBaseService {
|
export default class StockLocationService {
|
||||||
static Events = {
|
static Events = {
|
||||||
CREATED: "stock-location.created",
|
CREATED: "stock-location.created",
|
||||||
UPDATED: "stock-location.updated",
|
UPDATED: "stock-location.updated",
|
||||||
DELETED: "stock-location.deleted",
|
DELETED: "stock-location.deleted",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected readonly manager_: EntityManager
|
||||||
protected readonly eventBusService_: IEventBusService
|
protected readonly eventBusService_: IEventBusService
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{ eventBusService }: InjectedDependencies,
|
{ eventBusService, manager }: InjectedDependencies,
|
||||||
options?: unknown,
|
options?: unknown,
|
||||||
moduleDeclaration?: ConfigurableModuleDeclaration
|
moduleDeclaration?: InternalModuleDeclaration
|
||||||
) {
|
) {
|
||||||
// @ts-ignore
|
this.manager_ = manager
|
||||||
super(...arguments)
|
|
||||||
|
|
||||||
if (moduleDeclaration?.resources !== MODULE_RESOURCE_TYPE.SHARED) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_ARGUMENT,
|
|
||||||
"At the moment this module can only be used with shared resources"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.eventBusService_ = eventBusService
|
this.eventBusService_ = eventBusService
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,11 +49,13 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
* @param config - Additional configuration for the query.
|
* @param config - Additional configuration for the query.
|
||||||
* @return A list of stock locations.
|
* @return A list of stock locations.
|
||||||
*/
|
*/
|
||||||
|
@InjectEntityManager()
|
||||||
async list(
|
async list(
|
||||||
selector: FilterableStockLocationProps = {},
|
selector: FilterableStockLocationProps = {},
|
||||||
config: FindConfig<StockLocation> = { relations: [], skip: 0, take: 10 }
|
config: FindConfig<StockLocation> = { relations: [], skip: 0, take: 10 },
|
||||||
|
@MedusaContext() context: SharedContext = {}
|
||||||
): Promise<StockLocation[]> {
|
): Promise<StockLocation[]> {
|
||||||
const manager = this.activeManager_
|
const manager = context.transactionManager!
|
||||||
const locationRepo = manager.getRepository(StockLocation)
|
const locationRepo = manager.getRepository(StockLocation)
|
||||||
|
|
||||||
const query = buildQuery(selector, config)
|
const query = buildQuery(selector, config)
|
||||||
@@ -79,11 +68,13 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
* @param config - Additional configuration for the query.
|
* @param config - Additional configuration for the query.
|
||||||
* @return A list of stock locations and the count of matching stock locations.
|
* @return A list of stock locations and the count of matching stock locations.
|
||||||
*/
|
*/
|
||||||
|
@InjectEntityManager()
|
||||||
async listAndCount(
|
async listAndCount(
|
||||||
selector: FilterableStockLocationProps = {},
|
selector: FilterableStockLocationProps = {},
|
||||||
config: FindConfig<StockLocation> = { relations: [], skip: 0, take: 10 }
|
config: FindConfig<StockLocation> = { relations: [], skip: 0, take: 10 },
|
||||||
|
@MedusaContext() context: SharedContext = {}
|
||||||
): Promise<[StockLocation[], number]> {
|
): Promise<[StockLocation[], number]> {
|
||||||
const manager = this.activeManager_
|
const manager = context.transactionManager!
|
||||||
const locationRepo = manager.getRepository(StockLocation)
|
const locationRepo = manager.getRepository(StockLocation)
|
||||||
|
|
||||||
const query = buildQuery(selector, config)
|
const query = buildQuery(selector, config)
|
||||||
@@ -97,9 +88,11 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
* @return The stock location.
|
* @return The stock location.
|
||||||
* @throws If the stock location ID is not definedor the stock location with the given ID was not found.
|
* @throws If the stock location ID is not definedor the stock location with the given ID was not found.
|
||||||
*/
|
*/
|
||||||
|
@InjectEntityManager()
|
||||||
async retrieve(
|
async retrieve(
|
||||||
stockLocationId: string,
|
stockLocationId: string,
|
||||||
config: FindConfig<StockLocation> = {}
|
config: FindConfig<StockLocation> = {},
|
||||||
|
@MedusaContext() context: SharedContext = {}
|
||||||
): Promise<StockLocation> {
|
): Promise<StockLocation> {
|
||||||
if (!isDefined(stockLocationId)) {
|
if (!isDefined(stockLocationId)) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
@@ -108,7 +101,7 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const manager = this.activeManager_
|
const manager = context.transactionManager!
|
||||||
const locationRepo = manager.getRepository(StockLocation)
|
const locationRepo = manager.getRepository(StockLocation)
|
||||||
|
|
||||||
const query = buildQuery({ id: stockLocationId }, config)
|
const query = buildQuery({ id: stockLocationId }, config)
|
||||||
@@ -129,8 +122,13 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
* @param data - The input data for creating a Stock Location.
|
* @param data - The input data for creating a Stock Location.
|
||||||
* @returns The created stock location.
|
* @returns The created stock location.
|
||||||
*/
|
*/
|
||||||
async create(data: CreateStockLocationInput): Promise<StockLocation> {
|
@InjectEntityManager()
|
||||||
return await this.atomicPhase_(async (manager) => {
|
async create(
|
||||||
|
data: CreateStockLocationInput,
|
||||||
|
@MedusaContext() context: SharedContext = {}
|
||||||
|
): Promise<StockLocation> {
|
||||||
|
const manager = context.transactionManager!
|
||||||
|
|
||||||
const locationRepo = manager.getRepository(StockLocation)
|
const locationRepo = manager.getRepository(StockLocation)
|
||||||
|
|
||||||
const loc = locationRepo.create({
|
const loc = locationRepo.create({
|
||||||
@@ -153,7 +151,6 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
if (metadata) {
|
if (metadata) {
|
||||||
loc.metadata = setMetadata(loc, metadata)
|
loc.metadata = setMetadata(loc, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await locationRepo.save(loc)
|
const result = await locationRepo.save(loc)
|
||||||
|
|
||||||
await this.eventBusService_
|
await this.eventBusService_
|
||||||
@@ -163,7 +160,6 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,21 +168,22 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
* @param updateData - The update data for the stock location.
|
* @param updateData - The update data for the stock location.
|
||||||
* @returns The updated stock location.
|
* @returns The updated stock location.
|
||||||
*/
|
*/
|
||||||
|
@InjectEntityManager()
|
||||||
async update(
|
async update(
|
||||||
stockLocationId: string,
|
stockLocationId: string,
|
||||||
updateData: UpdateStockLocationInput
|
updateData: UpdateStockLocationInput,
|
||||||
|
@MedusaContext() context: SharedContext = {}
|
||||||
): Promise<StockLocation> {
|
): Promise<StockLocation> {
|
||||||
return await this.atomicPhase_(async (manager) => {
|
const manager = context.transactionManager!
|
||||||
const locationRepo = manager.getRepository(StockLocation)
|
const locationRepo = manager.getRepository(StockLocation)
|
||||||
|
|
||||||
const item = await this.retrieve(stockLocationId)
|
const item = await this.retrieve(stockLocationId, undefined, context)
|
||||||
|
|
||||||
const { address, ...data } = updateData
|
const { address, ...data } = updateData
|
||||||
|
|
||||||
if (address) {
|
if (address) {
|
||||||
if (item.address_id) {
|
if (item.address_id) {
|
||||||
await this.updateAddress(item.address_id, address)
|
await this.updateAddress(item.address_id, address, context)
|
||||||
} else {
|
} else {
|
||||||
const locAddressRepo = manager.getRepository(StockLocationAddress)
|
const locAddressRepo = manager.getRepository(StockLocationAddress)
|
||||||
const locAddress = locAddressRepo.create(address)
|
const locAddress = locAddressRepo.create(address)
|
||||||
@@ -211,7 +208,6 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return item
|
return item
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -220,10 +216,11 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
* @param address - The update data for the address.
|
* @param address - The update data for the address.
|
||||||
* @returns The updated stock location address.
|
* @returns The updated stock location address.
|
||||||
*/
|
*/
|
||||||
|
@InjectEntityManager()
|
||||||
protected async updateAddress(
|
protected async updateAddress(
|
||||||
addressId: string,
|
addressId: string,
|
||||||
address: StockLocationAddressInput
|
address: StockLocationAddressInput,
|
||||||
|
@MedusaContext() context: SharedContext = {}
|
||||||
): Promise<StockLocationAddress> {
|
): Promise<StockLocationAddress> {
|
||||||
if (!isDefined(addressId)) {
|
if (!isDefined(addressId)) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
@@ -232,7 +229,7 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.atomicPhase_(async (manager) => {
|
const manager = context.transactionManager!
|
||||||
const locationAddressRepo = manager.getRepository(StockLocationAddress)
|
const locationAddressRepo = manager.getRepository(StockLocationAddress)
|
||||||
|
|
||||||
const existingAddress = await locationAddressRepo.findOne({
|
const existingAddress = await locationAddressRepo.findOne({
|
||||||
@@ -253,7 +250,6 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return await locationAddressRepo.save(toSave)
|
return await locationAddressRepo.save(toSave)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -261,8 +257,12 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
* @param id - The ID of the stock location to delete.
|
* @param id - The ID of the stock location to delete.
|
||||||
* @returns An empty promise.
|
* @returns An empty promise.
|
||||||
*/
|
*/
|
||||||
async delete(id: string): Promise<void> {
|
@InjectEntityManager()
|
||||||
return await this.atomicPhase_(async (manager) => {
|
async delete(
|
||||||
|
id: string,
|
||||||
|
@MedusaContext() context: SharedContext = {}
|
||||||
|
): Promise<void> {
|
||||||
|
const manager = context.transactionManager!
|
||||||
const locationRepo = manager.getRepository(StockLocation)
|
const locationRepo = manager.getRepository(StockLocation)
|
||||||
|
|
||||||
await locationRepo.softRemove({ id })
|
await locationRepo.softRemove({ id })
|
||||||
@@ -272,6 +272,5 @@ export default class StockLocationService extends TransactionBaseService {
|
|||||||
.emit(StockLocationService.Events.DELETED, {
|
.emit(StockLocationService.Events.DELETED, {
|
||||||
id,
|
id,
|
||||||
})
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": [
|
"lib": ["es2020"],
|
||||||
"es5",
|
"target": "es2020",
|
||||||
"es6",
|
|
||||||
"es2019"
|
|
||||||
],
|
|
||||||
"target": "es5",
|
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
@@ -19,12 +15,11 @@
|
|||||||
"strictFunctionTypes": true,
|
"strictFunctionTypes": true,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true
|
||||||
"downlevelIteration": true // to use ES5 specific tooling
|
|
||||||
},
|
},
|
||||||
"include": ["./src/**/*", "index.d.ts"],
|
"include": ["src"],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"./dist/**/*",
|
"dist",
|
||||||
"./src/**/__tests__",
|
"./src/**/__tests__",
|
||||||
"./src/**/__mocks__",
|
"./src/**/__mocks__",
|
||||||
"node_modules"
|
"node_modules"
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "@medusajs/types",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Medusa Types definition",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/medusajs/medusa",
|
||||||
|
"directory": "packages/types"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"author": "Medusa",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"cross-env": "^5.2.1",
|
||||||
|
"typeorm": "^0.3.11",
|
||||||
|
"typescript": "^4.4.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"prepare": "cross-env NODE_ENV=production yarn run build",
|
||||||
|
"build": "tsc --build",
|
||||||
|
"test": "exit 0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./shared-context"
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
|
||||||
|
export type SharedContext = {
|
||||||
|
transactionManager?: EntityManager
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["es2020"],
|
||||||
|
"target": "es2020",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"declaration": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"./src/**/__tests__",
|
||||||
|
"./src/**/__mocks__",
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
module.exports = {
|
||||||
|
globals: {
|
||||||
|
"ts-jest": {
|
||||||
|
tsConfig: "tsconfig.json",
|
||||||
|
isolatedModules: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transform: {
|
||||||
|
"^.+\\.[jt]s?$": "ts-jest",
|
||||||
|
},
|
||||||
|
testEnvironment: `node`,
|
||||||
|
moduleFileExtensions: [`js`, `ts`],
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "@medusajs/utils",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Medusa utilities functions shared by Medusa core and Modules",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/medusajs/medusa",
|
||||||
|
"directory": "packages/utils"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"author": "Medusa",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@medusajs/types": "*",
|
||||||
|
"cross-env": "^5.2.1",
|
||||||
|
"jest": "^25.5.4",
|
||||||
|
"ts-jest": "^25.5.1",
|
||||||
|
"typeorm": "^0.3.11",
|
||||||
|
"typescript": "^4.4.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@medusajs/types": "^0.0.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"prepare": "cross-env NODE_ENV=production yarn run build",
|
||||||
|
"build": "tsc --build",
|
||||||
|
"test": "jest --passWithNoTests src"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
export function MedusaContext() {
|
||||||
|
return function (
|
||||||
|
target: any,
|
||||||
|
propertyKey: string | symbol,
|
||||||
|
parameterIndex: number
|
||||||
|
) {
|
||||||
|
if (!target.MedusaContext_) {
|
||||||
|
target.MedusaContext_ = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyKey in target.MedusaContext_) {
|
||||||
|
throw new Error(
|
||||||
|
`Only one MedusaContext is allowed on method "${String(propertyKey)}".`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
target.MedusaContext_[propertyKey] = parameterIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./context-parameter"
|
||||||
|
export * from "./inject-entity-manager"
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { SharedContext } from "@medusajs/types"
|
||||||
|
|
||||||
|
export function InjectEntityManager(
|
||||||
|
managerProperty = "manager_"
|
||||||
|
): MethodDecorator {
|
||||||
|
return function (
|
||||||
|
target: any,
|
||||||
|
propertyKey: string | symbol,
|
||||||
|
descriptor: any
|
||||||
|
): void {
|
||||||
|
if (!target.MedusaContext_) {
|
||||||
|
throw new Error(
|
||||||
|
`To apply @InjectEntityManager you have to flag a parameter using @MedusaContext`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalMethod = descriptor.value
|
||||||
|
|
||||||
|
const argIndex = target.MedusaContext_[propertyKey]
|
||||||
|
descriptor.value = async function (...args: any[]) {
|
||||||
|
const context: SharedContext = args[argIndex] ?? {}
|
||||||
|
|
||||||
|
if (context?.transactionManager) {
|
||||||
|
return await originalMethod.apply(this, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this[managerProperty].transaction(
|
||||||
|
async (transactionManager) => {
|
||||||
|
args[argIndex] = args[argIndex] ?? {}
|
||||||
|
args[argIndex].transactionManager = transactionManager
|
||||||
|
|
||||||
|
return await originalMethod.apply(this, args)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./decorators"
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["es2020"],
|
||||||
|
"target": "es2020",
|
||||||
|
"outDir": "./dist",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"declaration": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"./src/**/__tests__",
|
||||||
|
"./src/**/__mocks__",
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -5742,6 +5742,7 @@ __metadata:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@medusajs/medusa": "*"
|
"@medusajs/medusa": "*"
|
||||||
"@medusajs/modules-sdk": "*"
|
"@medusajs/modules-sdk": "*"
|
||||||
|
awilix: ^8.0.0
|
||||||
cross-env: ^5.2.1
|
cross-env: ^5.2.1
|
||||||
jest: ^25.5.4
|
jest: ^25.5.4
|
||||||
ts-jest: ^25.5.1
|
ts-jest: ^25.5.1
|
||||||
@@ -5749,7 +5750,6 @@ __metadata:
|
|||||||
typescript: ^4.4.4
|
typescript: ^4.4.4
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@medusajs/medusa": 1.7.13
|
"@medusajs/medusa": 1.7.13
|
||||||
typeorm: ^0.3.11
|
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
@@ -5919,7 +5919,9 @@ __metadata:
|
|||||||
dependencies:
|
dependencies:
|
||||||
awilix: ^8.0.0
|
awilix: ^8.0.0
|
||||||
cross-env: ^5.2.1
|
cross-env: ^5.2.1
|
||||||
|
glob: 7.1.6
|
||||||
jest: ^25.5.4
|
jest: ^25.5.4
|
||||||
|
medusa-core-utils: ^1.1.39
|
||||||
medusa-telemetry: ^0.0.16
|
medusa-telemetry: ^0.0.16
|
||||||
resolve-cwd: ^3.0.0
|
resolve-cwd: ^3.0.0
|
||||||
ts-jest: ^25.5.1
|
ts-jest: ^25.5.1
|
||||||
@@ -5982,6 +5984,9 @@ __metadata:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@medusajs/medusa": ^1.7.7
|
"@medusajs/medusa": ^1.7.7
|
||||||
"@medusajs/modules-sdk": "*"
|
"@medusajs/modules-sdk": "*"
|
||||||
|
"@medusajs/types": "*"
|
||||||
|
"@medusajs/utils": ^0.0.1
|
||||||
|
awilix: ^8.0.0
|
||||||
cross-env: ^5.2.1
|
cross-env: ^5.2.1
|
||||||
jest: ^25.5.4
|
jest: ^25.5.4
|
||||||
ts-jest: ^25.5.1
|
ts-jest: ^25.5.1
|
||||||
@@ -5989,7 +5994,32 @@ __metadata:
|
|||||||
typescript: ^4.4.4
|
typescript: ^4.4.4
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
"@medusajs/medusa": ^1.7.7
|
"@medusajs/medusa": ^1.7.7
|
||||||
|
"@medusajs/types": ^0.0.1
|
||||||
|
languageName: unknown
|
||||||
|
linkType: soft
|
||||||
|
|
||||||
|
"@medusajs/types@*, @medusajs/types@workspace:packages/types":
|
||||||
|
version: 0.0.0-use.local
|
||||||
|
resolution: "@medusajs/types@workspace:packages/types"
|
||||||
|
dependencies:
|
||||||
|
cross-env: ^5.2.1
|
||||||
typeorm: ^0.3.11
|
typeorm: ^0.3.11
|
||||||
|
typescript: ^4.4.4
|
||||||
|
languageName: unknown
|
||||||
|
linkType: soft
|
||||||
|
|
||||||
|
"@medusajs/utils@^0.0.1, @medusajs/utils@workspace:packages/utils":
|
||||||
|
version: 0.0.0-use.local
|
||||||
|
resolution: "@medusajs/utils@workspace:packages/utils"
|
||||||
|
dependencies:
|
||||||
|
"@medusajs/types": "*"
|
||||||
|
cross-env: ^5.2.1
|
||||||
|
jest: ^25.5.4
|
||||||
|
ts-jest: ^25.5.1
|
||||||
|
typeorm: ^0.3.11
|
||||||
|
typescript: ^4.4.4
|
||||||
|
peerDependencies:
|
||||||
|
"@medusajs/types": ^0.0.1
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
@@ -26283,14 +26313,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"joi-objectid@npm:^3.0.1":
|
"joi@npm:^17.4.2":
|
||||||
version: 3.0.1
|
|
||||||
resolution: "joi-objectid@npm:3.0.1"
|
|
||||||
checksum: 4820534ddff8779a7128661b8ed21bc7d552d53a8def60095db6e38f410c5893da21d7b91211d3af5dd5f6e4eda90a2f11b24a2e69efcef74c1a8249240ad83d
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"joi@npm:^17.3.0, joi@npm:^17.4.2":
|
|
||||||
version: 17.6.0
|
version: 17.6.0
|
||||||
resolution: "joi@npm:17.6.0"
|
resolution: "joi@npm:17.6.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -28185,12 +28208,8 @@ __metadata:
|
|||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "medusa-core-utils@workspace:packages/medusa-core-utils"
|
resolution: "medusa-core-utils@workspace:packages/medusa-core-utils"
|
||||||
dependencies:
|
dependencies:
|
||||||
class-transformer: ^0.5.1
|
|
||||||
class-validator: ^0.13.2
|
|
||||||
cross-env: ^5.2.1
|
cross-env: ^5.2.1
|
||||||
jest: ^25.5.4
|
jest: ^25.5.4
|
||||||
joi: ^17.3.0
|
|
||||||
joi-objectid: ^3.0.1
|
|
||||||
ts-jest: ^25.5.1
|
ts-jest: ^25.5.1
|
||||||
typescript: ^4.4.4
|
typescript: ^4.4.4
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
|
|||||||
Reference in new issue
Block a user