| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const _ = require('lodash'); | 
					
						
							|  |  |  | const mongoose = require('mongoose'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-18 17:09:21 +02:00
										 |  |  | const { models: utilsModels, contentTypes: contentTypesUtils } = require('strapi-utils'); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | const utils = require('./utils'); | 
					
						
							| 
									
										
										
										
											2020-09-16 10:08:18 +02:00
										 |  |  | const populateQueries = require('./utils/populate-queries'); | 
					
						
							| 
									
										
										
										
											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'); | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | const { didDefinitionChange, storeDefinition } = require('./utils/store-definition'); | 
					
						
							|  |  |  | const { migrateDraftAndPublish } = require('./database-migration'); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-18 17:09:21 +02:00
										 |  |  | const { | 
					
						
							|  |  |  |   PUBLISHED_AT_ATTRIBUTE, | 
					
						
							|  |  |  |   CREATED_BY_ATTRIBUTE, | 
					
						
							|  |  |  |   UPDATED_BY_ATTRIBUTE, | 
					
						
							| 
									
										
										
										
											2020-09-16 10:08:18 +02:00
										 |  |  |   DP_PUB_STATES, | 
					
						
							| 
									
										
										
										
											2020-08-18 17:09:21 +02:00
										 |  |  | } = contentTypesUtils.constants; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-06 11:51:03 +01:00
										 |  |  | const isPolymorphicAssoc = assoc => { | 
					
						
							|  |  |  |   return assoc.nature.toLowerCase().indexOf('morph') !== -1; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  | module.exports = async ({ models, target }, ctx) => { | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  |   const { instance } = ctx; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-27 11:39:53 +01:00
										 |  |  |   function mountModel(model) { | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     const definition = models[model]; | 
					
						
							|  |  |  |     definition.orm = 'mongoose'; | 
					
						
							|  |  |  |     definition.associations = []; | 
					
						
							|  |  |  |     definition.globalName = _.upperFirst(_.camelCase(definition.globalId)); | 
					
						
							|  |  |  |     definition.loadedModel = {}; | 
					
						
							| 
									
										
										
										
											2020-08-27 18:11:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const hasDraftAndPublish = contentTypesUtils.hasDraftAndPublish(definition); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     // 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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-01 13:03:30 +02:00
										 |  |  |     if (!definition.uid.startsWith('strapi::') && definition.modelType !== 'component') { | 
					
						
							| 
									
										
										
										
											2020-08-18 17:09:21 +02:00
										 |  |  |       if (contentTypesUtils.hasDraftAndPublish(definition)) { | 
					
						
							|  |  |  |         definition.attributes[PUBLISHED_AT_ATTRIBUTE] = { | 
					
						
							|  |  |  |           type: 'datetime', | 
					
						
							|  |  |  |           configurable: false, | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-05 12:00:03 +02:00
										 |  |  |       const isPrivate = !_.get(definition, 'options.populateCreatorFields', false); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-18 17:09:21 +02:00
										 |  |  |       definition.attributes[CREATED_BY_ATTRIBUTE] = { | 
					
						
							| 
									
										
										
										
											2020-06-29 11:12:53 +02:00
										 |  |  |         model: 'user', | 
					
						
							|  |  |  |         plugin: 'admin', | 
					
						
							| 
									
										
										
										
											2020-08-18 17:09:21 +02:00
										 |  |  |         configurable: false, | 
					
						
							|  |  |  |         writable: false, | 
					
						
							| 
									
										
										
										
											2020-10-01 17:47:08 +02:00
										 |  |  |         private: isPrivate, | 
					
						
							| 
									
										
										
										
											2020-06-29 11:12:53 +02:00
										 |  |  |       }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-18 17:09:21 +02:00
										 |  |  |       definition.attributes[UPDATED_BY_ATTRIBUTE] = { | 
					
						
							| 
									
										
										
										
											2020-06-29 11:12:53 +02:00
										 |  |  |         model: 'user', | 
					
						
							|  |  |  |         plugin: 'admin', | 
					
						
							| 
									
										
										
										
											2020-08-18 17:09:21 +02:00
										 |  |  |         configurable: false, | 
					
						
							|  |  |  |         writable: false, | 
					
						
							| 
									
										
										
										
											2020-10-01 17:47:08 +02:00
										 |  |  |         private: isPrivate, | 
					
						
							| 
									
										
										
										
											2020-06-29 11:12:53 +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]; | 
					
						
							| 
									
										
										
										
											2020-03-02 15:18:08 +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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-02 15:18:08 +01: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]; | 
					
						
							|  |  |  |       definition.loadedModel[name] = { | 
					
						
							|  |  |  |         ...attr, | 
					
						
							| 
									
										
										
										
											2020-02-13 15:28:54 +01:00
										 |  |  |         ...utils(instance).convertType(name, attr), | 
					
						
							| 
									
										
										
										
											2020-08-27 18:11:16 +02:00
										 |  |  |         // no require constraint to allow components in drafts
 | 
					
						
							|  |  |  |         required: | 
					
						
							|  |  |  |           definition.modelType === 'compo' || hasDraftAndPublish ? false : definition.required, | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-02 15:18:08 +01:00
										 |  |  |     const findLifecycles = ['find', 'findOne', 'findOneAndUpdate', 'findOneAndRemove']; | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* | 
					
						
							|  |  |  |         Override populate path for polymorphic association. | 
					
						
							|  |  |  |         It allows us to make Upload.find().populate('related') | 
					
						
							|  |  |  |         instead of Upload.find().populate('related.item') | 
					
						
							|  |  |  |       */ | 
					
						
							| 
									
										
										
										
											2020-03-02 15:18:08 +01:00
										 |  |  |     const morphAssociations = definition.associations.filter(isPolymorphicAssoc); | 
					
						
							| 
									
										
										
										
											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
										 |  |  |     // Add virtual key to provide populate and reverse populate
 | 
					
						
							|  |  |  |     _.forEach( | 
					
						
							| 
									
										
										
										
											2020-08-10 15:19:28 +02:00
										 |  |  |       _.pickBy(definition.loadedModel, ({ type }) => type === 'virtual'), | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       (value, key) => { | 
					
						
							| 
									
										
										
										
											2020-08-10 15:19:28 +02:00
										 |  |  |         schema.virtual(key, { | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |           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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |     const createAtCol = _.get(definition, 'options.timestamps.0', 'createdAt'); | 
					
						
							|  |  |  |     const updatedAtCol = _.get(definition, 'options.timestamps.1', 'updatedAt'); | 
					
						
							| 
									
										
										
										
											2019-07-22 18:16:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |     if (_.get(definition, 'options.timestamps', false)) { | 
					
						
							|  |  |  |       _.set(definition, 'options.timestamps', [createAtCol, updatedAtCol]); | 
					
						
							| 
									
										
										
										
											2019-07-22 18:16:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-05 12:00:03 +02:00
										 |  |  |       _.assign(target[model].allAttributes, { | 
					
						
							|  |  |  |         [createAtCol]: { type: 'timestamp' }, | 
					
						
							|  |  |  |         [updatedAtCol]: { type: 'timestamp' }, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-07-22 18:16:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |       schema.set('timestamps', { createdAt: createAtCol, updatedAt: updatedAtCol }); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       _.set(definition, 'options.timestamps', false); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-10-05 12:00:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-02 15:18:08 +01: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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-02 15:18:08 +01:00
										 |  |  |       let plainData = ref && typeof ref.toJSON === 'function' ? ref.toJSON() : ref; | 
					
						
							| 
									
										
										
										
											2019-12-10 17:11:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       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.
 | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |         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': | 
					
						
							| 
									
										
										
										
											2020-03-02 15:18:08 +01:00
										 |  |  |                 returned[association.alias] = refToStrapiRef(returned[association.alias][0]); | 
					
						
							| 
									
										
										
										
											2019-12-10 17:11:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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': | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |               case 'manyMorphToOne': { | 
					
						
							| 
									
										
										
										
											2020-03-02 15:18:08 +01:00
										 |  |  |                 returned[association.alias] = returned[association.alias].map(obj => | 
					
						
							|  |  |  |                   refToStrapiRef(obj) | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |                 ); | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |               } | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |               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] = | 
					
						
							| 
									
										
										
										
											2020-03-02 15:18:08 +01:00
										 |  |  |                 attribute.repeatable === true ? components : _.first(components) || null; | 
					
						
							| 
									
										
										
										
											2019-11-26 11:24:00 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 11:24:00 +01:00
										 |  |  |           if (type === 'dynamiczone') { | 
					
						
							| 
									
										
										
										
											2020-07-01 13:03:30 +02:00
										 |  |  |             if (returned[name]) { | 
					
						
							|  |  |  |               returned[name] = returned[name].map(el => { | 
					
						
							| 
									
										
										
										
											2020-07-06 03:35:55 -04:00
										 |  |  |                 return { | 
					
						
							|  |  |  |                   __component: findComponentByGlobalId(el.kind).uid, | 
					
						
							|  |  |  |                   ...el.ref, | 
					
						
							|  |  |  |                 }; | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |         }); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Instantiate model.
 | 
					
						
							| 
									
										
										
										
											2020-03-02 15:18:08 +01:00
										 |  |  |     const Model = instance.model(definition.globalId, schema, definition.collectionName); | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-09 15:29:07 +02:00
										 |  |  |     const handleIndexesErrors = () => { | 
					
						
							| 
									
										
										
										
											2020-03-27 11:39:53 +01: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-08-09 18:25:44 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-03-27 11:39:53 +01:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2020-04-09 15:29:07 +02:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Only sync indexes in development env while it's not possible to create complex indexes directly from models
 | 
					
						
							|  |  |  |     // In other environments it will simply create missing indexes (those defined in the models but not present in db)
 | 
					
						
							|  |  |  |     if (strapi.app.env === 'development') { | 
					
						
							|  |  |  |       // Ensure indexes are synced with the model, prevent duplicate index errors
 | 
					
						
							|  |  |  |       // Side-effect: Delete all the indexes not present in the model.json
 | 
					
						
							|  |  |  |       Model.syncIndexes(null, handleIndexesErrors); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       handleIndexesErrors(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-08-09 18:25:44 +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; | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |     target[model].deleteRelations = relations.deleteRelations; | 
					
						
							| 
									
										
										
										
											2020-10-01 17:47:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-05 12:00:03 +02:00
										 |  |  |     target[model].privateAttributes = contentTypesUtils.getPrivateAttributes(target[model]); | 
					
						
							| 
									
										
										
										
											2020-03-27 11:39:53 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-07 15:04:20 +02:00
										 |  |  |   // Instanciate every models
 | 
					
						
							|  |  |  |   Object.keys(models).forEach(mountModel); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Migrations + storing schema
 | 
					
						
							|  |  |  |   for (const model of Object.keys(models)) { | 
					
						
							|  |  |  |     const definition = models[model]; | 
					
						
							|  |  |  |     const modelInstance = target[model]; | 
					
						
							|  |  |  |     const definitionDidChange = await didDefinitionChange(definition, instance); | 
					
						
							|  |  |  |     if (definitionDidChange) { | 
					
						
							|  |  |  |       await migrateDraftAndPublish({ definition, model: modelInstance, ORM: instance }); | 
					
						
							|  |  |  |       await storeDefinition(definition, instance); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-02 15:18:08 +01:00
										 |  |  | const createOnFetchPopulateFn = ({ morphAssociations, componentAttributes, definition }) => { | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  |   return function() { | 
					
						
							|  |  |  |     const populatedPaths = this.getPopulatedPaths(); | 
					
						
							| 
									
										
										
										
											2020-09-16 10:08:18 +02:00
										 |  |  |     const { publicationState } = this.getOptions(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const getMatchQuery = assoc => { | 
					
						
							|  |  |  |       const assocModel = strapi.db.getModelByAssoc(assoc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if ( | 
					
						
							|  |  |  |         contentTypesUtils.hasDraftAndPublish(assocModel) && | 
					
						
							|  |  |  |         DP_PUB_STATES.includes(publicationState) | 
					
						
							|  |  |  |       ) { | 
					
						
							|  |  |  |         return populateQueries.publicationState[publicationState]; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return undefined; | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     morphAssociations.forEach(association => { | 
					
						
							| 
									
										
										
										
											2020-09-16 10:08:18 +02:00
										 |  |  |       const matchQuery = getMatchQuery(association); | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  |       const { alias, nature } = association; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (['oneToManyMorph', 'manyToManyMorph'].includes(nature)) { | 
					
						
							| 
									
										
										
										
											2020-09-16 10:08:18 +02:00
										 |  |  |         this.populate({ path: alias, match: matchQuery, options: { publicationState } }); | 
					
						
							| 
									
										
										
										
											2020-03-19 21:24:32 +01:00
										 |  |  |       } else if (populatedPaths.includes(alias)) { | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  |         _.set(this._mongooseOptions.populate, [alias, 'path'], `${alias}.ref`); | 
					
						
							| 
									
										
										
										
											2020-09-16 10:08:18 +02:00
										 |  |  |         _.set(this._mongooseOptions.populate, [alias, 'options'], { publicationState }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (matchQuery !== undefined) { | 
					
						
							|  |  |  |           _.set(this._mongooseOptions.populate, [alias, 'match'], matchQuery); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											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 => { | 
					
						
							| 
									
										
										
										
											2020-09-16 10:08:18 +02:00
										 |  |  |           this.populate({ | 
					
						
							|  |  |  |             path: ast.alias, | 
					
						
							|  |  |  |             match: getMatchQuery(ast), | 
					
						
							|  |  |  |             options: { publicationState }, | 
					
						
							|  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2019-12-19 11:05:54 +01:00
										 |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  |     componentAttributes.forEach(key => { | 
					
						
							| 
									
										
										
										
											2020-09-16 10:08:18 +02:00
										 |  |  |       this.populate({ path: `${key}.ref`, options: { publicationState } }); | 
					
						
							| 
									
										
										
										
											2019-11-26 17:01:58 +01:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											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 } = | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |     utilsModels.getNature({ | 
					
						
							|  |  |  |       attribute, | 
					
						
							|  |  |  |       attributeName: name, | 
					
						
							|  |  |  |       modelName: model.toLowerCase(), | 
					
						
							|  |  |  |     }) || {}; | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Build associations key
 | 
					
						
							| 
									
										
										
										
											2020-03-02 15:18:08 +01:00
										 |  |  |   utilsModels.defineAssociations(model.toLowerCase(), definition, attribute, name); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |   const getRef = (name, plugin) => { | 
					
						
							| 
									
										
										
										
											2020-05-13 18:16:12 +02:00
										 |  |  |     return strapi.db.getModel(name, plugin).globalId; | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const setField = (name, val) => { | 
					
						
							|  |  |  |     definition.loadedModel[name] = val; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const { ObjectId } = instance.Schema.Types; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |   switch (verbose) { | 
					
						
							|  |  |  |     case 'hasOne': { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |       const ref = getRef(attribute.model, attribute.plugin); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       setField(name, { type: ObjectId, ref }); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'hasMany': { | 
					
						
							|  |  |  |       const FK = _.find(definition.associations, { | 
					
						
							|  |  |  |         alias: name, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       const ref = getRef(attribute.collection, attribute.plugin); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if (FK) { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |         setField(name, { | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |           type: 'virtual', | 
					
						
							|  |  |  |           ref, | 
					
						
							|  |  |  |           via: FK.via, | 
					
						
							|  |  |  |           justOne: false, | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Set this info to be able to see if this field is a real database's field.
 | 
					
						
							|  |  |  |         attribute.isVirtual = true; | 
					
						
							|  |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |         setField(name, [{ type: ObjectId, ref }]); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'belongsTo': { | 
					
						
							|  |  |  |       const FK = _.find(definition.associations, { | 
					
						
							|  |  |  |         alias: name, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |       const ref = getRef(attribute.model, attribute.plugin); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if ( | 
					
						
							|  |  |  |         FK && | 
					
						
							|  |  |  |         FK.nature !== 'oneToOne' && | 
					
						
							|  |  |  |         FK.nature !== 'manyToOne' && | 
					
						
							|  |  |  |         FK.nature !== 'oneWay' && | 
					
						
							|  |  |  |         FK.nature !== 'oneToMorph' | 
					
						
							|  |  |  |       ) { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |         setField(name, { | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |           type: 'virtual', | 
					
						
							|  |  |  |           ref, | 
					
						
							|  |  |  |           via: FK.via, | 
					
						
							|  |  |  |           justOne: true, | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Set this info to be able to see if this field is a real database's field.
 | 
					
						
							|  |  |  |         attribute.isVirtual = true; | 
					
						
							|  |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |         setField(name, { type: ObjectId, ref }); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'belongsToMany': { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |       const ref = getRef(attribute.collection, attribute.plugin); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-08 11:06:11 +02:00
										 |  |  |       if (nature === 'manyWay') { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |         setField(name, [{ type: 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) { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |           setField(name, { | 
					
						
							| 
									
										
										
										
											2019-07-08 11:06:11 +02:00
										 |  |  |             type: 'virtual', | 
					
						
							|  |  |  |             ref, | 
					
						
							|  |  |  |             via: FK.via, | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |           }); | 
					
						
							| 
									
										
										
										
											2019-07-08 11:06:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |           // Set this info to be able to see if this field is a real database's field.
 | 
					
						
							|  |  |  |           attribute.isVirtual = true; | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |           setField(name, [{ type: ObjectId, ref }]); | 
					
						
							| 
									
										
										
										
											2019-07-08 11:06:11 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'morphOne': { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |       const ref = getRef(attribute.model, attribute.plugin); | 
					
						
							|  |  |  |       setField(name, { type: ObjectId, ref }); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'morphMany': { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |       const ref = getRef(attribute.collection, attribute.plugin); | 
					
						
							|  |  |  |       setField(name, [{ type: ObjectId, ref }]); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |     case 'belongsToMorph': { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |       setField(name, { | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |         kind: String, | 
					
						
							|  |  |  |         [attribute.filter]: String, | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |         ref: { type: ObjectId, refPath: `${name}.kind` }, | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case 'belongsToManyMorph': { | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |       setField(name, [ | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |           kind: String, | 
					
						
							|  |  |  |           [attribute.filter]: String, | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |           ref: { type: ObjectId, refPath: `${name}.kind` }, | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |         }, | 
					
						
							| 
									
										
										
										
											2020-03-19 16:46:27 +01:00
										 |  |  |       ]); | 
					
						
							| 
									
										
										
										
											2019-07-04 15:27:27 +02:00
										 |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-07-02 13:56:14 +02:00
										 |  |  | }; |