UNPKG

@hosoft/restful-api-framework

Version:

Base framework of the headless cms HoServer provided by http://helloreact.cn

195 lines (178 loc) 8.5 kB
/* eslint-disable handle-callback-err,max-len */ // Module Scope const extend = require('extend') const mongoose = require('mongoose') let counterSchema let IdentityCounter // Initialize plugin by creating counter collection in database. exports.initialize = function (connection) { try { IdentityCounter = connection.model('IdentityCounter') } catch (ex) { if (ex.name === 'MissingSchemaError') { // Create new counter schema. counterSchema = new mongoose.Schema({ model: { type: String, require: true }, field: { type: String, require: true }, count: { type: Number, default: 0 } }) // Create a unique index using the "field" and "modelMeta" fields. counterSchema.index({ field: 1, model: 1 }, { unique: true, required: true, index: -1 }) // Create modelMeta using new schema. IdentityCounter = connection.model('IdentityCounter', counterSchema) } else { throw ex } } } // The function to use when invoking the plugin on a custom schema. exports.plugin = function (schema, options) { // If we don't have reference to the counterSchema or the IdentityCounter modelMeta then the plugin was most // likely not initialized properly so throw an error. if (!counterSchema || !IdentityCounter) throw new Error('mongoose-auto-increment has not been initialized') // Default settings and plugin scope variables. const settings = { model: null, // The modelMeta to configure the plugin for. field: 'id', // The field the plugin should track. startAt: 0, // The number the count should start at. incrementBy: 1, // The number by which to increment the count each time. unique: true // Should we create a unique index for the field } const fields = {} // A hash of fields to add properties to in Mongoose. let ready = false // True if the counter collection has been updated and the document is ready to be saved. switch (typeof options) { // If string, the user chose to pass in just the modelMeta name. case 'string': settings.model = options break // If object, the user passed in a hash of options. case 'object': extend(settings, options) break } if (settings.model == null) { throw new Error('modelMeta must be set') } // Add properties for field in schema. fields[settings.field] = { type: Number, require: true } if (settings.field !== 'id') { fields[settings.field].unique = settings.unique } schema.add(fields) // Find the counter for this modelMeta and the relevant field. // eslint-disable-next-line node/handle-callback-err IdentityCounter.findOne({ model: settings.model, field: settings.field }, function (err, counter) { if (!counter) { // If no counter exists then create one and save it. counter = new IdentityCounter({ model: settings.model, field: settings.field, count: settings.startAt - settings.incrementBy }) counter.save(function () { ready = true }) } else { ready = true } }) // Declare a function to get the next counter for the modelMeta/schema. const nextCount = function (callback) { IdentityCounter.findOne( { model: settings.model, field: settings.field }, function (err, counter) { if (err) return callback(err) callback(null, counter === null ? settings.startAt : counter.count + settings.incrementBy) } ) } // Add nextCount as both a method on documents and a static on the schema for convenience. schema.method('nextCount', nextCount) schema.static('nextCount', nextCount) // Declare a function to reset counter at the start value - increment value. const resetCount = function (callback) { IdentityCounter.findOneAndUpdate( { model: settings.model, field: settings.field }, { count: settings.startAt - settings.incrementBy }, { new: true }, // new: true specifies that the callback should get the updated counter. function (err) { if (err) return callback(err) callback(null, settings.startAt) } ) } // Add resetCount as both a method on documents and a static on the schema for convenience. schema.method('resetCount', resetCount) schema.static('resetCount', resetCount) // Every time documents in this schema are saved, run this logic. schema.pre('save', function (next) { // Get reference to the document being saved. const doc = this // Only do this if it is a new document (see http://mongoosejs.com/docs/api.html#document_Document-isNew) if (doc.isNew) { // Declare self-invoking save function. ;(function save() { // If ready, run increment logic. // Note: ready is true when an existing counter collection is found or after it is created for the // first time. if (ready) { // check that a number has already been provided, and update the counter to that number if it is // greater than the current count if (typeof doc[settings.field] === 'number') { IdentityCounter.findOneAndUpdate( // IdentityCounter documents are identified by the modelMeta and field that the plugin was invoked for. // Check also that count is less than field value. { model: settings.model, field: settings.field, count: { $lt: doc[settings.field] } }, // Change the count of the value found to the new field value. { count: doc[settings.field] }, function (err) { if (err) return next(err) // Continue with default document save functionality. next() } ) } else { // Find the counter collection entry for this modelMeta and field and update it. IdentityCounter.findOneAndUpdate( // IdentityCounter documents are identified by the modelMeta and field that the plugin was invoked for. { model: settings.model, field: settings.field }, // Increment the count by `incrementBy`. { $inc: { count: settings.incrementBy } }, // new:true specifies that the callback should get the counter AFTER it is updated (incremented). { new: true }, // Receive the updated counter. function (err, updatedIdentityCounter) { if (err) return next(err) // If there are no errors then go ahead and set the document's field to the current count. doc[settings.field] = updatedIdentityCounter.count // Continue with default document save functionality. next() } ) } } // If not ready then set a 5 millisecond timer and try to save again. It will keep doing this until // the counter collection is ready. else { setTimeout(save, 5) } })() } // If the document does not have the field we're interested in or that field isn't a number AND the user did // not specify that we should increment on updates, then just continue the save without any increment logic. else { next() } }) }