Permalink
Browse files

fix(relations): Fix circulare module dependency issue caused by recip…

…rocal relations
  • Loading branch information...
zakhenry committed Jul 18, 2016
1 parent de8711d commit e78267e24df261c5d460240fc3718eb7786e37e6
@@ -0,0 +1,34 @@
/**
* @module common
*/
/** End Typedoc Module Declaration */
import { ModelStatic } from '../model';
import { initializeRelationMap, ForeignRelationModelGetter, Relation } from './index';
import { RelationOptions } from 'typeorm/decorator/options/RelationOptions';
/**
* Defines the relationship between the current model and a foreign model vial the decorated key
*
* Example:
* ```typescript
*
* @Model
* class Thumb extends AbstractModel {
*
* @BelongsTo(f => HandModel)
* public hand: HandModel;
*
* public handId: number;
* }
*
* ```
* Foreign model property is only required if there is no type annotation
*/
export function BelongsTo(foreignTypeGetter: ForeignRelationModelGetter, joinOptions?: RelationOptions): PropertyDecorator {
return (target: any, propertyKey: string) => {
initializeRelationMap(target, 'belongsTo');
target.constructor.__metadata.relations.get('belongsTo')
.set(propertyKey, new Relation(target.constructor, foreignTypeGetter, joinOptions));
};
}
@@ -0,0 +1,20 @@
import Moment = moment.Moment;
import { HasOne } from './hasOne.decorator';
import { AbstractModel } from '../model';
import { Primary } from '../types/primary.decorator';
import { ThumbModel } from './thumb.model.fixture';
export class HandModel extends AbstractModel {
@Primary()
public handId: string;//UUID;
public name: string;
@HasOne(f => ThumbModel)
left: ThumbModel;
@HasOne(f => ThumbModel)
right: ThumbModel;
}
@@ -3,7 +3,7 @@
*/
/** End Typedoc Module Declaration */
import { ModelStatic } from '../model';
import { initializeRelationMap } from './index';
import { initializeRelationMap, ForeignRelationModelGetter, Relation } from './index';
import { RelationOptions } from 'typeorm/decorator/options/RelationOptions';
/**
@@ -22,17 +22,11 @@ import { RelationOptions } from 'typeorm/decorator/options/RelationOptions';
* ```
* Foreign model property is only required if there is no type annotation
*/
export function HasOne(foreignModel?: ModelStatic<any>, joinOptions?:RelationOptions): PropertyDecorator {
export function HasOne(foreignTypeGetter: ForeignRelationModelGetter, joinOptions?: RelationOptions): PropertyDecorator {
return (target: any, propertyKey: string) => {
initializeRelationMap(target, 'hasOne');
if (!foreignModel) {
foreignModel = Reflect.getMetadata("design:type", target, propertyKey);
}
target.constructor.__metadata.relations.get('hasOne').set(propertyKey, {
model: foreignModel,
joinOptions
});
target.constructor.__metadata.relations.get('hasOne')
.set(propertyKey, new Relation(target.constructor, foreignTypeGetter, joinOptions));
};
}
@@ -7,9 +7,24 @@ import { initializeMetadata } from '../../metadata/metadata';
export type RelationType = 'hasOne' | 'hasMany' | 'belongsTo' | 'belongsToMany';
export interface Relation {
model: ModelStatic<any>;
databaseOptions: any;
/**
* This is a crude method to two-way register the type of binding for relations. This is to overcome
* a limitation of Typescripts design-time decorators and node's module resolution.
* @see https://github.com/Microsoft/TypeScript/issues/4521
*/
export type ForeignRelationModelGetter = (thisStatic?:ModelStatic<any>) => ModelStatic<any>;
export class Relation {
constructor(public model:ModelStatic<any>, private foreignRelationModelGetter:ForeignRelationModelGetter, public databaseOptions?:any) {
}
public get foreign(){
return this.foreignRelationModelGetter(this.model);
}
}
/**
@@ -32,3 +47,4 @@ export function initializeRelationMap(target: ModelConstructor<any>, type: Relat
}
export * from './hasOne.decorator';
export * from './belongsTo.decorator';
@@ -1,50 +1,42 @@
import Moment = moment.Moment;
import { HasOne } from './hasOne.decorator';
import { AbstractModel } from '../model';
import { Primary } from '../types/primary.decorator';
import { ThumbModel } from './thumb.model.fixture';
import { HandModel } from './hand.model.fixture';
class ChildModel extends AbstractModel {
@Primary()
public id: string;//UUID;
public name: string;
}
class BasicModel extends AbstractModel {
@Primary()
public id: string;//UUID;
public name: string;
describe('Model Relations', () => {
@HasOne(ChildModel)
child:ChildModel;
it('registers hasOne relationship with the constructor', () => {
@HasOne()
child2:ChildModel;
const model = new HandModel();
}
const relations = model.getMetadata().relations;
expect(relations)
.toBeDefined();
expect(relations.get('hasOne')
.get('left').foreign)
.toEqual(ThumbModel);
describe('Model Relations', () => {
});
it('registers hasOne relationship with the constructor', () => {
it('registers hasOne relationship correctly when the type is not passed', () => {
const model = new BasicModel();
const model = new HandModel();
const relations = model.getMetadata().relations;
expect(relations).toBeDefined();
expect(relations.get('hasOne').get('child').model).toEqual(ChildModel);
expect(relations.get('hasOne')
.get('right').foreign)
.toEqual(ThumbModel);
});
it('registers hasOne relationship correctly when the type is not passed', () => {
it('registers belongsTo relationship with metadata', () => {
const model = new BasicModel();
const model = new ThumbModel();
const relations = model.getMetadata().relations;
expect(relations.get('hasOne').get('child').model).toEqual(ChildModel);
expect(relations.get('belongsTo')
.get('hand').foreign)
.toEqual(HandModel);
});
@@ -0,0 +1,19 @@
import Moment = moment.Moment;
import { AbstractModel } from '../model';
import { Primary } from '../types/primary.decorator';
import { BelongsTo } from './belongsTo.decorator';
import { HandModel } from './hand.model.fixture';
export class ThumbModel extends AbstractModel {
@Primary()
public thumbId: string;//UUID;
public name: string;
@BelongsTo(f => HandModel)
public hand: HandModel;
public handId: string;
}

0 comments on commit e78267e

Please sign in to comment.