import { Schema , EntitySchema , RelationSchema , EntitiesByType , IdsByType , State } from './interfaces' ; import { Cardinalities } from './enums' ; import { validateSchema } from './validator' ; export class ModelSchemaReader { schema : Schema ; entitySchemaReaders : Record < string , EntitySchemaReader > ; private emptyState ? : State ; private emptyEntitiesByTypeState ? : EntitiesByType ; private emptyIdsByTypeState ? : IdsByType ; constructor ( schema ) { validateSchema ( schema ) ; this . schema = schema ; this . entitySchemaReaders = Object . entries ( schema ) . reduce ( ( entitySchemaReaders , [ type , entitySchema ] ) => { entitySchemaReaders [ type ] = new EntitySchemaReader ( type , entitySchema , this ) ; return entitySchemaReaders ; } , { } as Record < string , EntitySchemaReader > ) ; } typeExists ( type ) { return this . getEntities ( ) . includes ( type ) ; } type ( type ) { return this . entitySchemaReaders [ type ] ; } getEntities ( ) { return Object . keys ( this . schema ) ; } getEmptyEntitiesByTypeState ( ) { if ( ! this . emptyEntitiesByTypeState ) { this . emptyEntitiesByTypeState = this . getEntities ( ) . reduce ( ( emptyState , type ) => { emptyState [ type ] = { } ; return emptyState ; } , { } as EntitiesByType ) ; } return this . emptyEntitiesByTypeState ; } getEmptyIdsByTypeState ( ) { if ( ! this . emptyIdsByTypeState ) { this . emptyIdsByTypeState = this . getEntities ( ) . reduce ( ( idsState , type ) => { idsState [ type ] = [ ] ; return idsState ; } , { } as IdsByType ) ; } return this . emptyIdsByTypeState ; } getEmptyState < T extends State > ( ) { if ( ! this . emptyState ) { this . emptyState = { entities : this . getEmptyEntitiesByTypeState ( ) , ids : this . getEmptyIdsByTypeState ( ) } as T ; } return this . emptyState as T ; } } export class EntitySchemaReader { type : string ; schema : EntitySchema ; modelSchemaReader : ModelSchemaReader ; constructor ( type , schema , modelSchemaReader ) { this . type = type ; this . schema = schema ; this . modelSchemaReader = modelSchemaReader ; } getType ( ) { return this . type ; } hasRelationKey ( relationKey ) { return this . getRelationKeys ( ) . includes ( relationKey ) ; } resolveRelationKey ( typeOrKey ) { const hasRelationKey = this . hasRelationKey ( typeOrKey ) ; if ( hasRelationKey ) { return typeOrKey ; } const relationType = typeOrKey ; let found = undefined ; for ( let relationKey of this . getRelationKeys ( ) ) { const relationSchema = this . getRelationSchema ( relationKey ) ; if ( relationSchema . type === relationType ) { if ( found ) { return undefined ; } found = relationKey ; } } return found ; } resolveRelationType ( typeOrKey ) { const hasRelationKey = this . hasRelationKey ( typeOrKey ) ; if ( hasRelationKey ) { return this . getRelationType ( typeOrKey ) ; } const relationType = typeOrKey ; for ( let relationSchema of this . relationSchemas ( ) ) { if ( relationSchema . type === relationType ) { return relationType ; } } return undefined ; } resolveRelationCardinality ( typeOrKey ) { const relationKey = this . resolveRelationKey ( typeOrKey ) ; if ( ! relationKey ) { return undefined ; } return this . getRelationCardinality ( relationKey ) ; } resolveRelationReciprocalKey ( typeOrKey ) { const relationKey = this . resolveRelationKey ( typeOrKey ) ; if ( ! relationKey ) { return undefined ; } return this . getRelationReciprocalKey ( relationKey ) ; } resolveReciprocalCardinality ( typeOrKey ) { const relationKey = this . resolveRelationKey ( typeOrKey ) ; if ( ! relationKey ) { return undefined ; } const relationType = this . getRelationType ( relationKey ) ; const relationReciprocalKey = this . getRelationReciprocalKey ( relationKey ) ; if ( ! relationType || ! relationReciprocalKey ) { return undefined ; } return this . modelSchemaReader . type ( relationType ) . getRelationCardinality ( relationReciprocalKey ) ; } getRelationKeys ( ) { return Object . keys ( this . schema ) ; } relationSchemas ( ) { return Object . values ( this . schema ) ; } getRelationSchema ( relationKey ) { return this . schema [ relationKey ] ; } getRelationCardinality ( relationKey ) { return this . getRelationSchemaField ( relationKey , 'cardinality' ) ; } getRelationType ( relationKey ) { return this . getRelationSchemaField ( relationKey , 'type' ) ; } getRelationReciprocalKey ( relationKey ) { return this . getRelationSchemaField ( relationKey , 'reciprocal' ) ; } getEmptyEntityState ( emptyRels ? ) { if ( ! emptyRels ) { return { } ; } return Object . entries ( this . schema ) . reduce ( ( state , [ reltype , relSchema ] ) => { if ( relSchema . cardinality === Cardinalities . ONE ) { state [ reltype ] = undefined ; } if ( relSchema . cardinality === Cardinalities . MANY ) { state [ reltype ] = [ ] ; } return state ; } , { } as { [ k ] : any ; } ) ; } getEmptyRelationState ( relationKey ) { const cardinality = this . getRelationCardinality ( relationKey ) ; return cardinality === Cardinalities . ONE ? undefined : [ ] ; } private getRelationSchemaField ( relationKey , field ) { const relationSchema = this . getRelationSchema ( relationKey ) ; if ( ! relationSchema ) { return undefined ; } return relationSchema [ field ] as string ; } }