@wearesage/schema
Version:
A flexible schema definition and validation system for TypeScript with multi-database support
107 lines (92 loc) • 3.37 kB
text/typescript
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;
}
}