diff --git a/.changeset/shy-pugs-cough.md b/.changeset/shy-pugs-cough.md new file mode 100644 index 0000000000..9b83ee0c8d --- /dev/null +++ b/.changeset/shy-pugs-cough.md @@ -0,0 +1,5 @@ +--- +"@medusajs/utils": patch +--- + +feat: use module name as the snapshot name diff --git a/packages/core/utils/src/dal/mikro-orm/mikro-orm-create-connection.ts b/packages/core/utils/src/dal/mikro-orm/mikro-orm-create-connection.ts index 0a3574997f..99a892b88f 100644 --- a/packages/core/utils/src/dal/mikro-orm/mikro-orm-create-connection.ts +++ b/packages/core/utils/src/dal/mikro-orm/mikro-orm-create-connection.ts @@ -59,6 +59,7 @@ export type Filter = { export async function mikroOrmCreateConnection( database: ModuleServiceInitializeOptions["database"] & { connection?: any + snapshotName?: string filters?: Record }, entities: any[], @@ -99,6 +100,7 @@ export async function mikroOrmCreateConnection( migrations: { disableForeignKeys: false, path: pathToMigrations, + snapshotName: database.snapshotName, generator: CustomTsMigrationGenerator, silent: !( database.debug ?? diff --git a/packages/core/utils/src/migrations/index.ts b/packages/core/utils/src/migrations/index.ts index 3b3c956469..236edd535d 100644 --- a/packages/core/utils/src/migrations/index.ts +++ b/packages/core/utils/src/migrations/index.ts @@ -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 { 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 { } } + /** + * 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 { + 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 diff --git a/packages/core/utils/src/migrations/integration-tests/__tests__/migrations-generate.spec.ts b/packages/core/utils/src/migrations/integration-tests/__tests__/migrations-generate.spec.ts index 1005cc6e83..b60a29b6d6 100644 --- a/packages/core/utils/src/migrations/integration-tests/__tests__/migrations-generate.spec.ts +++ b/packages/core/utils/src/migrations/integration-tests/__tests__/migrations-generate.spec.ts @@ -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[]) { + 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) + }) }) diff --git a/packages/core/utils/src/modules-sdk/__tests__/mikro-orm-cli-config-builder.spec.ts b/packages/core/utils/src/modules-sdk/__tests__/mikro-orm-cli-config-builder.spec.ts index 2c47ce8d92..998ed3faea 100644 --- a/packages/core/utils/src/modules-sdk/__tests__/mikro-orm-cli-config-builder.spec.ts +++ b/packages/core/utils/src/modules-sdk/__tests__/mikro-orm-cli-config-builder.spec.ts @@ -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", }, }) }) diff --git a/packages/core/utils/src/modules-sdk/migration-scripts/migration-generate.ts b/packages/core/utils/src/modules-sdk/migration-scripts/migration-generate.ts index b682be3606..b28ed6e534 100644 --- a/packages/core/utils/src/modules-sdk/migration-scripts/migration-generate.ts +++ b/packages/core/utils/src/modules-sdk/migration-scripts/migration-generate.ts @@ -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 ) diff --git a/packages/core/utils/src/modules-sdk/mikro-orm-cli-config-builder.ts b/packages/core/utils/src/modules-sdk/mikro-orm-cli-config-builder.ts index 1dd9defde4..f5e41e46e7 100644 --- a/packages/core/utils/src/modules-sdk/mikro-orm-cli-config-builder.ts +++ b/packages/core/utils/src/modules-sdk/mikro-orm-cli-config-builder.ts @@ -61,6 +61,7 @@ export function defineMikroOrmCliConfig( ...(options as any), entities: entities.filter(Boolean), migrations: { + snapshotName: `.snapshot-${databaseName}`, generator: CustomTsMigrationGenerator, ...options.migrations, },