feat(medusa, modules-sdk, types, utils): Re work modules loading and remove legacy functions (#5496)

This commit is contained in:
Adrien de Peretti
2023-11-02 17:59:13 +01:00
committed by GitHub
parent ca411e54eb
commit 154c9b43bd
39 changed files with 616 additions and 393 deletions

View File

@@ -0,0 +1,8 @@
---
"@medusajs/medusa": patch
"@medusajs/modules-sdk": patch
"@medusajs/types": patch
"@medusajs/utils": patch
---
feat(medusa, modules-sdk, types, utils): Re work modules loading and remove legacy functions

View File

@@ -14,15 +14,17 @@ async function bootstrapApp({ cwd, env = {} } = {}) {
const loaders = require("@medusajs/medusa/dist/loaders").default
const { container, dbConnection, pgConnection } = await loaders({
directory: path.resolve(cwd || process.cwd()),
expressApp: app,
isTest: false,
})
const { container, dbConnection, pgConnection, disposeResources } =
await loaders({
directory: path.resolve(cwd || process.cwd()),
expressApp: app,
isTest: false,
})
const PORT = await getPort()
return {
disposeResources,
container,
db: dbConnection,
pgConnection,
@@ -55,6 +57,7 @@ module.exports = {
expressServer.close(),
db?.destroy(),
pgConnection?.context?.destroy(),
container.dispose(),
])
if (typeof global !== "undefined" && global?.gc) {

View File

@@ -9,6 +9,7 @@ module.exports = {
`/www/`,
`/dist/`,
`/node_modules/`,
"<rootDir>/node_modules",
`__tests__/fixtures`,
`__testfixtures__`,
`.cache`,

View File

@@ -13,7 +13,7 @@ eventEmitter.setMaxListeners(Infinity)
// eslint-disable-next-line max-len
export default class LocalEventBusService extends AbstractEventBusModuleService {
protected readonly logger_: Logger
protected readonly logger_?: Logger
protected readonly eventEmitter_: EventEmitter
constructor({ logger }: MedusaContainer & InjectedDependencies) {
@@ -53,7 +53,7 @@ export default class LocalEventBusService extends AbstractEventBusModuleService
event.eventName
)
this.logger_.info(
this.logger_?.info(
`Processing ${event.eventName} which has ${eventListenersCount} subscribers`
)
@@ -73,7 +73,7 @@ export default class LocalEventBusService extends AbstractEventBusModuleService
// @ts-ignore
await subscriber(...args)
} catch (e) {
this.logger_.error(
this.logger_?.error(
`An error occurred while processing ${event.toString()}: ${e}`
)
}

View File

@@ -46,7 +46,7 @@ export function getMigration(
const generator = orm.getSchemaGenerator()
if (hasTable) {
const updateSql = await generator.getUpdateSchemaSQL()
/* const updateSql = await generator.getUpdateSchemaSQL()
const entityUpdates = updateSql
.split(";")
.map((sql) => sql.trim())
@@ -65,7 +65,10 @@ export function getMigration(
}
} else {
logger.info(`Skipping "${tableName}" migration.`)
}
}*/
logger.info(
`Link module "${serviceName}" table update skipped because the table already exists. Please write your own migration if needed.`
)
} else {
try {
await generator.createSchema()

View File

@@ -6,20 +6,72 @@ import { composeTableName } from "./compose-link-name"
export function generateGraphQLSchema(
joinerConfig: ModuleJoinerConfig,
primary: ModuleJoinerRelationship,
foreign: ModuleJoinerRelationship
foreign: ModuleJoinerRelationship,
{ logger }: { logger } = { logger: console }
) {
const fieldNames = primary.foreignKey.split(",").concat(foreign.foreignKey)
let fieldNames!: string[]
let entityName!: string
const entityName = toPascalCase(
"Link_" +
(joinerConfig.databaseConfig?.tableName ??
composeTableName(
primary.serviceName,
primary.foreignKey,
foreign.serviceName,
foreign.foreignKey
))
)
if (!joinerConfig.isReadOnlyLink) {
fieldNames = primary.foreignKey.split(",").concat(foreign.foreignKey)
entityName = toPascalCase(
"Link_" +
(joinerConfig.databaseConfig?.tableName ??
composeTableName(
primary.serviceName,
primary.foreignKey,
foreign.serviceName,
foreign.foreignKey
))
)
}
let typeDef = ""
for (const extend of joinerConfig.extends ?? []) {
const extendedModule = MedusaModule.getModuleInstance(extend.serviceName)
if (!extendedModule && !extend.relationship.isInternalService) {
throw new Error(
`Module ${extend.serviceName} not found. Please verify that the module is configured and installed, also the module must be loaded before the link modules.`
)
}
const extJoinerConfig = MedusaModule.getJoinerConfig(
extend.relationship.serviceName
)
let extendedEntityName =
extJoinerConfig?.linkableKeys?.[extend.relationship.foreignKey]!
if (!extendedEntityName && (!primary || !foreign)) {
logger.warn(
`Link modules schema: No linkable key found for ${extend.relationship.foreignKey} on module ${extend.relationship.serviceName}.`
)
continue
}
const fieldName = camelToSnakeCase(
lowerCaseFirst(extend.relationship.alias)
)
let type = extend.relationship.isList ? `[${entityName}]` : entityName
if (extJoinerConfig?.isReadOnlyLink) {
type = extend.relationship.isList
? `[${extendedEntityName}]`
: extendedEntityName
}
typeDef += `
extend type ${extend.serviceName} {
${fieldName}: ${type}
}
`
}
if (joinerConfig.isReadOnlyLink) {
return typeDef
}
// Pivot table fields
const fields = fieldNames.reduce((acc, curr) => {
@@ -48,7 +100,7 @@ export function generateGraphQLSchema(
composeTableName(foreign.serviceName)
)}`
let typeDef = `
typeDef += `
type ${entityName} {
${(Object.entries(fields) as any)
.map(
@@ -66,36 +118,6 @@ export function generateGraphQLSchema(
}
`
for (const extend of joinerConfig.extends ?? []) {
const extendedModule = MedusaModule.getModuleInstance(extend.serviceName)
if (!extendedModule && !extend.relationship.isInternalService) {
throw new Error(
`Module ${extend.serviceName} not found. Please verify that the module is configured and installed, also the module must be loaded before the link modules.`
)
}
const joinerConfig = MedusaModule.getJoinerConfig(extend.serviceName)
let extendedEntityName =
joinerConfig?.linkableKeys?.[extend.relationship.primaryKey]!
if (!extendedEntityName) {
continue
}
extendedEntityName = toPascalCase(extendedEntityName)
const linkTableFieldName = camelToSnakeCase(
lowerCaseFirst(extend.relationship.alias)
)
const type = extend.relationship.isList ? `[${entityName}]` : entityName
typeDef += `
extend type ${extendedEntityName} {
${linkTableFieldName}: ${type}
}
`
}
return typeDef
}

View File

@@ -1,7 +1,9 @@
import { buildRegexpIfValid } from "./build-regexp-if-valid";
import { buildRegexpIfValid } from "./build-regexp-if-valid"
export function parseCorsOrigins(str: string): (string | RegExp)[] {
return str.split(",").map((subStr) => {
return buildRegexpIfValid(subStr) ?? subStr
})
return !str
? []
: str.split(",").map((subStr) => {
return buildRegexpIfValid(subStr) ?? subStr
})
}

View File

@@ -1,4 +1,3 @@
import { ModulesHelper } from "@medusajs/modules-sdk"
import { FlagRouter } from "@medusajs/utils"
import { defaultRelationsExtended } from "."
import {
@@ -7,6 +6,7 @@ import {
StoreService,
} from "../../../../services"
import { ExtendedStoreDTO } from "../../../../types/store"
import { MedusaModule } from "@medusajs/modules-sdk"
/**
* @oas [get] /admin/store
@@ -62,7 +62,6 @@ export default async (req, res) => {
const storeService: StoreService = req.scope.resolve("storeService")
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
const modulesHelper: ModulesHelper = req.scope.resolve("modulesHelper")
const paymentProviderService: PaymentProviderService = req.scope.resolve(
"paymentProviderService"
@@ -80,7 +79,16 @@ export default async (req, res) => {
})) as ExtendedStoreDTO
data.feature_flags = featureFlagRouter.listFlags()
data.modules = modulesHelper.modules
data.modules = MedusaModule.getLoadedModules()
.map((loadedModule) => {
return Object.entries(loadedModule).map(([key, service]) => {
return {
module: key,
resolution: service.__definition.defaultPackage,
}
})
})
.flat()
const paymentProviders = await paymentProviderService.list()
const fulfillmentProviders = await fulfillmentProviderService.list()

View File

@@ -12,23 +12,24 @@ import {
} from "../services"
import getMigrations, { getModuleSharedResources } from "./utils/get-migrations"
import { ConfigModule } from "../types/global"
import { CreateProductCategoryInput } from "../types/product-category"
import { CreateProductInput } from "../types/product"
import { IPricingModuleService } from "@medusajs/types"
import express from "express"
import fs from "fs"
import { sync as existsSync } from "fs-exists-cached"
import { getConfigFile } from "medusa-core-utils"
import { track } from "medusa-telemetry"
import path from "path"
import loaders from "../loaders"
import { handleConfigError } from "../loaders/config"
import featureFlagLoader from "../loaders/feature-flags"
import IsolatePricingDomainFeatureFlag from "../loaders/feature-flags/isolate-pricing-domain"
import Logger from "../loaders/logger"
import PublishableApiKeyService from "../services/publishable-api-key"
import { SalesChannel } from "../models"
import { sync as existsSync } from "fs-exists-cached"
import express from "express"
import featureFlagLoader from "../loaders/feature-flags"
import fs from "fs"
import { getConfigFile } from "medusa-core-utils"
import { handleConfigError } from "../loaders/config"
import loaders from "../loaders"
import path from "path"
import { track } from "medusa-telemetry"
import PublishableApiKeyService from "../services/publishable-api-key"
import { ConfigModule } from "../types/global"
import { CreateProductInput } from "../types/product"
import { CreateProductCategoryInput } from "../types/product-category"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
type SeedOptions = {
directory: string
@@ -122,7 +123,7 @@ const seed = async function ({ directory, migrate, seedFile }: SeedOptions) {
"shippingProfileService"
)
const pricingModuleService: IPricingModuleService = container.resolve(
"pricingModuleService"
ModuleRegistrationName.PRICING
)
/* eslint-enable */

View File

@@ -1,4 +1,4 @@
import { MedusaModule, registerModules } from "@medusajs/modules-sdk"
import { MedusaModule, registerMedusaModule } from "@medusajs/modules-sdk"
import fs from "fs"
import { sync as existsSync } from "fs-exists-cached"
import glob from "glob"
@@ -96,8 +96,13 @@ function resolvePlugin(pluginName) {
export function getInternalModules(configModule) {
const modules = []
const moduleResolutions = {}
const moduleResolutions = registerModules(configModule.modules)
Object.entries(configModule.modules ?? {}).forEach(([moduleKey, module]) => {
moduleResolutions[moduleKey] = registerMedusaModule(moduleKey, module)[
moduleKey
]
})
for (const moduleResolution of Object.values(moduleResolutions)) {
if (
@@ -256,7 +261,12 @@ export const getModuleSharedResources = (configModule, featureFlagsRouter) => {
}
export const runIsolatedModulesMigration = async (configModule) => {
const moduleResolutions = registerModules(configModule.modules)
const moduleResolutions = {}
Object.entries(configModule.modules ?? {}).forEach(([moduleKey, module]) => {
moduleResolutions[moduleKey] = registerMedusaModule(moduleKey, module)[
moduleKey
]
})
for (const moduleResolution of Object.values(moduleResolutions)) {
if (
@@ -276,7 +286,12 @@ export const runIsolatedModulesMigration = async (configModule) => {
}
export const revertIsolatedModulesMigration = async (configModule) => {
const moduleResolutions = registerModules(configModule.modules)
const moduleResolutions = {}
Object.entries(configModule.modules ?? {}).forEach(([moduleKey, module]) => {
moduleResolutions[moduleKey] = registerMedusaModule(moduleKey, module)[
moduleKey
]
})
for (const moduleResolution of Object.values(moduleResolutions)) {
if (

View File

@@ -3,7 +3,6 @@ import {
moduleLoader,
ModulesDefinition,
registerMedusaModule,
registerModules,
} from "@medusajs/modules-sdk"
import { asValue, createContainer } from "awilix"
import express from "express"
@@ -19,7 +18,6 @@ import passportLoader from "../loaders/passport"
import repositories from "../loaders/repositories"
import servicesLoader from "../loaders/services"
import strategiesLoader from "../loaders/strategies"
import modules from "../modules-config"
const adminSessionOpts = {
cookieName: "session",
@@ -33,12 +31,13 @@ const clientSessionOpts = {
secret: "test",
}
const moduleResolutions = registerModules({})
// Load non legacy modules
Object.keys(modules).map((moduleKey) => {
const moduleResolutions = {}
Object.entries(ModulesDefinition).forEach(([moduleKey, module]) => {
moduleResolutions[moduleKey] = registerMedusaModule(
moduleKey,
ModulesDefinition[moduleKey]
module.defaultModuleDeclaration,
undefined,
module
)[moduleKey]
})
@@ -84,7 +83,7 @@ container.register("modulesHelper", asValue(moduleHelper))
container.register("configModule", asValue(config))
container.register({
logger: asValue({
error: () => { },
error: () => {},
}),
manager: asValue(MockManager),
})
@@ -146,7 +145,10 @@ export async function request(method, url, opts = {}) {
headers.Cookie = headers.Cookie || ""
if (opts.adminSession) {
const token = jwt.sign(
{ user_id: opts.adminSession.userId || opts.adminSession.jwt?.userId, domain: "admin" },
{
user_id: opts.adminSession.userId || opts.adminSession.jwt?.userId,
domain: "admin",
},
config.projectConfig.jwt_secret
)
@@ -154,7 +156,11 @@ export async function request(method, url, opts = {}) {
}
if (opts.clientSession) {
const token = jwt.sign(
{ customer_id: opts.clientSession.customer_id || opts.clientSession.jwt?.customer_id, domain: "store" },
{
customer_id:
opts.clientSession.customer_id || opts.clientSession.jwt?.customer_id,
domain: "store",
},
config.projectConfig.jwt_secret
)

View File

@@ -1,4 +1,8 @@
import { moduleLoader, registerModules } from "@medusajs/modules-sdk"
import {
InternalModuleDeclaration,
ModulesDefinition,
} from "@medusajs/modules-sdk"
import { MODULE_RESOURCE_TYPE } from "@medusajs/types"
import { Express, NextFunction, Request, Response } from "express"
import databaseLoader, { dataSource } from "./database"
@@ -17,10 +21,8 @@ import loadConfig from "./config"
import defaultsLoader from "./defaults"
import expressLoader from "./express"
import featureFlagsLoader from "./feature-flags"
import IsolatePricingDomainFeatureFlag from "./feature-flags/isolate-pricing-domain"
import IsolateProductDomainFeatureFlag from "./feature-flags/isolate-product-domain"
import Logger from "./logger"
import loadMedusaApp from "./medusa-app"
import loadMedusaApp, { mergeDefaultModules } from "./medusa-app"
import modelsLoader from "./models"
import passportLoader from "./passport"
import pgConnectionLoader from "./pg-connection"
@@ -37,6 +39,34 @@ type Options = {
isTest: boolean
}
async function loadLegacyModulesEntities(configModules, container) {
for (const [moduleName, moduleConfig] of Object.entries(configModules)) {
const definition = ModulesDefinition[moduleName]
if (!definition.isLegacy) {
continue
}
if (
(moduleConfig as InternalModuleDeclaration).resources ===
MODULE_RESOURCE_TYPE.SHARED ||
(definition.defaultModuleDeclaration as InternalModuleDeclaration)
.resources === MODULE_RESOURCE_TYPE.SHARED
) {
const module = await import(
(moduleConfig as InternalModuleDeclaration).resolve ??
(definition.defaultPackage as string)
)
if (module.default?.models) {
module.default.models.map((model) =>
container.registerAdd("db_entities", asValue(model))
)
}
}
}
}
export default async ({
directory: rootDirectory,
expressApp,
@@ -98,16 +128,8 @@ export default async ({
const pgConnection = await pgConnectionLoader({ container, configModule })
const modulesActivity = Logger.activity(`Initializing modules${EOL}`)
track("MODULES_INIT_STARTED")
await moduleLoader({
container,
moduleResolutions: registerModules(configModule?.modules),
logger: Logger,
})
const modAct = Logger.success(modulesActivity, "Modules initialized") || {}
track("MODULES_INIT_COMPLETED", { duration: modAct.duration })
const configModules = mergeDefaultModules(configModule.modules)
await loadLegacyModulesEntities(configModules, container)
const dbActivity = Logger.activity(`Initializing database${EOL}`)
track("DATABASE_INIT_STARTED")
@@ -129,13 +151,6 @@ export default async ({
})
container.register("remoteQuery", asValue(null)) // ensure remoteQuery is always registered
// Only load non legacy modules, the legacy modules (non migrated yet) are retrieved by the registerModule above
if (
featureFlagRouter.isFeatureEnabled(IsolateProductDomainFeatureFlag.key) ||
featureFlagRouter.isFeatureEnabled(IsolatePricingDomainFeatureFlag.key)
) {
await loadMedusaApp({ configModule, container })
}
const servicesActivity = Logger.activity(`Initializing services${EOL}`)
track("SERVICES_INIT_STARTED")
@@ -143,6 +158,18 @@ export default async ({
const servAct = Logger.success(servicesActivity, "Services initialized") || {}
track("SERVICES_INIT_COMPLETED", { duration: servAct.duration })
const modulesActivity = Logger.activity(`Initializing modules${EOL}`)
track("MODULES_INIT_STARTED")
// Move before services init once all modules are migrated and do not rely on core resources anymore
await loadMedusaApp({
configModule,
container,
})
const modAct = Logger.success(modulesActivity, "Modules initialized") || {}
track("MODULES_INIT_COMPLETED", { duration: modAct.duration })
const expActivity = Logger.activity(`Initializing express${EOL}`)
track("EXPRESS_INIT_STARTED")
await expressLoader({ app: expressApp, configModule })
@@ -196,5 +223,10 @@ export default async ({
Logger.success(searchActivity, "Indexing event emitted") || {}
track("SEARCH_ENGINE_INDEXING_COMPLETED", { duration: searchAct.duration })
return { container, dbConnection, app: expressApp, pgConnection }
return {
container,
dbConnection,
app: expressApp,
pgConnection,
}
}

View File

@@ -1,26 +1,51 @@
import { CommonTypes, MedusaContainer } from "@medusajs/types"
import {
CommonTypes,
InternalModuleDeclaration,
MedusaContainer,
ModuleDefinition,
} from "@medusajs/types"
import {
MedusaApp,
MedusaAppOutput,
ModulesDefinition,
} from "@medusajs/modules-sdk"
import { ContainerRegistrationKeys } from "@medusajs/utils"
import { ContainerRegistrationKeys, isObject } from "@medusajs/utils"
import { asValue } from "awilix"
import { joinerConfig } from "../joiner-config"
import { mergeModulesConfig } from "../utils/merge-modules-config"
import modulesConfig from "../modules-config"
import { remoteQueryFetchData } from ".."
export function mergeDefaultModules(
modulesConfig: CommonTypes.ConfigModule["modules"]
) {
const defaultModules = Object.values(ModulesDefinition).filter(
(definition: ModuleDefinition) => {
return !!definition.defaultPackage
}
)
const configModules = { ...modulesConfig } ?? {}
for (const defaultModule of defaultModules as ModuleDefinition[]) {
configModules[defaultModule.key] ??= defaultModule.defaultModuleDeclaration
}
return configModules
}
export const loadMedusaApp = async (
{
configModule,
container,
}: { configModule: CommonTypes.ConfigModule; container: MedusaContainer },
}: {
configModule: {
modules?: CommonTypes.ConfigModule["modules"]
projectConfig: CommonTypes.ConfigModule["projectConfig"]
}
container: MedusaContainer
},
config = { registerInContainer: true }
): Promise<MedusaAppOutput> => {
mergeModulesConfig(configModule.modules ?? {}, modulesConfig)
const injectedDependencies = {
[ContainerRegistrationKeys.PG_CONNECTION]: container.resolve(
ContainerRegistrationKeys.PG_CONNECTION
@@ -34,8 +59,34 @@ export const loadMedusaApp = async (
},
}
container.register(ContainerRegistrationKeys.REMOTE_QUERY, asValue(undefined))
container.register(ContainerRegistrationKeys.REMOTE_LINK, asValue(undefined))
const configModules = mergeDefaultModules(configModule.modules)
// Apply default options to legacy modules
for (const moduleKey of Object.keys(configModules)) {
if (!ModulesDefinition[moduleKey].isLegacy) {
continue
}
if (isObject(configModules[moduleKey])) {
;(
configModules[moduleKey] as Partial<InternalModuleDeclaration>
).options ??= {
database: {
type: "postgres",
url: configModule.projectConfig.database_url,
extra: configModule.projectConfig.database_extra,
schema: configModule.projectConfig.database_schema,
logging: configModule.projectConfig.database_logging,
},
}
}
}
const medusaApp = await MedusaApp({
modulesConfig,
modulesConfig: configModules,
servicesConfig: joinerConfig,
remoteFetchData: remoteQueryFetchData(container),
sharedContainer: container,
@@ -48,18 +99,25 @@ export const loadMedusaApp = async (
}
container.register("remoteLink", asValue(medusaApp.link))
container.register(
ContainerRegistrationKeys.REMOTE_QUERY,
asValue(medusaApp.query)
)
const { query, modules } = medusaApp
// Medusa app load all non legacy modules, so we need to register them in the container since they are into their own container
// We might decide to do it elsewhere but for now I think it is fine
for (const [serviceKey, moduleService] of Object.entries(modules)) {
for (const [serviceKey, moduleService] of Object.entries(medusaApp.modules)) {
container.register(
ModulesDefinition[serviceKey].registrationName,
asValue(moduleService)
)
}
container.register("remoteQuery", asValue(query))
// Register all unresolved modules as undefined to be present in the container with undefined value by defaul
// but still resolvable
for (const [, moduleDefinition] of Object.entries(ModulesDefinition)) {
if (!container.hasRegistration(moduleDefinition.registrationName)) {
container.register(moduleDefinition.registrationName, asValue(undefined))
}
}
return medusaApp
}

View File

@@ -1,5 +1,5 @@
import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils"
import { AwilixContainer, asValue } from "awilix"
import { asValue, AwilixContainer } from "awilix"
import { ConfigModule } from "../types/global"
type Options = {
@@ -9,7 +9,7 @@ type Options = {
export default async ({ container, configModule }: Options): Promise<any> => {
if (container.hasRegistration(ContainerRegistrationKeys.PG_CONNECTION)) {
return
return container.resolve(ContainerRegistrationKeys.PG_CONNECTION)
}
// Share a knex connection to be consumed by the shared modules

View File

@@ -2,6 +2,7 @@ import { MedusaModuleConfig, Modules } from "@medusajs/modules-sdk"
const modules: MedusaModuleConfig = {
[Modules.PRODUCT]: true,
[Modules.PRICING]: true,
}
export default modules

View File

@@ -27,14 +27,17 @@ export default class EventBusService
protected readonly config_: ConfigModule
protected readonly stagedJobService_: StagedJobService
// eslint-disable-next-line max-len
protected readonly eventBusModuleService_: EventBusTypes.IEventBusModuleService
protected get eventBusModuleService_(): EventBusTypes.IEventBusModuleService {
return this.__container__.eventBusModuleService
}
protected readonly logger_: Logger
protected shouldEnqueuerRun: boolean
protected enqueue_: Promise<void>
constructor(
{ stagedJobService, eventBusModuleService, logger }: InjectedDependencies,
{ stagedJobService, logger }: InjectedDependencies,
config,
isSingleton = true
) {
@@ -43,7 +46,6 @@ export default class EventBusService
this.logger_ = logger
this.config_ = config
this.eventBusModuleService_ = eventBusModuleService
this.stagedJobService_ = stagedJobService
if (process.env.NODE_ENV !== "test" && isSingleton) {

View File

@@ -70,7 +70,10 @@ export default class OrderEditService extends TransactionBaseService {
protected readonly taxProviderService_: TaxProviderService
protected readonly lineItemAdjustmentService_: LineItemAdjustmentService
protected readonly orderEditItemChangeService_: OrderEditItemChangeService
protected readonly inventoryService_: IInventoryService | undefined
protected get inventoryService_(): IInventoryService | undefined {
return this.__container__.inventoryService
}
constructor({
orderEditRepository,
@@ -82,7 +85,6 @@ export default class OrderEditService extends TransactionBaseService {
orderEditItemChangeService,
lineItemAdjustmentService,
taxProviderService,
inventoryService,
}: InjectedDependencies) {
// eslint-disable-next-line prefer-rest-params
super(arguments[0])
@@ -96,7 +98,6 @@ export default class OrderEditService extends TransactionBaseService {
this.orderEditItemChangeService_ = orderEditItemChangeService
this.lineItemAdjustmentService_ = lineItemAdjustmentService
this.taxProviderService_ = taxProviderService
this.inventoryService_ = inventoryService
}
async retrieve(

View File

@@ -2,7 +2,6 @@ import {
CalculatedPriceSetDTO,
IPricingModuleService,
PriceSetMoneyAmountDTO,
RemoteJoinerQuery,
RemoteQueryFunction,
} from "@medusajs/types"
import { FlagRouter, removeNullish } from "@medusajs/utils"
@@ -56,8 +55,13 @@ class PricingService extends TransactionBaseService {
protected readonly priceSelectionStrategy: IPriceSelectionStrategy
protected readonly productVariantService: ProductVariantService
protected readonly featureFlagRouter: FlagRouter
protected readonly pricingModuleService: IPricingModuleService
protected readonly remoteQuery: RemoteQueryFunction
protected get pricingModuleService(): IPricingModuleService {
return this.__container__.pricingModuleService
}
protected get remoteQuery(): RemoteQueryFunction {
return this.__container__.remoteQuery
}
constructor({
productVariantService,
@@ -65,8 +69,6 @@ class PricingService extends TransactionBaseService {
regionService,
priceSelectionStrategy,
featureFlagRouter,
remoteQuery,
pricingModuleService,
}: InjectedDependencies) {
// eslint-disable-next-line prefer-rest-params
super(arguments[0])
@@ -76,8 +78,6 @@ class PricingService extends TransactionBaseService {
this.priceSelectionStrategy = priceSelectionStrategy
this.productVariantService = productVariantService
this.featureFlagRouter = featureFlagRouter
this.pricingModuleService = pricingModuleService
this.remoteQuery = remoteQuery
}
/**

View File

@@ -1,6 +1,5 @@
import { EntityManager, In } from "typeorm"
import {
ICacheService,
IEventBusService,
IInventoryService,
InventoryItemDTO,
@@ -42,17 +41,20 @@ class ProductVariantInventoryService extends TransactionBaseService {
protected readonly salesChannelLocationService_: SalesChannelLocationService
protected readonly salesChannelInventoryService_: SalesChannelInventoryService
protected readonly productVariantService_: ProductVariantService
protected readonly stockLocationService_: IStockLocationService
protected readonly inventoryService_: IInventoryService
protected readonly eventBusService_: IEventBusService
protected readonly cacheService_: ICacheService
protected get inventoryService_(): IInventoryService {
return this.__container__.inventoryService
}
protected get stockLocationService_(): IStockLocationService {
return this.__container__.stockLocationService
}
constructor({
stockLocationService,
salesChannelLocationService,
salesChannelInventoryService,
productVariantService,
inventoryService,
eventBusService,
}: InjectedDependencies) {
// eslint-disable-next-line prefer-rest-params
@@ -60,9 +62,7 @@ class ProductVariantInventoryService extends TransactionBaseService {
this.salesChannelLocationService_ = salesChannelLocationService
this.salesChannelInventoryService_ = salesChannelInventoryService
this.stockLocationService_ = stockLocationService
this.productVariantService_ = productVariantService
this.inventoryService_ = inventoryService
this.eventBusService_ = eventBusService
}

View File

@@ -13,11 +13,13 @@ type InjectedDependencies = {
class SalesChannelInventoryService extends TransactionBaseService {
protected readonly salesChannelLocationService_: SalesChannelLocationService
protected readonly eventBusService_: EventBusTypes.IEventBusService
protected readonly inventoryService_: IInventoryService
protected get inventoryService_(): IInventoryService {
return this.__container__.inventoryService
}
constructor({
salesChannelLocationService,
inventoryService,
eventBusService,
}: InjectedDependencies) {
// eslint-disable-next-line prefer-rest-params
@@ -25,7 +27,6 @@ class SalesChannelInventoryService extends TransactionBaseService {
this.salesChannelLocationService_ = salesChannelLocationService
this.eventBusService_ = eventBusService
this.inventoryService_ = inventoryService
}
/**

View File

@@ -19,19 +19,17 @@ type InjectedDependencies = {
class SalesChannelLocationService extends TransactionBaseService {
protected readonly salesChannelService_: SalesChannelService
protected readonly eventBusService_: IEventBusService
protected readonly stockLocationService_: IStockLocationService
constructor({
salesChannelService,
stockLocationService,
eventBusService,
}: InjectedDependencies) {
protected get stockLocationService_(): IStockLocationService {
return this.__container__.stockLocationService
}
constructor({ salesChannelService, eventBusService }: InjectedDependencies) {
// eslint-disable-next-line prefer-rest-params
super(arguments[0])
this.salesChannelService_ = salesChannelService
this.eventBusService_ = eventBusService
this.stockLocationService_ = stockLocationService
}
/**

View File

@@ -1,11 +1,6 @@
import {
ConfigModule,
ExternalModuleDeclaration,
InternalModuleDeclaration,
} from "@medusajs/types"
import { ConfigModule } from "@medusajs/types"
import { ModulesDefinition } from "@medusajs/modules-sdk"
import { isObject } from "./is-object"
/**
* Merge the modules config from the medusa-config file with the modules config from medusa package
@@ -13,24 +8,30 @@ import { isObject } from "./is-object"
* @param medusaInternalModulesConfig
*/
export function mergeModulesConfig(
modules: ConfigModule["modules"],
medusaInternalModulesConfig
modules: ConfigModule["modules"] = {},
medusaInternalModulesConfig = {}
) {
for (const [moduleName, moduleConfig] of Object.entries(modules as any)) {
const modules_ = ({ ...modules } as ConfigModule["modules"])!
const userModulesConfigKeys = Object.keys(modules)
const internalModulesConfigKeys = Object.keys(medusaInternalModulesConfig)
const allModulesKeys = new Set([
...userModulesConfigKeys,
...internalModulesConfigKeys,
])
for (const moduleName of allModulesKeys) {
const internalModuleConfig = medusaInternalModulesConfig[moduleName]
const moduleDefinition = ModulesDefinition[moduleName]
if (moduleDefinition?.isLegacy) {
continue
}
const isModuleEnabled = moduleConfig === true || isObject(moduleConfig)
if (!isModuleEnabled) {
delete medusaInternalModulesConfig[moduleName]
} else {
medusaInternalModulesConfig[moduleName] = moduleConfig as Partial<
InternalModuleDeclaration | ExternalModuleDeclaration
>
}
modules_[moduleName] ??= internalModuleConfig
}
return modules_
}

View File

@@ -3,6 +3,5 @@ export * from "./definitions"
export * from "./loaders"
export * from "./medusa-app"
export * from "./medusa-module"
export * from "./module-helper"
export * from "./remote-link"
export * from "./remote-query"

View File

@@ -4,8 +4,8 @@ import {
MODULE_SCOPE,
ModuleDefinition,
} from "@medusajs/types"
import MODULE_DEFINITIONS from "../../definitions"
import { registerModules } from "../register-modules"
import { ModulesDefinition } from "../../definitions"
import { registerMedusaModule } from "../register-modules"
const RESOLVED_PACKAGE = "@medusajs/test-service-resolved"
jest.mock("resolve-cwd", () => jest.fn(() => RESOLVED_PACKAGE))
@@ -30,13 +30,18 @@ describe("module definitions loader", () => {
jest.clearAllMocks()
// Clear module definitions
MODULE_DEFINITIONS.splice(0, MODULE_DEFINITIONS.length)
const allProperties = Object.getOwnPropertyNames(ModulesDefinition)
allProperties.forEach((property) => {
delete ModulesDefinition[property]
})
})
it("Resolves module with default definition given empty config", () => {
MODULE_DEFINITIONS.push({ ...defaultDefinition })
Object.assign(ModulesDefinition, {
[defaultDefinition.key]: defaultDefinition,
})
const res = registerModules({})
const res = registerMedusaModule(defaultDefinition.key)
expect(res[defaultDefinition.key]).toEqual(
expect.objectContaining({
@@ -53,9 +58,11 @@ describe("module definitions loader", () => {
describe("boolean config", () => {
it("Resolves module with no resolution path when given false", () => {
MODULE_DEFINITIONS.push({ ...defaultDefinition })
Object.assign(ModulesDefinition, {
[defaultDefinition.key]: defaultDefinition,
})
const res = registerModules({ [defaultDefinition.key]: false })
const res = registerMedusaModule(defaultDefinition.key, false)
expect(res[defaultDefinition.key]).toEqual(
expect.objectContaining({
@@ -68,10 +75,12 @@ describe("module definitions loader", () => {
it("Fails to resolve module with no resolution path when given false for a required module", () => {
expect.assertions(1)
MODULE_DEFINITIONS.push({ ...defaultDefinition, isRequired: true })
Object.assign(ModulesDefinition, {
[defaultDefinition.key]: { ...defaultDefinition, isRequired: true },
})
try {
registerModules({ [defaultDefinition.key]: false })
registerMedusaModule(defaultDefinition.key, false)
} catch (err) {
expect(err.message).toEqual(
`Module: ${defaultDefinition.label} is required`
@@ -87,9 +96,11 @@ describe("module definitions loader", () => {
isRequired: true,
}
MODULE_DEFINITIONS.push(definition)
Object.assign(ModulesDefinition, {
[defaultDefinition.key]: definition,
})
const res = registerModules({})
const res = registerMedusaModule(defaultDefinition.key)
expect(res[defaultDefinition.key]).toEqual(
expect.objectContaining({
@@ -106,12 +117,15 @@ describe("module definitions loader", () => {
describe("string config", () => {
it("Resolves module with default definition given empty config", () => {
MODULE_DEFINITIONS.push({ ...defaultDefinition })
const res = registerModules({
[defaultDefinition.key]: defaultDefinition.defaultPackage,
Object.assign(ModulesDefinition, {
[defaultDefinition.key]: defaultDefinition,
})
const res = registerMedusaModule(
defaultDefinition.key,
defaultDefinition.defaultPackage
)
expect(res[defaultDefinition.key]).toEqual(
expect.objectContaining({
resolutionPath: RESOLVED_PACKAGE,
@@ -128,16 +142,16 @@ describe("module definitions loader", () => {
describe("object config", () => {
it("Resolves resolution path and provides empty options when none are provided", () => {
MODULE_DEFINITIONS.push({ ...defaultDefinition })
const res = registerModules({
[defaultDefinition.key]: {
scope: MODULE_SCOPE.INTERNAL,
resolve: defaultDefinition.defaultPackage,
resources: MODULE_RESOURCE_TYPE.ISOLATED,
} as InternalModuleDeclaration,
Object.assign(ModulesDefinition, {
[defaultDefinition.key]: defaultDefinition,
})
const res = registerMedusaModule(defaultDefinition.key, {
scope: MODULE_SCOPE.INTERNAL,
resolve: defaultDefinition.defaultPackage,
resources: MODULE_RESOURCE_TYPE.ISOLATED,
} as InternalModuleDeclaration)
expect(res[defaultDefinition.key]).toEqual(
expect.objectContaining({
resolutionPath: RESOLVED_PACKAGE,
@@ -153,12 +167,12 @@ describe("module definitions loader", () => {
})
it("Resolves default resolution path and provides options when only options are provided", () => {
MODULE_DEFINITIONS.push({ ...defaultDefinition })
Object.assign(ModulesDefinition, {
[defaultDefinition.key]: defaultDefinition,
})
const res = registerModules({
[defaultDefinition.key]: {
options: { test: 123 },
},
const res = registerMedusaModule(defaultDefinition.key, {
options: { test: 123 },
} as any)
expect(res[defaultDefinition.key]).toEqual(
@@ -176,15 +190,15 @@ describe("module definitions loader", () => {
})
it("Resolves resolution path and provides options when only options are provided", () => {
MODULE_DEFINITIONS.push({ ...defaultDefinition })
Object.assign(ModulesDefinition, {
[defaultDefinition.key]: defaultDefinition,
})
const res = registerModules({
[defaultDefinition.key]: {
resolve: defaultDefinition.defaultPackage,
options: { test: 123 },
scope: "internal",
resources: "isolated",
},
const res = registerMedusaModule(defaultDefinition.key, {
resolve: defaultDefinition.defaultPackage,
options: { test: 123 },
scope: "internal",
resources: "isolated",
} as any)
expect(res[defaultDefinition.key]).toEqual(

View File

@@ -1,17 +1,14 @@
import {
Logger,
MODULE_SCOPE,
MedusaContainer,
MODULE_SCOPE,
ModuleResolution,
} from "@medusajs/types"
import { asValue } from "awilix"
import { EOL } from "os"
import { ModulesHelper } from "../module-helper"
import { loadInternalModule } from "./utils"
export const moduleHelper = new ModulesHelper()
export const moduleLoader = async ({
container,
moduleResolutions,
@@ -38,17 +35,6 @@ export const moduleLoader = async ({
)
}
}
moduleHelper.setModules(
Object.entries(moduleResolutions).reduce((acc, [k, v]) => {
if (v.resolutionPath) {
acc[k] = v
}
return acc
}, {})
)
container.register("modulesHelper", asValue(moduleHelper))
}
async function loadModule(

View File

@@ -9,57 +9,11 @@ import {
import { isObject } from "@medusajs/utils"
import resolveCwd from "resolve-cwd"
import { MODULE_DEFINITIONS, ModulesDefinition } from "../definitions"
/**
*
* @param modules
* @param isolatedModules Will be removed once the isolated flag is being removed
*/
// TODO: Remove once we have all modules migrated + rename to something like getResolutions
export const registerModules = (
modules?: Record<
string,
| false
| string
| Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
>,
{ loadLegacyOnly } = { loadLegacyOnly: false }
): Record<string, ModuleResolution> => {
const moduleResolutions = {} as Record<string, ModuleResolution>
const projectModules = modules ?? {}
for (const definition of MODULE_DEFINITIONS) {
// Skip non legacy modules
if (loadLegacyOnly && !definition.isLegacy) {
continue
}
const customConfig = projectModules[definition.key]
const canSkip =
!customConfig && !definition.isRequired && !definition.defaultPackage
const isObj = isObject(customConfig)
if (isObj && customConfig.scope === MODULE_SCOPE.EXTERNAL) {
// TODO: getExternalModuleResolution(...)
if (!canSkip) {
throw new Error("External Modules are not supported yet.")
}
}
moduleResolutions[definition.key] = getInternalModuleResolution(
definition,
customConfig as InternalModuleDeclaration
)
}
return moduleResolutions
}
import { ModulesDefinition } from "../definitions"
export const registerMedusaModule = (
moduleKey: string,
moduleDeclaration:
moduleDeclaration?:
| Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
| string
| false,
@@ -74,9 +28,17 @@ export const registerMedusaModule = (
throw new Error(`Module: ${moduleKey} is not defined.`)
}
const modDeclaration =
moduleDeclaration ??
(modDefinition?.defaultModuleDeclaration as InternalModuleDeclaration)
if (modDeclaration !== false && !modDeclaration) {
throw new Error(`Module: ${moduleKey} has no declaration.`)
}
if (
isObject(moduleDeclaration) &&
moduleDeclaration?.scope === MODULE_SCOPE.EXTERNAL
isObject(modDeclaration) &&
modDeclaration?.scope === MODULE_SCOPE.EXTERNAL
) {
// TODO: getExternalModuleResolution(...)
throw new Error("External Modules are not supported yet.")

View File

@@ -1,10 +1,8 @@
import {
Constructor,
InternalModuleDeclaration,
Logger,
MODULE_RESOURCE_TYPE,
MODULE_SCOPE,
MedusaContainer,
MODULE_RESOURCE_TYPE,
ModuleExports,
ModuleResolution,
} from "@medusajs/types"
@@ -65,18 +63,6 @@ export async function loadInternalModule(
}
}
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 = createMedusaContainer()
const dependencies = resolution?.dependencies ?? []

View File

@@ -40,8 +40,8 @@ export type RunMigrationFn = (
export type MedusaModuleConfig = {
[key: string | Modules]:
| boolean
| Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
| true
}
export type SharedResources = {

View File

@@ -1,16 +0,0 @@
import { ModuleResolution, ModulesResponse } from "@medusajs/types"
export class ModulesHelper {
private modules_: Record<string, ModuleResolution> = {}
setModules(modules: Record<string, ModuleResolution>) {
this.modules_ = modules
}
get modules(): ModulesResponse {
return Object.values(this.modules_ || {}).map((value) => ({
module: value.definition.key,
resolution: value.resolutionPath,
}))
}
}

View File

@@ -152,6 +152,23 @@ export class RemoteJoiner {
service.alias = [service.alias]
}
// handle alias.name as array
for (let idx = 0; idx < service.alias.length; idx++) {
const alias = service.alias[idx]
if (!Array.isArray(alias.name)) {
continue
}
for (const name of alias.name) {
service.alias.push({
name,
args: alias.args,
})
}
service.alias.splice(idx, 1)
idx--
}
// self-reference
for (const alias of service.alias) {
if (this.serviceConfigCache.has(`alias_${alias.name}}`)) {
@@ -167,7 +184,7 @@ export class RemoteJoiner {
: undefined
service.relationships?.push({
alias: alias.name,
alias: alias.name as string,
foreignKey: alias.name + "_id",
primaryKey: "id",
serviceName: service.serviceName!,
@@ -250,7 +267,7 @@ export class RemoteJoiner {
private cacheServiceConfig(
serviceConfigs,
serviceName?: string,
serviceAlias?: string
serviceAlias?: string | string[]
): void {
if (serviceAlias) {
const name = `alias_${serviceAlias}`

View File

@@ -33,7 +33,13 @@ export async function runMigrations({
const migrator = orm.getMigrator()
const pendingMigrations = await migrator.getPendingMigrations()
logger.info(`Running pending migrations: ${pendingMigrations}`)
logger.info(
`Running pending migrations: ${JSON.stringify(
pendingMigrations,
null,
2
)}`
)
await migrator.up({
migrations: pendingMigrations.map((m) => m.name),

View File

@@ -2,89 +2,101 @@ import { Migration } from "@mikro-orm/migrations"
export class Migration20230719100648 extends Migration {
async up(): Promise<void> {
try {
// Prevent from crashing if for some reason the migration is re run (example, typeorm and mikro orm does not have the same migration table)
await this.execute('SELECT 1 FROM "product" LIMIT 1;')
return
} catch {
// noop
}
this.addSql(
'create table "product_category" ("id" text not null, "name" text not null, "description" text not null default \'\', "handle" text not null, "mpath" text not null, "is_active" boolean not null default false, "is_internal" boolean not null default false, "rank" numeric not null default 0, "parent_category_id" text null, "created_at" timestamptz not null, "updated_at" timestamptz not null, constraint "product_category_pkey" primary key ("id"));'
'create table IF NOT EXISTS "product_category" ("id" text not null, "name" text not null, "description" text not null default \'\', "handle" text not null, "mpath" text not null, "is_active" boolean not null default false, "is_internal" boolean not null default false, "rank" numeric not null default 0, "parent_category_id" text null, "created_at" timestamptz not null, "updated_at" timestamptz not null, constraint "product_category_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_product_category_path" on "product_category" ("mpath");'
'create index IF NOT EXISTS "IDX_product_category_path" on "product_category" ("mpath");'
)
this.addSql(
'alter table "product_category" add constraint "IDX_product_category_handle" unique ("handle");'
)
this.addSql(
'create table "product_collection" ("id" text not null, "title" text not null, "handle" text not null, "metadata" jsonb null, "deleted_at" timestamptz null, constraint "product_collection_pkey" primary key ("id"));'
'create table IF NOT EXISTS "product_collection" ("id" text not null, "title" text not null, "handle" text not null, "metadata" jsonb null, "deleted_at" timestamptz null, constraint "product_collection_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_product_collection_deleted_at" on "product_collection" ("deleted_at");'
'create index IF NOT EXISTS "IDX_product_collection_deleted_at" on "product_collection" ("deleted_at");'
)
this.addSql(
'alter table "product_collection" add constraint "IDX_product_collection_handle_unique" unique ("handle");'
)
this.addSql(
'create table "image" ("id" text not null, "url" text not null, "metadata" jsonb null, "deleted_at" timestamptz null, constraint "image_pkey" primary key ("id"));'
'create table IF NOT EXISTS "image" ("id" text not null, "url" text not null, "metadata" jsonb null, "deleted_at" timestamptz null, constraint "image_pkey" primary key ("id"));'
)
this.addSql('create index "IDX_product_image_url" on "image" ("url");')
this.addSql(
'create index "IDX_product_image_deleted_at" on "image" ("deleted_at");'
'create index IF NOT EXISTS "IDX_product_image_url" on "image" ("url");'
)
this.addSql(
'create index IF NOT EXISTS "IDX_product_image_deleted_at" on "image" ("deleted_at");'
)
this.addSql(
'create table "product_tag" ("id" text not null, "value" text not null, "metadata" jsonb null, "deleted_at" timestamptz null, constraint "product_tag_pkey" primary key ("id"));'
'create table IF NOT EXISTS "product_tag" ("id" text not null, "value" text not null, "metadata" jsonb null, "deleted_at" timestamptz null, constraint "product_tag_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_product_tag_deleted_at" on "product_tag" ("deleted_at");'
'create index IF NOT EXISTS "IDX_product_tag_deleted_at" on "product_tag" ("deleted_at");'
)
this.addSql(
'create table "product_type" ("id" text not null, "value" text not null, "metadata" json null, "deleted_at" timestamptz null, constraint "product_type_pkey" primary key ("id"));'
'create table IF NOT EXISTS "product_type" ("id" text not null, "value" text not null, "metadata" json null, "deleted_at" timestamptz null, constraint "product_type_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_product_type_deleted_at" on "product_type" ("deleted_at");'
'create index IF NOT EXISTS "IDX_product_type_deleted_at" on "product_type" ("deleted_at");'
)
this.addSql(
'create table "product" ("id" text not null, "title" text not null, "handle" text not null, "subtitle" text null, "description" text null, "is_giftcard" boolean not null default false, "status" text check ("status" in (\'draft\', \'proposed\', \'published\', \'rejected\')) not null, "thumbnail" text null, "weight" text null, "length" text null, "height" text null, "width" text null, "origin_country" text null, "hs_code" text null, "mid_code" text null, "material" text null, "collection_id" text null, "type_id" text null, "discountable" boolean not null default true, "external_id" text null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, "metadata" jsonb null, constraint "product_pkey" primary key ("id"));'
'create table IF NOT EXISTS "product" ("id" text not null, "title" text not null, "handle" text not null, "subtitle" text null, "description" text null, "is_giftcard" boolean not null default false, "status" text check ("status" in (\'draft\', \'proposed\', \'published\', \'rejected\')) not null, "thumbnail" text null, "weight" text null, "length" text null, "height" text null, "width" text null, "origin_country" text null, "hs_code" text null, "mid_code" text null, "material" text null, "collection_id" text null, "type_id" text null, "discountable" boolean not null default true, "external_id" text null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, "metadata" jsonb null, constraint "product_pkey" primary key ("id"));'
)
this.addSql('create index "IDX_product_type_id" on "product" ("type_id");')
this.addSql(
'create index "IDX_product_deleted_at" on "product" ("deleted_at");'
'create index IF NOT EXISTS "IDX_product_type_id" on "product" ("type_id");'
)
this.addSql(
'create index IF NOT EXISTS "IDX_product_deleted_at" on "product" ("deleted_at");'
)
this.addSql(
'alter table "product" add constraint "IDX_product_handle_unique" unique ("handle");'
)
this.addSql(
'create table "product_option" ("id" text not null, "title" text not null, "product_id" text not null, "metadata" jsonb null, "deleted_at" timestamptz null, constraint "product_option_pkey" primary key ("id"));'
'create table IF NOT EXISTS "product_option" ("id" text not null, "title" text not null, "product_id" text not null, "metadata" jsonb null, "deleted_at" timestamptz null, constraint "product_option_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_product_option_product_id" on "product_option" ("product_id");'
'create index IF NOT EXISTS "IDX_product_option_product_id" on "product_option" ("product_id");'
)
this.addSql(
'create index "IDX_product_option_deleted_at" on "product_option" ("deleted_at");'
'create index IF NOT EXISTS "IDX_product_option_deleted_at" on "product_option" ("deleted_at");'
)
this.addSql(
'create table "product_tags" ("product_id" text not null, "product_tag_id" text not null, constraint "product_tags_pkey" primary key ("product_id", "product_tag_id"));'
'create table IF NOT EXISTS "product_tags" ("product_id" text not null, "product_tag_id" text not null, constraint "product_tags_pkey" primary key ("product_id", "product_tag_id"));'
)
this.addSql(
'create table "product_images" ("product_id" text not null, "image_id" text not null, constraint "product_images_pkey" primary key ("product_id", "image_id"));'
'create table IF NOT EXISTS "product_images" ("product_id" text not null, "image_id" text not null, constraint "product_images_pkey" primary key ("product_id", "image_id"));'
)
this.addSql(
'create table "product_category_product" ("product_id" text not null, "product_category_id" text not null, constraint "product_category_product_pkey" primary key ("product_id", "product_category_id"));'
'create table IF NOT EXISTS "product_category_product" ("product_id" text not null, "product_category_id" text not null, constraint "product_category_product_pkey" primary key ("product_id", "product_category_id"));'
)
this.addSql(
'create table "product_variant" ("id" text not null, "title" text not null, "sku" text null, "barcode" text null, "ean" text null, "upc" text null, "inventory_quantity" numeric not null default 100, "allow_backorder" boolean not null default false, "manage_inventory" boolean not null default true, "hs_code" text null, "origin_country" text null, "mid_code" text null, "material" text null, "weight" numeric null, "length" numeric null, "height" numeric null, "width" numeric null, "metadata" jsonb null, "variant_rank" numeric null default 0, "product_id" text not null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, constraint "product_variant_pkey" primary key ("id"));'
'create table IF NOT EXISTS "product_variant" ("id" text not null, "title" text not null, "sku" text null, "barcode" text null, "ean" text null, "upc" text null, "inventory_quantity" numeric not null default 100, "allow_backorder" boolean not null default false, "manage_inventory" boolean not null default true, "hs_code" text null, "origin_country" text null, "mid_code" text null, "material" text null, "weight" numeric null, "length" numeric null, "height" numeric null, "width" numeric null, "metadata" jsonb null, "variant_rank" numeric null default 0, "product_id" text not null, "created_at" timestamptz not null, "updated_at" timestamptz not null, "deleted_at" timestamptz null, constraint "product_variant_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_product_variant_deleted_at" on "product_variant" ("deleted_at");'
'create index IF NOT EXISTS "IDX_product_variant_deleted_at" on "product_variant" ("deleted_at");'
)
this.addSql(
'create index "IDX_product_variant_product_id" on "product_variant" ("product_id");'
'create index IF NOT EXISTS "IDX_product_variant_product_id" on "product_variant" ("product_id");'
)
this.addSql(
'alter table "product_variant" add constraint "IDX_product_variant_sku_unique" unique ("sku");'
@@ -100,16 +112,16 @@ export class Migration20230719100648 extends Migration {
)
this.addSql(
'create table "product_option_value" ("id" text not null, "value" text not null, "option_id" text not null, "variant_id" text not null, "metadata" jsonb null, "deleted_at" timestamptz null, constraint "product_option_value_pkey" primary key ("id"));'
'create table IF NOT EXISTS "product_option_value" ("id" text not null, "value" text not null, "option_id" text not null, "variant_id" text not null, "metadata" jsonb null, "deleted_at" timestamptz null, constraint "product_option_value_pkey" primary key ("id"));'
)
this.addSql(
'create index "IDX_product_option_value_option_id" on "product_option_value" ("option_id");'
'create index IF NOT EXISTS "IDX_product_option_value_option_id" on "product_option_value" ("option_id");'
)
this.addSql(
'create index "IDX_product_option_value_variant_id" on "product_option_value" ("variant_id");'
'create index IF NOT EXISTS "IDX_product_option_value_variant_id" on "product_option_value" ("variant_id");'
)
this.addSql(
'create index "IDX_product_option_value_deleted_at" on "product_option_value" ("deleted_at");'
'create index IF NOT EXISTS "IDX_product_option_value_deleted_at" on "product_option_value" ("deleted_at");'
)
this.addSql(

View File

@@ -1,69 +1,123 @@
import { Migration } from '@mikro-orm/migrations';
import { Migration } from "@mikro-orm/migrations"
export class Migration20230908084537 extends Migration {
async up(): Promise<void> {
this.addSql('alter table "product_category" alter column "created_at" type timestamptz using ("created_at"::timestamptz);');
this.addSql('alter table "product_category" alter column "created_at" set default now();');
this.addSql('alter table "product_category" alter column "updated_at" type timestamptz using ("updated_at"::timestamptz);');
this.addSql('alter table "product_category" alter column "updated_at" set default now();');
this.addSql(
'alter table "product_category" alter column "created_at" type timestamptz using ("created_at"::timestamptz);'
)
this.addSql(
'alter table "product_category" alter column "created_at" set default now();'
)
this.addSql(
'alter table "product_category" alter column "updated_at" type timestamptz using ("updated_at"::timestamptz);'
)
this.addSql(
'alter table "product_category" alter column "updated_at" set default now();'
)
this.addSql('alter table "product_collection" add column "created_at" timestamptz not null default now(), add column "updated_at" timestamptz not null default now();');
this.addSql(
'alter table "product_collection" add column IF NOT EXISTS "created_at" timestamptz not null default now(), add column IF NOT EXISTS "updated_at" timestamptz not null default now();'
)
this.addSql('alter table "image" add column "created_at" timestamptz not null default now(), add column "updated_at" timestamptz not null default now();');
this.addSql(
'alter table "image" add column IF NOT EXISTS "created_at" timestamptz not null default now(), add column IF NOT EXISTS "updated_at" timestamptz not null default now();'
)
this.addSql('alter table "product_tag" add column "created_at" timestamptz not null default now(), add column "updated_at" timestamptz not null default now();');
this.addSql(
'alter table "product_tag" add column IF NOT EXISTS "created_at" timestamptz not null default now(), add column IF NOT EXISTS "updated_at" timestamptz not null default now();'
)
this.addSql('alter table "product_type" add column "created_at" timestamptz not null default now(), add column "updated_at" timestamptz not null default now();');
this.addSql(
'alter table "product_type" add column IF NOT EXISTS "created_at" timestamptz not null default now(), add column IF NOT EXISTS "updated_at" timestamptz not null default now();'
)
this.addSql('alter table "product" alter column "created_at" type timestamptz using ("created_at"::timestamptz);');
this.addSql('alter table "product" alter column "created_at" set default now();');
this.addSql('alter table "product" alter column "updated_at" type timestamptz using ("updated_at"::timestamptz);');
this.addSql('alter table "product" alter column "updated_at" set default now();');
this.addSql(
'alter table "product" alter column "created_at" type timestamptz using ("created_at"::timestamptz);'
)
this.addSql(
'alter table "product" alter column "created_at" set default now();'
)
this.addSql(
'alter table "product" alter column "updated_at" type timestamptz using ("updated_at"::timestamptz);'
)
this.addSql(
'alter table "product" alter column "updated_at" set default now();'
)
this.addSql('alter table "product_option" add column "created_at" timestamptz not null default now(), add column "updated_at" timestamptz not null default now();');
this.addSql(
'alter table "product_option" add column IF NOT EXISTS "created_at" timestamptz not null default now(), add column IF NOT EXISTS "updated_at" timestamptz not null default now();'
)
this.addSql('alter table "product_variant" alter column "created_at" type timestamptz using ("created_at"::timestamptz);');
this.addSql('alter table "product_variant" alter column "created_at" set default now();');
this.addSql('alter table "product_variant" alter column "updated_at" type timestamptz using ("updated_at"::timestamptz);');
this.addSql('alter table "product_variant" alter column "updated_at" set default now();');
this.addSql(
'alter table "product_variant" alter column "created_at" type timestamptz using ("created_at"::timestamptz);'
)
this.addSql(
'alter table "product_variant" alter column "created_at" set default now();'
)
this.addSql(
'alter table "product_variant" alter column "updated_at" type timestamptz using ("updated_at"::timestamptz);'
)
this.addSql(
'alter table "product_variant" alter column "updated_at" set default now();'
)
this.addSql('alter table "product_option_value" add column "created_at" timestamptz not null default now(), add column "updated_at" timestamptz not null default now();');
this.addSql(
'alter table "product_option_value" add column IF NOT EXISTS "created_at" timestamptz not null default now(), add column IF NOT EXISTS "updated_at" timestamptz not null default now();'
)
}
async down(): Promise<void> {
this.addSql('alter table "product_category" alter column "created_at" drop default;');
this.addSql('alter table "product_category" alter column "created_at" type timestamptz using ("created_at"::timestamptz);');
this.addSql('alter table "product_category" alter column "updated_at" drop default;');
this.addSql('alter table "product_category" alter column "updated_at" type timestamptz using ("updated_at"::timestamptz);');
this.addSql(
'alter table "product_category" alter column "created_at" drop default;'
)
this.addSql(
'alter table "product_category" alter column "created_at" type timestamptz using ("created_at"::timestamptz);'
)
this.addSql(
'alter table "product_category" alter column "updated_at" drop default;'
)
this.addSql(
'alter table "product_category" alter column "updated_at" type timestamptz using ("updated_at"::timestamptz);'
)
this.addSql('alter table "product_collection" drop column "created_at";');
this.addSql('alter table "product_collection" drop column "updated_at";');
this.addSql('alter table "product_collection" drop column "created_at";')
this.addSql('alter table "product_collection" drop column "updated_at";')
this.addSql('alter table "image" drop column "created_at";');
this.addSql('alter table "image" drop column "updated_at";');
this.addSql('alter table "image" drop column "created_at";')
this.addSql('alter table "image" drop column "updated_at";')
this.addSql('alter table "product_tag" drop column "created_at";');
this.addSql('alter table "product_tag" drop column "updated_at";');
this.addSql('alter table "product_tag" drop column "created_at";')
this.addSql('alter table "product_tag" drop column "updated_at";')
this.addSql('alter table "product_type" drop column "created_at";');
this.addSql('alter table "product_type" drop column "updated_at";');
this.addSql('alter table "product_type" drop column "created_at";')
this.addSql('alter table "product_type" drop column "updated_at";')
this.addSql('alter table "product" alter column "created_at" drop default;');
this.addSql('alter table "product" alter column "created_at" type timestamptz using ("created_at"::timestamptz);');
this.addSql('alter table "product" alter column "updated_at" drop default;');
this.addSql('alter table "product" alter column "updated_at" type timestamptz using ("updated_at"::timestamptz);');
this.addSql('alter table "product" alter column "created_at" drop default;')
this.addSql(
'alter table "product" alter column "created_at" type timestamptz using ("created_at"::timestamptz);'
)
this.addSql('alter table "product" alter column "updated_at" drop default;')
this.addSql(
'alter table "product" alter column "updated_at" type timestamptz using ("updated_at"::timestamptz);'
)
this.addSql('alter table "product_option" drop column "created_at";');
this.addSql('alter table "product_option" drop column "updated_at";');
this.addSql('alter table "product_option" drop column "created_at";')
this.addSql('alter table "product_option" drop column "updated_at";')
this.addSql('alter table "product_variant" alter column "created_at" drop default;');
this.addSql('alter table "product_variant" alter column "created_at" type timestamptz using ("created_at"::timestamptz);');
this.addSql('alter table "product_variant" alter column "updated_at" drop default;');
this.addSql('alter table "product_variant" alter column "updated_at" type timestamptz using ("updated_at"::timestamptz);');
this.addSql(
'alter table "product_variant" alter column "created_at" drop default;'
)
this.addSql(
'alter table "product_variant" alter column "created_at" type timestamptz using ("created_at"::timestamptz);'
)
this.addSql(
'alter table "product_variant" alter column "updated_at" drop default;'
)
this.addSql(
'alter table "product_variant" alter column "updated_at" type timestamptz using ("updated_at"::timestamptz);'
)
this.addSql('alter table "product_option_value" drop column "created_at";');
this.addSql('alter table "product_option_value" drop column "updated_at";');
this.addSql('alter table "product_option_value" drop column "created_at";')
this.addSql('alter table "product_option_value" drop column "updated_at";')
}
}

View File

@@ -33,7 +33,13 @@ export async function runMigrations({
const migrator = orm.getMigrator()
const pendingMigrations = await migrator.getPendingMigrations()
logger.info(`Running pending migrations: ${pendingMigrations}`)
logger.info(
`Running pending migrations: ${JSON.stringify(
pendingMigrations,
null,
2
)}`
)
await migrator.up({
migrations: pendingMigrations.map((m) => m.name),

View File

@@ -1,17 +1,22 @@
import { ModuleJoinerConfig } from "@medusajs/types"
import { Modules } from "@medusajs/modules-sdk"
import { StockLocation } from "./models"
import moduleSchema from "./schema"
export const joinerConfig: ModuleJoinerConfig = {
serviceName: Modules.STOCK_LOCATION,
primaryKeys: ["id"],
linkableKeys: { stock_location_id: StockLocation.name },
linkableKeys: {
stock_location_id: StockLocation.name,
location_id: StockLocation.name,
},
schema: moduleSchema,
alias: [
{
name: "stock_location",
},
{
name: "stock_locations",
name: ["stock_location", "stock_locations"],
args: {
entity: "StockLocation",
},
},
],
}

View File

@@ -0,0 +1,29 @@
export default `
scalar DateTime
scalar JSON
type StockLocation {
id: ID!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
name: String!
address_id: String
address: StockLocationAddress
metadata: JSON
}
type StockLocationAddress {
id: ID!
created_at: DateTime!
updated_at: DateTime!
deleted_at: DateTime
address_1: String!
address_2: String
company: String
city: String
country_code: String!
phone: String
province: String
postal_code: String
metadata: JSON
}
`

View File

@@ -53,9 +53,7 @@ export type ConfigModule = {
featureFlags: Record<string, boolean | string>
modules?: Record<
string,
| false
| string
| Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
boolean | Partial<InternalModuleDeclaration | ExternalModuleDeclaration>
>
plugins: (
| {

View File

@@ -23,7 +23,7 @@ export type JoinerRelationship = {
}
export interface JoinerServiceConfigAlias {
name: string
name: string | string[]
/**
* Extra arguments to pass to the remoteFetchData callback
*/

View File

@@ -3,4 +3,6 @@ export const ContainerRegistrationKeys = {
MANAGER: "manager",
CONFIG_MODULE: "configModule",
LOGGER: "logger",
REMOTE_QUERY: "remoteQuery",
REMOTE_LINK: "remoteLink",
}