| 
									
										
										
										
											2021-05-18 10:16:03 +02:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  | const _ = require('lodash/fp'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 14:32:50 +02:00
										 |  |  | const types = require('../types'); | 
					
						
							|  |  |  | const { createRelation } = require('./relations'); | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Metadata extends Map { | 
					
						
							|  |  |  |   add(meta) { | 
					
						
							|  |  |  |     return this.set(meta.uid, meta); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-05-18 10:16:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 10:49:43 +02:00
										 |  |  | // TODO: check if there isn't an attribute with an id already
 | 
					
						
							| 
									
										
										
										
											2021-07-01 14:32:50 +02:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Create Metadata from models configurations | 
					
						
							|  |  |  |  * @param {object[]} models | 
					
						
							|  |  |  |  * @returns {Metadata} | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-06-02 15:53:22 +02:00
										 |  |  | const createMetadata = (models = []) => { | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |   const metadata = new Metadata(); | 
					
						
							| 
									
										
										
										
											2021-05-18 10:16:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |   // init pass
 | 
					
						
							| 
									
										
										
										
											2021-06-30 22:52:12 +02:00
										 |  |  |   for (const model of _.cloneDeep(models)) { | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |     metadata.add({ | 
					
						
							|  |  |  |       singularName: model.singularName, | 
					
						
							|  |  |  |       uid: model.uid, | 
					
						
							|  |  |  |       tableName: model.tableName, | 
					
						
							| 
									
										
										
										
											2021-05-18 10:16:03 +02:00
										 |  |  |       attributes: { | 
					
						
							|  |  |  |         id: { | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |           type: 'increments', | 
					
						
							| 
									
										
										
										
											2021-05-18 10:16:03 +02:00
										 |  |  |         }, | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |         ...model.attributes, | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2021-08-04 17:47:38 +02:00
										 |  |  |       lifecycles: model.lifecycles || {}, | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // build compos / relations
 | 
					
						
							|  |  |  |   for (const meta of metadata.values()) { | 
					
						
							|  |  |  |     if (hasComponentsOrDz(meta)) { | 
					
						
							|  |  |  |       const compoLinkModelMeta = createCompoLinkModelMeta(meta); | 
					
						
							|  |  |  |       meta.componentLink = compoLinkModelMeta; | 
					
						
							|  |  |  |       metadata.add(compoLinkModelMeta); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const [attributeName, attribute] of Object.entries(meta.attributes)) { | 
					
						
							| 
									
										
										
										
											2021-06-18 12:27:47 +02:00
										 |  |  |       try { | 
					
						
							|  |  |  |         if (types.isComponent(attribute.type)) { | 
					
						
							| 
									
										
										
										
											2021-09-22 10:49:43 +02:00
										 |  |  |           createComponent(attributeName, attribute, meta, metadata); | 
					
						
							| 
									
										
										
										
											2021-06-18 12:27:47 +02:00
										 |  |  |           continue; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 12:27:47 +02:00
										 |  |  |         if (types.isDynamicZone(attribute.type)) { | 
					
						
							| 
									
										
										
										
											2021-09-22 10:49:43 +02:00
										 |  |  |           createDynamicZone(attributeName, attribute, meta, metadata); | 
					
						
							| 
									
										
										
										
											2021-06-18 12:27:47 +02:00
										 |  |  |           continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (types.isRelation(attribute.type)) { | 
					
						
							|  |  |  |           createRelation(attributeName, attribute, meta, metadata); | 
					
						
							|  |  |  |           continue; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-09-22 10:49:43 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         createAttribute(attributeName, attribute, meta, metadata); | 
					
						
							| 
									
										
										
										
											2021-06-18 12:27:47 +02:00
										 |  |  |       } catch (error) { | 
					
						
							| 
									
										
										
										
											2021-09-22 10:49:43 +02:00
										 |  |  |         console.log(error); | 
					
						
							| 
									
										
										
										
											2021-06-18 12:27:47 +02:00
										 |  |  |         throw new Error( | 
					
						
							|  |  |  |           `Error on attribute ${attributeName} in model ${meta.singularName}(${meta.uid}): ${error.message}` | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2021-06-18 12:27:47 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 10:49:43 +02:00
										 |  |  |   for (const meta of metadata.values()) { | 
					
						
							|  |  |  |     const columnToAttribute = Object.keys(meta.attributes).reduce((acc, key) => { | 
					
						
							|  |  |  |       const attribute = meta.attributes[key]; | 
					
						
							|  |  |  |       return Object.assign(acc, { [attribute.columnName || key]: key }); | 
					
						
							|  |  |  |     }, {}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     meta.columnToAttribute = columnToAttribute; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 12:27:47 +02:00
										 |  |  |   return metadata; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 14:32:50 +02:00
										 |  |  | const hasComponentsOrDz = model => { | 
					
						
							|  |  |  |   return Object.values(model.attributes).some( | 
					
						
							|  |  |  |     ({ type }) => types.isComponent(type) || types.isDynamicZone(type) | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2021-05-18 10:16:03 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  | // NOTE: we might just move the compo logic outside this layer too at some point
 | 
					
						
							|  |  |  | const createCompoLinkModelMeta = baseModelMeta => { | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     // TODO: make sure there can't be any conflicts with a prefix
 | 
					
						
							|  |  |  |     // singularName: 'compo',
 | 
					
						
							| 
									
										
										
										
											2021-06-25 12:07:32 +02:00
										 |  |  |     uid: `${baseModelMeta.tableName}_components`, | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |     tableName: `${baseModelMeta.tableName}_components`, | 
					
						
							|  |  |  |     attributes: { | 
					
						
							|  |  |  |       id: { | 
					
						
							|  |  |  |         type: 'increments', | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       entity_id: { | 
					
						
							|  |  |  |         type: 'integer', | 
					
						
							|  |  |  |         column: { | 
					
						
							|  |  |  |           unsigned: true, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       component_id: { | 
					
						
							|  |  |  |         type: 'integer', | 
					
						
							|  |  |  |         column: { | 
					
						
							|  |  |  |           unsigned: true, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       component_type: { | 
					
						
							|  |  |  |         type: 'string', | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       field: { | 
					
						
							|  |  |  |         type: 'string', | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       order: { | 
					
						
							|  |  |  |         type: 'integer', | 
					
						
							|  |  |  |         column: { | 
					
						
							|  |  |  |           unsigned: true, | 
					
						
							| 
									
										
										
										
											2021-09-15 12:25:09 +02:00
										 |  |  |           defaultTo: 0, | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |         }, | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     indexes: [ | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: `${baseModelMeta.tableName}_field_index`, | 
					
						
							|  |  |  |         columns: ['field'], | 
					
						
							| 
									
										
										
										
											2021-09-15 12:25:09 +02:00
										 |  |  |         type: null, | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |       }, | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         name: `${baseModelMeta.tableName}_component_type_index`, | 
					
						
							|  |  |  |         columns: ['component_type'], | 
					
						
							| 
									
										
										
										
											2021-09-15 12:25:09 +02:00
										 |  |  |         type: null, | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |       }, | 
					
						
							| 
									
										
										
										
											2021-09-16 11:37:44 +02:00
										 |  |  |       { | 
					
						
							|  |  |  |         name: `${baseModelMeta.tableName}_entity_fk`, | 
					
						
							|  |  |  |         columns: ['entity_id'], | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |     ], | 
					
						
							|  |  |  |     foreignKeys: [ | 
					
						
							|  |  |  |       { | 
					
						
							| 
									
										
										
										
											2021-06-24 18:28:36 +02:00
										 |  |  |         name: `${baseModelMeta.tableName}_entity_fk`, | 
					
						
							| 
									
										
										
										
											2021-06-17 16:17:15 +02:00
										 |  |  |         columns: ['entity_id'], | 
					
						
							|  |  |  |         referencedColumns: ['id'], | 
					
						
							|  |  |  |         referencedTable: baseModelMeta.tableName, | 
					
						
							|  |  |  |         onDelete: 'CASCADE', | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     ], | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 10:49:43 +02:00
										 |  |  | const createDynamicZone = (attributeName, attribute, meta) => { | 
					
						
							|  |  |  |   Object.assign(attribute, { | 
					
						
							|  |  |  |     type: 'relation', | 
					
						
							|  |  |  |     relation: 'morphToMany', | 
					
						
							|  |  |  |     // TODO: handle restrictions at some point
 | 
					
						
							|  |  |  |     // target: attribute.components,
 | 
					
						
							|  |  |  |     joinTable: { | 
					
						
							|  |  |  |       name: meta.componentLink.tableName, | 
					
						
							|  |  |  |       joinColumn: { | 
					
						
							|  |  |  |         name: 'entity_id', | 
					
						
							|  |  |  |         referencedColumn: 'id', | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       morphColumn: { | 
					
						
							|  |  |  |         idColumn: { | 
					
						
							|  |  |  |           name: 'component_id', | 
					
						
							|  |  |  |           referencedColumn: 'id', | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         typeColumn: { | 
					
						
							|  |  |  |           name: 'component_type', | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         typeField: '__component', | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       on: { | 
					
						
							|  |  |  |         field: attributeName, | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       orderBy: { | 
					
						
							|  |  |  |         order: 'asc', | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const createComponent = (attributeName, attribute, meta) => { | 
					
						
							|  |  |  |   Object.assign(attribute, { | 
					
						
							|  |  |  |     type: 'relation', | 
					
						
							|  |  |  |     relation: attribute.repeatable === true ? 'oneToMany' : 'oneToOne', | 
					
						
							|  |  |  |     target: attribute.component, | 
					
						
							|  |  |  |     joinTable: { | 
					
						
							|  |  |  |       name: meta.componentLink.tableName, | 
					
						
							|  |  |  |       joinColumn: { | 
					
						
							|  |  |  |         name: 'entity_id', | 
					
						
							|  |  |  |         referencedColumn: 'id', | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       inverseJoinColumn: { | 
					
						
							|  |  |  |         name: 'component_id', | 
					
						
							|  |  |  |         referencedColumn: 'id', | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       on: { | 
					
						
							|  |  |  |         field: attributeName, | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       orderBy: { | 
					
						
							|  |  |  |         order: 'asc', | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const createAttribute = (attributeName, attribute) => { | 
					
						
							|  |  |  |   const columnName = _.snakeCase(attributeName); | 
					
						
							|  |  |  |   Object.assign(attribute, { columnName }); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-18 10:16:03 +02:00
										 |  |  | module.exports = createMetadata; |