@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
package/package.json
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
"name": "@tsed/objection",
|
|
3
3
|
"description": "Objection.js package for Ts.ED framework",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "8.0.
|
|
5
|
+
"version": "8.0.3",
|
|
6
6
|
"source": "./src/index.ts",
|
|
7
7
|
"main": "./lib/esm/index.js",
|
|
8
8
|
"module": "./lib/esm/index.js",
|
|
9
9
|
"typings": "./lib/types/index.d.ts",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
|
+
"@tsed/source": "./src/index.ts",
|
|
12
13
|
"types": "./lib/types/index.d.ts",
|
|
13
14
|
"import": "./lib/esm/index.js",
|
|
14
15
|
"default": "./lib/esm/index.js"
|
|
@@ -25,13 +26,13 @@
|
|
|
25
26
|
"tslib": "2.7.0"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
|
-
"@tsed/ajv": "8.0.
|
|
29
|
-
"@tsed/barrels": "8.0.
|
|
30
|
-
"@tsed/core": "8.0.
|
|
31
|
-
"@tsed/di": "8.0.
|
|
32
|
-
"@tsed/json-mapper": "8.0.
|
|
33
|
-
"@tsed/schema": "8.0.
|
|
34
|
-
"@tsed/typescript": "8.0.
|
|
29
|
+
"@tsed/ajv": "8.0.3",
|
|
30
|
+
"@tsed/barrels": "8.0.3",
|
|
31
|
+
"@tsed/core": "8.0.3",
|
|
32
|
+
"@tsed/di": "8.0.3",
|
|
33
|
+
"@tsed/json-mapper": "8.0.3",
|
|
34
|
+
"@tsed/schema": "8.0.3",
|
|
35
|
+
"@tsed/typescript": "8.0.3",
|
|
35
36
|
"eslint": "9.12.0",
|
|
36
37
|
"knex": "^3.1.0",
|
|
37
38
|
"objection": "^2.2.18",
|
|
@@ -40,11 +41,11 @@
|
|
|
40
41
|
"vitest": "2.1.2"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
|
-
"@tsed/ajv": "8.0.
|
|
44
|
-
"@tsed/core": "8.0.
|
|
45
|
-
"@tsed/di": "8.0.
|
|
46
|
-
"@tsed/json-mapper": "8.0.
|
|
47
|
-
"@tsed/schema": "8.0.
|
|
44
|
+
"@tsed/ajv": "8.0.3",
|
|
45
|
+
"@tsed/core": "8.0.3",
|
|
46
|
+
"@tsed/di": "8.0.3",
|
|
47
|
+
"@tsed/json-mapper": "8.0.3",
|
|
48
|
+
"@tsed/schema": "8.0.3",
|
|
48
49
|
"knex": ">=0.94.0",
|
|
49
50
|
"objection": ">=2.0.0"
|
|
50
51
|
},
|
package/readme.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p style="text-align: center" align="center">
|
|
2
|
-
<a href="https://tsed.
|
|
2
|
+
<a href="https://tsed.dev" target="_blank"><img src="https://tsed.dev/tsed-og.png" width="200" alt="Ts.ED logo"/></a>
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
18
|
<div align="center">
|
|
19
|
-
<a href="https://tsed.
|
|
19
|
+
<a href="https://tsed.dev/">Website</a>
|
|
20
20
|
<span> • </span>
|
|
21
|
-
<a href="https://tsed.
|
|
21
|
+
<a href="https://tsed.dev/getting-started/">Getting started</a>
|
|
22
22
|
<span> • </span>
|
|
23
23
|
<a href="https://api.tsed.io/rest/slack/tsedio/tsed">Slack</a>
|
|
24
24
|
<span> • </span>
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
<hr />
|
|
29
29
|
|
|
30
|
-
A package of Ts.ED framework. See website: https://tsed.
|
|
30
|
+
A package of Ts.ED framework. See website: https://tsed.dev/tutorials/objection.html
|
|
31
31
|
|
|
32
32
|
## Feature
|
|
33
33
|
|
|
@@ -78,7 +78,7 @@ class Server {}
|
|
|
78
78
|
|
|
79
79
|
## Contributors
|
|
80
80
|
|
|
81
|
-
Please read [contributing guidelines here](https://tsed.
|
|
81
|
+
Please read [contributing guidelines here](https://tsed.dev/contributing.html)
|
|
82
82
|
|
|
83
83
|
<a href="https://github.com/tsedio/tsed/graphs/contributors"><img src="https://opencollective.com/tsed/contributors.svg?width=890" /></a>
|
|
84
84
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {Property} from "@tsed/schema";
|
|
2
|
+
|
|
3
|
+
import {createTableStub} from "../../test/helpers/knex/table.js";
|
|
4
|
+
import {Entity} from "../decorators/entity.js";
|
|
5
|
+
import {createColumns} from "../utils/createColumns.js";
|
|
6
|
+
|
|
7
|
+
describe("createBooleanColumn", () => {
|
|
8
|
+
it("should create table from a given class", () => {
|
|
9
|
+
@Entity("users")
|
|
10
|
+
class User {
|
|
11
|
+
@Property()
|
|
12
|
+
activated: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const table = createTableStub();
|
|
16
|
+
createColumns(table, User);
|
|
17
|
+
|
|
18
|
+
expect(table.boolean).toHaveBeenCalledWith("activated");
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type {Knex} from "knex";
|
|
2
|
+
|
|
3
|
+
import {ColumnTypesContainer} from "../services/ColumnTypesContainer.js";
|
|
4
|
+
import {ColumnCtx} from "../utils/getColumnCtx.js";
|
|
5
|
+
/**
|
|
6
|
+
* @ignore
|
|
7
|
+
*/
|
|
8
|
+
export function createBooleanColumn(table: Knex.TableBuilder, {entity}: ColumnCtx) {
|
|
9
|
+
table.boolean(entity.propertyName);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
ColumnTypesContainer.set("boolean", createBooleanColumn);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {createTableStub} from "../../test/helpers/knex/table.js";
|
|
2
|
+
import {Entity} from "../decorators/entity.js";
|
|
3
|
+
import {IdColumn} from "../decorators/idColumn.js";
|
|
4
|
+
import {createColumns} from "../utils/createColumns.js";
|
|
5
|
+
|
|
6
|
+
describe("createIdColumn", () => {
|
|
7
|
+
it("should create table from a given class (bigIncrements)", () => {
|
|
8
|
+
@Entity("users")
|
|
9
|
+
class User {
|
|
10
|
+
@IdColumn("bigIncrements")
|
|
11
|
+
id: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const table = createTableStub();
|
|
15
|
+
createColumns(table, User);
|
|
16
|
+
|
|
17
|
+
expect(table.bigIncrements).toHaveBeenCalledWith("id");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should create table from a given class (increments)", () => {
|
|
21
|
+
@Entity("users")
|
|
22
|
+
class User {
|
|
23
|
+
@IdColumn("increments")
|
|
24
|
+
id: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const table = createTableStub();
|
|
28
|
+
createColumns(table, User);
|
|
29
|
+
|
|
30
|
+
expect(table.increments).toHaveBeenCalledWith("id");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should create table from a given class (uuid)", () => {
|
|
34
|
+
@Entity("users")
|
|
35
|
+
class User {
|
|
36
|
+
@IdColumn("uuid")
|
|
37
|
+
id: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const table = createTableStub();
|
|
41
|
+
createColumns(table, User);
|
|
42
|
+
|
|
43
|
+
expect(table.uuid).toHaveBeenCalledWith("id");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {randomUUID} from "crypto";
|
|
2
|
+
import type {Knex} from "knex";
|
|
3
|
+
|
|
4
|
+
import {ColumnTypesContainer} from "../services/ColumnTypesContainer.js";
|
|
5
|
+
import {ColumnCtx} from "../utils/getColumnCtx.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @ignore
|
|
9
|
+
*/
|
|
10
|
+
export function createIdColumn(table: Knex.TableBuilder, {entity, options}: ColumnCtx) {
|
|
11
|
+
switch (options.type) {
|
|
12
|
+
case "bigIncrements":
|
|
13
|
+
table.bigIncrements(entity.propertyName).primary();
|
|
14
|
+
break;
|
|
15
|
+
case "increments":
|
|
16
|
+
table.increments(entity.propertyName).primary();
|
|
17
|
+
break;
|
|
18
|
+
case "uuid":
|
|
19
|
+
table.uuid(entity.propertyName).primary().defaultTo(randomUUID());
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ColumnTypesContainer.set("idColumn", createIdColumn);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {Property} from "@tsed/schema";
|
|
2
|
+
|
|
3
|
+
import {createTableStub} from "../../test/helpers/knex/table.js";
|
|
4
|
+
import {Decimal} from "../decorators/decimal.js";
|
|
5
|
+
import {Entity} from "../decorators/entity.js";
|
|
6
|
+
import {createColumns} from "../utils/createColumns.js";
|
|
7
|
+
|
|
8
|
+
describe("createNumberColumn", () => {
|
|
9
|
+
it("should create table from a given class (default)", async () => {
|
|
10
|
+
@Entity("users")
|
|
11
|
+
class User {
|
|
12
|
+
@Property()
|
|
13
|
+
prop: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const table = createTableStub();
|
|
17
|
+
await createColumns(table, User);
|
|
18
|
+
|
|
19
|
+
expect(table.decimal).toHaveBeenCalledWith("prop", undefined, undefined);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should create table from a given class (decimal)", async () => {
|
|
23
|
+
@Entity("users")
|
|
24
|
+
class User {
|
|
25
|
+
@Decimal({scale: 1, precision: 12})
|
|
26
|
+
prop: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const table = createTableStub();
|
|
30
|
+
await createColumns(table, User);
|
|
31
|
+
|
|
32
|
+
expect(table.decimal).toHaveBeenCalledWith("prop", 12, 1);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type {Knex} from "knex";
|
|
2
|
+
|
|
3
|
+
import {ColumnTypesContainer} from "../services/ColumnTypesContainer.js";
|
|
4
|
+
import {ColumnCtx} from "../utils/getColumnCtx.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @ignore
|
|
8
|
+
*/
|
|
9
|
+
export function createNumberColumn(table: Knex.TableBuilder, {entity, options}: ColumnCtx) {
|
|
10
|
+
table.decimal(entity.propertyName, options.precision, options.scale);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
ColumnTypesContainer.set("number", createNumberColumn);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {MaxLength, Property} from "@tsed/schema";
|
|
2
|
+
|
|
3
|
+
import {createTableStub} from "../../test/helpers/knex/table.js";
|
|
4
|
+
import {Entity} from "../decorators/entity.js";
|
|
5
|
+
import {createColumns} from "../utils/createColumns.js";
|
|
6
|
+
|
|
7
|
+
describe("createNumberColumn", () => {
|
|
8
|
+
it("should create table from a given class (with maxLength)", async () => {
|
|
9
|
+
@Entity("users")
|
|
10
|
+
class User {
|
|
11
|
+
@Property()
|
|
12
|
+
@MaxLength(200)
|
|
13
|
+
prop: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const table = createTableStub();
|
|
17
|
+
await createColumns(table, User);
|
|
18
|
+
|
|
19
|
+
expect(table.string).toHaveBeenCalledWith("prop", 200);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should create table from a given class (without maxLength)", async () => {
|
|
23
|
+
@Entity("users")
|
|
24
|
+
class User {
|
|
25
|
+
@Property()
|
|
26
|
+
prop: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const table = createTableStub();
|
|
30
|
+
await createColumns(table, User);
|
|
31
|
+
|
|
32
|
+
expect(table.string).toHaveBeenCalledWith("prop", undefined);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type {Knex} from "knex";
|
|
2
|
+
|
|
3
|
+
import {ColumnTypesContainer} from "../services/ColumnTypesContainer.js";
|
|
4
|
+
import {ColumnCtx} from "../utils/getColumnCtx.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @ignore
|
|
8
|
+
*/
|
|
9
|
+
export function createStringColumn(table: Knex.TableBuilder, {entity, schema}: ColumnCtx) {
|
|
10
|
+
table.string(entity.propertyName, schema.maxLength);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
ColumnTypesContainer.set("string", createStringColumn);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {Model} from "objection";
|
|
2
|
+
|
|
3
|
+
import {BelongsToOne} from "./belongsToOne.js";
|
|
4
|
+
import {Entity} from "./entity.js";
|
|
5
|
+
import {IdColumn} from "./idColumn.js";
|
|
6
|
+
|
|
7
|
+
describe("@BelongsToOne", () => {
|
|
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
|
+
@BelongsToOne()
|
|
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
|
+
it("should set custom relationship path", () => {
|
|
35
|
+
@Entity("user")
|
|
36
|
+
class User extends Model {
|
|
37
|
+
@IdColumn()
|
|
38
|
+
id!: string;
|
|
39
|
+
userId!: string;
|
|
40
|
+
}
|
|
41
|
+
@Entity("movie")
|
|
42
|
+
class Movie extends Model {
|
|
43
|
+
@IdColumn()
|
|
44
|
+
id!: string;
|
|
45
|
+
ownerId!: string;
|
|
46
|
+
@BelongsToOne({from: "ownerId", to: "userId"})
|
|
47
|
+
user?: User;
|
|
48
|
+
}
|
|
49
|
+
expect(Movie.relationMappings).toEqual({
|
|
50
|
+
user: {
|
|
51
|
+
relation: Model.BelongsToOneRelation,
|
|
52
|
+
modelClass: User,
|
|
53
|
+
join: {
|
|
54
|
+
from: "movie.ownerId",
|
|
55
|
+
to: "user.userId"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {Model} from "objection";
|
|
2
|
+
|
|
3
|
+
import {RelationshipOptsWithoutThrough} from "../domain/RelationshipOpts.js";
|
|
4
|
+
import {RelatesTo} from "./relatesTo.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param opts
|
|
9
|
+
* @decorator
|
|
10
|
+
* @objection
|
|
11
|
+
*/
|
|
12
|
+
export function BelongsToOne(opts?: RelationshipOptsWithoutThrough): PropertyDecorator {
|
|
13
|
+
return RelatesTo(Model.BelongsToOneRelation, opts);
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {StoreSet} from "@tsed/core";
|
|
2
|
+
|
|
3
|
+
import type {ColumnOpts} from "../domain/ColumnOpts.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @param options
|
|
8
|
+
* @decorator
|
|
9
|
+
* @objection
|
|
10
|
+
*/
|
|
11
|
+
export function ColumnOptions(options: Partial<ColumnOpts>): PropertyDecorator {
|
|
12
|
+
return StoreSet("objection", options) as any;
|
|
13
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {Store} from "@tsed/core";
|
|
2
|
+
import {getJsonSchema} from "@tsed/schema";
|
|
3
|
+
|
|
4
|
+
import {Decimal} from "./decimal.js";
|
|
5
|
+
|
|
6
|
+
describe("@Decimal", () => {
|
|
7
|
+
it("should set metadata", () => {
|
|
8
|
+
class MyModel {
|
|
9
|
+
@Decimal({scale: 1, precision: 12})
|
|
10
|
+
prop: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
expect(getJsonSchema(MyModel)).toEqual({
|
|
14
|
+
properties: {
|
|
15
|
+
prop: {
|
|
16
|
+
type: "number"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
type: "object"
|
|
20
|
+
});
|
|
21
|
+
expect(Store.from(MyModel, "prop").get("objection")).toEqual({
|
|
22
|
+
columnType: "number",
|
|
23
|
+
options: {
|
|
24
|
+
precision: 12,
|
|
25
|
+
scale: 1,
|
|
26
|
+
type: "decimal"
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should set metadata (without options)", () => {
|
|
32
|
+
class MyModel {
|
|
33
|
+
@Decimal()
|
|
34
|
+
prop: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
expect(getJsonSchema(MyModel)).toEqual({
|
|
38
|
+
properties: {
|
|
39
|
+
prop: {
|
|
40
|
+
type: "number"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
type: "object"
|
|
44
|
+
});
|
|
45
|
+
expect(Store.from(MyModel, "prop").get("objection")).toEqual({
|
|
46
|
+
columnType: "number",
|
|
47
|
+
options: {
|
|
48
|
+
type: "decimal"
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {StoreSet, useDecorators} from "@tsed/core";
|
|
2
|
+
import {Property} from "@tsed/schema";
|
|
3
|
+
|
|
4
|
+
import {ColumnOptions} from "./columnOptions.js";
|
|
5
|
+
|
|
6
|
+
export interface DecimalOptions {
|
|
7
|
+
precision?: number | null;
|
|
8
|
+
scale?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param options
|
|
14
|
+
* @decorator
|
|
15
|
+
* @objection
|
|
16
|
+
*/
|
|
17
|
+
export function Decimal(options: DecimalOptions = {}) {
|
|
18
|
+
return useDecorators(
|
|
19
|
+
Property(),
|
|
20
|
+
ColumnOptions({
|
|
21
|
+
columnType: "number",
|
|
22
|
+
options: {
|
|
23
|
+
type: "decimal",
|
|
24
|
+
...options
|
|
25
|
+
}
|
|
26
|
+
}),
|
|
27
|
+
StoreSet("objection", {
|
|
28
|
+
columnType: "number",
|
|
29
|
+
options: {
|
|
30
|
+
type: "decimal",
|
|
31
|
+
...options
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {Entity} from "./entity.js";
|
|
2
|
+
|
|
3
|
+
describe("@Entity", () => {
|
|
4
|
+
it("should set metadata", () => {
|
|
5
|
+
@Entity("models")
|
|
6
|
+
class MyModel {}
|
|
7
|
+
|
|
8
|
+
expect((MyModel as any).jsonSchema).toEqual({
|
|
9
|
+
type: "object"
|
|
10
|
+
});
|
|
11
|
+
expect((MyModel as any).tableName).toEqual("models");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("should throw metadata when the tableName is empty", () => {
|
|
15
|
+
let actualError: any;
|
|
16
|
+
try {
|
|
17
|
+
@Entity("")
|
|
18
|
+
class MyModel {}
|
|
19
|
+
} catch (er) {
|
|
20
|
+
actualError = er;
|
|
21
|
+
}
|
|
22
|
+
expect(actualError.message).toEqual("Please provide a table name");
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {Type} from "@tsed/core";
|
|
2
|
+
import {getJsonSchema} from "@tsed/schema";
|
|
3
|
+
|
|
4
|
+
import {defineStaticGetter} from "../utils/defineStaticGetter.js";
|
|
5
|
+
import {getJsonEntityRelationships} from "../utils/getJsonEntityRelationships.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param tableName
|
|
10
|
+
* @decorator
|
|
11
|
+
* @objection
|
|
12
|
+
*/
|
|
13
|
+
export function Entity(tableName: string): ClassDecorator {
|
|
14
|
+
if (!tableName) {
|
|
15
|
+
throw new Error("Please provide a table name");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (target: any) => {
|
|
19
|
+
const originalRelationMappings = target["relationMappings"];
|
|
20
|
+
defineStaticGetter(target, "tableName", () => tableName);
|
|
21
|
+
defineStaticGetter(target, "jsonSchema", () => getJsonSchema(target));
|
|
22
|
+
defineStaticGetter(target, "relationMappings", () => originalRelationMappings || getJsonEntityRelationships(target));
|
|
23
|
+
return target;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface EntityMethods<T> extends Type<T> {
|
|
28
|
+
readonly tableName: string;
|
|
29
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {Model} from "objection";
|
|
2
|
+
|
|
3
|
+
import {Entity} from "./entity.js";
|
|
4
|
+
import {HasMany} from "./hasMany.js";
|
|
5
|
+
import {IdColumn} from "./idColumn.js";
|
|
6
|
+
|
|
7
|
+
describe("@HasMany", () => {
|
|
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
|
+
@HasMany(Pet)
|
|
21
|
+
pets?: Pet[];
|
|
22
|
+
}
|
|
23
|
+
expect(User.relationMappings).toEqual({
|
|
24
|
+
pets: {
|
|
25
|
+
relation: Model.HasManyRelation,
|
|
26
|
+
modelClass: Pet,
|
|
27
|
+
join: {
|
|
28
|
+
from: "user.id",
|
|
29
|
+
to: "pet.userId"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
it("should set custom relationship path", () => {
|
|
35
|
+
@Entity("pet")
|
|
36
|
+
class Pet extends Model {
|
|
37
|
+
@IdColumn()
|
|
38
|
+
id!: string;
|
|
39
|
+
ownerId?: string;
|
|
40
|
+
}
|
|
41
|
+
@Entity("user")
|
|
42
|
+
class User extends Model {
|
|
43
|
+
@IdColumn()
|
|
44
|
+
id!: string;
|
|
45
|
+
userId!: string;
|
|
46
|
+
@HasMany(Pet, {from: "userId", to: "ownerId"})
|
|
47
|
+
pets?: Pet[];
|
|
48
|
+
}
|
|
49
|
+
expect(User.relationMappings).toEqual({
|
|
50
|
+
pets: {
|
|
51
|
+
relation: Model.HasManyRelation,
|
|
52
|
+
modelClass: Pet,
|
|
53
|
+
join: {
|
|
54
|
+
from: "user.userId",
|
|
55
|
+
to: "pet.ownerId"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -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 HasMany(type: ModelClassSpecifier, opts?: RelationshipOptsWithThrough): PropertyDecorator {
|
|
13
|
+
return RelatesTo(Model.HasManyRelation, {...opts, type});
|
|
14
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {Model} from "objection";
|
|
2
|
+
|
|
3
|
+
import {Entity} from "./entity.js";
|
|
4
|
+
import {HasOne} from "./hasOne.js";
|
|
5
|
+
import {IdColumn} from "./idColumn.js";
|
|
6
|
+
|
|
7
|
+
describe("@HasOne", () => {
|
|
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
|
+
@HasOne()
|
|
21
|
+
pet?: Pet;
|
|
22
|
+
}
|
|
23
|
+
expect(User.relationMappings).toEqual({
|
|
24
|
+
pet: {
|
|
25
|
+
relation: Model.HasOneRelation,
|
|
26
|
+
modelClass: Pet,
|
|
27
|
+
join: {
|
|
28
|
+
from: "user.id",
|
|
29
|
+
to: "pet.userId"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
it("should set custom relationship path", () => {
|
|
35
|
+
@Entity("pet")
|
|
36
|
+
class Pet extends Model {
|
|
37
|
+
@IdColumn()
|
|
38
|
+
id!: string;
|
|
39
|
+
ownerId?: string;
|
|
40
|
+
}
|
|
41
|
+
@Entity("user")
|
|
42
|
+
class User extends Model {
|
|
43
|
+
@IdColumn()
|
|
44
|
+
id!: string;
|
|
45
|
+
userId!: string;
|
|
46
|
+
@HasOne({from: "userId", to: "ownerId"})
|
|
47
|
+
pet?: Pet;
|
|
48
|
+
}
|
|
49
|
+
expect(User.relationMappings).toEqual({
|
|
50
|
+
pet: {
|
|
51
|
+
relation: Model.HasOneRelation,
|
|
52
|
+
modelClass: Pet,
|
|
53
|
+
join: {
|
|
54
|
+
from: "user.userId",
|
|
55
|
+
to: "pet.ownerId"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {Model} from "objection";
|
|
2
|
+
|
|
3
|
+
import {RelationshipOptsWithoutThrough} from "../domain/RelationshipOpts.js";
|
|
4
|
+
import {RelatesTo} from "./relatesTo.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param opts
|
|
9
|
+
* @decorator
|
|
10
|
+
* @objection
|
|
11
|
+
*/
|
|
12
|
+
export function HasOne(opts?: RelationshipOptsWithoutThrough): PropertyDecorator {
|
|
13
|
+
return RelatesTo(Model.HasOneRelation, opts);
|
|
14
|
+
}
|