@entity-access/entity-access 1.0.1
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/.eslintrc.cjs +234 -0
- package/.github/workflows/node.yml +44 -0
- package/.github/workflows/npm-publish.yml +33 -0
- package/.vscode/launch.json +20 -0
- package/.vscode/settings.json +39 -0
- package/LICENSE +201 -0
- package/README.md +247 -0
- package/package.json +39 -0
- package/src/TestRunner.ts +2 -0
- package/src/common/cache/InstanceCache.ts +14 -0
- package/src/common/cache/TimedCache.ts +74 -0
- package/src/common/symbols/symbols.ts +2 -0
- package/src/common/usingAsync.ts +24 -0
- package/src/compiler/ISqlHelpers.ts +30 -0
- package/src/compiler/QueryCompiler.ts +88 -0
- package/src/compiler/postgres/PostgreSqlMethodTransformer.ts +83 -0
- package/src/compiler/sql-server/SqlServerSqlMethodTransformer.ts +83 -0
- package/src/decorators/Column.ts +46 -0
- package/src/decorators/ForeignKey.ts +42 -0
- package/src/decorators/IClassOf.ts +1 -0
- package/src/decorators/IColumn.ts +70 -0
- package/src/decorators/ISqlType.ts +70 -0
- package/src/decorators/SchemaRegistry.ts +19 -0
- package/src/decorators/Table.ts +18 -0
- package/src/decorators/parser/MemberParser.ts +8 -0
- package/src/drivers/base/BaseDriver.ts +134 -0
- package/src/drivers/postgres/PostgreSqlDriver.ts +178 -0
- package/src/drivers/sql-server/ExpressionToSqlServer.ts +78 -0
- package/src/drivers/sql-server/SqlServerDriver.ts +215 -0
- package/src/drivers/sql-server/SqlServerLiteral.ts +12 -0
- package/src/drivers/sql-server/SqlServerQueryCompiler.ts +25 -0
- package/src/entity-query/EntityType.ts +116 -0
- package/src/migrations/Migrations.ts +17 -0
- package/src/migrations/postgres/PostgresAutomaticMigrations.ts +97 -0
- package/src/migrations/postgres/PostgresMigrations.ts +56 -0
- package/src/migrations/sql-server/SqlServerAutomaticMigrations.ts +102 -0
- package/src/migrations/sql-server/SqlServerMigrations.ts +60 -0
- package/src/model/ChangeEntry.ts +154 -0
- package/src/model/ChangeSet.ts +88 -0
- package/src/model/EntityContext.ts +48 -0
- package/src/model/EntityModel.ts +27 -0
- package/src/model/EntityQuery.ts +152 -0
- package/src/model/EntitySchema.ts +21 -0
- package/src/model/EntitySource.ts +98 -0
- package/src/model/IFilterWithParameter.ts +38 -0
- package/src/model/IdentityService.ts +23 -0
- package/src/model/SourceExpression.ts +137 -0
- package/src/query/Query.ts +158 -0
- package/src/query/ast/ExpressionToSql.ts +348 -0
- package/src/query/ast/Expressions.ts +294 -0
- package/src/query/ast/IStringTransformer.ts +74 -0
- package/src/query/ast/SqlLiteral.ts +25 -0
- package/src/query/ast/Visitor.ts +159 -0
- package/src/query/parser/ArrowToExpression.ts +160 -0
- package/src/query/parser/BabelVisitor.ts +86 -0
- package/src/sql/ISql.ts +31 -0
- package/src/sql/Sql.ts +4 -0
- package/src/tests/TestConfig.ts +6 -0
- package/src/tests/db-tests/tests/select-items.ts +37 -0
- package/src/tests/db-tests/tests/update-items.ts +17 -0
- package/src/tests/drivers/postgres/connection-test.ts +29 -0
- package/src/tests/drivers/sql-server/sql-server-test.ts +9 -0
- package/src/tests/expressions/left-joins/child-joins.ts +71 -0
- package/src/tests/expressions/simple/parse-arrow.ts +46 -0
- package/src/tests/expressions/trimInternal.ts +23 -0
- package/src/tests/model/ShoppingContext.ts +203 -0
- package/src/tests/model/createContext.ts +294 -0
- package/src/tests/query/combine.ts +28 -0
- package/test.js +107 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as bp from "@babel/parser";
|
|
2
|
+
import * as bpe from "@babel/types";
|
|
3
|
+
|
|
4
|
+
export class BabelVisitor<T> {
|
|
5
|
+
visit(nodeAny: bpe.Expression | bpe.Node): T {
|
|
6
|
+
const node = nodeAny as bpe.Expression;
|
|
7
|
+
switch (node.type) {
|
|
8
|
+
case "BinaryExpression":
|
|
9
|
+
return this.visitBinaryExpression(node);
|
|
10
|
+
case "LogicalExpression":
|
|
11
|
+
return this.visitLogicalExpression(node);
|
|
12
|
+
case "CallExpression":
|
|
13
|
+
return this.visitCallExpression(node);
|
|
14
|
+
case "ArrowFunctionExpression":
|
|
15
|
+
return this.visitArrowFunctionExpression(node);
|
|
16
|
+
case "ConditionalExpression":
|
|
17
|
+
return this.visitConditionalExpression(node);
|
|
18
|
+
case "NullLiteral":
|
|
19
|
+
return this.visitNullLiteral(node);
|
|
20
|
+
case "StringLiteral":
|
|
21
|
+
return this.visitStringLiteral(node);
|
|
22
|
+
case "BigIntLiteral":
|
|
23
|
+
return this.visitBigIntLiteral(node);
|
|
24
|
+
case "BooleanLiteral":
|
|
25
|
+
return this.visitBooleanLiteral(node);
|
|
26
|
+
case "DecimalLiteral":
|
|
27
|
+
return this.visitDecimalLiteral(node);
|
|
28
|
+
case "NumericLiteral":
|
|
29
|
+
return this.visitNumericLiteral(node);
|
|
30
|
+
case "TemplateLiteral":
|
|
31
|
+
return this.visitTemplateLiteral(node);
|
|
32
|
+
case "Identifier":
|
|
33
|
+
return this.visitIdentifier(node);
|
|
34
|
+
case "MemberExpression":
|
|
35
|
+
return this.visitMemberExpression(node);
|
|
36
|
+
case "RegExpLiteral":
|
|
37
|
+
default:
|
|
38
|
+
throw new Error(`Translation from ${node.type} not supported`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
visitMemberExpression(node: bpe.MemberExpression): T {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
visitLogicalExpression(node: bpe.LogicalExpression): T {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
visitIdentifier(node: bpe.Identifier): T {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
visitTemplateLiteral(node: bpe.TemplateLiteral): T {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
visitNumericLiteral(node: bpe.NumericLiteral): T {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
visitDecimalLiteral(node: bpe.DecimalLiteral): T {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
visitBooleanLiteral(node: bpe.BooleanLiteral): T {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
visitBigIntLiteral(node: bpe.BigIntLiteral): T {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
visitStringLiteral(node: bpe.StringLiteral): T {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
visitNullLiteral(node: bpe.NullLiteral): T {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
visitConditionalExpression(node: bpe.ConditionalExpression): T {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
visitArrowFunctionExpression(node: bpe.ArrowFunctionExpression): T {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
visitCallExpression(node: bpe.CallExpression): T {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
visitBinaryExpression(node: bpe.BinaryExpression): T {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
package/src/sql/ISql.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface ISql {
|
|
2
|
+
|
|
3
|
+
in<T>(a: T, array: T[]): boolean;
|
|
4
|
+
|
|
5
|
+
text: {
|
|
6
|
+
concat(... fragments: string[]): string;
|
|
7
|
+
like(text: string, test: string): boolean;
|
|
8
|
+
iLike(text: string, test: string): boolean;
|
|
9
|
+
left(text: string, length: number): string;
|
|
10
|
+
right(text: string, length: number): string;
|
|
11
|
+
startsWith(text: string, test: string): boolean;
|
|
12
|
+
endsWith(text: string, test: string): boolean;
|
|
13
|
+
indexOf(text: string, test: string): number;
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
date: {
|
|
17
|
+
yearOf(d: Date): number;
|
|
18
|
+
monthOf(d: Date): number;
|
|
19
|
+
dayOf(d: Date): number;
|
|
20
|
+
minuteOf(d: Date): number;
|
|
21
|
+
hourOf(d: Date): number;
|
|
22
|
+
secondOf(d: Date): number;
|
|
23
|
+
addYears(d: Date, n: number): Date;
|
|
24
|
+
addMonths(d: Date, n: number): Date;
|
|
25
|
+
addDays(d: Date, n: number): Date;
|
|
26
|
+
addHours(d: Date, n: number): Date;
|
|
27
|
+
addMinutes(d: Date, n: number): Date;
|
|
28
|
+
addSeconds(d: Date, n: number): Date;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
}
|
package/src/sql/Sql.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { TestConfig } from "../../TestConfig.js";
|
|
3
|
+
import { createContext, headPhoneCategory } from "../../model/createContext.js";
|
|
4
|
+
|
|
5
|
+
export default async function(this: TestConfig) {
|
|
6
|
+
|
|
7
|
+
if (!this.db) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const context = await createContext(this.driver);
|
|
12
|
+
|
|
13
|
+
const headphone = await context.products.where({ name: "Jabber Head Phones" }, (p) => (x) => x.name === p.name).first();
|
|
14
|
+
|
|
15
|
+
assert.notEqual(null, headphone);
|
|
16
|
+
|
|
17
|
+
const products = await context.products.where({}, (p) => (x) => x.productID > 0)
|
|
18
|
+
.offset(2)
|
|
19
|
+
.limit(5)
|
|
20
|
+
.orderBy({}, (p) => (x) => x.name)
|
|
21
|
+
.toArray();
|
|
22
|
+
|
|
23
|
+
assert.equal(5, products.length);
|
|
24
|
+
|
|
25
|
+
const sorted = [ ... products].sort((a, b) => a.name.localeCompare(b.name));
|
|
26
|
+
|
|
27
|
+
assert.equal(products.join(","), sorted.join(","));
|
|
28
|
+
|
|
29
|
+
// count all headphones...
|
|
30
|
+
let allHeadphones = await context.productCategories.where({ headPhoneCategory }, (p) => (x) => x.category.categoryID === p.headPhoneCategory).count();
|
|
31
|
+
|
|
32
|
+
assert.equal(6, allHeadphones);
|
|
33
|
+
|
|
34
|
+
allHeadphones = await context.products.where({ headPhoneCategory }, (p) => (x) => x.categories.some((pc) => pc.categoryID === p.headPhoneCategory)).count();
|
|
35
|
+
|
|
36
|
+
assert.equal(6, allHeadphones);
|
|
37
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { TestConfig } from "../../TestConfig.js";
|
|
2
|
+
import { createContext } from "../../model/createContext.js";
|
|
3
|
+
|
|
4
|
+
export default async function(this: TestConfig) {
|
|
5
|
+
|
|
6
|
+
if (!this.db) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const context = await createContext(this.driver);
|
|
11
|
+
|
|
12
|
+
const first = await context.products.all().first();
|
|
13
|
+
|
|
14
|
+
first.name = "First product";
|
|
15
|
+
|
|
16
|
+
await context.saveChanges();
|
|
17
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import PostgreSqlDriver from "../../../drivers/postgres/PostgreSqlDriver.js";
|
|
3
|
+
import { Query } from "../../../query/Query.js";
|
|
4
|
+
import { TestConfig } from "../../TestConfig.js";
|
|
5
|
+
|
|
6
|
+
export default async function (this: TestConfig) {
|
|
7
|
+
|
|
8
|
+
if(!this.db) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const connection = this.driver;
|
|
13
|
+
|
|
14
|
+
await connection.ensureDatabase();
|
|
15
|
+
// create table...
|
|
16
|
+
await connection.executeQuery(`SELECT 1;`);
|
|
17
|
+
|
|
18
|
+
// select items...
|
|
19
|
+
const items = await connection.executeReader(`SELECT * FROM (VALUES (1, 1), (1, 1)) as V(ID, Value)`);
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
for await (const iterator of items.next(100)) {
|
|
23
|
+
console.log(iterator);
|
|
24
|
+
}
|
|
25
|
+
} finally {
|
|
26
|
+
await items.dispose();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import SqlServerDriver from "../../../drivers/sql-server/SqlServerDriver.js";
|
|
3
|
+
import { SqlServerLiteral } from "../../../drivers/sql-server/SqlServerLiteral.js";
|
|
4
|
+
|
|
5
|
+
export default function () {
|
|
6
|
+
|
|
7
|
+
assert.equal("N'A\\'B'", SqlServerLiteral.escapeLiteral("A'B") );
|
|
8
|
+
|
|
9
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import QueryCompiler from "../../../compiler/QueryCompiler.js";
|
|
3
|
+
import { ShoppingContext } from "../../model/ShoppingContext.js";
|
|
4
|
+
import { assertSqlMatch, trimInternal } from "../trimInternal.js";
|
|
5
|
+
import PostgreSqlDriver from "../../../drivers/postgres/PostgreSqlDriver.js";
|
|
6
|
+
|
|
7
|
+
const sql1 = `SELECT
|
|
8
|
+
"P1"."productID",
|
|
9
|
+
"P1"."name",
|
|
10
|
+
"P1"."ownerID"
|
|
11
|
+
FROM "Products" AS "P1"
|
|
12
|
+
WHERE
|
|
13
|
+
EXISTS (SELECT
|
|
14
|
+
$1 AS "o1"
|
|
15
|
+
FROM "OrderItems" AS "O0"
|
|
16
|
+
WHERE
|
|
17
|
+
("P1"."productID" = "O0"."productID")
|
|
18
|
+
AND ("O0"."productID" = $2)
|
|
19
|
+
)
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
const sql2 = `SELECT
|
|
23
|
+
"P1"."productID","P1"."name","P1"."ownerID"
|
|
24
|
+
FROM "Products" AS "P1"
|
|
25
|
+
WHERE EXISTS (SELECT
|
|
26
|
+
$1 AS "o1"
|
|
27
|
+
FROM "OrderItems" AS "O0"
|
|
28
|
+
WHERE ("P1"."productID" = "O0"."productID") AND ("O0"."productID" = $2)) AND EXISTS (SELECT
|
|
29
|
+
$3 AS "o1"
|
|
30
|
+
FROM "OrderItems" AS "O1"
|
|
31
|
+
WHERE ("P1"."productID" = "O1"."productID") AND ("O1"."amount" > $4))`;
|
|
32
|
+
|
|
33
|
+
const sql3 = `SELECT
|
|
34
|
+
"P1"."productID","P1"."name","P1"."ownerID"
|
|
35
|
+
FROM "Products" AS "P1"
|
|
36
|
+
WHERE EXISTS (SELECT
|
|
37
|
+
$1 AS "o1"
|
|
38
|
+
FROM "OrderItems" AS "O0"
|
|
39
|
+
WHERE ("P1"."productID" = "O0"."productID") AND ("O0"."productID" = $2)) AND EXISTS (SELECT
|
|
40
|
+
$3 AS "o1"
|
|
41
|
+
FROM "OrderItems" AS "O1"
|
|
42
|
+
INNER JOIN "Orders" AS "O2" ON "O1"."orderID" = "O2"."orderID"
|
|
43
|
+
WHERE ("P1"."productID" = "O1"."productID") AND ("O2"."orderDate" > $4))`;
|
|
44
|
+
|
|
45
|
+
const productJoin = `SELECT
|
|
46
|
+
"P1"."productID","P1"."name","P1"."ownerID"
|
|
47
|
+
FROM "Products" AS "P1"
|
|
48
|
+
LEFT JOIN "Users" AS "U0" ON "P1"."ownerID" = "U0"."userID"
|
|
49
|
+
WHERE "U0"."dateCreated" > $1`;
|
|
50
|
+
|
|
51
|
+
export default function() {
|
|
52
|
+
|
|
53
|
+
const context = new ShoppingContext(new PostgreSqlDriver({}));
|
|
54
|
+
let query = context.products.where({ productID: 1 }, (p) => (x) => x.orderItems.some((o) => o.productID === p.productID));
|
|
55
|
+
|
|
56
|
+
let r = query.toQuery();
|
|
57
|
+
assertSqlMatch(sql1, r.text);
|
|
58
|
+
|
|
59
|
+
const old = query;
|
|
60
|
+
query = query.where({ amount: 200}, (p) => (x) => x.orderItems.some((o) => o.amount > p.amount));
|
|
61
|
+
r = query.toQuery();
|
|
62
|
+
assertSqlMatch(sql2, r.text);
|
|
63
|
+
|
|
64
|
+
query = old.where({ date: new Date()}, (p) => (x) => x.orderItems.some((o) => o.order.orderDate > p.date));
|
|
65
|
+
r = query.toQuery();
|
|
66
|
+
assertSqlMatch(sql3, r.text);
|
|
67
|
+
|
|
68
|
+
query = context.products.where({ date: new Date()}, (p) => (x) => x.owner.dateCreated > p.date);
|
|
69
|
+
r = query.toQuery();
|
|
70
|
+
assertSqlMatch(productJoin, r.text);
|
|
71
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import Sql from "../../../sql/Sql.js";
|
|
3
|
+
import QueryCompiler from "../../../compiler/QueryCompiler.js";
|
|
4
|
+
|
|
5
|
+
export default function () {
|
|
6
|
+
|
|
7
|
+
const compiler = QueryCompiler.instance;
|
|
8
|
+
|
|
9
|
+
const name = "Akash";
|
|
10
|
+
|
|
11
|
+
let r = compiler.execute({ name }, (p) => (x) => x.firstName === p.name);
|
|
12
|
+
|
|
13
|
+
assert.equal(`"x"."firstName" = $1`, r.text);
|
|
14
|
+
|
|
15
|
+
r = compiler.execute({ name }, (p) => (x) => x.firstName === p.name && x.lastName !== p.name);
|
|
16
|
+
|
|
17
|
+
assert.equal(`("x"."firstName" = $1) AND ("x"."lastName" <> $2)`, r.text);
|
|
18
|
+
|
|
19
|
+
r = compiler.execute({ name }, (p) => (x) => (x.firstName === p.name || x.middleName === p.name) && x.lastName !== p.name);
|
|
20
|
+
|
|
21
|
+
assert.equal(`(("x"."firstName" = $1) OR ("x"."middleName" = $2)) AND ("x"."lastName" <> $3)`, r.text);
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
const sqlServerCompiler = new QueryCompiler({ quotedLiteral: (x) => `[${x}]`});
|
|
25
|
+
r = sqlServerCompiler.execute({ name }, (p) => (x) => x.firstName === p.name && x.lastName !== p.name);
|
|
26
|
+
|
|
27
|
+
assert.equal(`([x].[firstName] = $1) AND ([x].[lastName] <> $2)`, r.text);
|
|
28
|
+
|
|
29
|
+
r = compiler.execute({ name }, (p) => (x) => ( x.firstName ?? x.lastName ) === p.name);
|
|
30
|
+
|
|
31
|
+
assert.equal(`COALESCE("x"."firstName", "x"."lastName") = $1`, r.text);
|
|
32
|
+
|
|
33
|
+
r = compiler.execute({ name }, (p) => (x) => Sql.text.like(x.firstName, p.name));
|
|
34
|
+
|
|
35
|
+
assert.equal(`("x"."firstName" LIKE $1)`, r.text);
|
|
36
|
+
|
|
37
|
+
r = compiler.execute({ days: 1 }, (p) => (x) => Sql.date.addDays(x.birthDate, p.days));
|
|
38
|
+
|
|
39
|
+
assert.equal(`("x"."birthDate" + ($1 * interval '1 day'))`, r.text);
|
|
40
|
+
|
|
41
|
+
r = compiler.execute({name}, (p) => (x) => Sql.text.startsWith(x.firstName, p.name));
|
|
42
|
+
|
|
43
|
+
assert.equal(`starts_with("x"."firstName", $1)`, r.text);
|
|
44
|
+
assert.equal("Akash", r.values[0]);
|
|
45
|
+
|
|
46
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AssertionError } from "assert";
|
|
2
|
+
|
|
3
|
+
export const trimInternal = (text: string) => {
|
|
4
|
+
let r = "";
|
|
5
|
+
for (const iterator of text) {
|
|
6
|
+
switch(iterator) {
|
|
7
|
+
case "\n":
|
|
8
|
+
case "\t":
|
|
9
|
+
case "\r":
|
|
10
|
+
case " ":
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
r += iterator;
|
|
14
|
+
}
|
|
15
|
+
return r;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const assertSqlMatch = (expected: string, actual: string) => {
|
|
19
|
+
if (trimInternal(expected) === trimInternal(actual)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
throw new AssertionError({ expected, actual, operator: "===" });
|
|
23
|
+
};
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import EntityContext from "../../model/EntityContext.js";
|
|
2
|
+
import Column from "../../decorators/Column.js";
|
|
3
|
+
import ForeignKey from "../../decorators/ForeignKey.js";
|
|
4
|
+
import Table from "../../decorators/Table.js";
|
|
5
|
+
import PostgreSqlDriver from "../../drivers/postgres/PostgreSqlDriver.js";
|
|
6
|
+
import { BaseDriver } from "../../drivers/base/BaseDriver.js";
|
|
7
|
+
import { Console } from "console";
|
|
8
|
+
|
|
9
|
+
export class ShoppingContext extends EntityContext {
|
|
10
|
+
|
|
11
|
+
public categories = this.model.register(Category);
|
|
12
|
+
|
|
13
|
+
public productCategories = this.model.register(ProductCategory);
|
|
14
|
+
|
|
15
|
+
public products = this.model.register(Product);
|
|
16
|
+
|
|
17
|
+
public productPrices = this.model.register(ProductPrice);
|
|
18
|
+
|
|
19
|
+
public orders = this.model.register(Order);
|
|
20
|
+
|
|
21
|
+
public orderItems = this.model.register(OrderItem);
|
|
22
|
+
|
|
23
|
+
public users = this.model.register(User);
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Table("Users")
|
|
28
|
+
export class User {
|
|
29
|
+
|
|
30
|
+
@Column({ key: true , autoGenerate: true, dataType: "BigInt" })
|
|
31
|
+
public userID: number;
|
|
32
|
+
|
|
33
|
+
@Column({})
|
|
34
|
+
public dateCreated: Date;
|
|
35
|
+
|
|
36
|
+
public ownedProducts: Product[];
|
|
37
|
+
|
|
38
|
+
public orders: Order[];
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Table("Categories")
|
|
43
|
+
export class Category {
|
|
44
|
+
|
|
45
|
+
@Column({ key: true, dataType: "Char", length: 200 })
|
|
46
|
+
public categoryID: string;
|
|
47
|
+
|
|
48
|
+
@Column({ length: 200 })
|
|
49
|
+
public name: string;
|
|
50
|
+
|
|
51
|
+
public productCategories: ProductCategory[];
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@Table("Products")
|
|
57
|
+
export class Product {
|
|
58
|
+
|
|
59
|
+
@Column({ key: true, autoGenerate: true, dataType: "BigInt" })
|
|
60
|
+
public productID: number;
|
|
61
|
+
|
|
62
|
+
@Column()
|
|
63
|
+
public name: string;
|
|
64
|
+
|
|
65
|
+
@Column({ nullable: true })
|
|
66
|
+
public ownerID: number;
|
|
67
|
+
|
|
68
|
+
public orderItems: OrderItem[];
|
|
69
|
+
|
|
70
|
+
public prices: ProductPrice[];
|
|
71
|
+
|
|
72
|
+
public categories: ProductCategory[];
|
|
73
|
+
|
|
74
|
+
@ForeignKey({
|
|
75
|
+
key: (product) => product.ownerID,
|
|
76
|
+
related: User,
|
|
77
|
+
relatedProperty: (user) => user.ownedProducts
|
|
78
|
+
})
|
|
79
|
+
public owner: User;
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@Table("ProductCategories")
|
|
85
|
+
export class ProductCategory {
|
|
86
|
+
|
|
87
|
+
@Column({ key: true, dataType: "BigInt", autoGenerate: true })
|
|
88
|
+
public productCategoryID: number;
|
|
89
|
+
|
|
90
|
+
@Column({ dataType: "BigInt" })
|
|
91
|
+
public productID: number;
|
|
92
|
+
|
|
93
|
+
@Column({ dataType: "Char", length: 200})
|
|
94
|
+
public categoryID: string;
|
|
95
|
+
|
|
96
|
+
@ForeignKey({
|
|
97
|
+
key: (pc) => pc.productID,
|
|
98
|
+
related: Product,
|
|
99
|
+
relatedProperty: (c) => c.categories
|
|
100
|
+
})
|
|
101
|
+
public product: Product;
|
|
102
|
+
|
|
103
|
+
@ForeignKey({
|
|
104
|
+
key: (pc) => pc.categoryID,
|
|
105
|
+
related: Category,
|
|
106
|
+
relatedProperty: (c) => c.productCategories
|
|
107
|
+
})
|
|
108
|
+
public category: Category;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@Table("ProductPrices")
|
|
112
|
+
export class ProductPrice {
|
|
113
|
+
|
|
114
|
+
@Column({ key: true, autoGenerate: true, dataType: "BigInt"})
|
|
115
|
+
public priceID: number;
|
|
116
|
+
|
|
117
|
+
@Column()
|
|
118
|
+
public active: boolean;
|
|
119
|
+
|
|
120
|
+
@Column()
|
|
121
|
+
public startDate: Date;
|
|
122
|
+
|
|
123
|
+
@Column({ nullable: true})
|
|
124
|
+
public endDate?: Date;
|
|
125
|
+
|
|
126
|
+
public amount: number;
|
|
127
|
+
|
|
128
|
+
public productID: number;
|
|
129
|
+
|
|
130
|
+
@ForeignKey({
|
|
131
|
+
key: (productPrice) => productPrice.productID,
|
|
132
|
+
related: Product,
|
|
133
|
+
relatedProperty: (product) => product.prices
|
|
134
|
+
})
|
|
135
|
+
public product: Product;
|
|
136
|
+
|
|
137
|
+
public orderItems: OrderItem[];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@Table("Orders")
|
|
141
|
+
export class Order {
|
|
142
|
+
|
|
143
|
+
@Column({ key: true, autoGenerate: true, dataType: "BigInt"})
|
|
144
|
+
public orderID: number;
|
|
145
|
+
|
|
146
|
+
@Column()
|
|
147
|
+
public orderDate: Date;
|
|
148
|
+
|
|
149
|
+
@Column()
|
|
150
|
+
public customerID: number;
|
|
151
|
+
|
|
152
|
+
public orderItems: OrderItem[];
|
|
153
|
+
|
|
154
|
+
@ForeignKey({
|
|
155
|
+
key: (order) => order.customerID,
|
|
156
|
+
related: User,
|
|
157
|
+
relatedProperty: (user) => user.orders
|
|
158
|
+
})
|
|
159
|
+
public customer: User;
|
|
160
|
+
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@Table("OrderItems")
|
|
164
|
+
export class OrderItem {
|
|
165
|
+
|
|
166
|
+
@Column({ key: true, autoGenerate: true, dataType: "BigInt"})
|
|
167
|
+
public orderItemID: number;
|
|
168
|
+
|
|
169
|
+
@Column()
|
|
170
|
+
public orderID: number;
|
|
171
|
+
|
|
172
|
+
@Column()
|
|
173
|
+
public productID: number;
|
|
174
|
+
|
|
175
|
+
@Column()
|
|
176
|
+
public priceID: number;
|
|
177
|
+
|
|
178
|
+
@Column()
|
|
179
|
+
public amount: number;
|
|
180
|
+
|
|
181
|
+
@ForeignKey({
|
|
182
|
+
key: (orderItem) => orderItem.orderID,
|
|
183
|
+
related: Order,
|
|
184
|
+
relatedProperty: (order) => order.orderItems
|
|
185
|
+
})
|
|
186
|
+
public order: Order;
|
|
187
|
+
|
|
188
|
+
@ForeignKey({
|
|
189
|
+
key: (orderItem) => orderItem.productID,
|
|
190
|
+
related: Product,
|
|
191
|
+
relatedProperty:(product) => product.orderItems
|
|
192
|
+
})
|
|
193
|
+
public product: Product;
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@ForeignKey({
|
|
197
|
+
key: (orderItem) => orderItem.priceID,
|
|
198
|
+
related: ProductPrice,
|
|
199
|
+
relatedProperty: (productPrice) => productPrice.orderItems
|
|
200
|
+
})
|
|
201
|
+
public productPrice: ProductPrice;
|
|
202
|
+
|
|
203
|
+
}
|