blow-data
Version:
Data access layer for Blow.
173 lines (144 loc) • 4.8 kB
text/typescript
'use strict';
import * as Joi from 'joi';
import {pluralize} from 'inflection';
import {isUndefined} from 'util';
import {ModelPropertyMetadata} from './ModelPropertyMetadata';
import {ModelRelationMetadata} from './ModelRelationMetadata';
import {
IBaseModelConstructor,
IModelMetadataOptions,
IModelMetadata,
IModelPropertyMetadata,
IModelRelationMetadata,
IPersistedModelConstructor,
IModelPropertyMetadataOptions,
IModelRelationMetadataOptions
} from './interfaces';
const DEFAULT_CONNECTION_NAME = 'default';
const ASYNC_VALIDATORS = ['custom'];
export class ModelMetadata implements IModelMetadata {
protected _name: string;
protected _pluralName: string;
protected _autoId: boolean;
protected _connectionName: string;
protected _properties: Map<string, IModelPropertyMetadata>;
protected _relations: Map<string, IModelRelationMetadata>;
protected _model: IBaseModelConstructor;
protected _raw: any;
constructor(options: IModelMetadataOptions, properties?: {[key: string]: IModelPropertyMetadataOptions}, relations?: {[key: string]: IModelRelationMetadataOptions}) {
this._raw = {
options,
properties,
relations
}
this._name = options.name;
this._pluralName = isUndefined(options.pluralName) ? pluralize(options.name) : options.pluralName;
this._autoId = isUndefined(options.autoId) ? true : options.autoId;
this._properties = new Map();
this._relations = new Map();
this._connectionName = isUndefined(options.connection) ? DEFAULT_CONNECTION_NAME : options.connection;
}
get name(): string {
return this._name;
}
get pluralName(): string {
return this._pluralName;
}
get autoId(): boolean {
return this._autoId;
}
get properties(): IterableIterator<IModelPropertyMetadata> {
return this._properties.values();
}
get relations(): IterableIterator<IModelRelationMetadata> {
return this._relations.values();
}
get connectionName(): string {
return this._connectionName;
}
get idProperty(): IModelPropertyMetadata {
for(const property of this.properties) {
if(property.id) {
return property;
}
}
}
get validationSchema(): {[key: string]: any} {
const schema = {};
for(const property of this.properties) {
schema[property.name] = Joi;
for(const validation of property.validations) {
if(ASYNC_VALIDATORS.indexOf(validation[0]) === -1 && schema[property.name][validation[0]]) {
schema[property.name] = schema[property.name][validation[0]](validation[1]);
}
}
}
return schema;
}
get asyncValidationSchema(): {[key: string]: any} {
const schema = {};
for(const property of this.properties) {
schema[property.name] = [];
for(const validation of property.validations) {
if(ASYNC_VALIDATORS.indexOf(validation[0]) !== -1) {
schema[property.name].push(validation[1]);
}
}
}
return schema;
}
defineProperty(options: IModelPropertyMetadataOptions): void {
if(options.id) {
this._autoId = false;
}
const property = new ModelPropertyMetadata(options);
this._properties.set(options.name, property);
if(this._model) {
property.apply(this._model);
}
}
buildPropertyId(name: string, type: any): void {
if(this.autoId && !this.idProperty) {
this.defineProperty({
name: name,
type: type,
id: true
});
}
}
getProperty(name: string): IModelPropertyMetadata {
return this._properties.get(name);
}
hasProperty(name: string): boolean {
return this._properties.has(name);
}
isAllowedProperty(name: string): boolean {
return this.hasProperty(name);
}
defineRelation(options: IModelRelationMetadataOptions): void {
const relation = new ModelRelationMetadata(options);
this._relations.set(relation.name, relation);
if(this._model) {
relation.apply(<IPersistedModelConstructor>this._model);
}
}
apply(model): void {
if(!isUndefined(this._raw.properties)) {
Object.keys(this._raw.properties).forEach(propertyName => {
this.defineProperty(Object.assign({name: propertyName}, this._raw.properties[propertyName]));
});
}
if(!isUndefined(this._raw.relations)) {
Object.keys(this._raw.relations).forEach(relationName => {
this.defineRelation(Object.assign({name: relationName}, this._raw.relations[relationName]));
});
}
this._model = model;
for(const property of this.properties) {
property.apply(model);
}
for(const relation of this.relations) {
relation.apply(model);
}
}
}