fix: unhance migration logic

This commit is contained in:
yassinedorbozgithub 2025-01-04 10:00:29 +01:00
parent a1765b647b
commit e0d2388e95
3 changed files with 53 additions and 44 deletions

View File

@ -28,6 +28,7 @@ import {
MigrationAction, MigrationAction,
MigrationRunParams, MigrationRunParams,
MigrationSuccessCallback, MigrationSuccessCallback,
MigrationTrigger,
} from './types'; } from './types';
@Injectable() @Injectable()
@ -146,23 +147,18 @@ module.exports = {
}: MigrationRunParams) { }: MigrationRunParams) {
if (!name) { if (!name) {
if (isAutoMigrate) { if (isAutoMigrate) {
const newVersion = await this.runFromVersion(action, version); await this.runFromVersion(action, version);
await this.metadataService.findOrCreate({
name: 'db-version',
value: newVersion,
});
} else { } else {
await this.runAll(action); await this.runAll(action);
this.exit(); this.exit();
} }
} else { } else {
await this.runOne({ action, name }); await this.runOne({ action, name, trigger: MigrationTrigger.CLI });
this.exit(); this.exit();
} }
} }
private async runOne({ name, action }: MigrationRunParams) { private async runOne({ name, action, trigger }: MigrationRunParams) {
// verify DB status // verify DB status
const { exist, migrationDocument } = await this.verifyStatus({ const { exist, migrationDocument } = await this.verifyStatus({
name, name,
@ -179,6 +175,7 @@ module.exports = {
name, name,
action, action,
migrationDocument, migrationDocument,
trigger,
}); });
} catch (e) { } catch (e) {
this.failureCallback({ this.failureCallback({
@ -216,37 +213,27 @@ module.exports = {
} }
private async runFromVersion(action: MigrationAction, version: string) { private async runFromVersion(action: MigrationAction, version: string) {
const files = await this.getDirFiles(); const filenames = await this.getDirFiles();
const migrationFiles = files const versions = this.getVersionsFromFilenames(filenames);
.filter((fileName) => fileName.endsWith('.migration.js')) const filteredVersions = versions.filter(
.map((fileName) => { (v) => v === version || this.isNewerVersion(v, version),
const [migrationFileName] = fileName.split('.'); );
const [, , ...migrationVersion] = migrationFileName.split('-');
return `v${migrationVersion.join('.')}`;
})
.filter((v) => this.isNewerVersion(v, version));
let lastVersion = version; let lastVersion = version;
for (const name of migrationFiles) {
await this.runOne({ name, action }); for (const version of filteredVersions) {
lastVersion = name; await this.runOne({ name: version, action });
lastVersion = version;
} }
return lastVersion; return lastVersion;
} }
private async runAll(action: MigrationAction) { private async runAll(action: MigrationAction) {
const files = await this.getDirFiles(); const filenames = await this.getDirFiles();
const migrationFiles = files const versions = this.getVersionsFromFilenames(filenames);
.filter((fileName) => fileName.includes('migration'))
.map((fileName) => {
const [migrationFileName] = fileName.split('.');
const [, , ...migrationVersion] = migrationFileName.split('-');
return `v${migrationVersion.join('.')}`;
});
for (const name of migrationFiles) { for (const version of versions) {
await this.runOne({ name, action }); await this.runOne({ name: version, action });
} }
} }
@ -285,6 +272,16 @@ module.exports = {
return migrationName; return migrationName;
} }
private getVersionsFromFilenames(filenames: string[]) {
return filenames
.filter((fileName) => fileName.endsWith('.migration.js'))
.map((filename: string) => {
const [migrationFileName] = filename.split('.');
const [, , ...migrationVersion] = migrationFileName.split('-');
return `v${migrationVersion.join('.')}`;
});
}
async findMigrationFileByName(name: string): Promise<string | null> { async findMigrationFileByName(name: string): Promise<string | null> {
const files = await this.getMigrationFiles(); const files = await this.getMigrationFiles();
return ( return (
@ -342,10 +339,19 @@ module.exports = {
name, name,
action, action,
migrationDocument, migrationDocument,
trigger,
}: MigrationSuccessCallback) { }: MigrationSuccessCallback) {
await this.updateStatus({ name, action, migrationDocument }); await this.updateStatus({ name, action, migrationDocument });
const migrationDisplayName = `${name} [${action}]`; const migrationDisplayName = `${name} [${action}]`;
this.logger.log(`"${migrationDisplayName}" migration done`); this.logger.log(`"${migrationDisplayName}" migration done`);
if (trigger === MigrationTrigger.CLI) {
const result = await this.metadataService.createOrUpdate({
name: 'db-version',
value: name,
});
const operation = result ? 'updated' : 'created';
this.logger.log(`db-version metadata ${operation} "${name}"`);
}
} }
private failureCallback({ name, action }: MigrationRunParams) { private failureCallback({ name, action }: MigrationRunParams) {

View File

@ -13,15 +13,26 @@ enum MigrationAction {
DOWN = 'down', DOWN = 'down',
} }
enum MigrationTrigger {
CLI = 'cli',
PROGRAM = 'program',
}
interface MigrationRunParams { interface MigrationRunParams {
name?: string; name?: string;
action: MigrationAction; action: MigrationAction;
version?: string; version?: string;
isAutoMigrate?: boolean; isAutoMigrate?: boolean;
trigger?: MigrationTrigger;
} }
interface MigrationSuccessCallback extends MigrationRunParams { interface MigrationSuccessCallback extends MigrationRunParams {
migrationDocument: MigrationDocument; migrationDocument: MigrationDocument;
} }
export { MigrationAction, MigrationRunParams, MigrationSuccessCallback }; export {
MigrationAction,
MigrationRunParams,
MigrationSuccessCallback,
MigrationTrigger,
};

View File

@ -19,18 +19,10 @@ export class MetadataService {
private readonly metadataModel: Model<Metadata>, private readonly metadataModel: Model<Metadata>,
) {} ) {}
async createMetadata(dto: Partial<Metadata>) { async createOrUpdate(dto: Metadata) {
return await this.metadataModel.create(dto); return await this.metadataModel.findOneAndUpdate({ name: dto.name }, dto, {
} upsert: true,
});
async findOrCreate(dto: Partial<Metadata>) {
const metadata = await this.metadataModel.findOne({ name: dto.name });
if (metadata) {
await this.setMetadata(dto.name, dto.value);
} else {
await this.createMetadata(dto);
}
} }
async getMetadata(name: string) { async getMetadata(name: string) {