@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.
Files changed (70) hide show
  1. package/.eslintrc.cjs +234 -0
  2. package/.github/workflows/node.yml +44 -0
  3. package/.github/workflows/npm-publish.yml +33 -0
  4. package/.vscode/launch.json +20 -0
  5. package/.vscode/settings.json +39 -0
  6. package/LICENSE +201 -0
  7. package/README.md +247 -0
  8. package/package.json +39 -0
  9. package/src/TestRunner.ts +2 -0
  10. package/src/common/cache/InstanceCache.ts +14 -0
  11. package/src/common/cache/TimedCache.ts +74 -0
  12. package/src/common/symbols/symbols.ts +2 -0
  13. package/src/common/usingAsync.ts +24 -0
  14. package/src/compiler/ISqlHelpers.ts +30 -0
  15. package/src/compiler/QueryCompiler.ts +88 -0
  16. package/src/compiler/postgres/PostgreSqlMethodTransformer.ts +83 -0
  17. package/src/compiler/sql-server/SqlServerSqlMethodTransformer.ts +83 -0
  18. package/src/decorators/Column.ts +46 -0
  19. package/src/decorators/ForeignKey.ts +42 -0
  20. package/src/decorators/IClassOf.ts +1 -0
  21. package/src/decorators/IColumn.ts +70 -0
  22. package/src/decorators/ISqlType.ts +70 -0
  23. package/src/decorators/SchemaRegistry.ts +19 -0
  24. package/src/decorators/Table.ts +18 -0
  25. package/src/decorators/parser/MemberParser.ts +8 -0
  26. package/src/drivers/base/BaseDriver.ts +134 -0
  27. package/src/drivers/postgres/PostgreSqlDriver.ts +178 -0
  28. package/src/drivers/sql-server/ExpressionToSqlServer.ts +78 -0
  29. package/src/drivers/sql-server/SqlServerDriver.ts +215 -0
  30. package/src/drivers/sql-server/SqlServerLiteral.ts +12 -0
  31. package/src/drivers/sql-server/SqlServerQueryCompiler.ts +25 -0
  32. package/src/entity-query/EntityType.ts +116 -0
  33. package/src/migrations/Migrations.ts +17 -0
  34. package/src/migrations/postgres/PostgresAutomaticMigrations.ts +97 -0
  35. package/src/migrations/postgres/PostgresMigrations.ts +56 -0
  36. package/src/migrations/sql-server/SqlServerAutomaticMigrations.ts +102 -0
  37. package/src/migrations/sql-server/SqlServerMigrations.ts +60 -0
  38. package/src/model/ChangeEntry.ts +154 -0
  39. package/src/model/ChangeSet.ts +88 -0
  40. package/src/model/EntityContext.ts +48 -0
  41. package/src/model/EntityModel.ts +27 -0
  42. package/src/model/EntityQuery.ts +152 -0
  43. package/src/model/EntitySchema.ts +21 -0
  44. package/src/model/EntitySource.ts +98 -0
  45. package/src/model/IFilterWithParameter.ts +38 -0
  46. package/src/model/IdentityService.ts +23 -0
  47. package/src/model/SourceExpression.ts +137 -0
  48. package/src/query/Query.ts +158 -0
  49. package/src/query/ast/ExpressionToSql.ts +348 -0
  50. package/src/query/ast/Expressions.ts +294 -0
  51. package/src/query/ast/IStringTransformer.ts +74 -0
  52. package/src/query/ast/SqlLiteral.ts +25 -0
  53. package/src/query/ast/Visitor.ts +159 -0
  54. package/src/query/parser/ArrowToExpression.ts +160 -0
  55. package/src/query/parser/BabelVisitor.ts +86 -0
  56. package/src/sql/ISql.ts +31 -0
  57. package/src/sql/Sql.ts +4 -0
  58. package/src/tests/TestConfig.ts +6 -0
  59. package/src/tests/db-tests/tests/select-items.ts +37 -0
  60. package/src/tests/db-tests/tests/update-items.ts +17 -0
  61. package/src/tests/drivers/postgres/connection-test.ts +29 -0
  62. package/src/tests/drivers/sql-server/sql-server-test.ts +9 -0
  63. package/src/tests/expressions/left-joins/child-joins.ts +71 -0
  64. package/src/tests/expressions/simple/parse-arrow.ts +46 -0
  65. package/src/tests/expressions/trimInternal.ts +23 -0
  66. package/src/tests/model/ShoppingContext.ts +203 -0
  67. package/src/tests/model/createContext.ts +294 -0
  68. package/src/tests/query/combine.ts +28 -0
  69. package/test.js +107 -0
  70. 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
+ }
@@ -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,4 @@
1
+ import { ISql } from "./ISql.js";
2
+
3
+ const Sql: ISql = void 0;
4
+ export default Sql;
@@ -0,0 +1,6 @@
1
+ import { BaseDriver } from "../drivers/base/BaseDriver.js";
2
+
3
+ export class TestConfig {
4
+ db:boolean;
5
+ driver: BaseDriver;
6
+ }
@@ -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
+ }