1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | 16× 16× 16× 20× 20× 20× 8× 12× 12× 16× 26× 23× 23× 3× 20× 7× 13× 13× 26× 26× 23× 23× 7× 16× 13× 3× 26× 26× 26× 26× 26× 33× 1× 32× 2× 30× 21× 3× 18× 18× 16× 3× 13× 26× 26× 13× | import isEmpty from 'lodash/isEmpty'; import omit from 'lodash/omit'; import { schema } from 'normalizr'; function getRelatedThrough(entity1, entity2) { const entity1Schema = entity1.schema; const entity2Schema = entity2; const t = Object.keys(entity1Schema).find((prop) => { Iif (prop.startsWith('_')) { return false; } const relation = entity1Schema[prop]; if (relation.key) { return relation.key === entity2Schema.key; } else Eif (Array.isArray(relation)) { return relation[0].key === entity2Schema.key; } return false; }); return t; } function createSchemaDefinition(config, bag) { const definition = Object.keys(config.attributes).reduce((memo, a) => { const attribute = config.attributes[a]; if (typeof attribute === 'function') { // Treat it like a computed property return { ...memo, _computed: { ...memo._computed, [a]: attribute } }; } else if (typeof attribute === 'string') { // Single reference to another schema return { ...memo, [a]: bag[attribute] }; } else Eif (attribute.isArray) { // Array reference to another schema return { ...memo, [a]: [ bag[attribute.relatedSchema] ] }; } return memo; // Should never come to here }, {}); return { ...definition, ...omit(config, 'attributes') }; } function extractRelatedEntities(attributes) { return Object.keys(attributes).reduce((memo, a) => { const attribute = attributes[a]; if (typeof attribute === 'string') { // Single reference to another schema return [ ...memo, { prop: a, entity: attribute, isArray: false } ]; } else if (attribute.isArray) { // Array reference to another schema return [ ...memo, { prop: a, entity: attribute.relatedSchema, isArray: true } ]; } return memo; }, []); } function getRelations(schema, relatedEntities, bag) { return () => relatedEntities.map((relatedEntity) => ({ to: relatedEntity.entity, through: relatedEntity.prop, foreign: getRelatedThrough(bag[relatedEntity.entity], schema), isMany: relatedEntity.isArray, })); } function generateSchema(schema, bag) { const finalSchema = bag[schema.name]; finalSchema.define(createSchemaDefinition(schema.config, bag)); Object.defineProperty(finalSchema, 'getRelations', { enumerable: false, value: getRelations(finalSchema, extractRelatedEntities(schema.config.attributes), bag), }); return finalSchema; } export function defineSchema(name, config={}) { if (isEmpty(name)) { throw new Error('[INVALID NAME]'); } if ((typeof config !== 'object') && config) { throw new Error('[INVALID CONFIG]'); } return { name, config: { ...config, attributes: config.attributes ? config.attributes : {}}, }; } export function hasMany(schema) { if (isEmpty(schema)) { throw new Error('[INVALID SCHEMA]'); } Iif (typeof schema !== 'string' && ! schema.hasOwnProperty('name')) { throw new Error('[INVALID SCHEMA]'); } return { relatedSchema: schema.hasOwnProperty('name') ? schema.name : schema, isArray: true, }; } export function generateSchemas(schemas) { if (isEmpty(schemas)) { throw new Error('[INVALID SCHEMAS]'); } Iif ( ! Array.isArray(schemas)) { throw new Error('[INVALID SCHEMAS]'); } const schemasBag = schemas.reduce((bag, s) => ({ ...bag, [s.name]: new schema.Entity(s.name), }), {}); const t = schemas.reduce((result, s) => ({ ...result, [s.name]: generateSchema(s, schemasBag), }), {}); return t; } |