All files / core SchemaBuilder.ts

100% Statements 25/25
100% Branches 15/15
100% Functions 6/6
100% Lines 25/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 10712x                                     12x 176x                 783x 783x     783x     783x             222x 719x               783x       783x     783x   3083x 3083x     3083x 3083x 649x                 783x       783x     783x 942x 942x     942x               942x               2x    
import "reflect-metadata";
import { MetadataRegistry } from "./MetadataRegistry";
import {
  EntityOptions,
  PropertyOptions,
  RelationshipOptions,
  Neo4jRelationshipOptions,
  MongoDBRelationshipOptions,
  PostgreSQLRelationshipOptions
} from "./types";
 
/**
 * SchemaBuilder extracts and processes metadata from decorated classes
 * and registers it with a MetadataRegistry instance.
 * 
 * This provides a clear separation between:
 * 1. Schema definition (via decorators that store metadata on classes)
 * 2. Schema registration (via this builder that processes the metadata)
 */
export class SchemaBuilder {
  constructor(private registry: MetadataRegistry) {}
 
  /**
   * Register a class decorated with @Entity and related decorators
   * This extracts all metadata and registers it with the registry
   */
  registerEntity<T>(entityClass: new (...args: any[]) => T): void {
    // Register the entity itself
    const entityMetadata: EntityOptions = 
      Reflect.getMetadata("entity:options", entityClass) || {};
    this.registry.registerEntity(entityClass, entityMetadata);
 
    // Get all property metadata
    this.registerEntityProperties(entityClass);
    
    // Get all relationship metadata
    this.registerEntityRelationships(entityClass);
  }
 
  /**
   * Register multiple entity classes at once
   */
  registerEntities(entityClasses: Array<new (...args: any[]) => any>): void {
    for (const entityClass of entityClasses) {
      this.registerEntity(entityClass);
    }
  }
 
  /**
   * Extract and register property metadata from an entity class
   */
  private registerEntityProperties(entityClass: Function): void {
    const prototype = entityClass.prototype;
    
    // Get property keys from metadata
    const propertyKeys: string[] = 
      Reflect.getMetadata("entity:properties", entityClass) || [];
    
    // Register each property
    for (const key of propertyKeys) {
      const options: PropertyOptions = 
        Reflect.getMetadata("property:options", prototype, key) || {};
      this.registry.registerProperty(entityClass, key, options);
      
      // Check if this is an ID property
      const isId = Reflect.getMetadata("property:id", prototype, key);
      if (isId) {
        this.registry.registerIdProperty(entityClass, key);
      }
    }
  }
 
  /**
   * Extract and register relationship metadata from an entity class
   */
  private registerEntityRelationships(entityClass: Function): void {
    const prototype = entityClass.prototype;
    
    // Get relationship keys from metadata
    const relationshipKeys: string[] = 
      Reflect.getMetadata("entity:relationships", entityClass) || [];
    
    // Register each relationship
    for (const key of relationshipKeys) {
      const baseOptions = Reflect.getMetadata("relationship:options", prototype, key) || {};
      const relType = Reflect.getMetadata("relationship:type", prototype, key);
      
      // Build full relationship options
      const options: RelationshipOptions = {
        ...baseOptions,
        cardinality: 
          relType === "one-to-many" || relType === "many-to-many" 
            ? "many" 
            : "one"
      };
      
      this.registry.registerRelationship(entityClass, key, options);
    }
  }
  
  /**
   * Get the underlying registry
   */
  getRegistry(): MetadataRegistry {
    return this.registry;
  }
}