UNPKG

perfect-validator

Version:

A TypeScript-based validation library that supports both static and dynamic validation with serializable models.

169 lines (142 loc) 5.01 kB
import { Db } from 'mongodb'; import { PerfectValidator } from '../types'; import { serializeValidationModel } from '../utils'; export class MongoStorage implements PerfectValidator.IModelStorage { private db: Db; private defaultCollection = 'validation_models'; constructor(db: Db) { this.db = db; // Check for required indexes instead of creating them this.checkRequiredIndexes(); } /** * Checks if any indexes exist on the collection beyond the default _id index * This allows index creation to be handled externally (e.g., by bash scripts) */ private async checkRequiredIndexes(): Promise<void> { try { const collection = this.getCollection(); const indexInfo = await collection.indexInformation(); // Count indexes - MongoDB always has at least the _id index const indexCount = Object.keys(indexInfo).length; // If only the default _id index exists if (indexCount <= 1) { console.warn('================================================================='); console.warn('WARNING: No custom indexes found on validation_models collection'); console.warn('Suggested indexes:'); console.warn('1. { name: 1, version: 1 } with unique constraint'); console.warn('2. { name: 1, version: -1 } for latest version queries'); console.warn('================================================================='); } } catch (error) { console.error('Failed to check indexes:', error); } } private getCollection(collection?: string) { return this.db.collection(collection || this.defaultCollection); } async getModel(modelName: string): Promise<string | null> { // Get the model with the highest version const doc = await this.getCollection() .findOne({ name: modelName }, { sort: { version: -1 } }) return doc ? doc.model : null; } async storeModelVersion( modelName: string, serializedModel: string, version: number, collection?: string ): Promise<void> { const col = this.getCollection(collection); // Check if version exists const existingVersion = await col.findOne({ name: modelName, version: version, }); if (existingVersion) { throw new Error( `Version ${version} already exists for model ${modelName}` ); } // Insert the new version without isLatest flag await col.insertOne({ name: modelName, version: version, model: serializedModel, createdAt: new Date(), }); } async getModelVersion( modelName: string, version: number, collection?: string ): Promise<PerfectValidator.ModelVersion | null> { const doc = await this.getCollection(collection).findOne({ name: modelName, version: version, }); if (!doc || !doc.model || Object.keys(doc.model).length === 0) return null; return { version: doc.version, model: doc.model, createdAt: doc.createdAt, }; } async getLatestModelVersion( modelName: string, collection?: string ): Promise<PerfectValidator.ModelVersion | null> { // Create a cursor to find the model with the highest version number const cursor = this.getCollection(collection) .find({ name: modelName }) .sort({ version: -1 }) .limit(1); // Check if there are any results if (await cursor.hasNext()) { const doc = await cursor.next(); if (doc && doc.model && Object.keys(doc.model).length > 0) { return { version: doc.version, model: doc.model, createdAt: doc.createdAt, }; } } return null; } async listModelVersions( modelName: string, collection?: string ): Promise<number[]> { const cursor = this.getCollection(collection) .find({ name: modelName }, { projection: { version: 1 } }) .sort({ version: -1 }); const versions: number[] = []; // Use cursor.hasNext() for explicit cursor navigation while (await cursor.hasNext()) { const doc = await cursor.next(); versions.push(doc.version); } return versions; } // These methods are now just aliases for versioned operations async insertModel( modelName: string, model: PerfectValidator.ValidationModel ): Promise<void> { const serializedModel = serializeValidationModel(model); await this.storeModelVersion(modelName, serializedModel, 1); } async updateModel( modelName: string, model: PerfectValidator.ValidationModel ): Promise<void> { const serializedModel = serializeValidationModel(model); const latestVersion = await this.getLatestModelVersion(modelName); const newVersion = latestVersion ? latestVersion.version + 1 : 1; await this.storeModelVersion(modelName, serializedModel, newVersion); } async deleteModel(modelName: string): Promise<void> { await this.getCollection().deleteMany({ name: modelName }); } }