@tsed/objection 8.0.1 → 8.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +14 -13
- package/readme.md +5 -5
- package/src/components/createBooleanColumn.spec.ts +20 -0
- package/src/components/createBooleanColumn.ts +12 -0
- package/src/components/createIdColumn.spec.ts +45 -0
- package/src/components/createIdColumn.ts +23 -0
- package/src/components/createNumberColumn.spec.ts +34 -0
- package/src/components/createNumberColumn.ts +13 -0
- package/src/components/createStringColumn.spec.ts +34 -0
- package/src/components/createStringColumn.ts +13 -0
- package/src/decorators/belongsToOne.spec.ts +60 -0
- package/src/decorators/belongsToOne.ts +14 -0
- package/src/decorators/columnOptions.ts +13 -0
- package/src/decorators/decimal.spec.ts +52 -0
- package/src/decorators/decimal.ts +35 -0
- package/src/decorators/entity.spec.ts +24 -0
- package/src/decorators/entity.ts +29 -0
- package/src/decorators/hasMany.spec.ts +60 -0
- package/src/decorators/hasMany.ts +14 -0
- package/src/decorators/hasOne.spec.ts +60 -0
- package/src/decorators/hasOne.ts +14 -0
- package/src/decorators/hasOneThroughRelation.spec.ts +72 -0
- package/src/decorators/hasOneThroughRelation.ts +14 -0
- package/src/decorators/idColumn.spec.ts +31 -0
- package/src/decorators/idColumn.ts +26 -0
- package/src/decorators/manyToMany.spec.ts +72 -0
- package/src/decorators/manyToMany.ts +14 -0
- package/src/decorators/relatesTo.spec.ts +34 -0
- package/src/decorators/relatesTo.ts +26 -0
- package/src/domain/ColumnOpts.ts +4 -0
- package/src/domain/RelationshipOpts.ts +14 -0
- package/src/domain/interfaces.ts +9 -0
- package/src/index.ts +29 -0
- package/src/services/ColumnTypesContainer.ts +8 -0
- package/src/services/ObjectionConnection.ts +26 -0
- package/src/utils/connect.ts +9 -0
- package/src/utils/createColumns.spec.ts +79 -0
- package/src/utils/createColumns.ts +39 -0
- package/src/utils/createJoinKeys.ts +38 -0
- package/src/utils/createRelationshipMapping.ts +21 -0
- package/src/utils/defineStaticGetter.ts +10 -0
- package/src/utils/getColumnCtx.ts +21 -0
- package/src/utils/getJsonEntityRelationships.ts +16 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {Model} from "objection";
|
|
2
|
+
|
|
3
|
+
import {Entity} from "./entity.js";
|
|
4
|
+
import {HasOneThroughRelation} from "./hasOneThroughRelation.js";
|
|
5
|
+
import {IdColumn} from "./idColumn.js";
|
|
6
|
+
|
|
7
|
+
describe("@HasOneThroughRelation", () => {
|
|
8
|
+
it("should set metadata", () => {
|
|
9
|
+
@Entity("pet")
|
|
10
|
+
class Pet extends Model {
|
|
11
|
+
@IdColumn()
|
|
12
|
+
id!: string;
|
|
13
|
+
userId?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Entity("user")
|
|
17
|
+
class User extends Model {
|
|
18
|
+
@IdColumn()
|
|
19
|
+
id!: string;
|
|
20
|
+
@HasOneThroughRelation()
|
|
21
|
+
pet?: Pet;
|
|
22
|
+
}
|
|
23
|
+
expect(User.relationMappings).toEqual({
|
|
24
|
+
pet: {
|
|
25
|
+
relation: Model.HasOneThroughRelation,
|
|
26
|
+
modelClass: Pet,
|
|
27
|
+
join: {
|
|
28
|
+
from: "user.id",
|
|
29
|
+
through: {
|
|
30
|
+
from: "user_pet.userId",
|
|
31
|
+
to: "user_pet.petId"
|
|
32
|
+
},
|
|
33
|
+
to: "pet.id"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
it("should set custom relationship path", () => {
|
|
39
|
+
@Entity("pet")
|
|
40
|
+
class Pet extends Model {
|
|
41
|
+
@IdColumn()
|
|
42
|
+
id!: string;
|
|
43
|
+
petId?: string;
|
|
44
|
+
}
|
|
45
|
+
@Entity("user")
|
|
46
|
+
class User extends Model {
|
|
47
|
+
@IdColumn()
|
|
48
|
+
id!: string;
|
|
49
|
+
userId!: string;
|
|
50
|
+
@HasOneThroughRelation({
|
|
51
|
+
from: "userId",
|
|
52
|
+
through: {from: "user_pet.ownerId", to: "user_pet.petId"},
|
|
53
|
+
to: "petId"
|
|
54
|
+
})
|
|
55
|
+
pet?: Pet;
|
|
56
|
+
}
|
|
57
|
+
expect(User.relationMappings).toEqual({
|
|
58
|
+
pet: {
|
|
59
|
+
relation: Model.HasOneThroughRelation,
|
|
60
|
+
modelClass: Pet,
|
|
61
|
+
join: {
|
|
62
|
+
from: "user.userId",
|
|
63
|
+
through: {
|
|
64
|
+
from: "user_pet.ownerId",
|
|
65
|
+
to: "user_pet.petId"
|
|
66
|
+
},
|
|
67
|
+
to: "pet.petId"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {Model} from "objection";
|
|
2
|
+
|
|
3
|
+
import {RelationshipOptsWithoutThrough, RelationshipOptsWithThrough} from "../domain/RelationshipOpts.js";
|
|
4
|
+
import {RelatesTo} from "./relatesTo.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param opts
|
|
9
|
+
* @decorator
|
|
10
|
+
* @objection
|
|
11
|
+
*/
|
|
12
|
+
export function HasOneThroughRelation(opts?: RelationshipOptsWithThrough): PropertyDecorator {
|
|
13
|
+
return RelatesTo(Model.HasOneThroughRelation, opts);
|
|
14
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {Store} from "@tsed/core";
|
|
2
|
+
import {getJsonSchema} from "@tsed/schema";
|
|
3
|
+
import {Model} from "objection";
|
|
4
|
+
|
|
5
|
+
import {IdColumn} from "./idColumn.js";
|
|
6
|
+
|
|
7
|
+
describe("@IdColumn", () => {
|
|
8
|
+
it("should set metadata", () => {
|
|
9
|
+
class MyModel extends Model {
|
|
10
|
+
@IdColumn()
|
|
11
|
+
id: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
expect(getJsonSchema(MyModel)).toEqual({
|
|
15
|
+
properties: {
|
|
16
|
+
id: {
|
|
17
|
+
type: "number"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
type: "object"
|
|
21
|
+
});
|
|
22
|
+
expect(Store.from(MyModel, "id").get("objection")).toEqual({
|
|
23
|
+
columnType: "idColumn",
|
|
24
|
+
options: {
|
|
25
|
+
type: "increments"
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(MyModel.idColumn).toEqual("id");
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {useDecorators} from "@tsed/core";
|
|
2
|
+
import {Property} from "@tsed/schema";
|
|
3
|
+
|
|
4
|
+
import {defineStaticGetter} from "../utils/defineStaticGetter.js";
|
|
5
|
+
import {ColumnOptions} from "./columnOptions.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param type
|
|
10
|
+
* @decorator
|
|
11
|
+
* @objection
|
|
12
|
+
*/
|
|
13
|
+
export function IdColumn(type: "uuid" | "increments" | "bigIncrements" = "increments"): PropertyDecorator {
|
|
14
|
+
return useDecorators(
|
|
15
|
+
Property(),
|
|
16
|
+
ColumnOptions({
|
|
17
|
+
columnType: "idColumn",
|
|
18
|
+
options: {
|
|
19
|
+
type
|
|
20
|
+
}
|
|
21
|
+
}),
|
|
22
|
+
(target: any, propertyKey: string) => {
|
|
23
|
+
defineStaticGetter(target, "idColumn", () => propertyKey);
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {Model} from "objection";
|
|
2
|
+
|
|
3
|
+
import {Entity} from "./entity.js";
|
|
4
|
+
import {IdColumn} from "./idColumn.js";
|
|
5
|
+
import {ManyToMany} from "./manyToMany.js";
|
|
6
|
+
|
|
7
|
+
describe("@ManyToMany", () => {
|
|
8
|
+
it("should set metadata", () => {
|
|
9
|
+
@Entity("pet")
|
|
10
|
+
class Pet extends Model {
|
|
11
|
+
@IdColumn()
|
|
12
|
+
id!: string;
|
|
13
|
+
userId?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Entity("user")
|
|
17
|
+
class User extends Model {
|
|
18
|
+
@IdColumn()
|
|
19
|
+
id!: string;
|
|
20
|
+
@ManyToMany(Pet)
|
|
21
|
+
pets?: Pet[];
|
|
22
|
+
}
|
|
23
|
+
expect(User.relationMappings).toEqual({
|
|
24
|
+
pets: {
|
|
25
|
+
relation: Model.ManyToManyRelation,
|
|
26
|
+
modelClass: Pet,
|
|
27
|
+
join: {
|
|
28
|
+
from: "user.id",
|
|
29
|
+
through: {
|
|
30
|
+
from: "user_pet.userId",
|
|
31
|
+
to: "user_pet.petId"
|
|
32
|
+
},
|
|
33
|
+
to: "pet.id"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
it("should set custom relationship path", () => {
|
|
39
|
+
@Entity("pet")
|
|
40
|
+
class Pet extends Model {
|
|
41
|
+
@IdColumn()
|
|
42
|
+
id!: string;
|
|
43
|
+
petId?: string;
|
|
44
|
+
}
|
|
45
|
+
@Entity("user")
|
|
46
|
+
class User extends Model {
|
|
47
|
+
@IdColumn()
|
|
48
|
+
id!: string;
|
|
49
|
+
userId!: string;
|
|
50
|
+
@ManyToMany(Pet, {
|
|
51
|
+
from: "userId",
|
|
52
|
+
through: {from: "user_pet.ownerId", to: "user_pet.petId"},
|
|
53
|
+
to: "petId"
|
|
54
|
+
})
|
|
55
|
+
pets?: Pet[];
|
|
56
|
+
}
|
|
57
|
+
expect(User.relationMappings).toEqual({
|
|
58
|
+
pets: {
|
|
59
|
+
relation: Model.ManyToManyRelation,
|
|
60
|
+
modelClass: Pet,
|
|
61
|
+
join: {
|
|
62
|
+
from: "user.userId",
|
|
63
|
+
through: {
|
|
64
|
+
from: "user_pet.ownerId",
|
|
65
|
+
to: "user_pet.petId"
|
|
66
|
+
},
|
|
67
|
+
to: "pet.petId"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {Model, ModelClassSpecifier} from "objection";
|
|
2
|
+
|
|
3
|
+
import {RelationshipOptsWithThrough} from "../domain/RelationshipOpts.js";
|
|
4
|
+
import {RelatesTo} from "./relatesTo.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param opts
|
|
9
|
+
* @decorator
|
|
10
|
+
* @objection
|
|
11
|
+
*/
|
|
12
|
+
export function ManyToMany(type: ModelClassSpecifier, opts?: RelationshipOptsWithThrough): PropertyDecorator {
|
|
13
|
+
return RelatesTo(Model.ManyToManyRelation, {...opts, type});
|
|
14
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {Model} from "objection";
|
|
2
|
+
|
|
3
|
+
import {Entity} from "./entity.js";
|
|
4
|
+
import {IdColumn} from "./idColumn.js";
|
|
5
|
+
import {RelatesTo} from "./relatesTo.js";
|
|
6
|
+
|
|
7
|
+
describe("@RelatesTo", () => {
|
|
8
|
+
it("should set metadata", () => {
|
|
9
|
+
@Entity("user")
|
|
10
|
+
class User extends Model {
|
|
11
|
+
@IdColumn()
|
|
12
|
+
id!: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@Entity("movie")
|
|
16
|
+
class Movie extends Model {
|
|
17
|
+
@IdColumn()
|
|
18
|
+
id!: string;
|
|
19
|
+
userId!: string;
|
|
20
|
+
@RelatesTo(Model.BelongsToOneRelation)
|
|
21
|
+
user?: User;
|
|
22
|
+
}
|
|
23
|
+
expect(Movie.relationMappings).toEqual({
|
|
24
|
+
user: {
|
|
25
|
+
relation: Model.BelongsToOneRelation,
|
|
26
|
+
modelClass: User,
|
|
27
|
+
join: {
|
|
28
|
+
from: "movie.userId",
|
|
29
|
+
to: "user.id"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {StoreFn, useDecorators} from "@tsed/core";
|
|
2
|
+
import {CollectionOf, Property} from "@tsed/schema";
|
|
3
|
+
import {RelationType} from "objection";
|
|
4
|
+
|
|
5
|
+
import {isModelClassFactory, RelationshipOpts} from "../domain/RelationshipOpts.js";
|
|
6
|
+
import {createRelationshipMapping} from "../utils/createRelationshipMapping.js";
|
|
7
|
+
import {OBJECTION_RELATIONSHIP_KEY} from "../utils/getJsonEntityRelationships.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @param relation
|
|
12
|
+
* @param opts
|
|
13
|
+
* @decorator
|
|
14
|
+
* @objection
|
|
15
|
+
*/
|
|
16
|
+
export function RelatesTo(relation: RelationType, opts?: RelationshipOpts): PropertyDecorator {
|
|
17
|
+
return useDecorators(
|
|
18
|
+
opts?.type ? CollectionOf(opts.type) : Property(),
|
|
19
|
+
StoreFn((store, params) => {
|
|
20
|
+
if (opts && isModelClassFactory(opts.type)) {
|
|
21
|
+
opts.type = opts.type();
|
|
22
|
+
}
|
|
23
|
+
store.set(OBJECTION_RELATIONSHIP_KEY, createRelationshipMapping(params, relation, opts));
|
|
24
|
+
})
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {isFunction, isString} from "@tsed/core";
|
|
2
|
+
import {ModelClassFactory, ModelClassSpecifier, RelationJoin, RelationMapping} from "objection";
|
|
3
|
+
|
|
4
|
+
export type RelationshipOptsWithThrough = Partial<RelationJoin> & Omit<RelationMapping<any>, "relation" | "join" | "modelClass">;
|
|
5
|
+
|
|
6
|
+
export type RelationshipOptsWithoutThrough = Omit<RelationshipOptsWithThrough, "through">;
|
|
7
|
+
|
|
8
|
+
export type RelationshipOpts = (RelationshipOptsWithThrough | RelationshipOptsWithoutThrough) & {type?: ModelClassSpecifier};
|
|
9
|
+
|
|
10
|
+
export const isRelationshipOptsWithThrough = (opts?: RelationshipOpts): opts is RelationshipOptsWithThrough =>
|
|
11
|
+
opts !== undefined && (<RelationshipOptsWithThrough>opts).through !== undefined;
|
|
12
|
+
|
|
13
|
+
export const isModelClassFactory = (type?: ModelClassSpecifier): type is ModelClassFactory =>
|
|
14
|
+
type !== undefined && !isString(type) && isFunction(type) && !("prototype" in type);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Automatically generated by @tsed/barrels.
|
|
3
|
+
*/
|
|
4
|
+
export * from "./components/createBooleanColumn.js";
|
|
5
|
+
export * from "./components/createIdColumn.js";
|
|
6
|
+
export * from "./components/createNumberColumn.js";
|
|
7
|
+
export * from "./components/createStringColumn.js";
|
|
8
|
+
export * from "./decorators/belongsToOne.js";
|
|
9
|
+
export * from "./decorators/columnOptions.js";
|
|
10
|
+
export * from "./decorators/decimal.js";
|
|
11
|
+
export * from "./decorators/entity.js";
|
|
12
|
+
export * from "./decorators/hasMany.js";
|
|
13
|
+
export * from "./decorators/hasOne.js";
|
|
14
|
+
export * from "./decorators/hasOneThroughRelation.js";
|
|
15
|
+
export * from "./decorators/idColumn.js";
|
|
16
|
+
export * from "./decorators/manyToMany.js";
|
|
17
|
+
export * from "./decorators/relatesTo.js";
|
|
18
|
+
export * from "./domain/ColumnOpts.js";
|
|
19
|
+
export * from "./domain/interfaces.js";
|
|
20
|
+
export * from "./domain/RelationshipOpts.js";
|
|
21
|
+
export * from "./services/ColumnTypesContainer.js";
|
|
22
|
+
export * from "./services/ObjectionConnection.js";
|
|
23
|
+
export * from "./utils/connect.js";
|
|
24
|
+
export * from "./utils/createColumns.js";
|
|
25
|
+
export * from "./utils/createJoinKeys.js";
|
|
26
|
+
export * from "./utils/createRelationshipMapping.js";
|
|
27
|
+
export * from "./utils/defineStaticGetter.js";
|
|
28
|
+
export * from "./utils/getColumnCtx.js";
|
|
29
|
+
export * from "./utils/getJsonEntityRelationships.js";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {Configuration, registerProvider} from "@tsed/di";
|
|
2
|
+
import type {Knex} from "knex";
|
|
3
|
+
|
|
4
|
+
import {createConnection} from "../utils/connect.js";
|
|
5
|
+
|
|
6
|
+
export const OBJECTION_CONNECTION = Symbol.for("DEFAULT_CONNECTION");
|
|
7
|
+
export type OBJECTION_CONNECTION = Knex<any, unknown[]>;
|
|
8
|
+
|
|
9
|
+
registerProvider({
|
|
10
|
+
token: OBJECTION_CONNECTION,
|
|
11
|
+
deps: [Configuration],
|
|
12
|
+
useAsyncFactory(configuration: Configuration) {
|
|
13
|
+
const connectionOptions = configuration.get<Knex.Config>("knex")!;
|
|
14
|
+
|
|
15
|
+
if (connectionOptions) {
|
|
16
|
+
try {
|
|
17
|
+
return createConnection(connectionOptions);
|
|
18
|
+
} catch (er) {
|
|
19
|
+
// istanbul ignore next
|
|
20
|
+
console.error(er);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return Promise.resolve({});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {catchError} from "@tsed/core";
|
|
2
|
+
import {MaxLength, Property} from "@tsed/schema";
|
|
3
|
+
|
|
4
|
+
import {createTableStub} from "../../test/helpers/knex/table.js";
|
|
5
|
+
import {Decimal} from "../decorators/decimal.js";
|
|
6
|
+
import {Entity} from "../decorators/entity.js";
|
|
7
|
+
import {IdColumn} from "../decorators/idColumn.js";
|
|
8
|
+
import {createColumns} from "./createColumns.js";
|
|
9
|
+
|
|
10
|
+
describe("createTable", () => {
|
|
11
|
+
it("should create table from a given class (columns doesn't exists)", () => {
|
|
12
|
+
@Entity("users")
|
|
13
|
+
class User {
|
|
14
|
+
@IdColumn()
|
|
15
|
+
id: number;
|
|
16
|
+
|
|
17
|
+
@Property()
|
|
18
|
+
@MaxLength(200)
|
|
19
|
+
name: string;
|
|
20
|
+
|
|
21
|
+
@Property()
|
|
22
|
+
age: number;
|
|
23
|
+
|
|
24
|
+
@Decimal({scale: 1, precision: 12})
|
|
25
|
+
score: number;
|
|
26
|
+
|
|
27
|
+
@Property()
|
|
28
|
+
active: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const table = createTableStub();
|
|
32
|
+
|
|
33
|
+
createColumns(table, User);
|
|
34
|
+
|
|
35
|
+
expect(table.increments).toHaveBeenCalledWith("id");
|
|
36
|
+
expect(table.primary).toHaveBeenCalledWith();
|
|
37
|
+
expect(table.string).toHaveBeenCalledWith("name", 200);
|
|
38
|
+
expect(table.decimal).toHaveBeenCalledWith("age", undefined, undefined);
|
|
39
|
+
expect(table.decimal).toHaveBeenCalledWith("score", 12, 1);
|
|
40
|
+
expect(table.boolean).toHaveBeenCalledWith("active");
|
|
41
|
+
});
|
|
42
|
+
it("should create table from a given class (columns exists)", () => {
|
|
43
|
+
@Entity("users")
|
|
44
|
+
class User {
|
|
45
|
+
@IdColumn()
|
|
46
|
+
id: number;
|
|
47
|
+
|
|
48
|
+
@Property()
|
|
49
|
+
@MaxLength(200)
|
|
50
|
+
name: string;
|
|
51
|
+
|
|
52
|
+
@Property()
|
|
53
|
+
age: number;
|
|
54
|
+
|
|
55
|
+
@Decimal({scale: 1, precision: 12})
|
|
56
|
+
score: number;
|
|
57
|
+
|
|
58
|
+
@Property()
|
|
59
|
+
active: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const table = createTableStub();
|
|
63
|
+
|
|
64
|
+
createColumns(table, User);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should throw error when type isn't supported", () => {
|
|
68
|
+
@Entity("users")
|
|
69
|
+
class User {
|
|
70
|
+
@Property()
|
|
71
|
+
date: any;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const table = createTableStub();
|
|
75
|
+
const error = catchError(() => createColumns(table, User));
|
|
76
|
+
|
|
77
|
+
expect(error).toBe("Column type object isn't supported. Add function to ColumnTypesContainer to map the column with Knex");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import "../components/createBooleanColumn.js";
|
|
2
|
+
import "../components/createIdColumn.js";
|
|
3
|
+
import "../components/createNumberColumn.js";
|
|
4
|
+
import "../components/createStringColumn.js";
|
|
5
|
+
|
|
6
|
+
import {getProperties, JsonEntityStore} from "@tsed/schema";
|
|
7
|
+
import {Knex} from "knex";
|
|
8
|
+
|
|
9
|
+
import {ColumnTypesContainer} from "../services/ColumnTypesContainer.js";
|
|
10
|
+
import {getColumnCtx} from "./getColumnCtx.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @ignore
|
|
14
|
+
*/
|
|
15
|
+
export function createColumn(table: Knex.TableBuilder, entity: JsonEntityStore) {
|
|
16
|
+
const ctx = getColumnCtx(entity);
|
|
17
|
+
|
|
18
|
+
if (!ColumnTypesContainer.has(ctx.columnType)) {
|
|
19
|
+
throw `Column type ${ctx.columnType} isn't supported. Add function to ColumnTypesContainer to map the column with Knex`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
ColumnTypesContainer.get(ctx.columnType)!(table, ctx);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @ignore
|
|
27
|
+
*/
|
|
28
|
+
export function getColumns(model: any) {
|
|
29
|
+
return [...getProperties(model, {withIgnoredProps: true, objection: true, groups: false}).values()];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @ignore
|
|
34
|
+
*/
|
|
35
|
+
export function createColumns(table: Knex.TableBuilder, model: any) {
|
|
36
|
+
getColumns(model).map((entity) => {
|
|
37
|
+
createColumn(table, entity);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {Metadata} from "@tsed/core";
|
|
2
|
+
import {Model, RelationType} from "objection";
|
|
3
|
+
|
|
4
|
+
import {isRelationshipOptsWithThrough, RelationshipOpts} from "../domain/RelationshipOpts.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @ignore
|
|
8
|
+
*/
|
|
9
|
+
export function createJoinKeys(targetClass: any, targetSuper: any, propertyKey: string, relation: RelationType, opts?: RelationshipOpts) {
|
|
10
|
+
const FOREIGN_MODEL = opts?.type || Metadata.getType(targetSuper, propertyKey);
|
|
11
|
+
const SOURCE_MODEL_KEY = `${targetClass.tableName}.${opts?.from || targetClass.idColumn}`;
|
|
12
|
+
const FOREIGN_MODEL_KEY = `${FOREIGN_MODEL.tableName}.${opts?.to || FOREIGN_MODEL.idColumn}`;
|
|
13
|
+
|
|
14
|
+
switch (relation) {
|
|
15
|
+
case Model.HasManyRelation:
|
|
16
|
+
case Model.HasOneRelation:
|
|
17
|
+
return {
|
|
18
|
+
from: SOURCE_MODEL_KEY,
|
|
19
|
+
to: `${FOREIGN_MODEL.tableName}.${opts?.to || targetClass.tableName + "Id"}`
|
|
20
|
+
};
|
|
21
|
+
case Model.ManyToManyRelation:
|
|
22
|
+
case Model.HasOneThroughRelation:
|
|
23
|
+
return {
|
|
24
|
+
from: SOURCE_MODEL_KEY,
|
|
25
|
+
through: (isRelationshipOptsWithThrough(opts) && opts.through) || {
|
|
26
|
+
from: `${targetClass.tableName}_${FOREIGN_MODEL.tableName}.${targetClass.tableName}Id`,
|
|
27
|
+
to: `${targetClass.tableName}_${FOREIGN_MODEL.tableName}.${FOREIGN_MODEL.tableName}Id`
|
|
28
|
+
},
|
|
29
|
+
to: FOREIGN_MODEL_KEY
|
|
30
|
+
};
|
|
31
|
+
case Model.BelongsToOneRelation:
|
|
32
|
+
default:
|
|
33
|
+
return {
|
|
34
|
+
from: `${targetClass.tableName}.${opts?.from || propertyKey + "Id"}`,
|
|
35
|
+
to: FOREIGN_MODEL_KEY
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {DecoratorParameters, getClass, Metadata} from "@tsed/core";
|
|
2
|
+
import {RelationType} from "objection";
|
|
3
|
+
|
|
4
|
+
import {RelationshipOpts} from "../domain/RelationshipOpts.js";
|
|
5
|
+
import {createJoinKeys} from "./createJoinKeys.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @ignore
|
|
9
|
+
*/
|
|
10
|
+
export function createRelationshipMapping([target, propertyKey]: DecoratorParameters, relation: RelationType, opts?: RelationshipOpts) {
|
|
11
|
+
return (targetClass: any) => ({
|
|
12
|
+
[propertyKey]: {
|
|
13
|
+
relation,
|
|
14
|
+
modelClass: opts?.type || Metadata.getType(target, propertyKey),
|
|
15
|
+
join: createJoinKeys(targetClass, target, String(propertyKey), relation, opts),
|
|
16
|
+
modify: opts?.modify,
|
|
17
|
+
filter: opts?.filter,
|
|
18
|
+
beforeInsert: opts?.beforeInsert
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {JsonEntityStore} from "@tsed/schema";
|
|
2
|
+
|
|
3
|
+
import {ColumnOpts} from "../domain/ColumnOpts.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @ignore
|
|
7
|
+
*/
|
|
8
|
+
export interface ColumnCtx extends ColumnOpts {
|
|
9
|
+
entity: JsonEntityStore;
|
|
10
|
+
schema: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @ignore
|
|
15
|
+
*/
|
|
16
|
+
export function getColumnCtx(entity: JsonEntityStore): ColumnCtx {
|
|
17
|
+
const schema = entity.schema.toJSON();
|
|
18
|
+
const {columnType = schema.type, options = {}} = entity.store.get<Partial<ColumnOpts>>("objection", {});
|
|
19
|
+
|
|
20
|
+
return {entity, columnType, options, schema};
|
|
21
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {getColumns} from "./createColumns.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @ignore
|
|
5
|
+
*/
|
|
6
|
+
export const OBJECTION_RELATIONSHIP_KEY = "objectionRelationship";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @ignore
|
|
10
|
+
*/
|
|
11
|
+
export function getJsonEntityRelationships(target: any) {
|
|
12
|
+
return getColumns(target)
|
|
13
|
+
.filter((col) => col.store.get(OBJECTION_RELATIONSHIP_KEY))
|
|
14
|
+
.map((col) => col.store.get(OBJECTION_RELATIONSHIP_KEY)(target))
|
|
15
|
+
.reduce((dict, col) => ({...dict, ...col}), {});
|
|
16
|
+
}
|