UNPKG

baqend

Version:

Baqend JavaScript SDK

162 lines (141 loc) 5.51 kB
import { BasicType } from './BasicType'; import { EntityType } from './EntityType'; import { EmbeddableType } from './EmbeddableType'; import { ListAttribute } from './ListAttribute'; import { MapAttribute } from './MapAttribute'; import { SetAttribute } from './SetAttribute'; import { SingularAttribute } from './SingularAttribute'; import { PersistentError } from '../error'; import { JsonMap } from '../util'; import { Type } from './Type'; import { ManagedType } from './ManagedType'; import { Attribute } from './Attribute'; import { Permission, Validator } from '../intersection'; export class ModelBuilder { private models: { [name: string]: Type<any> } = {}; private modelDescriptors: { [name: string]: JsonMap } | null = null; constructor() { (Object.keys(BasicType) as (keyof typeof BasicType)[]).forEach((typeName) => { const basicType = BasicType[typeName]; if (basicType instanceof BasicType) { this.models[basicType.ref] = basicType; } }); } /** * @param ref * @return */ getModel(ref: string): ManagedType<any> { if (ref in this.models) { return this.models[ref] as ManagedType<any>; } const model = this.buildModel(ref); this.models[ref] = model; return model; } /** * @param modelDescriptors * @return */ buildModels(modelDescriptors: JsonMap[]): { [name: string]: Type<any> } { this.modelDescriptors = {}; modelDescriptors.forEach((modelDescriptor: JsonMap) => { this.modelDescriptors![modelDescriptor.class as string] = modelDescriptor; }); Object.keys(this.modelDescriptors).forEach((ref) => { try { const model = this.getModel(ref); this.buildAttributes(model); } catch (e: any) { throw new PersistentError(`Can't create model for entity class ${ref}`, e); } }); // ensure at least an object entity this.getModel(EntityType.Object.ref); return this.models; } /** * @param ref * @return */ buildModel(ref: string): ManagedType<any> { const modelDescriptor = this.modelDescriptors![ref]; let type: ManagedType<any>; if (ref === EntityType.Object.ref) { type = new EntityType.Object(); } else if (modelDescriptor) { if (modelDescriptor.embedded) { type = new EmbeddableType(ref); } else { const superTypeIdentifier = modelDescriptor.superClass as string || EntityType.Object.ref; type = new EntityType(ref, this.getModel(superTypeIdentifier) as EntityType<any>); } } else { throw new TypeError(`No model available for ${ref}`); } type.metadata = {}; if (modelDescriptor) { type.metadata = modelDescriptor.metadata as { [key: string]: string } || {}; const permissions = modelDescriptor.acl || {}; (Object.keys(permissions) as Array<keyof typeof permissions>).forEach((permission) => { const permissionProperty = `${permission}Permission`; ((type as any)[permissionProperty] as Permission).fromJSON(permissions[permission]); }); } return type; } /** * @param model * @return */ buildAttributes(model: ManagedType<any>): void { const modelDescriptor = this.modelDescriptors![model.ref]; const fields = modelDescriptor.fields as JsonMap; Object.keys(fields).forEach((name) => { const field = fields[name] as JsonMap; if (!model.getAttribute(name)) { // skip predefined attributes model.addAttribute(this.buildAttribute(field as any), field.order as number); } }); if (typeof modelDescriptor.validationCode === 'string') { // eslint-disable-next-line no-param-reassign (model as EntityType<any>).validationCode = Validator.compile(model, modelDescriptor.validationCode); } } /** * @param field The field metadata * @param field.name The name of zhe field * @param field.type The type reference of the field * @param field.order The order number of the field * @param field.metadata Additional metadata of the field * @return */ buildAttribute(field: { name: string, type: string, order: number, metadata: { [key: string]: string }, flags: string[] }): Attribute<any> { // TODO: remove readonly if createdAt and updatedAt becomes real metadata fields in the schema const isMetadata = field.flags && (field.flags.indexOf('METADATA') !== -1 || field.flags.indexOf('READONLY') !== -1); const { name } = field; const ref = field.type; if (ref.indexOf('/db/collection.') !== 0) { const singularAttribute = new SingularAttribute(name, this.getModel(ref), isMetadata); singularAttribute.metadata = field.metadata; return singularAttribute; } const collectionType = ref.substring(0, ref.indexOf('[')); const elementType = ref.substring(ref.indexOf('[') + 1, ref.indexOf(']')).trim(); switch (collectionType) { case ListAttribute.ref: return new ListAttribute(name, this.getModel(elementType)); case SetAttribute.ref: return new SetAttribute(name, this.getModel(elementType)); case MapAttribute.ref: { const keyType = elementType.substring(0, elementType.indexOf(',')).trim(); const valueType = elementType.substring(elementType.indexOf(',') + 1).trim(); return new MapAttribute(name, this.getModel(keyType), this.getModel(valueType)); } default: throw new TypeError(`No collection available for ${ref}`); } } }