Skip to content

Commit

Permalink
feat(objection): add model relationship decorators
Browse files Browse the repository at this point in the history
  • Loading branch information
Ross MacPhee committed Sep 4, 2022
1 parent e907869 commit e97a55a
Show file tree
Hide file tree
Showing 16 changed files with 317 additions and 0 deletions.
25 changes: 25 additions & 0 deletions packages/orm/objection/src/decorators/belongsToOne.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {BelongsToOne} from "@tsed/objection";
import {Model} from "objection";

describe("@BelongsToOne", () => {
it("should set metadata", () => {
class RelatedMyModel extends Model {
id!: string;
}
class MyModel extends Model {
id!: string;
@BelongsToOne("mymodel.id", "relatedmymodel.id")
relatedMyModel?: RelatedMyModel;
}
expect(MyModel.relationMappings).toEqual({
relatedMyModel: {
relation: Model.BelongsToOneRelation,
modelClass: RelatedMyModel,
join: {
from: "mymodel.id",
to: "relatedmymodel.id"
}
}
});
});
});
15 changes: 15 additions & 0 deletions packages/orm/objection/src/decorators/belongsToOne.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {MappingOpts} from "../domain/MappingOpts";
import {Model} from "objection";
import {RelatesTo} from "./relatesTo";

/**
*
* @param from
* @param to
* @param mappingOpts
* @decorator
* @objection
*/
export function BelongsToOne(from: string, to: string, mappingOpts?: MappingOpts): PropertyDecorator {
return RelatesTo(Model.BelongsToOneRelation, from, to, mappingOpts);
}
24 changes: 24 additions & 0 deletions packages/orm/objection/src/decorators/hasMany.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {HasMany} from "@tsed/objection";
import {Model} from "objection";

describe("@HasMany", () => {
it("should set metadata", () => {
class RelatedMyModel extends Model {
id: string;
}
class MyModel extends Model {
@HasMany("relatedmymodel.id", "mymodel.id")
relatedMyModel?: RelatedMyModel;
}
expect(MyModel.relationMappings).toEqual({
relatedMyModel: {
relation: Model.HasManyRelation,
modelClass: RelatedMyModel,
join: {
from: "relatedmymodel.id",
to: "mymodel.id"
}
}
});
});
});
15 changes: 15 additions & 0 deletions packages/orm/objection/src/decorators/hasMany.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {MappingOpts} from "../domain/MappingOpts";
import {Model} from "objection";
import {RelatesTo} from "./relatesTo";

/**
*
* @param from
* @param to
* @param mappingOpts
* @decorator
* @objection
*/
export function HasMany(from: string, to: string, mappingOpts?: MappingOpts): PropertyDecorator {
return RelatesTo(Model.HasManyRelation, from, to, mappingOpts);
}
24 changes: 24 additions & 0 deletions packages/orm/objection/src/decorators/hasOne.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {HasOne} from "@tsed/objection";
import {Model} from "objection";

describe("@HasOne", () => {
it("should set metadata", () => {
class RelatedMyModel extends Model {
id: string;
}
class MyModel extends Model {
@HasOne("mymodel.id", "relatedmymodel.id")
relatedMyModel?: RelatedMyModel;
}
expect(MyModel.relationMappings).toEqual({
relatedMyModel: {
relation: Model.HasOneRelation,
modelClass: RelatedMyModel,
join: {
from: "mymodel.id",
to: "relatedmymodel.id"
}
}
});
});
});
15 changes: 15 additions & 0 deletions packages/orm/objection/src/decorators/hasOne.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {MappingOpts} from "../domain/MappingOpts";
import {Model} from "objection";
import {RelatesTo} from "./relatesTo";

/**
*
* @param from
* @param to
* @param mappingOpts
* @decorator
* @objection
*/
export function HasOne(from: string, to: string, mappingOpts?: MappingOpts): PropertyDecorator {
return RelatesTo(Model.HasOneRelation, from, to, mappingOpts);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {HasOneThroughRelation} from "@tsed/objection";
import {Model} from "objection";

describe("@HasOneThroughRelation", () => {
it("should set metadata", () => {
class RelatedMyModel extends Model {
id: string;
}
class MyModel extends Model {
@HasOneThroughRelation("mymodel.id", "relatedmymodel.id", {
through: {
from: "mymodel_relatedmymodel.mymodelId",
to: "mymodel_relatedmymodel.relatedmymodel"
}
})
relatedMyModel?: RelatedMyModel;
}
expect(MyModel.relationMappings).toEqual({
relatedMyModel: {
relation: Model.HasOneThroughRelation,
modelClass: RelatedMyModel,
join: {
from: "mymodel.id",
through: {
from: "mymodel_relatedmymodel.mymodelId",
to: "mymodel_relatedmymodel.relatedmymodel"
},
to: "relatedmymodel.id"
}
}
});
});
});
15 changes: 15 additions & 0 deletions packages/orm/objection/src/decorators/hasOneThroughRelation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {MappingOpts} from "../domain/MappingOpts";
import {Model} from "objection";
import {RelatesTo} from "./relatesTo";

/**
*
* @param from
* @param to
* @param mappingOpts
* @decorator
* @objection
*/
export function HasOneThroughRelation(from: string, to: string, mappingOpts?: MappingOpts): PropertyDecorator {
return RelatesTo(Model.HasOneThroughRelation, from, to, mappingOpts);
}
33 changes: 33 additions & 0 deletions packages/orm/objection/src/decorators/manyToMany.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {ManyToMany} from "@tsed/objection";
import {Model} from "objection";

describe("@ManyToMany", () => {
it("should set metadata", () => {
class RelatedMyModel extends Model {
id: string;
}
class MyModel extends Model {
@ManyToMany("mymodel.id", "relatedmymodel.id", {
through: {
from: "mymodel_relatedmymodel.mymodelId",
to: "mymodel_relatedmymodel.relatedmymodel"
}
})
relatedMyModel?: RelatedMyModel;
}
expect(MyModel.relationMappings).toEqual({
relatedMyModel: {
relation: Model.ManyToManyRelation,
modelClass: RelatedMyModel,
join: {
from: "mymodel.id",
through: {
from: "mymodel_relatedmymodel.mymodelId",
to: "mymodel_relatedmymodel.relatedmymodel"
},
to: "relatedmymodel.id"
}
}
});
});
});
15 changes: 15 additions & 0 deletions packages/orm/objection/src/decorators/manyToMany.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {MappingOpts} from "../domain/MappingOpts";
import {Model} from "objection";
import {RelatesTo} from "./relatesTo";

/**
*
* @param from
* @param to
* @param mappingOpts
* @decorator
* @objection
*/
export function ManyToMany(from: string, to: string, mappingOpts?: MappingOpts): PropertyDecorator {
return RelatesTo(Model.ManyToManyRelation, from, to, mappingOpts);
}
25 changes: 25 additions & 0 deletions packages/orm/objection/src/decorators/relatesTo.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {Model} from "objection";
import {RelatesTo} from "@tsed/objection";

describe("@RelatesTo", () => {
it("should set metadata", () => {
class RelatedMyModel extends Model {
id!: string;
}
class MyModel extends Model {
id!: string;
@RelatesTo(Model.BelongsToOneRelation, "mymodel.id", "relatedmymodel.id")
relatedMyModel?: RelatedMyModel;
}
expect(MyModel.relationMappings).toEqual({
relatedMyModel: {
relation: Model.BelongsToOneRelation,
modelClass: RelatedMyModel,
join: {
from: "mymodel.id",
to: "relatedmymodel.id"
}
}
});
});
});
38 changes: 38 additions & 0 deletions packages/orm/objection/src/decorators/relatesTo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {StoreSet, useDecorators} from "@tsed/core";

import {MappingOpts} from "../domain/MappingOpts";
import {Property} from "@tsed/schema";
import {RelationType} from "objection";
import {defineRelationMappings} from "../utils/defineRelationMappings";

/**
* @ignore
*/
function createRelationMapping(relation: RelationType, from: string, to: string, mappingOpts?: MappingOpts) {
return {
relation,
join: {from, to, through: mappingOpts?.through},
modify: mappingOpts?.modify,
filter: mappingOpts?.filter,
beforeInsert: mappingOpts?.beforeInsert
};
}

/**
*
* @param relation
* @param from
* @param to
* @param mappingOpts
* @decorator
* @objection
*/
export function RelatesTo(relation: RelationType, from: string, to: string, mappingOpts?: MappingOpts): PropertyDecorator {
return useDecorators(
Property(),
StoreSet("objectionRelationship", createRelationMapping(relation, from, to, mappingOpts)),
(target: any) => {
defineRelationMappings(target);
}
);
}
3 changes: 3 additions & 0 deletions packages/orm/objection/src/domain/MappingOpts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {RelationJoin, RelationMapping} from "objection";

export type MappingOpts = Omit<RelationMapping<any>, "relation" | "join" | "modelClass"> & Omit<RelationJoin, "from" | "to">;
9 changes: 9 additions & 0 deletions packages/orm/objection/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,24 @@ export * from "./components/createBooleanColumn";
export * from "./components/createIdColumn";
export * from "./components/createNumberColumn";
export * from "./components/createStringColumn";
export * from "./decorators/belongsToOne";
export * from "./decorators/columnOptions";
export * from "./decorators/decimal";
export * from "./decorators/entity";
export * from "./decorators/hasMany";
export * from "./decorators/hasOne";
export * from "./decorators/hasOneThroughRelation";
export * from "./decorators/idColumn";
export * from "./decorators/manyToMany";
export * from "./decorators/relatesTo";
export * from "./domain/ColumnOpts";
export * from "./domain/MappingOpts";
export * from "./domain/interfaces";
export * from "./services/ColumnTypesContainer";
export * from "./services/ObjectionConnection";
export * from "./utils/connect";
export * from "./utils/createColumns";
export * from "./utils/defineRelationMappings";
export * from "./utils/defineStaticGetter";
export * from "./utils/getColumnCtx";
export * from "./utils/getJsonEntityRelationships";
10 changes: 10 additions & 0 deletions packages/orm/objection/src/utils/defineRelationMappings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {classOf} from "@tsed/core";
import {defineStaticGetter} from "./defineStaticGetter";
import {getJsonEntityRelationships} from "./getJsonEntityRelationships";

/**
* @ignore
*/
export function defineRelationMappings(target: any) {
return classOf(target).relationMappings || defineStaticGetter(target, "relationMappings", () => getJsonEntityRelationships(target));
}
18 changes: 18 additions & 0 deletions packages/orm/objection/src/utils/getJsonEntityRelationships.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {getColumns} from "./createColumns";

/**
* @ignore
*/
export function getJsonEntityRelationships(target: any) {
return getColumns(target).reduce(
(map, prop) =>
(map = {
...map,
[prop.propertyKey]: {
modelClass: prop.type,
...prop.store.get("objectionRelationship")
}
}),
{}
);
}

0 comments on commit e97a55a

Please sign in to comment.