Skip to content

Commit

Permalink
feat: export function for reading/writing model/collection versions d…
Browse files Browse the repository at this point in the history
…irectly
  • Loading branch information
hellivan committed Aug 27, 2020
1 parent 98854a6 commit 9e144eb
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 46 deletions.
19 changes: 18 additions & 1 deletion src/abstract-migrator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class TestVersionStorage implements VersionStorage {
}

describe('AbstractMigrator', () => {
test('Migrator should not update is existing version matches target version', async () => {
test('Migrator should not update if existing version matches target version', async () => {
const initialVersion = { current: 1, updated: new Date() };
const versionStorage = new TestVersionStorage(initialVersion);
const migrator = new TestMigrator(versionStorage);
Expand All @@ -50,4 +50,21 @@ describe('AbstractMigrator', () => {
expect(writeVersionSpy).toHaveBeenCalledTimes(0);
expect(readVersionSpy).toHaveBeenCalledTimes(1);
});

test('Migrator should update if current version is lower than target version', async () => {
const initialVersion = { current: 1, updated: new Date() };
const versionStorage = new TestVersionStorage(initialVersion);
const migrator = new TestMigrator(versionStorage);
const upgradeSpy = jest.spyOn(migrator, 'upgrade');
const downgradeSpy = jest.spyOn(migrator, 'downgrade');
const writeVersionSpy = jest.spyOn(versionStorage, 'writeVersion');
const readVersionSpy = jest.spyOn(versionStorage, 'readVersion');

const result = await migrator.migrate(2);
expect(result).toMatchObject({ last: 1, current: 2, updated: expect.any(Date) });
expect(upgradeSpy).toHaveBeenCalledTimes(1);
expect(downgradeSpy).toHaveBeenCalledTimes(0);
expect(writeVersionSpy).toHaveBeenCalledTimes(1);
expect(readVersionSpy).toHaveBeenCalledTimes(1);
});
});
16 changes: 12 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
export { AbstractMigrator, VersionInformation, VersionStorage } from './abstract-migrator';
export { CollectionMigrationHandler } from './mongodb-collection-migrator';
export { ModelMigrationHandler } from './mongoose-model-migrator';
export {
CollectionMigrationHandler,
CollectionMigratorOptions,
migrateCollection
} from './mongodb-collection-migrator';
export { ModelMigrationHandler, ModelMigratorOptions, migrateModel } from './mongoose-model-migrator';
migrateCollection,
readCollectionVersion,
writeCollectionVersion
} from './mongodb-collection-versioning-utils';
export {
ModelMigratorOptions,
migrateModel,
readModelVersion,
writeModelVersion
} from './mongoose-model-versioning-utils';
18 changes: 0 additions & 18 deletions src/mongodb-collection-migrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Collection, Db } from 'mongodb';

import { AbstractMigrator } from './abstract-migrator';
import { MongodbCollectionVersionStorage } from './mongodb-collection-version-storage';
import { getGlobalMongooseConnectionDb } from './utils';

// TODO: remove
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -31,20 +30,3 @@ export class CollectionMigrator<TCollectionSchema = any> extends AbstractMigrato
throw new Error(`Downgrading a collection from version ${fromVersion} to ${toVersion} not supported yet!`);
}
}

export interface CollectionMigratorOptions {
db?: Db;
versionCollectionName?: string;
}

export async function migrateCollection(
collectionName: string,
version: number,
migrationHandler: CollectionMigrationHandler,
options?: CollectionMigratorOptions
): Promise<void> {
const versionCollectionName = options?.versionCollectionName || `${collectionName}.version`;
const db = options?.db ?? (await getGlobalMongooseConnectionDb());
const migrator = new CollectionMigrator(db, collectionName, versionCollectionName, migrationHandler);
await migrator.migrate(version);
}
33 changes: 33 additions & 0 deletions src/mongodb-collection-version-storage.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { MongodbCollectionVersionStorage } from './mongodb-collection-version-storage';
import { VersionInformation } from './abstract-migrator';

class VersionCollectionMock {
private versionInformation: VersionInformation | null;

constructor(versionInformation?: VersionInformation) {
this.versionInformation = versionInformation ?? null;
}

public async findOne(_filter: unknown): Promise<VersionInformation | null> {
return this.versionInformation;
}
public async insertOne(versionInformation: VersionInformation): Promise<void> {
this.versionInformation = versionInformation;
}
public async findOneAndUpdate(filter: unknown, updateOptions: { $set: VersionInformation }): Promise<void> {
this.versionInformation = { ...this.versionInformation, ...updateOptions.$set };
}
}

describe('MongodbCollectionVersionStorage', () => {
test('read version should return the current version information', async () => {
const currentVersion = { current: 1, updated: new Date() };
const collectionMock = new VersionCollectionMock(currentVersion);
const versionStorage = new MongodbCollectionVersionStorage(collectionMock as any);
const findOneSpy = jest.spyOn(collectionMock, 'findOne');

const resultVersion = await versionStorage.readVersion();
expect(resultVersion).toEqual(currentVersion);
expect(findOneSpy).toHaveBeenCalledWith({});
});
});
5 changes: 0 additions & 5 deletions src/mongodb-collection-version-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,3 @@ export class MongodbCollectionVersionStorage implements VersionStorage {
return this.versionCollection.findOne({});
}
}

/*protected getVersionCollection(): Collection<CollectionVersion> {
const collectionName = this.options?.versionCollectionName || `${this.collectionName}.version`;
return connection.db.collection<CollectionVersion>(collectionName);
}*/
50 changes: 50 additions & 0 deletions src/mongodb-collection-versioning-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Db } from 'mongodb';

import { VersionInformation } from './abstract-migrator';
import { CollectionMigrationHandler, CollectionMigrator } from './mongodb-collection-migrator';
import { MongodbCollectionVersionStorage } from './mongodb-collection-version-storage';
import { getGlobalMongooseConnectionDb } from './utils';

export interface CollectionMigratorOptions {
db?: Db;
versionCollectionName?: string;
}

async function sanitizeCollectionMigratorOptions(
collectionName: string,
options?: CollectionMigratorOptions
): Promise<Required<CollectionMigratorOptions>> {
const versionCollectionName = options?.versionCollectionName || `${collectionName}.version`;
const db = options?.db ?? (await getGlobalMongooseConnectionDb());
return { db, versionCollectionName };
}

export async function migrateCollection(
collectionName: string,
version: number,
migrationHandler: CollectionMigrationHandler,
options?: CollectionMigratorOptions
): Promise<void> {
const { db, versionCollectionName } = await sanitizeCollectionMigratorOptions(collectionName, options);
const migrator = new CollectionMigrator(db, collectionName, versionCollectionName, migrationHandler);
await migrator.migrate(version);
}

export async function writeCollectionVersion(
collectionName: string,
version: number,
options?: CollectionMigratorOptions
): Promise<VersionInformation> {
const { db, versionCollectionName } = await sanitizeCollectionMigratorOptions(collectionName, options);
const versionStorage = new MongodbCollectionVersionStorage(db.collection(versionCollectionName));
return versionStorage.writeVersion(version);
}

export async function readCollectionVersion(
collectionName: string,
options?: CollectionMigratorOptions
): Promise<VersionInformation | null> {
const { db, versionCollectionName } = await sanitizeCollectionMigratorOptions(collectionName, options);
const versionStorage = new MongodbCollectionVersionStorage(db.collection(versionCollectionName));
return versionStorage.readVersion();
}
18 changes: 0 additions & 18 deletions src/mongoose-model-migrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Db } from 'mongodb';

import { AbstractMigrator } from './abstract-migrator';
import { MongodbCollectionVersionStorage } from './mongodb-collection-version-storage';
import { getGlobalMongooseConnectionDb } from './utils';

export interface ModelMigrationHandler<TModelDocument extends Document> {
up(db: Db, model: Model<TModelDocument>, fromVersion: number, toVersion: number): Promise<void>;
Expand All @@ -28,20 +27,3 @@ export class ModelMigrator<TModelDocument extends Document> extends AbstractMigr
throw new Error(`Downgrading a model version from ${fromVersion} to ${toVersion} not supported yet!`);
}
}

export interface ModelMigratorOptions {
db?: Db;
versionCollectionName?: string;
}

export async function migrateModel<TModelDocument extends Document>(
model: Model<TModelDocument>,
version: number,
migrationHandler: ModelMigrationHandler<TModelDocument>,
options?: ModelMigratorOptions
): Promise<void> {
const versionCollectionName = options?.versionCollectionName || `${model.collection.collectionName}.version`;
const db = options?.db ?? (await getGlobalMongooseConnectionDb());
const migrator = new ModelMigrator(db, model, versionCollectionName, migrationHandler);
await migrator.migrate(version);
}
51 changes: 51 additions & 0 deletions src/mongoose-model-versioning-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Document, Model } from 'mongoose';
import { Db } from 'mongodb';

import { ModelMigrationHandler, ModelMigrator } from './mongoose-model-migrator';
import { getGlobalMongooseConnectionDb } from './utils';
import { VersionInformation } from './abstract-migrator';
import { MongodbCollectionVersionStorage } from './mongodb-collection-version-storage';

export interface ModelMigratorOptions {
db?: Db;
versionCollectionName?: string;
}

async function sanitizeModelMigratorOptions<TModelDocument extends Document>(
model: Model<TModelDocument>,
options?: ModelMigratorOptions
): Promise<Required<ModelMigratorOptions>> {
const versionCollectionName = options?.versionCollectionName || `${model.collection.collectionName}.version`;
const db = options?.db ?? (await getGlobalMongooseConnectionDb());
return { db, versionCollectionName };
}

export async function migrateModel<TModelDocument extends Document>(
model: Model<TModelDocument>,
version: number,
migrationHandler: ModelMigrationHandler<TModelDocument>,
options?: ModelMigratorOptions
): Promise<void> {
const { db, versionCollectionName } = await sanitizeModelMigratorOptions(model, options);
const migrator = new ModelMigrator(db, model, versionCollectionName, migrationHandler);
await migrator.migrate(version);
}

export async function writeModelVersion<TModelDocument extends Document>(
model: Model<TModelDocument>,
version: number,
options?: ModelMigratorOptions
): Promise<VersionInformation> {
const { db, versionCollectionName } = await sanitizeModelMigratorOptions(model, options);
const versionStorage = new MongodbCollectionVersionStorage(db.collection(versionCollectionName));
return versionStorage.writeVersion(version);
}

export async function readModelVersion<TModelDocument extends Document>(
model: Model<TModelDocument>,
options?: ModelMigratorOptions
): Promise<VersionInformation | null> {
const { db, versionCollectionName } = await sanitizeModelMigratorOptions(model, options);
const versionStorage = new MongodbCollectionVersionStorage(db.collection(versionCollectionName));
return versionStorage.readVersion();
}

0 comments on commit 9e144eb

Please sign in to comment.