UNPKG

papr

Version:

MongoDB TypeScript-aware Models

154 lines (153 loc) 5.37 kB
import { Db } from 'mongodb'; import { abstract, build } from "./model.js"; import types from "./types.js"; export default class Papr { db; // eslint-disable-next-line @typescript-eslint/no-explicit-any models = new Map(); options; // eslint-disable-next-line @typescript-eslint/no-explicit-any schemas = new Map(); /** * Returns a new instance of `Papr`. * * It may be called with some options for before and after [hooks](api/hooks.md) and a maximum execution time for queries. * * @name Papr * * @param [options] {ModelOptions} * @param [options.hooks] {Hooks} * @param [options.maxTime] {number} * * @example * const papr = new Papr(); * * const paprWithOptions = new Papr({ * hooks: { * after: [afterHook], * before: [beforeHook] * }, * maxTime: 1000 * }); */ constructor(options) { this.options = options; } /** * Initialize existing and future registered models with a mongo db instance. * * @param db {mongodb.Db} * * @example * import { MongoClient } from 'mongodb'; * * const connection = await MongoClient.connect('mongodb://localhost:27017'); * * papr.initialize(connection.db('test')); */ initialize(db) { if (this.db) { return; } this.db = db; // If we have models defined before initializing a database, we build them now for (const [collectionName, collectionSchema] of this.schemas.entries()) { const model = this.models.get(collectionName); if (model && !model.collection) { build(collectionSchema, model, this.db.collection(collectionName), this.options); } } } /** * Builds a model instance and associates its collection name and schema. * * @param collectionName {string} * @param collectionSchema {TSchema} * * @returns {Model<TSchema, TOptions>} * * @example * const User = papr.model('users', userSchema); */ model(collectionName, collectionSchema) { const model = abstract(collectionSchema); if (this.db) { build(collectionSchema, model, this.db.collection(collectionName), this.options); } this.models.set(collectionName, model); this.schemas.set(collectionName, collectionSchema); return model; } /** * Updates the validation schema and validation options on the MongoDB collection used by a model. * * It uses the [`createCollection`](https://docs.mongodb.com/manual/reference/method/db.createCollection/) * method for new collections, and the [`collMod`](https://docs.mongodb.com/manual/reference/command/collMod/#dbcmd.collMod) * command for existing collections. * * @param model {Model<TSchema, TOptions>} * * @returns {Promise<void>} * * @example * await papr.updateSchema(User); */ async updateSchema(model) { if (!this.db) { throw new Error('The DB is not connected'); } if (!model.collection) { throw new Error('The model collection is not initialized'); } const { collectionName } = model.collection; const { // @ts-expect-error We're defining the defaults in the JSON Schema object, but TS sees these as `any` $defaults, // @ts-expect-error We're defining these timestamp options in the JSON Schema object, but TS sees these as `any` $timestamps, // @ts-expect-error We're defining these validation options in the JSON Schema object, but TS sees these as `any` $validationAction: validationAction, // @ts-expect-error We're defining these validation options in the JSON Schema object, but TS sees these as `any` $validationLevel: validationLevel, ...schema } = model.schema; const options = { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment validationAction, // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment validationLevel, validator: { $jsonSchema: schema, }, }; const collections = await this.db.collections(); const exists = collections.find((item) => item.collectionName === collectionName); if (exists) { await this.db.command({ collMod: collectionName, ...options, }); return; } await this.db.createCollection(collectionName, options); } /** * Updates the validation schemas and validation options on all the MongoDB collections registered by models. * * @returns {Promise<void>} * * @example * await papr.updateSchemas(); */ async updateSchemas() { if (!this.db) { throw new Error('The DB is not connected'); } // prettier-ignore await Promise.all([...this.models.values()].map(model => this.updateSchema(model))); } } export { types, types as Types }; export * from "./hooks.js"; export * from "./model.js"; export * from "./mongodbTypes.js"; export * from "./schema.js"; export * from "./utils.js";