refactor: use module name as the snapshot name (#11802)
Fixes: FRMW-2930 This PR updates the MikroORM config to use the module name as the snapshot name when generating migration files. Otherwise, MikroORM defaults to the database name and every time you update the database name, it will create a new snapshot. Also, we migrate existing snapshot files to be same the new file name to avoid breaking changes.
This commit is contained in:
5
.changeset/shy-pugs-cough.md
Normal file
5
.changeset/shy-pugs-cough.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/utils": patch
|
||||
---
|
||||
|
||||
feat: use module name as the snapshot name
|
||||
@@ -59,6 +59,7 @@ export type Filter = {
|
||||
export async function mikroOrmCreateConnection(
|
||||
database: ModuleServiceInitializeOptions["database"] & {
|
||||
connection?: any
|
||||
snapshotName?: string
|
||||
filters?: Record<string, Filter>
|
||||
},
|
||||
entities: any[],
|
||||
@@ -99,6 +100,7 @@ export async function mikroOrmCreateConnection(
|
||||
migrations: {
|
||||
disableForeignKeys: false,
|
||||
path: pathToMigrations,
|
||||
snapshotName: database.snapshotName,
|
||||
generator: CustomTsMigrationGenerator,
|
||||
silent: !(
|
||||
database.debug ??
|
||||
|
||||
@@ -6,8 +6,9 @@ import {
|
||||
} from "@mikro-orm/migrations"
|
||||
import { defineConfig, PostgreSqlDriver } from "@mikro-orm/postgresql"
|
||||
import { EventEmitter } from "events"
|
||||
import { access, mkdir, writeFile } from "fs/promises"
|
||||
import { dirname } from "path"
|
||||
import { access, mkdir, rename, writeFile } from "fs/promises"
|
||||
import { dirname, join } from "path"
|
||||
import { readDir } from "../common"
|
||||
|
||||
/**
|
||||
* Events emitted by the migrations class
|
||||
@@ -62,6 +63,7 @@ export class Migrations extends EventEmitter<MigrationsEvents> {
|
||||
const migrator = connection.getMigrator()
|
||||
|
||||
try {
|
||||
await this.migrateSnapshotFile(migrator["snapshotPath"])
|
||||
await this.ensureSnapshot(migrator["snapshotPath"])
|
||||
return await migrator.createMigration()
|
||||
} finally {
|
||||
@@ -141,6 +143,33 @@ export class Migrations extends EventEmitter<MigrationsEvents> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the existing snapshot file of a module to follow to be
|
||||
* named after the current snapshot file.
|
||||
*
|
||||
* If there are multiple snapshot files inside the directory, then
|
||||
* the first one will be used.
|
||||
*/
|
||||
protected async migrateSnapshotFile(snapshotPath: string): Promise<void> {
|
||||
const entries = await readDir(dirname(snapshotPath), {
|
||||
ignoreMissing: true,
|
||||
})
|
||||
|
||||
/**
|
||||
* We assume all JSON files are snapshot files in this directory
|
||||
*/
|
||||
const snapshotFile = entries.find(
|
||||
(entry) => entry.isFile() && entry.name.endsWith(".json")
|
||||
)
|
||||
|
||||
if (snapshotFile) {
|
||||
const absoluteName = join(snapshotFile.path, snapshotFile.name)
|
||||
if (absoluteName !== snapshotPath) {
|
||||
await rename(absoluteName, snapshotPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a default snapshot file if it does not already exists. This
|
||||
* prevent from creating a database to manage the migrations and instead
|
||||
|
||||
@@ -134,4 +134,50 @@ describe("Generate migrations", () => {
|
||||
|
||||
expect(run1.fileName).not.toEqual(run2.fileName)
|
||||
})
|
||||
|
||||
test("rename existing snapshot file to the new filename", async () => {
|
||||
await fs.createJson(".snapshot-foo.json", {
|
||||
tables: [],
|
||||
namespaces: [],
|
||||
})
|
||||
|
||||
function run(entities: DmlEntity<any, any>[]) {
|
||||
const config = defineMikroOrmCliConfig(moduleName, {
|
||||
entities,
|
||||
dbName: dbName,
|
||||
migrations: {
|
||||
path: fs.basePath,
|
||||
},
|
||||
...pgGodCredentials,
|
||||
})
|
||||
|
||||
const migrations = new Migrations(config)
|
||||
return migrations.generate()
|
||||
}
|
||||
|
||||
const User = model.define("User", {
|
||||
id: model.id().primaryKey(),
|
||||
email: model.text().unique(),
|
||||
fullName: model.text().nullable(),
|
||||
})
|
||||
|
||||
const run1 = await run([User])
|
||||
expect(await fs.exists(run1.fileName))
|
||||
expect(await fs.exists(".snapshot-foo.json")).toBeFalsy()
|
||||
expect(
|
||||
await fs.exists(".snapshot-medusa-my-test-generate.json")
|
||||
).toBeTruthy()
|
||||
|
||||
const Car = model.define("Car", {
|
||||
id: model.id().primaryKey(),
|
||||
name: model.text(),
|
||||
})
|
||||
|
||||
await setTimeout(1000)
|
||||
|
||||
const run2 = await run([User, Car])
|
||||
expect(await fs.exists(run2.fileName))
|
||||
|
||||
expect(run1.fileName).not.toEqual(run2.fileName)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -26,6 +26,7 @@ describe("defineMikroOrmCliConfig", () => {
|
||||
dbName: "medusa-fulfillment",
|
||||
migrations: {
|
||||
generator: expect.any(Function),
|
||||
snapshotName: ".snapshot-medusa-my-test",
|
||||
},
|
||||
})
|
||||
})
|
||||
@@ -44,6 +45,7 @@ describe("defineMikroOrmCliConfig", () => {
|
||||
password: "",
|
||||
migrations: {
|
||||
generator: expect.any(Function),
|
||||
snapshotName: ".snapshot-medusa-my-test",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,6 +3,7 @@ import { mikroOrmCreateConnection } from "../../dal"
|
||||
import { loadDatabaseConfig } from "../load-module-database-config"
|
||||
import { Migrations } from "../../migrations"
|
||||
import { toMikroOrmEntities } from "../../dml"
|
||||
import { kebabCase } from "../../common/to-kebab-case"
|
||||
|
||||
const TERMINAL_SIZE = process.stdout.columns
|
||||
|
||||
@@ -42,7 +43,12 @@ export function buildGenerateMigrationScript({
|
||||
|
||||
const normalizedModels = toMikroOrmEntities(models)
|
||||
const orm = await mikroOrmCreateConnection(
|
||||
dbData,
|
||||
{
|
||||
...dbData,
|
||||
snapshotName: `.snapshot-${kebabCase(
|
||||
moduleName.replace("Service", "")
|
||||
)}`,
|
||||
},
|
||||
normalizedModels,
|
||||
pathToMigrations
|
||||
)
|
||||
|
||||
@@ -61,6 +61,7 @@ export function defineMikroOrmCliConfig(
|
||||
...(options as any),
|
||||
entities: entities.filter(Boolean),
|
||||
migrations: {
|
||||
snapshotName: `.snapshot-${databaseName}`,
|
||||
generator: CustomTsMigrationGenerator,
|
||||
...options.migrations,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user