mirror of
				https://github.com/strapi/strapi.git
				synced 2025-10-31 09:56:44 +00:00 
			
		
		
		
	init schema mongoose
This commit is contained in:
		
							parent
							
								
									c8a509a223
								
							
						
					
					
						commit
						b24d88f740
					
				| @ -30,53 +30,54 @@ const defaults = { | |||||||
|   host: 'localhost', |   host: 'localhost', | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | const isBookshelfConnection = ({ connector }) => | ||||||
|  |   connector === 'strapi-hook-bookshelf'; | ||||||
|  | 
 | ||||||
| module.exports = function(strapi) { | module.exports = function(strapi) { | ||||||
|   function initialize(cb) { |   function initialize(cb) { | ||||||
|  |     const { connections } = strapi.config; | ||||||
|     const GLOBALS = {}; |     const GLOBALS = {}; | ||||||
| 
 | 
 | ||||||
|     const connections = _.pickBy( |     const connectionsPromises = Object.keys(connections) | ||||||
|       strapi.config.connections, |       .filter(key => isBookshelfConnection(connections[key])) | ||||||
|       ({ connector }) => connector === 'strapi-hook-bookshelf' |       .map(connectionName => { | ||||||
|     ); |         const connection = connections[connectionName]; | ||||||
| 
 | 
 | ||||||
|     const connectionsPromises = Object.keys(connections).map(connectionName => { |         _.defaults(connection.settings, strapi.config.hook.settings.bookshelf); | ||||||
|       const connection = connections[connectionName]; |  | ||||||
|       _.defaults(connection.settings, strapi.config.hook.settings.bookshelf); |  | ||||||
| 
 | 
 | ||||||
|       // Create Bookshelf instance for this connection.
 |         // Create Bookshelf instance for this connection.
 | ||||||
|       const ORM = new bookshelf(strapi.connections[connectionName]); |         const ORM = new bookshelf(strapi.connections[connectionName]); | ||||||
| 
 | 
 | ||||||
|       const initFunctionPath = path.resolve( |         const initFunctionPath = path.resolve( | ||||||
|         strapi.config.appPath, |           strapi.config.appPath, | ||||||
|         'config', |           'config', | ||||||
|         'functions', |           'functions', | ||||||
|         'bookshelf.js' |           'bookshelf.js' | ||||||
|       ); |         ); | ||||||
| 
 | 
 | ||||||
|       if (fs.existsSync(initFunctionPath)) { |         if (fs.existsSync(initFunctionPath)) { | ||||||
|         // Require `config/functions/bookshelf.js` file to customize connection.
 |           require(initFunctionPath)(ORM, connection); | ||||||
|         require(initFunctionPath)(ORM, connection); |         } | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       // Load plugins
 |         // Load plugins
 | ||||||
|       if (_.get(connection, 'options.plugins', true) !== false) { |         if (_.get(connection, 'options.plugins', true) !== false) { | ||||||
|         ORM.plugin('visibility'); |           ORM.plugin('visibility'); | ||||||
|         ORM.plugin('pagination'); |           ORM.plugin('pagination'); | ||||||
|       } |         } | ||||||
| 
 | 
 | ||||||
|       const ctx = { |         const ctx = { | ||||||
|         GLOBALS, |           GLOBALS, | ||||||
|         connection, |           connection, | ||||||
|         ORM, |           ORM, | ||||||
|       }; |         }; | ||||||
| 
 | 
 | ||||||
|       return Promise.all([ |         return Promise.all([ | ||||||
|         mountGroups(connectionName, ctx), |           mountGroups(connectionName, ctx), | ||||||
|         mountApis(connectionName, ctx), |           mountApis(connectionName, ctx), | ||||||
|         mountAdmin(connectionName, ctx), |           mountAdmin(connectionName, ctx), | ||||||
|         mountPlugins(connectionName, ctx), |           mountPlugins(connectionName, ctx), | ||||||
|       ]); |         ]); | ||||||
|     }); |       }); | ||||||
| 
 | 
 | ||||||
|     return Promise.all(connectionsPromises).then(() => cb(), err => cb(err)); |     return Promise.all(connectionsPromises).then(() => cb(), err => cb(err)); | ||||||
|   } |   } | ||||||
|  | |||||||
							
								
								
									
										74
									
								
								packages/strapi-hook-mongoose/lib/get-query-params.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								packages/strapi-hook-mongoose/lib/get-query-params.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | |||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | const _ = require('lodash'); | ||||||
|  | 
 | ||||||
|  | module.exports = (value, type, key) => { | ||||||
|  |   const result = {}; | ||||||
|  | 
 | ||||||
|  |   switch (type) { | ||||||
|  |     case '=': | ||||||
|  |       result.key = `where.${key}`; | ||||||
|  |       result.value = value; | ||||||
|  |       break; | ||||||
|  |     case '_ne': | ||||||
|  |       result.key = `where.${key}.$ne`; | ||||||
|  |       result.value = value; | ||||||
|  |       break; | ||||||
|  |     case '_lt': | ||||||
|  |       result.key = `where.${key}.$lt`; | ||||||
|  |       result.value = value; | ||||||
|  |       break; | ||||||
|  |     case '_gt': | ||||||
|  |       result.key = `where.${key}.$gt`; | ||||||
|  |       result.value = value; | ||||||
|  |       break; | ||||||
|  |     case '_lte': | ||||||
|  |       result.key = `where.${key}.$lte`; | ||||||
|  |       result.value = value; | ||||||
|  |       break; | ||||||
|  |     case '_gte': | ||||||
|  |       result.key = `where.${key}.$gte`; | ||||||
|  |       result.value = value; | ||||||
|  |       break; | ||||||
|  |     case '_sort': | ||||||
|  |       result.key = 'sort'; | ||||||
|  |       result.value = _.toLower(value) === 'desc' ? '-' : ''; | ||||||
|  |       result.value += key; | ||||||
|  |       break; | ||||||
|  |     case '_start': | ||||||
|  |       result.key = 'start'; | ||||||
|  |       result.value = parseFloat(value); | ||||||
|  |       break; | ||||||
|  |     case '_limit': | ||||||
|  |       result.key = 'limit'; | ||||||
|  |       result.value = parseFloat(value); | ||||||
|  |       break; | ||||||
|  |     case '_populate': | ||||||
|  |       result.key = 'populate'; | ||||||
|  |       result.value = value; | ||||||
|  |       break; | ||||||
|  |     case '_contains': | ||||||
|  |       result.key = `where.${key}`; | ||||||
|  |       result.value = { | ||||||
|  |         $regex: value, | ||||||
|  |         $options: 'i', | ||||||
|  |       }; | ||||||
|  |       break; | ||||||
|  |     case '_containss': | ||||||
|  |       result.key = `where.${key}.$regex`; | ||||||
|  |       result.value = value; | ||||||
|  |       break; | ||||||
|  |     case '_in': | ||||||
|  |       result.key = `where.${key}.$in`; | ||||||
|  |       result.value = _.castArray(value); | ||||||
|  |       break; | ||||||
|  |     case '_nin': | ||||||
|  |       result.key = `where.${key}.$nin`; | ||||||
|  |       result.value = _.castArray(value); | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       return undefined; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return result; | ||||||
|  | }; | ||||||
| @ -10,796 +10,194 @@ const path = require('path'); | |||||||
| const _ = require('lodash'); | const _ = require('lodash'); | ||||||
| const mongoose = require('mongoose'); | const mongoose = require('mongoose'); | ||||||
| const Mongoose = mongoose.Mongoose; | const Mongoose = mongoose.Mongoose; | ||||||
| const mongooseUtils = require('mongoose/lib/utils'); |  | ||||||
| 
 |  | ||||||
| // Strapi helpers for models.
 |  | ||||||
| const { models: utilsModels } = require('strapi-utils'); |  | ||||||
| 
 |  | ||||||
| // Local helpers.
 |  | ||||||
| const utils = require('./utils/'); |  | ||||||
| 
 | 
 | ||||||
| const relations = require('./relations'); | const relations = require('./relations'); | ||||||
| const buildQuery = require('./buildQuery'); | const buildQuery = require('./buildQuery'); | ||||||
|  | const getQueryParams = require('./get-query-params'); | ||||||
|  | const mountModels = require('./mount-models'); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Mongoose hook |  * Mongoose hook | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| /* eslint-disable prefer-template */ | const defaults = { | ||||||
| /* eslint-disable no-case-declarations */ |   defaultConnection: 'default', | ||||||
| /* eslint-disable no-const-assign */ |   host: 'localhost', | ||||||
| /* eslint-disable no-unused-vars */ |   port: 27017, | ||||||
| /* eslint-disable no-unexpected-multiline */ |   database: 'strapi', | ||||||
| /* eslint-disable indent */ |   authenticationDatabase: '', | ||||||
|  |   ssl: false, | ||||||
|  |   debug: false, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const isMongooseConnection = ({ connector }) => | ||||||
|  |   connector === 'strapi-hook-mongoose'; | ||||||
|  | 
 | ||||||
| module.exports = function(strapi) { | module.exports = function(strapi) { | ||||||
|   const hook = _.merge( |   function initialize(cb) { | ||||||
|     { |     const { connections } = strapi.config; | ||||||
|       /** | 
 | ||||||
|        * Default options |     const connectionsPromises = Object.keys(connections) | ||||||
|        */ |       .filter(key => isMongooseConnection(connections[key])) | ||||||
| 
 |       .map(async connectionName => { | ||||||
|       defaults: { |         const connection = connections[connectionName]; | ||||||
|         defaultConnection: 'default', |         const instance = new Mongoose(); | ||||||
|         host: 'localhost', |         _.defaults(connection.settings, strapi.config.hook.settings.mongoose); | ||||||
|         port: 27017, | 
 | ||||||
|         database: 'strapi', |         const { | ||||||
|         authenticationDatabase: '', |           uri, | ||||||
|         ssl: false, |           host, | ||||||
|         debug: false, |           port, | ||||||
|       }, |           username, | ||||||
| 
 |           password, | ||||||
|       /** |           database, | ||||||
|        * Initialize the hook |           srv, | ||||||
|        */ |         } = connection.settings; | ||||||
| 
 | 
 | ||||||
|       initialize: cb => |         const uriOptions = uri ? url.parse(uri, true).query : {}; | ||||||
|         _.forEach( |         const { authenticationDatabase, ssl, debug } = _.defaults( | ||||||
|           _.pickBy(strapi.config.connections, { |           connection.options, | ||||||
|             connector: 'strapi-hook-mongoose', |           uriOptions, | ||||||
|           }), |           strapi.config.hook.settings.mongoose | ||||||
|           async (connection, connectionName) => { |         ); | ||||||
|             const instance = new Mongoose(); |         const isSrv = srv === true || srv === 'true'; | ||||||
|             const { | 
 | ||||||
|               uri, |         // Connect to mongo database
 | ||||||
|               host, |         const connectOptions = {}; | ||||||
|               port, |         const options = { | ||||||
|               username, |           useFindAndModify: false, | ||||||
|               password, |         }; | ||||||
|               database, | 
 | ||||||
|               srv, |         if (!_.isEmpty(username)) { | ||||||
|             } = _.defaults( |           connectOptions.user = username; | ||||||
|               connection.settings, | 
 | ||||||
|               strapi.config.hook.settings.mongoose, |           if (!_.isEmpty(password)) { | ||||||
|             ); |             connectOptions.pass = password; | ||||||
|             const uriOptions = uri ? url.parse(uri, true).query : {}; |           } | ||||||
|             const { authenticationDatabase, ssl, debug } = _.defaults( |  | ||||||
|               connection.options, |  | ||||||
|               uriOptions, |  | ||||||
|               strapi.config.hook.settings.mongoose, |  | ||||||
|             ); |  | ||||||
|             const isSrv = srv === true || srv === 'true'; |  | ||||||
| 
 |  | ||||||
|             // Connect to mongo database
 |  | ||||||
|             const connectOptions = {}; |  | ||||||
|             const options = { |  | ||||||
|               useFindAndModify: false, |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             if (!_.isEmpty(username)) { |  | ||||||
|               connectOptions.user = username; |  | ||||||
| 
 |  | ||||||
|               if (!_.isEmpty(password)) { |  | ||||||
|                 connectOptions.pass = password; |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (!_.isEmpty(authenticationDatabase)) { |  | ||||||
|               connectOptions.authSource = authenticationDatabase; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             connectOptions.ssl = ssl === true || ssl === 'true'; |  | ||||||
|             connectOptions.useNewUrlParser = true; |  | ||||||
|             connectOptions.dbName = database; |  | ||||||
|             connectOptions.useCreateIndex = true; |  | ||||||
| 
 |  | ||||||
|             options.debug = debug === true || debug === 'true'; |  | ||||||
| 
 |  | ||||||
|             try { |  | ||||||
|               /* FIXME: for now, mongoose doesn't support srv auth except the way including user/pass in URI. |  | ||||||
|                * https://github.com/Automattic/mongoose/issues/6881 */
 |  | ||||||
|               await instance.connect( |  | ||||||
|                 uri || |  | ||||||
|                   `mongodb${ |  | ||||||
|                     isSrv ? '+srv' : '' |  | ||||||
|                   }://${username}:${password}@${host}${
 |  | ||||||
|                     !isSrv ? ':' + port : '' |  | ||||||
|                   }/`,
 |  | ||||||
|                 connectOptions, |  | ||||||
|               ); |  | ||||||
|             } catch ({ message }) { |  | ||||||
|               const errMsg = message.includes(`:${port}`) |  | ||||||
|                 ? 'Make sure your MongoDB database is running...' |  | ||||||
|                 : message; |  | ||||||
| 
 |  | ||||||
|               return cb(errMsg); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             try { |  | ||||||
|               // Require `config/functions/mongoose.js` file to customize connection.
 |  | ||||||
|               require(path.resolve( |  | ||||||
|                 strapi.config.appPath, |  | ||||||
|                 'config', |  | ||||||
|                 'functions', |  | ||||||
|                 'mongoose.js', |  | ||||||
|               ))(instance, connection); |  | ||||||
|             } catch (err) { |  | ||||||
|               // This is not an error if the file is not found.
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             Object.keys(options, key => instance.set(key, options[key])); |  | ||||||
| 
 |  | ||||||
|             const mountModels = (models, target, plugin = false) => { |  | ||||||
|               if (!target) return; |  | ||||||
| 
 |  | ||||||
|               const loadedAttributes = _.after(_.size(models), () => { |  | ||||||
|                 _.forEach(models, (definition, model) => { |  | ||||||
|                   try { |  | ||||||
|                     let collection = |  | ||||||
|                       strapi.config.hook.settings.mongoose.collections[ |  | ||||||
|                         mongooseUtils.toCollectionName(definition.globalName) |  | ||||||
|                       ]; |  | ||||||
| 
 |  | ||||||
|                     // Set the default values to model settings.
 |  | ||||||
|                     _.defaults(definition, { |  | ||||||
|                       primaryKey: '_id', |  | ||||||
|                     }); |  | ||||||
| 
 |  | ||||||
|                     // Initialize lifecycle callbacks.
 |  | ||||||
|                     const preLifecycle = { |  | ||||||
|                       validate: 'beforeCreate', |  | ||||||
|                       findOneAndUpdate: 'beforeUpdate', |  | ||||||
|                       findOneAndRemove: 'beforeDestroy', |  | ||||||
|                       remove: 'beforeDestroy', |  | ||||||
|                       update: 'beforeUpdate', |  | ||||||
|                       updateOne: 'beforeUpdate', |  | ||||||
|                       find: 'beforeFetchAll', |  | ||||||
|                       findOne: 'beforeFetch', |  | ||||||
|                       save: 'beforeSave', |  | ||||||
|                     }; |  | ||||||
| 
 |  | ||||||
|                     /* |  | ||||||
|                   Override populate path for polymorphic association. |  | ||||||
|                   It allows us to make Upload.find().populate('related') |  | ||||||
|                   instead of Upload.find().populate('related.item') |  | ||||||
|                 */ |  | ||||||
| 
 |  | ||||||
|                     const morphAssociations = definition.associations.filter( |  | ||||||
|                       association => |  | ||||||
|                         association.nature.toLowerCase().indexOf('morph') !== |  | ||||||
|                         -1, |  | ||||||
|                     ); |  | ||||||
| 
 |  | ||||||
|                     if (morphAssociations.length > 0) { |  | ||||||
|                       morphAssociations.forEach(association => { |  | ||||||
|                         Object.keys(preLifecycle) |  | ||||||
|                           .filter(key => key.indexOf('find') !== -1) |  | ||||||
|                           .forEach(key => { |  | ||||||
|                             collection.schema.pre(key, function(next) { |  | ||||||
|                               if ( |  | ||||||
|                                 this._mongooseOptions.populate && |  | ||||||
|                                 this._mongooseOptions.populate[ |  | ||||||
|                                   association.alias |  | ||||||
|                                 ] |  | ||||||
|                               ) { |  | ||||||
|                                 if ( |  | ||||||
|                                   association.nature === 'oneToManyMorph' || |  | ||||||
|                                   association.nature === 'manyToManyMorph' |  | ||||||
|                                 ) { |  | ||||||
|                                   this._mongooseOptions.populate[ |  | ||||||
|                                     association.alias |  | ||||||
|                                   ].match = { |  | ||||||
|                                     [`${association.via}.${ |  | ||||||
|                                       association.filter |  | ||||||
|                                     }`]: association.alias,
 |  | ||||||
|                                     [`${ |  | ||||||
|                                       association.via |  | ||||||
|                                     }.kind`]: definition.globalId,
 |  | ||||||
|                                   }; |  | ||||||
| 
 |  | ||||||
|                                   // Select last related to an entity.
 |  | ||||||
|                                   this._mongooseOptions.populate[ |  | ||||||
|                                     association.alias |  | ||||||
|                                   ].options = { |  | ||||||
|                                     sort: '-createdAt', |  | ||||||
|                                   }; |  | ||||||
|                                 } else { |  | ||||||
|                                   this._mongooseOptions.populate[ |  | ||||||
|                                     association.alias |  | ||||||
|                                   ].path = `${association.alias}.ref`; |  | ||||||
|                                 } |  | ||||||
|                               } else { |  | ||||||
|                                 if (!this._mongooseOptions.populate) { |  | ||||||
|                                   this._mongooseOptions.populate = {}; |  | ||||||
|                                 } |  | ||||||
| 
 |  | ||||||
|                                 // Images are not displayed in populated data.
 |  | ||||||
|                                 // We automatically populate morph relations.
 |  | ||||||
|                                 if ( |  | ||||||
|                                   association.nature === 'oneToManyMorph' || |  | ||||||
|                                   association.nature === 'manyToManyMorph' |  | ||||||
|                                 ) { |  | ||||||
|                                   this._mongooseOptions.populate[ |  | ||||||
|                                     association.alias |  | ||||||
|                                   ] = { |  | ||||||
|                                     path: association.alias, |  | ||||||
|                                     match: { |  | ||||||
|                                       [`${association.via}.${ |  | ||||||
|                                         association.filter |  | ||||||
|                                       }`]: association.alias,
 |  | ||||||
|                                       [`${ |  | ||||||
|                                         association.via |  | ||||||
|                                       }.kind`]: definition.globalId,
 |  | ||||||
|                                     }, |  | ||||||
|                                     options: { |  | ||||||
|                                       sort: '-createdAt', |  | ||||||
|                                     }, |  | ||||||
|                                     select: undefined, |  | ||||||
|                                     model: undefined, |  | ||||||
|                                     _docs: {}, |  | ||||||
|                                   }; |  | ||||||
|                                 } |  | ||||||
|                               } |  | ||||||
|                               next(); |  | ||||||
|                             }); |  | ||||||
|                           }); |  | ||||||
|                       }); |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     _.forEach(preLifecycle, (fn, key) => { |  | ||||||
|                       if (_.isFunction(target[model.toLowerCase()][fn])) { |  | ||||||
|                         collection.schema.pre(key, function(next) { |  | ||||||
|                           target[model.toLowerCase()] |  | ||||||
|                             [fn](this) |  | ||||||
|                             .then(next) |  | ||||||
|                             .catch(err => strapi.log.error(err)); |  | ||||||
|                         }); |  | ||||||
|                       } |  | ||||||
|                     }); |  | ||||||
| 
 |  | ||||||
|                     const postLifecycle = { |  | ||||||
|                       validate: 'afterCreate', |  | ||||||
|                       findOneAndRemove: 'afterDestroy', |  | ||||||
|                       remove: 'afterDestroy', |  | ||||||
|                       update: 'afterUpdate', |  | ||||||
|                       updateOne: 'afterUpdate', |  | ||||||
|                       find: 'afterFetchAll', |  | ||||||
|                       findOne: 'afterFetch', |  | ||||||
|                       save: 'afterSave', |  | ||||||
|                     }; |  | ||||||
| 
 |  | ||||||
|                     // Mongoose doesn't allow post 'remove' event on model.
 |  | ||||||
|                     // See https://github.com/Automattic/mongoose/issues/3054
 |  | ||||||
|                     _.forEach(postLifecycle, (fn, key) => { |  | ||||||
|                       if (_.isFunction(target[model.toLowerCase()][fn])) { |  | ||||||
|                         collection.schema.post(key, function(doc, next) { |  | ||||||
|                           target[model.toLowerCase()] |  | ||||||
|                             [fn](this, doc) |  | ||||||
|                             .then(next) |  | ||||||
|                             .catch(err => { |  | ||||||
|                               strapi.log.error(err); |  | ||||||
|                               next(err); |  | ||||||
|                             }); |  | ||||||
|                         }); |  | ||||||
|                       } |  | ||||||
|                     }); |  | ||||||
| 
 |  | ||||||
|                     // Add virtual key to provide populate and reverse populate
 |  | ||||||
|                     _.forEach( |  | ||||||
|                       _.pickBy(definition.loadedModel, model => { |  | ||||||
|                         return model.type === 'virtual'; |  | ||||||
|                       }), |  | ||||||
|                       (value, key) => { |  | ||||||
|                         collection.schema.virtual(key.replace('_v', ''), { |  | ||||||
|                           ref: value.ref, |  | ||||||
|                           localField: '_id', |  | ||||||
|                           foreignField: value.via, |  | ||||||
|                           justOne: value.justOne || false, |  | ||||||
|                         }); |  | ||||||
|                       }, |  | ||||||
|                     ); |  | ||||||
| 
 |  | ||||||
|                     // Use provided timestamps if the elemnets in the array are string else use default.
 |  | ||||||
|                     if (_.isArray(_.get(definition, 'options.timestamps'))) { |  | ||||||
|                       const timestamps = { |  | ||||||
|                         createdAt: _.isString( |  | ||||||
|                           _.get(definition, 'options.timestamps[0]'), |  | ||||||
|                         ) |  | ||||||
|                           ? _.get(definition, 'options.timestamps[0]') |  | ||||||
|                           : 'createdAt', |  | ||||||
|                         updatedAt: _.isString( |  | ||||||
|                           _.get(definition, 'options.timestamps[1]'), |  | ||||||
|                         ) |  | ||||||
|                           ? _.get(definition, 'options.timestamps[1]') |  | ||||||
|                           : 'updatedAt', |  | ||||||
|                       }; |  | ||||||
|                       collection.schema.set('timestamps', timestamps); |  | ||||||
|                     } else { |  | ||||||
|                       collection.schema.set( |  | ||||||
|                         'timestamps', |  | ||||||
|                         _.get(definition, 'options.timestamps') === true, |  | ||||||
|                       ); |  | ||||||
|                       _.set( |  | ||||||
|                         definition, |  | ||||||
|                         'options.timestamps', |  | ||||||
|                         _.get(definition, 'options.timestamps') === true |  | ||||||
|                           ? ['createdAt', 'updatedAt'] |  | ||||||
|                           : false, |  | ||||||
|                       ); |  | ||||||
|                     } |  | ||||||
|                     collection.schema.set( |  | ||||||
|                       'minimize', |  | ||||||
|                       _.get(definition, 'options.minimize', false) === true, |  | ||||||
|                     ); |  | ||||||
| 
 |  | ||||||
|                     // Save all attributes (with timestamps)
 |  | ||||||
|                     target[model].allAttributes = _.clone(definition.attributes); |  | ||||||
| 
 |  | ||||||
|                     collection.schema.options.toObject = collection.schema.options.toJSON = { |  | ||||||
|                       virtuals: true, |  | ||||||
|                       transform: function(doc, returned, opts) { |  | ||||||
|                         // Remover $numberDecimal nested property.
 |  | ||||||
|                         Object.keys(returned) |  | ||||||
|                           .filter( |  | ||||||
|                             key => |  | ||||||
|                               returned[key] instanceof |  | ||||||
|                               mongoose.Types.Decimal128, |  | ||||||
|                           ) |  | ||||||
|                           .forEach((key, index) => { |  | ||||||
|                             // Parse to float number.
 |  | ||||||
|                             returned[key] = parseFloat( |  | ||||||
|                               returned[key].toString(), |  | ||||||
|                             ); |  | ||||||
|                           }); |  | ||||||
| 
 |  | ||||||
|                         morphAssociations.forEach(association => { |  | ||||||
|                           if ( |  | ||||||
|                             Array.isArray(returned[association.alias]) && |  | ||||||
|                             returned[association.alias].length > 0 |  | ||||||
|                           ) { |  | ||||||
|                             // Reformat data by bypassing the many-to-many relationship.
 |  | ||||||
|                             switch (association.nature) { |  | ||||||
|                               case 'oneMorphToOne': |  | ||||||
|                                 returned[association.alias] = |  | ||||||
|                                   returned[association.alias][0].ref; |  | ||||||
|                                 break; |  | ||||||
|                               case 'manyMorphToMany': |  | ||||||
|                               case 'manyMorphToOne': |  | ||||||
|                                 returned[association.alias] = returned[ |  | ||||||
|                                   association.alias |  | ||||||
|                                 ].map(obj => obj.ref); |  | ||||||
|                                 break; |  | ||||||
|                               default: |  | ||||||
|                             } |  | ||||||
|                           } |  | ||||||
|                         }); |  | ||||||
|                       }, |  | ||||||
|                     }; |  | ||||||
| 
 |  | ||||||
|                     // Instantiate model.
 |  | ||||||
|                     const Model = instance.model( |  | ||||||
|                       definition.globalId, |  | ||||||
|                       collection.schema, |  | ||||||
|                       definition.collectionName, |  | ||||||
|                     ); |  | ||||||
| 
 |  | ||||||
|                     if (!plugin) { |  | ||||||
|                       global[definition.globalName] = Model; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     // Expose ORM functions through the `target` object.
 |  | ||||||
|                     target[model] = _.assign(Model, target[model]); |  | ||||||
| 
 |  | ||||||
|                     // Push attributes to be aware of model schema.
 |  | ||||||
|                     target[model]._attributes = definition.attributes; |  | ||||||
|                     target[model].updateRelations = relations.update; |  | ||||||
|                   } catch (err) { |  | ||||||
|                     strapi.log.error( |  | ||||||
|                       'Impossible to register the `' + model + '` model.', |  | ||||||
|                     ); |  | ||||||
|                     strapi.log.error(err); |  | ||||||
|                     strapi.stop(); |  | ||||||
|                   } |  | ||||||
|                 }); |  | ||||||
|               }); |  | ||||||
| 
 |  | ||||||
|               // Parse every authenticated model.
 |  | ||||||
|               _.forEach(models, (definition, model) => { |  | ||||||
|                 definition.globalName = _.upperFirst( |  | ||||||
|                   _.camelCase(definition.globalId), |  | ||||||
|                 ); |  | ||||||
| 
 |  | ||||||
|                 // Make sure the model has a connection.
 |  | ||||||
|                 // If not, use the default connection.
 |  | ||||||
|                 if (_.isEmpty(definition.connection)) { |  | ||||||
|                   definition.connection = |  | ||||||
|                     strapi.config.currentEnvironment.database.defaultConnection; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 // Make sure this connection exists.
 |  | ||||||
|                 if (!_.has(strapi.config.connections, definition.connection)) { |  | ||||||
|                   strapi.log.error( |  | ||||||
|                     'The connection `' + |  | ||||||
|                       definition.connection + |  | ||||||
|                       '` specified in the `' + |  | ||||||
|                       model + |  | ||||||
|                       '` model does not exist.', |  | ||||||
|                   ); |  | ||||||
|                   strapi.stop(); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 // Add some informations about ORM & client connection
 |  | ||||||
|                 definition.orm = 'mongoose'; |  | ||||||
|                 definition.client = _.get( |  | ||||||
|                   strapi.config.connections[definition.connection], |  | ||||||
|                   'client', |  | ||||||
|                 ); |  | ||||||
|                 definition.associations = []; |  | ||||||
| 
 |  | ||||||
|                 // Register the final model for Mongoose.
 |  | ||||||
|                 definition.loadedModel = _.cloneDeep(definition.attributes); |  | ||||||
| 
 |  | ||||||
|                 // Initialize the global variable with the
 |  | ||||||
|                 // capitalized model name.
 |  | ||||||
|                 if (!plugin) { |  | ||||||
|                   global[definition.globalName] = {}; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if (_.isEmpty(definition.attributes)) { |  | ||||||
|                   // Generate empty schema
 |  | ||||||
|                   _.set( |  | ||||||
|                     strapi.config.hook.settings.mongoose, |  | ||||||
|                     'collections.' + |  | ||||||
|                       mongooseUtils.toCollectionName(definition.globalName) + |  | ||||||
|                       '.schema', |  | ||||||
|                     new instance.Schema({}), |  | ||||||
|                   ); |  | ||||||
| 
 |  | ||||||
|                   return loadedAttributes(); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 // Call this callback function after we are done parsing
 |  | ||||||
|                 // all attributes for relationships-- see below.
 |  | ||||||
|                 const done = _.after(_.size(definition.attributes), () => { |  | ||||||
|                   // Generate schema without virtual populate
 |  | ||||||
|                   const schema = new instance.Schema( |  | ||||||
|                     _.omitBy(definition.loadedModel, model => { |  | ||||||
|                       return model.type === 'virtual'; |  | ||||||
|                     }), |  | ||||||
|                   ); |  | ||||||
| 
 |  | ||||||
|                   _.set( |  | ||||||
|                     strapi.config.hook.settings.mongoose, |  | ||||||
|                     'collections.' + |  | ||||||
|                       mongooseUtils.toCollectionName(definition.globalName) + |  | ||||||
|                       '.schema', |  | ||||||
|                     schema, |  | ||||||
|                   ); |  | ||||||
| 
 |  | ||||||
|                   loadedAttributes(); |  | ||||||
|                 }); |  | ||||||
| 
 |  | ||||||
|                 // Add every relationships to the loaded model for Bookshelf.
 |  | ||||||
|                 // Basic attributes don't need this-- only relations.
 |  | ||||||
|                 _.forEach(definition.attributes, (details, name) => { |  | ||||||
|                   const verbose = |  | ||||||
|                     _.get( |  | ||||||
|                       utilsModels.getNature( |  | ||||||
|                         details, |  | ||||||
|                         name, |  | ||||||
|                         undefined, |  | ||||||
|                         model.toLowerCase(), |  | ||||||
|                       ), |  | ||||||
|                       'verbose', |  | ||||||
|                     ) || ''; |  | ||||||
| 
 |  | ||||||
|                   // Build associations key
 |  | ||||||
|                   utilsModels.defineAssociations( |  | ||||||
|                     model.toLowerCase(), |  | ||||||
|                     definition, |  | ||||||
|                     details, |  | ||||||
|                     name, |  | ||||||
|                   ); |  | ||||||
| 
 |  | ||||||
|                   if (_.isEmpty(verbose)) { |  | ||||||
|                     definition.loadedModel[name].type = utils( |  | ||||||
|                       instance, |  | ||||||
|                     ).convertType(details.type); |  | ||||||
|                   } |  | ||||||
| 
 |  | ||||||
|                   switch (verbose) { |  | ||||||
|                     case 'hasOne': { |  | ||||||
|                       const ref = details.plugin |  | ||||||
|                         ? strapi.plugins[details.plugin].models[details.model] |  | ||||||
|                             .globalId |  | ||||||
|                         : strapi.models[details.model].globalId; |  | ||||||
| 
 |  | ||||||
|                       definition.loadedModel[name] = { |  | ||||||
|                         type: instance.Schema.Types.ObjectId, |  | ||||||
|                         ref, |  | ||||||
|                       }; |  | ||||||
|                       break; |  | ||||||
|                     } |  | ||||||
|                     case 'hasMany': { |  | ||||||
|                       const FK = _.find(definition.associations, { |  | ||||||
|                         alias: name, |  | ||||||
|                       }); |  | ||||||
|                       const ref = details.plugin |  | ||||||
|                         ? strapi.plugins[details.plugin].models[ |  | ||||||
|                             details.collection |  | ||||||
|                           ].globalId |  | ||||||
|                         : strapi.models[details.collection].globalId; |  | ||||||
| 
 |  | ||||||
|                       if (FK) { |  | ||||||
|                         definition.loadedModel[name] = { |  | ||||||
|                           type: 'virtual', |  | ||||||
|                           ref, |  | ||||||
|                           via: FK.via, |  | ||||||
|                           justOne: false, |  | ||||||
|                         }; |  | ||||||
| 
 |  | ||||||
|                         // Set this info to be able to see if this field is a real database's field.
 |  | ||||||
|                         details.isVirtual = true; |  | ||||||
|                       } else { |  | ||||||
|                         definition.loadedModel[name] = [ |  | ||||||
|                           { |  | ||||||
|                             type: instance.Schema.Types.ObjectId, |  | ||||||
|                             ref, |  | ||||||
|                           }, |  | ||||||
|                         ]; |  | ||||||
|                       } |  | ||||||
|                       break; |  | ||||||
|                     } |  | ||||||
|                     case 'belongsTo': { |  | ||||||
|                       const FK = _.find(definition.associations, { |  | ||||||
|                         alias: name, |  | ||||||
|                       }); |  | ||||||
|                       const ref = details.plugin |  | ||||||
|                         ? strapi.plugins[details.plugin].models[details.model] |  | ||||||
|                             .globalId |  | ||||||
|                         : strapi.models[details.model].globalId; |  | ||||||
| 
 |  | ||||||
|                       if ( |  | ||||||
|                         FK && |  | ||||||
|                         FK.nature !== 'oneToOne' && |  | ||||||
|                         FK.nature !== 'manyToOne' && |  | ||||||
|                         FK.nature !== 'oneWay' && |  | ||||||
|                         FK.nature !== 'oneToMorph' |  | ||||||
|                       ) { |  | ||||||
|                         definition.loadedModel[name] = { |  | ||||||
|                           type: 'virtual', |  | ||||||
|                           ref, |  | ||||||
|                           via: FK.via, |  | ||||||
|                           justOne: true, |  | ||||||
|                         }; |  | ||||||
| 
 |  | ||||||
|                         // Set this info to be able to see if this field is a real database's field.
 |  | ||||||
|                         details.isVirtual = true; |  | ||||||
|                       } else { |  | ||||||
|                         definition.loadedModel[name] = { |  | ||||||
|                           type: instance.Schema.Types.ObjectId, |  | ||||||
|                           ref, |  | ||||||
|                         }; |  | ||||||
|                       } |  | ||||||
| 
 |  | ||||||
|                       break; |  | ||||||
|                     } |  | ||||||
|                     case 'belongsToMany': { |  | ||||||
|                       const FK = _.find(definition.associations, { |  | ||||||
|                         alias: name, |  | ||||||
|                       }); |  | ||||||
|                       const ref = details.plugin |  | ||||||
|                         ? strapi.plugins[details.plugin].models[ |  | ||||||
|                             details.collection |  | ||||||
|                           ].globalId |  | ||||||
|                         : strapi.models[details.collection].globalId; |  | ||||||
| 
 |  | ||||||
|                       // One-side of the relationship has to be a virtual field to be bidirectional.
 |  | ||||||
|                       if ( |  | ||||||
|                         (FK && _.isUndefined(FK.via)) || |  | ||||||
|                         details.dominant !== true |  | ||||||
|                       ) { |  | ||||||
|                         definition.loadedModel[name] = { |  | ||||||
|                           type: 'virtual', |  | ||||||
|                           ref, |  | ||||||
|                           via: FK.via, |  | ||||||
|                         }; |  | ||||||
| 
 |  | ||||||
|                         // Set this info to be able to see if this field is a real database's field.
 |  | ||||||
|                         details.isVirtual = true; |  | ||||||
|                       } else { |  | ||||||
|                         definition.loadedModel[name] = [ |  | ||||||
|                           { |  | ||||||
|                             type: instance.Schema.Types.ObjectId, |  | ||||||
|                             ref, |  | ||||||
|                           }, |  | ||||||
|                         ]; |  | ||||||
|                       } |  | ||||||
|                       break; |  | ||||||
|                     } |  | ||||||
|                     case 'morphOne': { |  | ||||||
|                       const FK = _.find(definition.associations, { |  | ||||||
|                         alias: name, |  | ||||||
|                       }); |  | ||||||
|                       const ref = details.plugin |  | ||||||
|                         ? strapi.plugins[details.plugin].models[details.model] |  | ||||||
|                             .globalId |  | ||||||
|                         : strapi.models[details.model].globalId; |  | ||||||
| 
 |  | ||||||
|                       definition.loadedModel[name] = { |  | ||||||
|                         type: 'virtual', |  | ||||||
|                         ref, |  | ||||||
|                         via: `${FK.via}.ref`, |  | ||||||
|                         justOne: true, |  | ||||||
|                       }; |  | ||||||
| 
 |  | ||||||
|                       // Set this info to be able to see if this field is a real database's field.
 |  | ||||||
|                       details.isVirtual = true; |  | ||||||
|                       break; |  | ||||||
|                     } |  | ||||||
|                     case 'morphMany': { |  | ||||||
|                       const FK = _.find(definition.associations, { |  | ||||||
|                         alias: name, |  | ||||||
|                       }); |  | ||||||
|                       const ref = details.plugin |  | ||||||
|                         ? strapi.plugins[details.plugin].models[ |  | ||||||
|                             details.collection |  | ||||||
|                           ].globalId |  | ||||||
|                         : strapi.models[details.collection].globalId; |  | ||||||
| 
 |  | ||||||
|                       definition.loadedModel[name] = { |  | ||||||
|                         type: 'virtual', |  | ||||||
|                         ref, |  | ||||||
|                         via: `${FK.via}.ref`, |  | ||||||
|                       }; |  | ||||||
| 
 |  | ||||||
|                       // Set this info to be able to see if this field is a real database's field.
 |  | ||||||
|                       details.isVirtual = true; |  | ||||||
|                       break; |  | ||||||
|                     } |  | ||||||
|                     case 'belongsToMorph': { |  | ||||||
|                       definition.loadedModel[name] = { |  | ||||||
|                         kind: String, |  | ||||||
|                         [details.filter]: String, |  | ||||||
|                         ref: { |  | ||||||
|                           type: instance.Schema.Types.ObjectId, |  | ||||||
|                           refPath: `${name}.kind`, |  | ||||||
|                         }, |  | ||||||
|                       }; |  | ||||||
|                       break; |  | ||||||
|                     } |  | ||||||
|                     case 'belongsToManyMorph': { |  | ||||||
|                       definition.loadedModel[name] = [ |  | ||||||
|                         { |  | ||||||
|                           kind: String, |  | ||||||
|                           [details.filter]: String, |  | ||||||
|                           ref: { |  | ||||||
|                             type: instance.Schema.Types.ObjectId, |  | ||||||
|                             refPath: `${name}.kind`, |  | ||||||
|                           }, |  | ||||||
|                         }, |  | ||||||
|                       ]; |  | ||||||
|                       break; |  | ||||||
|                     } |  | ||||||
|                     default: |  | ||||||
|                       break; |  | ||||||
|                   } |  | ||||||
| 
 |  | ||||||
|                   done(); |  | ||||||
|                 }); |  | ||||||
|               }); |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             // Mount `./api` models.
 |  | ||||||
|             mountModels( |  | ||||||
|               _.pickBy(strapi.models, { connection: connectionName }), |  | ||||||
|               strapi.models, |  | ||||||
|             ); |  | ||||||
| 
 |  | ||||||
|             // Mount `./admin` models.
 |  | ||||||
|             mountModels( |  | ||||||
|               _.pickBy(strapi.admin.models, { connection: connectionName }), |  | ||||||
|               strapi.admin.models, |  | ||||||
|             ); |  | ||||||
| 
 |  | ||||||
|             // Mount `./plugins` models.
 |  | ||||||
|             _.forEach(strapi.plugins, (plugin, name) => { |  | ||||||
|               mountModels( |  | ||||||
|                 _.pickBy(strapi.plugins[name].models, { |  | ||||||
|                   connection: connectionName, |  | ||||||
|                 }), |  | ||||||
|                 plugin.models, |  | ||||||
|                 name, |  | ||||||
|               ); |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|             cb(); |  | ||||||
|           }, |  | ||||||
|         ), |  | ||||||
| 
 |  | ||||||
|       getQueryParams: (value, type, key) => { |  | ||||||
|         const result = {}; |  | ||||||
| 
 |  | ||||||
|         switch (type) { |  | ||||||
|           case '=': |  | ||||||
|             result.key = `where.${key}`; |  | ||||||
|             result.value = value; |  | ||||||
|             break; |  | ||||||
|           case '_ne': |  | ||||||
|             result.key = `where.${key}.$ne`; |  | ||||||
|             result.value = value; |  | ||||||
|             break; |  | ||||||
|           case '_lt': |  | ||||||
|             result.key = `where.${key}.$lt`; |  | ||||||
|             result.value = value; |  | ||||||
|             break; |  | ||||||
|           case '_gt': |  | ||||||
|             result.key = `where.${key}.$gt`; |  | ||||||
|             result.value = value; |  | ||||||
|             break; |  | ||||||
|           case '_lte': |  | ||||||
|             result.key = `where.${key}.$lte`; |  | ||||||
|             result.value = value; |  | ||||||
|             break; |  | ||||||
|           case '_gte': |  | ||||||
|             result.key = `where.${key}.$gte`; |  | ||||||
|             result.value = value; |  | ||||||
|             break; |  | ||||||
|           case '_sort': |  | ||||||
|             result.key = 'sort'; |  | ||||||
|             result.value = _.toLower(value) === 'desc' ? '-' : ''; |  | ||||||
|             result.value += key; |  | ||||||
|             break; |  | ||||||
|           case '_start': |  | ||||||
|             result.key = 'start'; |  | ||||||
|             result.value = parseFloat(value); |  | ||||||
|             break; |  | ||||||
|           case '_limit': |  | ||||||
|             result.key = 'limit'; |  | ||||||
|             result.value = parseFloat(value); |  | ||||||
|             break; |  | ||||||
|           case '_populate': |  | ||||||
|             result.key = 'populate'; |  | ||||||
|             result.value = value; |  | ||||||
|             break; |  | ||||||
|           case '_contains': |  | ||||||
|             result.key = `where.${key}`; |  | ||||||
|             result.value = { |  | ||||||
|               $regex: value, |  | ||||||
|               $options: 'i', |  | ||||||
|             }; |  | ||||||
|             break; |  | ||||||
|           case '_containss': |  | ||||||
|             result.key = `where.${key}.$regex`; |  | ||||||
|             result.value = value; |  | ||||||
|             break; |  | ||||||
|           case '_in': |  | ||||||
|             result.key = `where.${key}.$in`; |  | ||||||
|             result.value = _.castArray(value); |  | ||||||
|             break; |  | ||||||
|           case '_nin': |  | ||||||
|             result.key = `where.${key}.$nin`; |  | ||||||
|             result.value = _.castArray(value); |  | ||||||
|             break; |  | ||||||
|           default: |  | ||||||
|             result = undefined; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return result; |         if (!_.isEmpty(authenticationDatabase)) { | ||||||
|       }, |           connectOptions.authSource = authenticationDatabase; | ||||||
|       buildQuery, |         } | ||||||
|     }, |  | ||||||
|     relations, |  | ||||||
|   ); |  | ||||||
| 
 | 
 | ||||||
|   return hook; |         connectOptions.ssl = ssl === true || ssl === 'true'; | ||||||
|  |         connectOptions.useNewUrlParser = true; | ||||||
|  |         connectOptions.dbName = database; | ||||||
|  |         connectOptions.useCreateIndex = true; | ||||||
|  | 
 | ||||||
|  |         options.debug = debug === true || debug === 'true'; | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |           /* FIXME: for now, mongoose doesn't support srv auth except the way including user/pass in URI. | ||||||
|  |            * https://github.com/Automattic/mongoose/issues/6881 */
 | ||||||
|  |           await instance.connect( | ||||||
|  |             uri || | ||||||
|  |               `mongodb${isSrv ? '+srv' : ''}://${username}:${password}@${host}${ | ||||||
|  |                 !isSrv ? ':' + port : '' | ||||||
|  |               }/`,
 | ||||||
|  |             connectOptions | ||||||
|  |           ); | ||||||
|  |         } catch ({ message }) { | ||||||
|  |           const errMsg = message.includes(`:${port}`) | ||||||
|  |             ? 'Make sure your MongoDB database is running...' | ||||||
|  |             : message; | ||||||
|  | 
 | ||||||
|  |           return cb(errMsg); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |           // Require `config/functions/mongoose.js` file to customize connection.
 | ||||||
|  |           require(path.resolve( | ||||||
|  |             strapi.config.appPath, | ||||||
|  |             'config', | ||||||
|  |             'functions', | ||||||
|  |             'mongoose.js' | ||||||
|  |           ))(instance, connection); | ||||||
|  |         } catch (err) { | ||||||
|  |           // This is not an error if the file is not found.
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Object.keys(options, key => instance.set(key, options[key])); | ||||||
|  | 
 | ||||||
|  |         const ctx = { | ||||||
|  |           instance, | ||||||
|  |           connection, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         return Promise.all([ | ||||||
|  |           mountGroups(connectionName, ctx), | ||||||
|  |           mountApis(connectionName, ctx), | ||||||
|  |           mountAdmin(connectionName, ctx), | ||||||
|  |           mountPlugins(connectionName, ctx), | ||||||
|  |         ]); | ||||||
|  |       }); | ||||||
|  | 
 | ||||||
|  |     return Promise.all(connectionsPromises).then(() => cb(), err => cb(err)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function mountGroups(connectionName, ctx) { | ||||||
|  |     const options = { | ||||||
|  |       models: _.pickBy( | ||||||
|  |         strapi.groups, | ||||||
|  |         ({ connection }) => connection === connectionName | ||||||
|  |       ), | ||||||
|  |       target: strapi.groups, | ||||||
|  |       plugin: false, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return mountModels(options, ctx); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function mountApis(connectionName, ctx) { | ||||||
|  |     const options = { | ||||||
|  |       models: _.pickBy( | ||||||
|  |         strapi.models, | ||||||
|  |         ({ connection }) => connection === connectionName | ||||||
|  |       ), | ||||||
|  |       target: strapi.models, | ||||||
|  |       plugin: false, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return mountModels(options, ctx); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function mountAdmin(connectionName, ctx) { | ||||||
|  |     const options = { | ||||||
|  |       models: _.pickBy( | ||||||
|  |         strapi.admin.models, | ||||||
|  |         ({ connection }) => connection === connectionName | ||||||
|  |       ), | ||||||
|  |       target: strapi.admin.models, | ||||||
|  |       plugin: false, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return mountModels(options, ctx); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function mountPlugins(connectionName, ctx) { | ||||||
|  |     return Promise.all( | ||||||
|  |       Object.keys(strapi.plugins).map(name => { | ||||||
|  |         const plugin = strapi.plugins[name]; | ||||||
|  |         return mountModels( | ||||||
|  |           { | ||||||
|  |             models: _.pickBy( | ||||||
|  |               plugin.models, | ||||||
|  |               ({ connection }) => connection === connectionName | ||||||
|  |             ), | ||||||
|  |             target: plugin.models, | ||||||
|  |             plugin: name, | ||||||
|  |           }, | ||||||
|  |           ctx | ||||||
|  |         ); | ||||||
|  |       }) | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     defaults, | ||||||
|  |     initialize, | ||||||
|  |     getQueryParams, | ||||||
|  |     buildQuery, | ||||||
|  |     ...relations, | ||||||
|  |   }; | ||||||
| }; | }; | ||||||
|  | |||||||
							
								
								
									
										526
									
								
								packages/strapi-hook-mongoose/lib/mount-models.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										526
									
								
								packages/strapi-hook-mongoose/lib/mount-models.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,526 @@ | |||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | const _ = require('lodash'); | ||||||
|  | const mongoose = require('mongoose'); | ||||||
|  | const mongooseUtils = require('mongoose/lib/utils'); | ||||||
|  | 
 | ||||||
|  | const utilsModels = require('strapi-utils').models; | ||||||
|  | const utils = require('./utils/'); | ||||||
|  | const relations = require('./relations'); | ||||||
|  | 
 | ||||||
|  | module.exports = ({ models, target, plugin = false }, ctx) => { | ||||||
|  |   const { instance } = ctx; | ||||||
|  | 
 | ||||||
|  |   const loadedAttributes = _.after(_.size(models), () => { | ||||||
|  |     _.forEach(models, (definition, model) => { | ||||||
|  |       try { | ||||||
|  |         let collection = | ||||||
|  |           strapi.config.hook.settings.mongoose.collections[ | ||||||
|  |             mongooseUtils.toCollectionName(definition.globalName) | ||||||
|  |           ]; | ||||||
|  | 
 | ||||||
|  |         // Set the default values to model settings.
 | ||||||
|  |         _.defaults(definition, { | ||||||
|  |           primaryKey: '_id', | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // Initialize lifecycle callbacks.
 | ||||||
|  |         const preLifecycle = { | ||||||
|  |           validate: 'beforeCreate', | ||||||
|  |           findOneAndUpdate: 'beforeUpdate', | ||||||
|  |           findOneAndRemove: 'beforeDestroy', | ||||||
|  |           remove: 'beforeDestroy', | ||||||
|  |           update: 'beforeUpdate', | ||||||
|  |           updateOne: 'beforeUpdate', | ||||||
|  |           find: 'beforeFetchAll', | ||||||
|  |           findOne: 'beforeFetch', | ||||||
|  |           save: 'beforeSave', | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |           Override populate path for polymorphic association. | ||||||
|  |           It allows us to make Upload.find().populate('related') | ||||||
|  |           instead of Upload.find().populate('related.item') | ||||||
|  |         */ | ||||||
|  | 
 | ||||||
|  |         const morphAssociations = definition.associations.filter( | ||||||
|  |           association => | ||||||
|  |             association.nature.toLowerCase().indexOf('morph') !== -1 | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         if (morphAssociations.length > 0) { | ||||||
|  |           morphAssociations.forEach(association => { | ||||||
|  |             Object.keys(preLifecycle) | ||||||
|  |               .filter(key => key.indexOf('find') !== -1) | ||||||
|  |               .forEach(key => { | ||||||
|  |                 collection.schema.pre(key, function(next) { | ||||||
|  |                   if ( | ||||||
|  |                     this._mongooseOptions.populate && | ||||||
|  |                     this._mongooseOptions.populate[association.alias] | ||||||
|  |                   ) { | ||||||
|  |                     if ( | ||||||
|  |                       association.nature === 'oneToManyMorph' || | ||||||
|  |                       association.nature === 'manyToManyMorph' | ||||||
|  |                     ) { | ||||||
|  |                       this._mongooseOptions.populate[ | ||||||
|  |                         association.alias | ||||||
|  |                       ].match = { | ||||||
|  |                         [`${association.via}.${association.filter}`]: association.alias, | ||||||
|  |                         [`${association.via}.kind`]: definition.globalId, | ||||||
|  |                       }; | ||||||
|  | 
 | ||||||
|  |                       // Select last related to an entity.
 | ||||||
|  |                       this._mongooseOptions.populate[ | ||||||
|  |                         association.alias | ||||||
|  |                       ].options = { | ||||||
|  |                         sort: '-createdAt', | ||||||
|  |                       }; | ||||||
|  |                     } else { | ||||||
|  |                       this._mongooseOptions.populate[ | ||||||
|  |                         association.alias | ||||||
|  |                       ].path = `${association.alias}.ref`; | ||||||
|  |                     } | ||||||
|  |                   } else { | ||||||
|  |                     if (!this._mongooseOptions.populate) { | ||||||
|  |                       this._mongooseOptions.populate = {}; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     // Images are not displayed in populated data.
 | ||||||
|  |                     // We automatically populate morph relations.
 | ||||||
|  |                     if ( | ||||||
|  |                       association.nature === 'oneToManyMorph' || | ||||||
|  |                       association.nature === 'manyToManyMorph' | ||||||
|  |                     ) { | ||||||
|  |                       this._mongooseOptions.populate[association.alias] = { | ||||||
|  |                         path: association.alias, | ||||||
|  |                         match: { | ||||||
|  |                           [`${association.via}.${association.filter}`]: association.alias, | ||||||
|  |                           [`${association.via}.kind`]: definition.globalId, | ||||||
|  |                         }, | ||||||
|  |                         options: { | ||||||
|  |                           sort: '-createdAt', | ||||||
|  |                         }, | ||||||
|  |                         select: undefined, | ||||||
|  |                         model: undefined, | ||||||
|  |                         _docs: {}, | ||||||
|  |                       }; | ||||||
|  |                     } | ||||||
|  |                   } | ||||||
|  |                   next(); | ||||||
|  |                 }); | ||||||
|  |               }); | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         _.forEach(preLifecycle, (fn, key) => { | ||||||
|  |           if (_.isFunction(target[model.toLowerCase()][fn])) { | ||||||
|  |             collection.schema.pre(key, function(next) { | ||||||
|  |               target[model.toLowerCase()] | ||||||
|  |                 [fn](this) | ||||||
|  |                 .then(next) | ||||||
|  |                 .catch(err => strapi.log.error(err)); | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         const postLifecycle = { | ||||||
|  |           validate: 'afterCreate', | ||||||
|  |           findOneAndRemove: 'afterDestroy', | ||||||
|  |           remove: 'afterDestroy', | ||||||
|  |           update: 'afterUpdate', | ||||||
|  |           updateOne: 'afterUpdate', | ||||||
|  |           find: 'afterFetchAll', | ||||||
|  |           findOne: 'afterFetch', | ||||||
|  |           save: 'afterSave', | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         // Mongoose doesn't allow post 'remove' event on model.
 | ||||||
|  |         // See https://github.com/Automattic/mongoose/issues/3054
 | ||||||
|  |         _.forEach(postLifecycle, (fn, key) => { | ||||||
|  |           if (_.isFunction(target[model.toLowerCase()][fn])) { | ||||||
|  |             collection.schema.post(key, function(doc, next) { | ||||||
|  |               target[model.toLowerCase()] | ||||||
|  |                 [fn](this, doc) | ||||||
|  |                 .then(next) | ||||||
|  |                 .catch(err => { | ||||||
|  |                   strapi.log.error(err); | ||||||
|  |                   next(err); | ||||||
|  |                 }); | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // Add virtual key to provide populate and reverse populate
 | ||||||
|  |         _.forEach( | ||||||
|  |           _.pickBy(definition.loadedModel, model => { | ||||||
|  |             return model.type === 'virtual'; | ||||||
|  |           }), | ||||||
|  |           (value, key) => { | ||||||
|  |             collection.schema.virtual(key.replace('_v', ''), { | ||||||
|  |               ref: value.ref, | ||||||
|  |               localField: '_id', | ||||||
|  |               foreignField: value.via, | ||||||
|  |               justOne: value.justOne || false, | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         // Use provided timestamps if the elemnets in the array are string else use default.
 | ||||||
|  |         if (_.isArray(_.get(definition, 'options.timestamps'))) { | ||||||
|  |           const timestamps = { | ||||||
|  |             createdAt: _.isString(_.get(definition, 'options.timestamps[0]')) | ||||||
|  |               ? _.get(definition, 'options.timestamps[0]') | ||||||
|  |               : 'createdAt', | ||||||
|  |             updatedAt: _.isString(_.get(definition, 'options.timestamps[1]')) | ||||||
|  |               ? _.get(definition, 'options.timestamps[1]') | ||||||
|  |               : 'updatedAt', | ||||||
|  |           }; | ||||||
|  |           collection.schema.set('timestamps', timestamps); | ||||||
|  |         } else { | ||||||
|  |           collection.schema.set( | ||||||
|  |             'timestamps', | ||||||
|  |             _.get(definition, 'options.timestamps') === true | ||||||
|  |           ); | ||||||
|  |           _.set( | ||||||
|  |             definition, | ||||||
|  |             'options.timestamps', | ||||||
|  |             _.get(definition, 'options.timestamps') === true | ||||||
|  |               ? ['createdAt', 'updatedAt'] | ||||||
|  |               : false | ||||||
|  |           ); | ||||||
|  |         } | ||||||
|  |         collection.schema.set( | ||||||
|  |           'minimize', | ||||||
|  |           _.get(definition, 'options.minimize', false) === true | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         // Save all attributes (with timestamps)
 | ||||||
|  |         target[model].allAttributes = _.clone(definition.attributes); | ||||||
|  | 
 | ||||||
|  |         collection.schema.options.toObject = collection.schema.options.toJSON = { | ||||||
|  |           virtuals: true, | ||||||
|  |           transform: function(doc, returned) { | ||||||
|  |             // Remover $numberDecimal nested property.
 | ||||||
|  |             Object.keys(returned) | ||||||
|  |               .filter(key => returned[key] instanceof mongoose.Types.Decimal128) | ||||||
|  |               .forEach(key => { | ||||||
|  |                 // Parse to float number.
 | ||||||
|  |                 returned[key] = parseFloat(returned[key].toString()); | ||||||
|  |               }); | ||||||
|  | 
 | ||||||
|  |             morphAssociations.forEach(association => { | ||||||
|  |               if ( | ||||||
|  |                 Array.isArray(returned[association.alias]) && | ||||||
|  |                 returned[association.alias].length > 0 | ||||||
|  |               ) { | ||||||
|  |                 // Reformat data by bypassing the many-to-many relationship.
 | ||||||
|  |                 switch (association.nature) { | ||||||
|  |                   case 'oneMorphToOne': | ||||||
|  |                     returned[association.alias] = | ||||||
|  |                       returned[association.alias][0].ref; | ||||||
|  |                     break; | ||||||
|  |                   case 'manyMorphToMany': | ||||||
|  |                   case 'manyMorphToOne': | ||||||
|  |                     returned[association.alias] = returned[ | ||||||
|  |                       association.alias | ||||||
|  |                     ].map(obj => obj.ref); | ||||||
|  |                     break; | ||||||
|  |                   default: | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |             }); | ||||||
|  |           }, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         // Instantiate model.
 | ||||||
|  |         const Model = instance.model( | ||||||
|  |           definition.globalId, | ||||||
|  |           collection.schema, | ||||||
|  |           definition.collectionName | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         if (!plugin) { | ||||||
|  |           global[definition.globalName] = Model; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Expose ORM functions through the `target` object.
 | ||||||
|  |         target[model] = _.assign(Model, target[model]); | ||||||
|  | 
 | ||||||
|  |         // Push attributes to be aware of model schema.
 | ||||||
|  |         target[model]._attributes = definition.attributes; | ||||||
|  |         target[model].updateRelations = relations.update; | ||||||
|  |       } catch (err) { | ||||||
|  |         strapi.log.error('Impossible to register the `' + model + '` model.'); | ||||||
|  |         strapi.log.error(err); | ||||||
|  |         strapi.stop(); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   // Parse every authenticated model.
 | ||||||
|  |   _.forEach(models, (definition, model) => { | ||||||
|  |     definition.globalName = _.upperFirst(_.camelCase(definition.globalId)); | ||||||
|  | 
 | ||||||
|  |     // Make sure the model has a connection.
 | ||||||
|  |     // If not, use the default connection.
 | ||||||
|  |     if (_.isEmpty(definition.connection)) { | ||||||
|  |       definition.connection = | ||||||
|  |         strapi.config.currentEnvironment.database.defaultConnection; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Make sure this connection exists.
 | ||||||
|  |     if (!_.has(strapi.config.connections, definition.connection)) { | ||||||
|  |       strapi.log.error( | ||||||
|  |         'The connection `' + | ||||||
|  |           definition.connection + | ||||||
|  |           '` specified in the `' + | ||||||
|  |           model + | ||||||
|  |           '` model does not exist.' | ||||||
|  |       ); | ||||||
|  |       strapi.stop(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Add some informations about ORM & client connection
 | ||||||
|  |     definition.orm = 'mongoose'; | ||||||
|  |     definition.client = _.get( | ||||||
|  |       strapi.config.connections[definition.connection], | ||||||
|  |       'client' | ||||||
|  |     ); | ||||||
|  |     definition.associations = []; | ||||||
|  | 
 | ||||||
|  |     // Register the final model for Mongoose.
 | ||||||
|  |     definition.loadedModel = _.cloneDeep(definition.attributes); | ||||||
|  | 
 | ||||||
|  |     // Initialize the global variable with the
 | ||||||
|  |     // capitalized model name.
 | ||||||
|  |     if (!plugin) { | ||||||
|  |       global[definition.globalName] = {}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (_.isEmpty(definition.attributes)) { | ||||||
|  |       // Generate empty schema
 | ||||||
|  |       _.set( | ||||||
|  |         strapi.config.hook.settings.mongoose, | ||||||
|  |         'collections.' + | ||||||
|  |           mongooseUtils.toCollectionName(definition.globalName) + | ||||||
|  |           '.schema', | ||||||
|  |         new instance.Schema({}) | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       return loadedAttributes(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Call this callback function after we are done parsing
 | ||||||
|  |     // all attributes for relationships-- see below.
 | ||||||
|  |     const done = _.after(_.size(definition.attributes), () => { | ||||||
|  |       // Generate schema without virtual populate
 | ||||||
|  |       const schema = new instance.Schema( | ||||||
|  |         _.omitBy(definition.loadedModel, model => { | ||||||
|  |           return model.type === 'virtual'; | ||||||
|  |         }) | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       _.set( | ||||||
|  |         strapi.config.hook.settings.mongoose, | ||||||
|  |         'collections.' + | ||||||
|  |           mongooseUtils.toCollectionName(definition.globalName) + | ||||||
|  |           '.schema', | ||||||
|  |         schema | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       loadedAttributes(); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // Add every relationships to the loaded model for Bookshelf.
 | ||||||
|  |     // Basic attributes don't need this-- only relations.
 | ||||||
|  |     _.forEach(definition.attributes, (details, name) => { | ||||||
|  |       const verbose = | ||||||
|  |         _.get( | ||||||
|  |           utilsModels.getNature(details, name, undefined, model.toLowerCase()), | ||||||
|  |           'verbose' | ||||||
|  |         ) || ''; | ||||||
|  | 
 | ||||||
|  |       // Build associations key
 | ||||||
|  |       utilsModels.defineAssociations( | ||||||
|  |         model.toLowerCase(), | ||||||
|  |         definition, | ||||||
|  |         details, | ||||||
|  |         name | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       if (_.isEmpty(verbose)) { | ||||||
|  |         definition.loadedModel[name].type = utils(instance).convertType( | ||||||
|  |           details.type | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       switch (verbose) { | ||||||
|  |         case 'hasOne': { | ||||||
|  |           const ref = details.plugin | ||||||
|  |             ? strapi.plugins[details.plugin].models[details.model].globalId | ||||||
|  |             : strapi.models[details.model].globalId; | ||||||
|  | 
 | ||||||
|  |           definition.loadedModel[name] = { | ||||||
|  |             type: instance.Schema.Types.ObjectId, | ||||||
|  |             ref, | ||||||
|  |           }; | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         case 'hasMany': { | ||||||
|  |           const FK = _.find(definition.associations, { | ||||||
|  |             alias: name, | ||||||
|  |           }); | ||||||
|  |           const ref = details.plugin | ||||||
|  |             ? strapi.plugins[details.plugin].models[details.collection].globalId | ||||||
|  |             : strapi.models[details.collection].globalId; | ||||||
|  | 
 | ||||||
|  |           if (FK) { | ||||||
|  |             definition.loadedModel[name] = { | ||||||
|  |               type: 'virtual', | ||||||
|  |               ref, | ||||||
|  |               via: FK.via, | ||||||
|  |               justOne: false, | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             // Set this info to be able to see if this field is a real database's field.
 | ||||||
|  |             details.isVirtual = true; | ||||||
|  |           } else { | ||||||
|  |             definition.loadedModel[name] = [ | ||||||
|  |               { | ||||||
|  |                 type: instance.Schema.Types.ObjectId, | ||||||
|  |                 ref, | ||||||
|  |               }, | ||||||
|  |             ]; | ||||||
|  |           } | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         case 'belongsTo': { | ||||||
|  |           const FK = _.find(definition.associations, { | ||||||
|  |             alias: name, | ||||||
|  |           }); | ||||||
|  |           const ref = details.plugin | ||||||
|  |             ? strapi.plugins[details.plugin].models[details.model].globalId | ||||||
|  |             : strapi.models[details.model].globalId; | ||||||
|  | 
 | ||||||
|  |           if ( | ||||||
|  |             FK && | ||||||
|  |             FK.nature !== 'oneToOne' && | ||||||
|  |             FK.nature !== 'manyToOne' && | ||||||
|  |             FK.nature !== 'oneWay' && | ||||||
|  |             FK.nature !== 'oneToMorph' | ||||||
|  |           ) { | ||||||
|  |             definition.loadedModel[name] = { | ||||||
|  |               type: 'virtual', | ||||||
|  |               ref, | ||||||
|  |               via: FK.via, | ||||||
|  |               justOne: true, | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             // Set this info to be able to see if this field is a real database's field.
 | ||||||
|  |             details.isVirtual = true; | ||||||
|  |           } else { | ||||||
|  |             definition.loadedModel[name] = { | ||||||
|  |               type: instance.Schema.Types.ObjectId, | ||||||
|  |               ref, | ||||||
|  |             }; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         case 'belongsToMany': { | ||||||
|  |           const FK = _.find(definition.associations, { | ||||||
|  |             alias: name, | ||||||
|  |           }); | ||||||
|  |           const ref = details.plugin | ||||||
|  |             ? strapi.plugins[details.plugin].models[details.collection].globalId | ||||||
|  |             : strapi.models[details.collection].globalId; | ||||||
|  | 
 | ||||||
|  |           // One-side of the relationship has to be a virtual field to be bidirectional.
 | ||||||
|  |           if ((FK && _.isUndefined(FK.via)) || details.dominant !== true) { | ||||||
|  |             definition.loadedModel[name] = { | ||||||
|  |               type: 'virtual', | ||||||
|  |               ref, | ||||||
|  |               via: FK.via, | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             // Set this info to be able to see if this field is a real database's field.
 | ||||||
|  |             details.isVirtual = true; | ||||||
|  |           } else { | ||||||
|  |             definition.loadedModel[name] = [ | ||||||
|  |               { | ||||||
|  |                 type: instance.Schema.Types.ObjectId, | ||||||
|  |                 ref, | ||||||
|  |               }, | ||||||
|  |             ]; | ||||||
|  |           } | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         case 'morphOne': { | ||||||
|  |           const FK = _.find(definition.associations, { | ||||||
|  |             alias: name, | ||||||
|  |           }); | ||||||
|  |           const ref = details.plugin | ||||||
|  |             ? strapi.plugins[details.plugin].models[details.model].globalId | ||||||
|  |             : strapi.models[details.model].globalId; | ||||||
|  | 
 | ||||||
|  |           definition.loadedModel[name] = { | ||||||
|  |             type: 'virtual', | ||||||
|  |             ref, | ||||||
|  |             via: `${FK.via}.ref`, | ||||||
|  |             justOne: true, | ||||||
|  |           }; | ||||||
|  | 
 | ||||||
|  |           // Set this info to be able to see if this field is a real database's field.
 | ||||||
|  |           details.isVirtual = true; | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         case 'morphMany': { | ||||||
|  |           const FK = _.find(definition.associations, { | ||||||
|  |             alias: name, | ||||||
|  |           }); | ||||||
|  |           const ref = details.plugin | ||||||
|  |             ? strapi.plugins[details.plugin].models[details.collection].globalId | ||||||
|  |             : strapi.models[details.collection].globalId; | ||||||
|  | 
 | ||||||
|  |           definition.loadedModel[name] = { | ||||||
|  |             type: 'virtual', | ||||||
|  |             ref, | ||||||
|  |             via: `${FK.via}.ref`, | ||||||
|  |           }; | ||||||
|  | 
 | ||||||
|  |           // Set this info to be able to see if this field is a real database's field.
 | ||||||
|  |           details.isVirtual = true; | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         case 'belongsToMorph': { | ||||||
|  |           definition.loadedModel[name] = { | ||||||
|  |             kind: String, | ||||||
|  |             [details.filter]: String, | ||||||
|  |             ref: { | ||||||
|  |               type: instance.Schema.Types.ObjectId, | ||||||
|  |               refPath: `${name}.kind`, | ||||||
|  |             }, | ||||||
|  |           }; | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         case 'belongsToManyMorph': { | ||||||
|  |           definition.loadedModel[name] = [ | ||||||
|  |             { | ||||||
|  |               kind: String, | ||||||
|  |               [details.filter]: String, | ||||||
|  |               ref: { | ||||||
|  |                 type: instance.Schema.Types.ObjectId, | ||||||
|  |                 refPath: `${name}.kind`, | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |           ]; | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         default: | ||||||
|  |           break; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       done(); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  | }; | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Alexandre Bodin
						Alexandre Bodin