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 107 108 109 | 4x 4x 4x 76x 2x 2x 1x 1x 1x 1x 1x 3x 3x 3x 3x 1x 2x 4x 1x 2x 4x 4x 1x 3x 3x 3x 1x 2x 1x 1x | import { MetadataRegistry } from "./MetadataRegistry";
import 'reflect-metadata';
export class SchemaReflector {
private registry = MetadataRegistry.getInstance();
getEntitySchema(entity: Function): any {
const metadata = this.registry.getEntityMetadata(entity);
if (!metadata) {
throw new Error(`Class ${entity.name} is not decorated as an Entity`);
}
const properties = this.registry.getAllProperties(entity) || new Map();
const idProperties = this.registry.getIdProperties(entity) || new Set();
const relationships =
this.registry.getAllRelationships(entity) || new Map();
// Build schema representation
return {
name: metadata.name || entity.name,
description: metadata.description,
properties: Object.fromEntries(properties),
idProperties: Array.from(idProperties),
relationships: Object.fromEntries(relationships),
};
}
validateEntity(entity: any): { valid: boolean; errors: string[] } {
const constructor = entity.constructor;
const properties = this.registry.getAllProperties(constructor);
const errors: string[] = [];
if (!properties) {
return {
valid: false,
errors: ["Entity class is not properly decorated"],
};
}
// Validate required properties
for (const [propertyKey, metadata] of properties.entries()) {
if (
metadata.required &&
(entity[propertyKey] === undefined || entity[propertyKey] === null)
) {
errors.push(`Required property '${propertyKey}' is missing`);
}
}
return {
valid: errors.length === 0,
errors,
};
}
/**
* Infer relationship type based on property type
* This method can be used to automatically determine relationship types
* based on TypeScript's metadata when using TypeScript with emitDecoratorMetadata
*
* @param target The target object (prototype)
* @param propertyKey The property name
* @returns The inferred relationship type or undefined if it cannot be determined
*/
inferRelationshipType(target: any, propertyKey: string): 'one-to-one' | 'one-to-many' | 'many-to-one' | 'many-to-many' | undefined {
// First check if explicit relationship type is defined via decorators
const explicitType = Reflect.getMetadata('relationship:type', target, propertyKey);
if (explicitType) {
return explicitType as any;
}
// Try to infer from property design type (requires emitDecoratorMetadata in tsconfig)
const designType = Reflect.getMetadata('design:type', target, propertyKey);
if (designType) {
// Check if it's an array
if (designType === Array) {
// It's a "to-many" relationship, but we can't distinguish
// between one-to-many and many-to-many without more context
return undefined;
} else if (designType.prototype && this.registry.getEntityMetadata(designType)) {
// It's a "to-one" relationship to an entity, but we can't distinguish
// between one-to-one and many-to-one without more context
return undefined;
}
}
return undefined;
}
/**
* Automatically detect and register relationships from property types
* This method would be used to enhance the reflection capabilities in the future
*
* @param entityClass The entity class to analyze
*/
detectRelationships(entityClass: Function): void {
// This is a placeholder for future implementation
// It would scan all properties of the class, and for those that are entities
// or arrays of entities, it would register appropriate relationships
// Implementation would require:
// 1. Get all properties from class prototype
// 2. For each property, check if it's an entity type or array of entity type
// 3. Register appropriate relationship metadata
// Note: This would require emitDecoratorMetadata and appropriate tsconfig settings
}
}
|