| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const _ = require('lodash'); | 
					
						
							|  |  |  | const mongoose = require('mongoose'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const utilsModels = require('strapi-utils').models; | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | const utils = require('./utils'); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | const relations = require('./relations'); | 
					
						
							| 
									
										
										
										
											2019-11-26 11:24:00 +01:00
										 |  |  | const { findComponentByGlobalId } = require('./utils/helpers'); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-06 11:51:03 +01:00
										 |  |  | const isPolymorphicAssoc = assoc => { | 
					
						
							|  |  |  |   return assoc.nature.toLowerCase().indexOf('morph') !== -1; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | module.exports = ({ models, target, plugin = false }, ctx) => { | 
					
						
							|  |  |  |   const { instance } = ctx; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |   // Parse every authenticated model.
 | 
					
						
							|  |  |  |   Object.keys(models).map(model => { | 
					
						
							|  |  |  |     const definition = models[model]; | 
					
						
							|  |  |  |     definition.orm = 'mongoose'; | 
					
						
							|  |  |  |     definition.associations = []; | 
					
						
							|  |  |  |     definition.globalName = _.upperFirst(_.camelCase(definition.globalId)); | 
					
						
							|  |  |  |     definition.loadedModel = {}; | 
					
						
							|  |  |  |     // Set the default values to model settings.
 | 
					
						
							|  |  |  |     _.defaults(definition, { | 
					
						
							|  |  |  |       primaryKey: '_id', | 
					
						
							| 
									
										
										
										
											2019-08-09 13:48:31 +02:00
										 |  |  |       primaryKeyType: 'string', | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     if (!plugin) { | 
					
						
							|  |  |  |       global[definition.globalName] = {}; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-25 17:23:17 +01:00
										 |  |  |     const componentAttributes = Object.keys(definition.attributes).filter(key => | 
					
						
							|  |  |  |       ['component', 'dynamiczone'].includes(definition.attributes[key].type) | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     const scalarAttributes = Object.keys(definition.attributes).filter(key => { | 
					
						
							|  |  |  |       const { type } = definition.attributes[key]; | 
					
						
							| 
									
										
										
										
											2019-11-25 17:23:17 +01:00
										 |  |  |       return ( | 
					
						
							|  |  |  |         type !== undefined && | 
					
						
							|  |  |  |         type !== null && | 
					
						
							|  |  |  |         type !== 'component' && | 
					
						
							|  |  |  |         type !== 'dynamiczone' | 
					
						
							|  |  |  |       ); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     const relationalAttributes = Object.keys(definition.attributes).filter( | 
					
						
							|  |  |  |       key => { | 
					
						
							|  |  |  |         const { type } = definition.attributes[key]; | 
					
						
							|  |  |  |         return type === undefined; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-25 17:23:17 +01:00
										 |  |  |     // handle component and dynamic zone attrs
 | 
					
						
							| 
									
										
										
										
											2019-10-22 18:01:03 +02:00
										 |  |  |     if (componentAttributes.length > 0) { | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       // create join morph collection thingy
 | 
					
						
							| 
									
										
										
										
											2019-10-22 18:01:03 +02:00
										 |  |  |       componentAttributes.forEach(name => { | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |         definition.loadedModel[name] = [ | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             kind: String, | 
					
						
							|  |  |  |             ref: { | 
					
						
							|  |  |  |               type: mongoose.Schema.Types.ObjectId, | 
					
						
							|  |  |  |               refPath: `${name}.kind`, | 
					
						
							|  |  |  |             }, | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  |           }, | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |         ]; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     // handle scalar attrs
 | 
					
						
							|  |  |  |     scalarAttributes.forEach(name => { | 
					
						
							|  |  |  |       const attr = definition.attributes[name]; | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       definition.loadedModel[name] = { | 
					
						
							|  |  |  |         ...attr, | 
					
						
							| 
									
										
										
										
											2020-01-06 11:15:00 +01:00
										 |  |  |         ...utils(instance).convertType(attr), | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       }; | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     // handle relational attrs
 | 
					
						
							|  |  |  |     relationalAttributes.forEach(name => { | 
					
						
							|  |  |  |       buildRelation({ | 
					
						
							|  |  |  |         definition, | 
					
						
							|  |  |  |         model, | 
					
						
							|  |  |  |         instance, | 
					
						
							|  |  |  |         name, | 
					
						
							|  |  |  |         attribute: definition.attributes[name], | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     const schema = new instance.Schema( | 
					
						
							|  |  |  |       _.omitBy(definition.loadedModel, ({ type }) => type === 'virtual') | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     // Initialize lifecycle callbacks.
 | 
					
						
							|  |  |  |     const preLifecycle = { | 
					
						
							|  |  |  |       validate: 'beforeCreate', | 
					
						
							|  |  |  |       find: 'beforeFetchAll', | 
					
						
							|  |  |  |       findOne: 'beforeFetch', | 
					
						
							|  |  |  |       findOneAndUpdate: 'beforeUpdate', | 
					
						
							|  |  |  |       findOneAndRemove: 'beforeDestroy', | 
					
						
							|  |  |  |       remove: 'beforeDestroy', | 
					
						
							|  |  |  |       update: 'beforeUpdate', | 
					
						
							|  |  |  |       updateOne: 'beforeUpdate', | 
					
						
							|  |  |  |       save: 'beforeSave', | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const findLifecycles = [ | 
					
						
							|  |  |  |       'find', | 
					
						
							|  |  |  |       'findOne', | 
					
						
							|  |  |  |       'findOneAndUpdate', | 
					
						
							|  |  |  |       'findOneAndRemove', | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* | 
					
						
							|  |  |  |         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( | 
					
						
							| 
									
										
										
										
											2020-01-06 11:51:03 +01:00
										 |  |  |       isPolymorphicAssoc | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     const populateFn = createOnFetchPopulateFn({ | 
					
						
							| 
									
										
										
										
											2019-10-22 18:01:03 +02:00
										 |  |  |       componentAttributes, | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       morphAssociations, | 
					
						
							|  |  |  |       definition, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     findLifecycles.forEach(key => { | 
					
						
							|  |  |  |       schema.pre(key, populateFn); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     Object.keys(preLifecycle).forEach(key => { | 
					
						
							|  |  |  |       const fn = preLifecycle[key]; | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       if (_.isFunction(target[model.toLowerCase()][fn])) { | 
					
						
							| 
									
										
										
										
											2019-10-29 09:42:49 +02:00
										 |  |  |         schema.pre(key, function() { | 
					
						
							| 
									
										
										
										
											2019-11-25 17:23:17 +01:00
										 |  |  |           return target[model.toLowerCase()][fn](this); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     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
 | 
					
						
							|  |  |  |     Object.keys(postLifecycle).forEach(key => { | 
					
						
							|  |  |  |       const fn = postLifecycle[key]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (_.isFunction(target[model.toLowerCase()][fn])) { | 
					
						
							|  |  |  |         schema.post(key, function(doc, next) { | 
					
						
							|  |  |  |           target[model.toLowerCase()] | 
					
						
							|  |  |  |             [fn](this, doc) | 
					
						
							|  |  |  |             .then(next) | 
					
						
							|  |  |  |             .catch(err => { | 
					
						
							|  |  |  |               strapi.log.error(err); | 
					
						
							|  |  |  |               next(err); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     // Add virtual key to provide populate and reverse populate
 | 
					
						
							|  |  |  |     _.forEach( | 
					
						
							|  |  |  |       _.pickBy(definition.loadedModel, model => { | 
					
						
							|  |  |  |         return model.type === 'virtual'; | 
					
						
							|  |  |  |       }), | 
					
						
							|  |  |  |       (value, key) => { | 
					
						
							|  |  |  |         schema.virtual(key.replace('_v', ''), { | 
					
						
							|  |  |  |           ref: value.ref, | 
					
						
							|  |  |  |           localField: '_id', | 
					
						
							|  |  |  |           foreignField: value.via, | 
					
						
							|  |  |  |           justOne: value.justOne || false, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-22 18:16:16 +02:00
										 |  |  |     target[model].allAttributes = _.clone(definition.attributes); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     // Use provided timestamps if the elemnets in the array are string else use default.
 | 
					
						
							| 
									
										
										
										
											2019-07-22 18:16:16 +02:00
										 |  |  |     const timestampsOption = _.get(definition, 'options.timestamps', true); | 
					
						
							|  |  |  |     if (_.isArray(timestampsOption)) { | 
					
						
							|  |  |  |       const [ | 
					
						
							|  |  |  |         createAtCol = 'createdAt', | 
					
						
							|  |  |  |         updatedAtCol = 'updatedAt', | 
					
						
							|  |  |  |       ] = timestampsOption; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       schema.set('timestamps', { | 
					
						
							|  |  |  |         createdAt: createAtCol, | 
					
						
							|  |  |  |         updatedAt: updatedAtCol, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       target[model].allAttributes[createAtCol] = { | 
					
						
							|  |  |  |         type: 'timestamp', | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       target[model].allAttributes[updatedAtCol] = { | 
					
						
							| 
									
										
										
										
											2019-12-04 15:29:17 +01:00
										 |  |  |         type: 'timestamp', | 
					
						
							| 
									
										
										
										
											2019-07-22 18:16:16 +02:00
										 |  |  |       }; | 
					
						
							|  |  |  |     } else if (timestampsOption === true) { | 
					
						
							|  |  |  |       schema.set('timestamps', true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       _.set(definition, 'options.timestamps', ['createdAt', 'updatedAt']); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       target[model].allAttributes.createdAt = { | 
					
						
							|  |  |  |         type: 'timestamp', | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       target[model].allAttributes.updatedAt = { | 
					
						
							| 
									
										
										
										
											2019-12-04 15:29:17 +01:00
										 |  |  |         type: 'timestamp', | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     schema.set( | 
					
						
							|  |  |  |       'minimize', | 
					
						
							|  |  |  |       _.get(definition, 'options.minimize', false) === true | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-10 17:11:39 +01:00
										 |  |  |     const refToStrapiRef = obj => { | 
					
						
							|  |  |  |       const ref = obj.ref; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       let plainData = typeof ref.toJSON === 'function' ? ref.toJSON() : ref; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (typeof plainData !== 'object') return ref; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return { | 
					
						
							|  |  |  |         __contentType: obj.kind, | 
					
						
							|  |  |  |         ...ref, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     schema.options.toObject = 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()); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  |           }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |         morphAssociations.forEach(association => { | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  |           if ( | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |             Array.isArray(returned[association.alias]) && | 
					
						
							|  |  |  |             returned[association.alias].length > 0 | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  |           ) { | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |             // Reformat data by bypassing the many-to-many relationship.
 | 
					
						
							|  |  |  |             switch (association.nature) { | 
					
						
							|  |  |  |               case 'oneMorphToOne': | 
					
						
							| 
									
										
										
										
											2019-12-10 17:11:39 +01:00
										 |  |  |                 returned[association.alias] = refToStrapiRef( | 
					
						
							|  |  |  |                   returned[association.alias][0] | 
					
						
							|  |  |  |                 ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2019-12-10 17:11:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |               case 'manyMorphToMany': | 
					
						
							|  |  |  |               case 'manyMorphToOne': | 
					
						
							|  |  |  |                 returned[association.alias] = returned[association.alias].map( | 
					
						
							| 
									
										
										
										
											2019-12-10 17:11:39 +01:00
										 |  |  |                   obj => refToStrapiRef(obj) | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |                 ); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |               default: | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-22 18:01:03 +02:00
										 |  |  |         componentAttributes.forEach(name => { | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |           const attribute = definition.attributes[name]; | 
					
						
							| 
									
										
										
										
											2019-11-26 11:24:00 +01:00
										 |  |  |           const { type } = attribute; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (type === 'component') { | 
					
						
							|  |  |  |             if (Array.isArray(returned[name])) { | 
					
						
							|  |  |  |               const components = returned[name].map(el => el.ref); | 
					
						
							|  |  |  |               // Reformat data by bypassing the many-to-many relationship.
 | 
					
						
							|  |  |  |               returned[name] = | 
					
						
							|  |  |  |                 attribute.repeatable === true | 
					
						
							|  |  |  |                   ? components | 
					
						
							|  |  |  |                   : _.first(components) || null; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 11:24:00 +01:00
										 |  |  |           if (type === 'dynamiczone') { | 
					
						
							|  |  |  |             const components = returned[name].map(el => { | 
					
						
							|  |  |  |               return { | 
					
						
							|  |  |  |                 __component: findComponentByGlobalId(el.kind).uid, | 
					
						
							|  |  |  |                 ...el.ref, | 
					
						
							|  |  |  |               }; | 
					
						
							|  |  |  |             }); | 
					
						
							| 
									
										
										
										
											2019-11-25 17:23:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 11:24:00 +01:00
										 |  |  |             returned[name] = components; | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |         }); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Instantiate model.
 | 
					
						
							|  |  |  |     const Model = instance.model( | 
					
						
							|  |  |  |       definition.globalId, | 
					
						
							|  |  |  |       schema, | 
					
						
							|  |  |  |       definition.collectionName | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 18:25:44 +02:00
										 |  |  |     Model.on('index', error => { | 
					
						
							|  |  |  |       if (error) { | 
					
						
							|  |  |  |         if (error.code === 11000) { | 
					
						
							|  |  |  |           strapi.log.error( | 
					
						
							|  |  |  |             `Unique constraint fails, make sure to update your data and restart to apply the unique constraint.\n\t- ${error.message}` | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           strapi.log.error( | 
					
						
							|  |  |  |             `An index error happened, it wasn't applied.\n\t- ${error.message}` | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     if (!plugin) { | 
					
						
							|  |  |  |       global[definition.globalName] = Model; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     // Expose ORM functions through the `target` object.
 | 
					
						
							|  |  |  |     target[model] = _.assign(Model, target[model]); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     // Push attributes to be aware of model schema.
 | 
					
						
							|  |  |  |     target[model]._attributes = definition.attributes; | 
					
						
							|  |  |  |     target[model].updateRelations = relations.update; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const createOnFetchPopulateFn = ({ | 
					
						
							|  |  |  |   morphAssociations, | 
					
						
							| 
									
										
										
										
											2019-10-22 18:01:03 +02:00
										 |  |  |   componentAttributes, | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |   definition, | 
					
						
							|  |  |  | }) => { | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  |   return function() { | 
					
						
							|  |  |  |     const populatedPaths = this.getPopulatedPaths(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     morphAssociations.forEach(association => { | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  |       const { alias, nature } = association; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (['oneToManyMorph', 'manyToManyMorph'].includes(nature)) { | 
					
						
							|  |  |  |         this.populate({ | 
					
						
							|  |  |  |           path: alias, | 
					
						
							|  |  |  |           match: { | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |             [`${association.via}.${association.filter}`]: association.alias, | 
					
						
							|  |  |  |             [`${association.via}.kind`]: definition.globalId, | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  |           }, | 
					
						
							|  |  |  |           options: { | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |             sort: '-createdAt', | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  |           }, | 
					
						
							| 
									
										
										
										
											2019-11-26 09:53:18 +01:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  |         return; | 
					
						
							| 
									
										
										
										
											2019-11-26 11:24:00 +01:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  |       if (populatedPaths.includes(alias)) { | 
					
						
							|  |  |  |         _.set(this._mongooseOptions.populate, [alias, 'path'], `${alias}.ref`); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-19 11:05:54 +01:00
										 |  |  |     if (definition.modelType === 'component') { | 
					
						
							|  |  |  |       definition.associations | 
					
						
							| 
									
										
										
										
											2020-01-06 11:51:03 +01:00
										 |  |  |         .filter(assoc => !isPolymorphicAssoc(assoc)) | 
					
						
							| 
									
										
										
										
											2019-12-19 11:05:54 +01:00
										 |  |  |         .filter(ast => ast.autoPopulate !== false) | 
					
						
							|  |  |  |         .forEach(ast => { | 
					
						
							|  |  |  |           this.populate({ path: ast.alias }); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  |     componentAttributes.forEach(key => { | 
					
						
							|  |  |  |       this.populate({ path: `${key}.ref` }); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |   }; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const buildRelation = ({ definition, model, instance, attribute, name }) => { | 
					
						
							| 
									
										
										
										
											2019-07-08 11:06:11 +02:00
										 |  |  |   const { nature, verbose } = | 
					
						
							|  |  |  |     utilsModels.getNature(attribute, name, undefined, model.toLowerCase()) || | 
					
						
							|  |  |  |     {}; | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Build associations key
 | 
					
						
							|  |  |  |   utilsModels.defineAssociations( | 
					
						
							|  |  |  |     model.toLowerCase(), | 
					
						
							|  |  |  |     definition, | 
					
						
							|  |  |  |     attribute, | 
					
						
							|  |  |  |     name | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   switch (verbose) { | 
					
						
							|  |  |  |     case 'hasOne': { | 
					
						
							|  |  |  |       const ref = attribute.plugin | 
					
						
							|  |  |  |         ? strapi.plugins[attribute.plugin].models[attribute.model].globalId | 
					
						
							|  |  |  |         : strapi.models[attribute.model].globalId; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       definition.loadedModel[name] = { | 
					
						
							|  |  |  |         type: instance.Schema.Types.ObjectId, | 
					
						
							|  |  |  |         ref, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'hasMany': { | 
					
						
							|  |  |  |       const FK = _.find(definition.associations, { | 
					
						
							|  |  |  |         alias: name, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       const ref = attribute.plugin | 
					
						
							|  |  |  |         ? strapi.plugins[attribute.plugin].models[attribute.collection].globalId | 
					
						
							|  |  |  |         : strapi.models[attribute.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.
 | 
					
						
							|  |  |  |         attribute.isVirtual = true; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         definition.loadedModel[name] = [ | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             type: instance.Schema.Types.ObjectId, | 
					
						
							|  |  |  |             ref, | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |         ]; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'belongsTo': { | 
					
						
							|  |  |  |       const FK = _.find(definition.associations, { | 
					
						
							|  |  |  |         alias: name, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       const ref = attribute.plugin | 
					
						
							|  |  |  |         ? strapi.plugins[attribute.plugin].models[attribute.model].globalId | 
					
						
							|  |  |  |         : strapi.models[attribute.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.
 | 
					
						
							|  |  |  |         attribute.isVirtual = true; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         definition.loadedModel[name] = { | 
					
						
							|  |  |  |           type: instance.Schema.Types.ObjectId, | 
					
						
							|  |  |  |           ref, | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'belongsToMany': { | 
					
						
							| 
									
										
										
										
											2019-07-08 11:06:11 +02:00
										 |  |  |       const targetModel = attribute.plugin | 
					
						
							|  |  |  |         ? strapi.plugins[attribute.plugin].models[attribute.collection] | 
					
						
							|  |  |  |         : strapi.models[attribute.collection]; | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-08 11:06:11 +02:00
										 |  |  |       const ref = targetModel.globalId; | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-08 11:06:11 +02:00
										 |  |  |       if (nature === 'manyWay') { | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |         definition.loadedModel[name] = [ | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             type: instance.Schema.Types.ObjectId, | 
					
						
							|  |  |  |             ref, | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |         ]; | 
					
						
							| 
									
										
										
										
											2019-07-08 11:06:11 +02:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         const FK = _.find(definition.associations, { | 
					
						
							|  |  |  |           alias: name, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // One-side of the relationship has to be a virtual field to be bidirectional.
 | 
					
						
							|  |  |  |         if ((FK && _.isUndefined(FK.via)) || attribute.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.
 | 
					
						
							|  |  |  |           attribute.isVirtual = true; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           definition.loadedModel[name] = [ | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               type: instance.Schema.Types.ObjectId, | 
					
						
							|  |  |  |               ref, | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |           ]; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'morphOne': { | 
					
						
							|  |  |  |       const FK = _.find(definition.associations, { | 
					
						
							|  |  |  |         alias: name, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       const ref = attribute.plugin | 
					
						
							|  |  |  |         ? strapi.plugins[attribute.plugin].models[attribute.model].globalId | 
					
						
							|  |  |  |         : strapi.models[attribute.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.
 | 
					
						
							|  |  |  |       attribute.isVirtual = true; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'morphMany': { | 
					
						
							|  |  |  |       const FK = _.find(definition.associations, { | 
					
						
							|  |  |  |         alias: name, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       const ref = attribute.plugin | 
					
						
							|  |  |  |         ? strapi.plugins[attribute.plugin].models[attribute.collection].globalId | 
					
						
							|  |  |  |         : strapi.models[attribute.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.
 | 
					
						
							|  |  |  |       attribute.isVirtual = true; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'belongsToMorph': { | 
					
						
							|  |  |  |       definition.loadedModel[name] = { | 
					
						
							|  |  |  |         kind: String, | 
					
						
							|  |  |  |         [attribute.filter]: String, | 
					
						
							|  |  |  |         ref: { | 
					
						
							|  |  |  |           type: instance.Schema.Types.ObjectId, | 
					
						
							|  |  |  |           refPath: `${name}.kind`, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'belongsToManyMorph': { | 
					
						
							|  |  |  |       definition.loadedModel[name] = [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           kind: String, | 
					
						
							|  |  |  |           [attribute.filter]: String, | 
					
						
							|  |  |  |           ref: { | 
					
						
							|  |  |  |             type: instance.Schema.Types.ObjectId, | 
					
						
							|  |  |  |             refPath: `${name}.kind`, | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       ]; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | }; |