Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Improvements to the migrations CLI and workflow #8060

Merged
merged 14 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/cli/medusa-cli/src/create-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,18 @@ function buildLocalCommands(cli, isLocalProject) {
),
})
.command({
command: `migrations [action]`,
command: `migrations [action] [modules...]`,
thetutlage marked this conversation as resolved.
Show resolved Hide resolved
desc: `Manage migrations from the core and your own project`,
builder: {
action: {
demand: true,
description: "The action to perform on migrations",
choices: ["run", "revert", "show"],
},
modules: {
description: "Revert migrations for defined modules",
demand: false,
},
},
handler: handlerP(
getCommandHandler(`migrate`, (args, cmd) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ export async function loadModuleMigrations(
let runMigrations = loadedModule.runMigrations
let revertMigration = loadedModule.revertMigration

// Generate migration scripts if they are not present
if (!runMigrations || !revertMigration) {
const moduleResources = await loadResources(
resolution,
Expand All @@ -190,8 +189,7 @@ export async function loadModuleMigrations(

const migrationScriptOptions = {
moduleName: resolution.definition.key,
models: moduleResources.models,
pathToMigrations: moduleResources.normalizedPath + "/migrations",
pathToMigrations: join(moduleResources.normalizedPath, "migrations"),
}

runMigrations ??= ModulesSdkUtils.buildMigrationScript(
Expand Down
137 changes: 93 additions & 44 deletions packages/core/modules-sdk/src/medusa-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
LoadedModule,
Logger,
MedusaContainer,
ModuleBootstrapDeclaration,
ModuleDefinition,
ModuleExports,
ModuleJoinerConfig,
Expand All @@ -21,6 +22,7 @@ import {
createMedusaContainer,
isObject,
isString,
MedusaError,
ModuleRegistrationName,
Modules,
ModulesSdkUtils,
Expand All @@ -47,10 +49,8 @@ declare module "@medusajs/types" {
}
}

export type RunMigrationFn = (
options?: ModuleServiceInitializeOptions,
injectedDependencies?: Record<any, any>
) => Promise<void>
export type RunMigrationFn = () => Promise<void>
export type RevertMigrationFn = (moduleNames: string[]) => Promise<void>

export type MedusaModuleConfig = {
[key: string | Modules]:
Expand Down Expand Up @@ -176,11 +176,11 @@ async function initializeLinks({
}
} catch (err) {
console.warn("Error initializing link modules.", err)

return {
remoteLink: undefined,
linkResolution: undefined,
runMigrations: undefined,
runMigrations: () => void 0,
revertMigrations: () => void 0,
}
}
}
Expand Down Expand Up @@ -224,7 +224,7 @@ export type MedusaAppOutput = {
entitiesMap?: Record<string, any>
notFound?: Record<string, Record<string, string>>
runMigrations: RunMigrationFn
revertMigrations: RunMigrationFn
revertMigrations: RevertMigrationFn
onApplicationShutdown: () => Promise<void>
onApplicationPrepareShutdown: () => Promise<void>
sharedContainer?: MedusaContainer
Expand Down Expand Up @@ -317,10 +317,12 @@ async function MedusaApp_({
delete modules[LinkModulePackage]
delete modules[Modules.LINK]

let linkModuleOptions = {}
let linkModuleOrOptions:
| Partial<ModuleServiceInitializeOptions>
| Partial<ModuleBootstrapDeclaration> = {}

if (isObject(linkModule)) {
linkModuleOptions = linkModule
linkModuleOrOptions = linkModule
}

for (const injectedDependency of Object.keys(injectedDependencies)) {
Expand Down Expand Up @@ -380,7 +382,7 @@ async function MedusaApp_({
runMigrations: linkModuleMigration,
revertMigrations: revertLinkModuleMigration,
} = await initializeLinks({
config: linkModuleOptions,
config: linkModuleOrOptions,
linkModules,
injectedDependencies,
moduleExports: isMedusaModule(linkModule) ? linkModule : undefined,
Expand All @@ -402,10 +404,38 @@ async function MedusaApp_({
return await remoteQuery.query(query, variables, options)
}

const applyMigration = async (linkModuleOptions, revert = false) => {
for (const moduleName of Object.keys(allModules)) {
const moduleResolution = MedusaModule.getModuleResolutions(moduleName)
const applyMigration = async ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: As we discussed, we can probably move this one outside the main function wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uses sharedContainer and sharedConfig from the upper scope. Should we pass them down as parameters?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so, just to lighten this function a bit to simplify reading and understanding

modulesNames,
revert = false,
}: {
modulesNames: string[]
revert?: boolean
}) => {
const moduleResolutions = modulesNames.map((moduleName) => {
return {
moduleName,
resolution: MedusaModule.getModuleResolutions(moduleName),
}
})

const missingModules = moduleResolutions
.filter(({ resolution }) => !resolution)
.map(({ moduleName }) => moduleName)

if (missingModules.length) {
const action = revert ? "revert" : "run"
const error = new MedusaError(
MedusaError.Types.UNKNOWN_MODULES,
`Cannot ${action} migrations for unknown module(s) ${missingModules.join(
","
)}`,
MedusaError.Codes.UNKNOWN_MODULES
)
error["allModules"] = Object.keys(allModules)
throw error
}

for (const { resolution: moduleResolution } of moduleResolutions) {
if (!moduleResolution.options?.database) {
moduleResolution.options ??= {}
moduleResolution.options.database = {
Expand All @@ -417,55 +447,73 @@ async function MedusaApp_({
await MedusaModule.migrateDown(
moduleResolution.definition.key,
moduleResolution.resolutionPath as string,
sharedContainer,
moduleResolution.options,
moduleResolution.moduleExports
)
} else {
await MedusaModule.migrateUp(
moduleResolution.definition.key,
moduleResolution.resolutionPath as string,
sharedContainer,
moduleResolution.options,
moduleResolution.moduleExports
)
}
}
}

const linkModuleOpt = { ...(linkModuleOptions ?? {}) }
linkModuleOpt.database ??= {
...(sharedResourcesConfig?.database ?? {}),
}
const runMigrations: RunMigrationFn = async (): Promise<void> => {
await applyMigration({
modulesNames: Object.keys(allModules),
})

if (revert) {
revertLinkModuleMigration &&
(await revertLinkModuleMigration(
{
options: linkModuleOpt,
injectedDependencies,
},
linkModules
))
} else {
linkModuleMigration &&
(await linkModuleMigration(
{
options: linkModuleOpt,
injectedDependencies,
},
linkModules
))
const options: Partial<ModuleServiceInitializeOptions> =
"scope" in linkModuleOrOptions
? { ...linkModuleOrOptions.options }
: {
...(linkModuleOrOptions as Partial<ModuleServiceInitializeOptions>),
}

options.database ??= {
...sharedResourcesConfig?.database,
}
}

const runMigrations: RunMigrationFn = async (
linkModuleOptions
): Promise<void> => {
await applyMigration(linkModuleOptions)
await linkModuleMigration(
{
options,
injectedDependencies,
},
linkModules
)
}

const revertMigrations: RunMigrationFn = async (
linkModuleOptions
const revertMigrations: RevertMigrationFn = async (
modulesNames
): Promise<void> => {
await applyMigration(linkModuleOptions, true)
await applyMigration({
modulesNames,
revert: true,
})

const options: Partial<ModuleServiceInitializeOptions> =
"scope" in linkModuleOrOptions
? { ...linkModuleOrOptions.options }
: {
...(linkModuleOrOptions as Partial<ModuleServiceInitializeOptions>),
}

options.database ??= {
...sharedResourcesConfig?.database,
}

await revertLinkModuleMigration(
thetutlage marked this conversation as resolved.
Show resolved Hide resolved
{
options,
injectedDependencies,
},
linkModules
)
}

return {
Expand Down Expand Up @@ -506,6 +554,7 @@ export async function MedusaAppMigrateUp(
}

export async function MedusaAppMigrateDown(
moduleNames: string[],
options: MedusaAppOptions = {}
): Promise<void> {
const migrationOnly = true
Expand All @@ -515,5 +564,5 @@ export async function MedusaAppMigrateDown(
migrationOnly,
})

await revertMigrations().finally(MedusaModule.clearInstances)
await revertMigrations(moduleNames).finally(MedusaModule.clearInstances)
}
25 changes: 21 additions & 4 deletions packages/core/modules-sdk/src/medusa-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ModuleResolution,
} from "@medusajs/types"
import {
ContainerRegistrationKeys,
createMedusaContainer,
promiseAll,
simpleHash,
Expand Down Expand Up @@ -344,7 +345,9 @@ class MedusaModule {
)

const logger_ =
container.resolve("logger", { allowUnregistered: true }) ?? logger
container.resolve(ContainerRegistrationKeys.LOGGER, {
allowUnregistered: true,
}) ?? logger

try {
await moduleLoader({
Expand Down Expand Up @@ -475,7 +478,9 @@ class MedusaModule {
)

const logger_ =
container.resolve("logger", { allowUnregistered: true }) ?? logger
container.resolve(ContainerRegistrationKeys.LOGGER, {
allowUnregistered: true,
}) ?? logger

try {
await moduleLoader({
Expand Down Expand Up @@ -534,6 +539,7 @@ class MedusaModule {
public static async migrateUp(
moduleKey: string,
modulePath: string,
container?: MedusaContainer,
options?: Record<string, any>,
moduleExports?: ModuleExports
): Promise<void> {
Expand All @@ -544,6 +550,11 @@ class MedusaModule {
options,
})

const logger_ =
container?.resolve(ContainerRegistrationKeys.LOGGER, {
allowUnregistered: true,
}) ?? logger

for (const mod in moduleResolutions) {
const [migrateUp] = await loadModuleMigrations(
moduleResolutions[mod],
Expand All @@ -553,7 +564,7 @@ class MedusaModule {
if (typeof migrateUp === "function") {
await migrateUp({
options,
logger,
logger: logger_,
})
}
}
Expand All @@ -562,6 +573,7 @@ class MedusaModule {
public static async migrateDown(
moduleKey: string,
modulePath: string,
container?: MedusaContainer,
options?: Record<string, any>,
moduleExports?: ModuleExports
): Promise<void> {
Expand All @@ -572,6 +584,11 @@ class MedusaModule {
options,
})

const logger_ =
container?.resolve(ContainerRegistrationKeys.LOGGER, {
allowUnregistered: true,
}) ?? logger

for (const mod in moduleResolutions) {
const [, migrateDown] = await loadModuleMigrations(
moduleResolutions[mod],
Expand All @@ -581,7 +598,7 @@ class MedusaModule {
if (typeof migrateDown === "function") {
await migrateDown({
options,
logger,
logger: logger_,
})
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/core/utils/src/common/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ export const MedusaErrorTypes = {
NOT_ALLOWED: "not_allowed",
UNEXPECTED_STATE: "unexpected_state",
CONFLICT: "conflict",
UNKNOWN_MODULES: "unknown_modules",
PAYMENT_AUTHORIZATION_ERROR: "payment_authorization_error",
PAYMENT_REQUIRES_MORE_ERROR: "payment_requires_more_error",
}

export const MedusaErrorCodes = {
INSUFFICIENT_INVENTORY: "insufficient_inventory",
CART_INCOMPATIBLE_STATE: "cart_incompatible_state",
UNKNOWN_MODULES: "unknown_modules",
}

/**
Expand Down
Loading
Loading