@entity-access/entity-access 1.0.5 → 1.0.6

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.
@@ -2,21 +2,27 @@ import { parseExpression } from "@babel/parser";
2
2
  import { ArrowFunctionExpression, BinaryExpression, CallExpression, CoalesceExpression, ConditionalExpression, Constant, Expression, ExpressionAs, Identifier, MemberExpression, NewObjectExpression, NullExpression, NumberLiteral, ParameterExpression, QuotedLiteral, StringLiteral, TemplateLiteral } from "../ast/Expressions.js";
3
3
  import { BabelVisitor } from "./BabelVisitor.js";
4
4
  import * as bpe from "@babel/types";
5
- import EntityType from "../../entity-query/EntityType.js";
6
- import { EntitySource } from "../../model/EntitySource.js";
7
5
  import Restructure from "./Restructure.js";
8
6
  import { NotSupportedError } from "./NotSupportedError.js";
7
+ import TimedCache from "../../common/cache/TimedCache.js";
9
8
 
10
9
  type IQueryFragment = string | { name?: string, value?: any };
11
- type IQueryFragments = IQueryFragment[];
10
+
11
+ const parsedCache = new TimedCache<string, bpe.Node>();
12
12
 
13
13
  export default class ArrowToExpression extends BabelVisitor<Expression> {
14
14
 
15
15
  public static transform(fx: (p: any) => (x: any) => any, target?: ParameterExpression) {
16
+ const key = fx.toString();
17
+ const node = parsedCache.getOrCreate(key, fx, (k, f) => {
18
+ const rs = new Restructure();
19
+ return rs.visit(parseExpression(f.toString()));
20
+ });
21
+ return this.transformUncached(node, target);
22
+ }
16
23
 
17
- const rs = new Restructure();
24
+ private static transformUncached(node: bpe.Node, target?: ParameterExpression) {
18
25
 
19
- const node = rs.visit(parseExpression(fx.toString()));
20
26
  if (node.type !== "ArrowFunctionExpression") {
21
27
  throw new Error("Expecting an arrow function");
22
28
  }
@@ -66,7 +72,12 @@ export default class ArrowToExpression extends BabelVisitor<Expression> {
66
72
  for (const iterator of params) {
67
73
  this.targetStack.set(iterator.name, iterator);
68
74
  }
69
- this.targetStack.set(targetName ?? target.name, target);
75
+ if (targetName) {
76
+ this.targetStack.set(targetName, target);
77
+ }
78
+ if (target?.name) {
79
+ this.targetStack.set(target.name, target);
80
+ }
70
81
  }
71
82
 
72
83
  visitBigIntLiteral({ value }: bpe.BigIntLiteral) {
@@ -129,6 +140,10 @@ export default class ArrowToExpression extends BabelVisitor<Expression> {
129
140
  return BinaryExpression.create({ left, operator, right });
130
141
  }
131
142
 
143
+ visitArrayExpression(node: bpe.ArrayExpression): Expression {
144
+ return Expression.array(node.elements.map((e) => this.visit(e)));
145
+ }
146
+
132
147
  visitBinaryExpression(node: bpe.BinaryExpression) {
133
148
  let operator = node.operator as string;
134
149
  switch(node.operator) {
@@ -194,7 +209,7 @@ export default class ArrowToExpression extends BabelVisitor<Expression> {
194
209
  throw new NotSupportedError();
195
210
  }
196
211
  names.push(iterator.name);
197
- const p = ParameterExpression.create({ value: iterator.name });
212
+ const p = ParameterExpression.create({ name: iterator.name });
198
213
  this.targetStack.set(iterator.name, p);
199
214
  params.push(p);
200
215
  }
@@ -39,11 +39,16 @@ export abstract class BabelVisitor<T> {
39
39
  return this.visitObjectProperty(node);
40
40
  case "TemplateElement":
41
41
  return this.visitTemplateElement(node);
42
+ case "ArrayExpression":
43
+ return this.visitArrayExpression(node);
42
44
  case "RegExpLiteral":
43
45
  default:
44
46
  throw new Error(`Translation from ${node.type} not supported`);
45
47
  }
46
48
  }
49
+ visitArrayExpression(node: bpe.ArrayExpression): T {
50
+ throw new Error("Method not implemented.");
51
+ }
47
52
  abstract visitTemplateElement(node: bpe.TemplateElement): T;
48
53
  visitObjectProperty(node: bpe.ObjectProperty): T {
49
54
  throw new Error("Method not implemented.");
@@ -5,41 +5,64 @@ import { assertSqlMatch, trimInternal } from "../trimInternal.js";
5
5
  import PostgreSqlDriver from "../../../drivers/postgres/PostgreSqlDriver.js";
6
6
 
7
7
  const sql1 = `SELECT
8
- "P1"."productID","P1"."name","P1"."ownerID","P1"."status"
9
- FROM "Products" AS "P1"
8
+ "p1"."productID",
9
+ "p1"."name",
10
+ "p1"."ownerID",
11
+ "p1"."status"
12
+ FROM "Products" AS "p1"
10
13
  WHERE EXISTS (SELECT
11
14
  1
12
- FROM "OrderItems" AS "O0"
13
- WHERE ("P1"."productID" = "O0"."productID") AND ("O0"."productID" = $1))`;
15
+ FROM "OrderItems" AS "o"
16
+ WHERE ("p1"."productID" = "o"."productID") AND ("o"."productID" = $1))`;
14
17
 
15
18
  const sql2 = `SELECT
16
- "P1"."productID","P1"."name","P1"."ownerID","P1"."status"
17
- FROM "Products" AS "P1"
19
+ "p1"."productID",
20
+ "p1"."name",
21
+ "p1"."ownerID",
22
+ "p1"."status"
23
+ FROM "Products" AS "p1"
18
24
  WHERE EXISTS (SELECT
19
25
  1
20
- FROM "OrderItems" AS "O1"
21
- WHERE ("P1"."productID" = "O1"."productID") AND ("O1"."productID" = $1)) AND EXISTS (SELECT
26
+ FROM "OrderItems" AS "o"
27
+ WHERE ("p1"."productID" = "o"."productID") AND ("o"."productID" = $1)) AND EXISTS (SELECT
22
28
  1
23
- FROM "OrderItems" AS "O2"
24
- WHERE ("P1"."productID" = "O2"."productID") AND ("O2"."amount" > $2))`;
29
+ FROM "OrderItems" AS "o1"
30
+ WHERE ("p1"."productID" = "o1"."productID") AND ("o1"."amount" > $2))`;
25
31
 
26
32
  const sql3 = `SELECT
27
- "P1"."productID","P1"."name","P1"."ownerID","P1"."status"
28
- FROM "Products" AS "P1"
33
+ "p1"."productID",
34
+ "p1"."name",
35
+ "p1"."ownerID",
36
+ "p1"."status"
37
+ FROM "Products" AS "p1"
29
38
  WHERE EXISTS (SELECT
30
39
  1
31
- FROM "OrderItems" AS "O1"
32
- WHERE ("P1"."productID" = "O1"."productID") AND ("O1"."productID" = $1)) AND EXISTS (SELECT
40
+ FROM "OrderItems" AS "o"
41
+ WHERE ("p1"."productID" = "o"."productID") AND ("o"."productID" = $1)) AND EXISTS (SELECT
33
42
  1
34
- FROM "OrderItems" AS "O2"
35
- INNER JOIN "Orders" AS "O0" ON "O2"."orderID" = "O0"."orderID"
36
- WHERE ("P1"."productID" = "O2"."productID") AND ("O0"."orderDate" > $2))`;
43
+ FROM "OrderItems" AS "o1"
44
+ INNER JOIN "Orders" AS "o2" ON "o1"."orderID" = "o2"."orderID"
45
+ WHERE ("p1"."productID" = "o1"."productID") AND ("o2"."orderDate" > $2))`;
37
46
 
38
47
  const productJoin = `SELECT
39
- "P1"."productID","P1"."name","P1"."ownerID","P1"."status"
40
- FROM "Products" AS "P1"
41
- LEFT JOIN "Users" AS "U0" ON "P1"."ownerID" = "U0"."userID"
42
- WHERE "U0"."dateCreated" > $1`;
48
+ "p1"."productID",
49
+ "p1"."name",
50
+ "p1"."ownerID",
51
+ "p1"."status"
52
+ FROM "Products" AS "p1"
53
+ LEFT JOIN "Users" AS "u" ON "p1"."ownerID" = "u"."userID"
54
+ WHERE "u"."dateCreated" > $1`;
55
+
56
+
57
+ const join2 = `SELECT
58
+ "o1"."orderItemID",
59
+ "o1"."orderID",
60
+ "o1"."productID",
61
+ "o1"."priceID",
62
+ "o1"."amount"
63
+ FROM "OrderItems" AS "o1"
64
+ INNER JOIN "Products" AS "p" ON "o1"."productID" = "p"."productID"
65
+ WHERE ("o1"."productID" = $1) OR ("p"."ownerID" = $2)`;
43
66
 
44
67
  export default function() {
45
68
 
@@ -61,4 +84,8 @@ export default function() {
61
84
  query = context.products.where({ date: new Date()}, (p) => (x) => x.owner.dateCreated > p.date);
62
85
  r = query.toQuery();
63
86
  assertSqlMatch(productJoin, r.text);
87
+
88
+ const q = context.orderItems.where({ o: 1, owner: 1}, (p) => (x) => x.productID === p.o || x.product.ownerID === p.owner);
89
+ r = q.toQuery();
90
+ assertSqlMatch(join2, r.text);
64
91
  }
@@ -1,6 +1,6 @@
1
1
  import { IClassOf } from "../../decorators/IClassOf.js";
2
2
  import { BaseDriver } from "../../drivers/base/BaseDriver.js";
3
- import { ShoppingContext, statusPublished } from "./ShoppingContext.js";
3
+ import { ShoppingContext, User, statusPublished } from "./ShoppingContext.js";
4
4
 
5
5
  const status = statusPublished;
6
6
 
@@ -28,11 +28,16 @@ export async function createContext(driver: BaseDriver) {
28
28
  async function seed(context: ShoppingContext) {
29
29
  const now = new Date();
30
30
 
31
- addHeadPhones(context, now);
31
+ // add admin user...
32
+ context.users.add({ dateCreated: new Date()});
33
+ // add seller
34
+ const seller = context.users.add({ dateCreated: new Date()});
32
35
 
33
- const clothes = addMaleClothes(context);
36
+ addHeadPhones(context, now, seller);
34
37
 
35
- addFemaleClothes(context);
38
+ const clothes = addMaleClothes(context, seller);
39
+
40
+ addFemaleClothes(context, seller);
36
41
 
37
42
  await context.saveChanges();
38
43
 
@@ -58,7 +63,7 @@ async function seed(context: ShoppingContext) {
58
63
  await context.saveChanges();
59
64
  }
60
65
 
61
- function addFemaleClothes(context: ShoppingContext) {
66
+ function addFemaleClothes(context: ShoppingContext, owner: User) {
62
67
  const category = context.categories.add({
63
68
  name: "Female Clothes",
64
69
  categoryID: "clothes/female"
@@ -69,6 +74,7 @@ function addFemaleClothes(context: ShoppingContext) {
69
74
 
70
75
  context.products.add({
71
76
  name: "White T-Shirt",
77
+ owner,
72
78
  status,
73
79
  categories: [
74
80
  context.productCategories.add({
@@ -87,6 +93,7 @@ function addFemaleClothes(context: ShoppingContext) {
87
93
  context.products.add({
88
94
  name: "Red T-Shirt",
89
95
  status,
96
+ owner,
90
97
  categories: [
91
98
  context.productCategories.add({
92
99
  category
@@ -104,6 +111,7 @@ function addFemaleClothes(context: ShoppingContext) {
104
111
  context.products.add({
105
112
  name: "Blue T-Shirt",
106
113
  status,
114
+ owner,
107
115
  categories: [
108
116
  context.productCategories.add({
109
117
  category
@@ -121,6 +129,7 @@ function addFemaleClothes(context: ShoppingContext) {
121
129
  context.products.add({
122
130
  name: "Pink T-Shirt",
123
131
  status,
132
+ owner,
124
133
  categories: [
125
134
  context.productCategories.add({
126
135
  category
@@ -136,7 +145,7 @@ function addFemaleClothes(context: ShoppingContext) {
136
145
  });
137
146
  }
138
147
 
139
- function addMaleClothes(context: ShoppingContext) {
148
+ function addMaleClothes(context: ShoppingContext, owner: User) {
140
149
  const category = context.categories.add({
141
150
  name: "Male Clothes",
142
151
  categoryID: "clothes/male"
@@ -148,6 +157,7 @@ function addMaleClothes(context: ShoppingContext) {
148
157
  return [context.products.add({
149
158
  name: "White T-Shirt",
150
159
  status,
160
+ owner,
151
161
  categories: [
152
162
  context.productCategories.add({
153
163
  category
@@ -164,6 +174,7 @@ function addMaleClothes(context: ShoppingContext) {
164
174
  context.products.add({
165
175
  name: "Red T-Shirt",
166
176
  status,
177
+ owner,
167
178
  categories: [
168
179
  context.productCategories.add({
169
180
  category
@@ -180,6 +191,7 @@ function addMaleClothes(context: ShoppingContext) {
180
191
  context.products.add({
181
192
  name: "Blue T-Shirt",
182
193
  status,
194
+ owner,
183
195
  categories: [
184
196
  context.productCategories.add({
185
197
  category
@@ -196,6 +208,7 @@ function addMaleClothes(context: ShoppingContext) {
196
208
  context.products.add({
197
209
  name: "Pink T-Shirt",
198
210
  status,
211
+ owner,
199
212
  categories: [
200
213
  context.productCategories.add({
201
214
  category
@@ -212,7 +225,7 @@ function addMaleClothes(context: ShoppingContext) {
212
225
  }
213
226
 
214
227
  export const headPhoneCategory = "head-phones";
215
- function addHeadPhones(context: ShoppingContext, now: Date) {
228
+ function addHeadPhones(context: ShoppingContext, now: Date, owner: User) {
216
229
  const category = context.categories.add({
217
230
  name: "Headphones",
218
231
  categoryID: headPhoneCategory
@@ -223,6 +236,7 @@ function addHeadPhones(context: ShoppingContext, now: Date) {
223
236
 
224
237
  context.products.add({
225
238
  name: "Jabber Head Phones",
239
+ owner,
226
240
  status,
227
241
  categories: [
228
242
  context.productCategories.add({
@@ -240,6 +254,7 @@ function addHeadPhones(context: ShoppingContext, now: Date) {
240
254
 
241
255
  context.products.add({
242
256
  name: "Sony Head Phones",
257
+ owner,
243
258
  status,
244
259
  categories: [
245
260
  context.productCategories.add({
@@ -258,6 +273,7 @@ function addHeadPhones(context: ShoppingContext, now: Date) {
258
273
  context.products.add({
259
274
  name: "Sony Head Phones Black",
260
275
  status,
276
+ owner,
261
277
  categories: [
262
278
  context.productCategories.add({
263
279
  category
@@ -274,6 +290,7 @@ function addHeadPhones(context: ShoppingContext, now: Date) {
274
290
 
275
291
  context.products.add({
276
292
  name: "Sony Head Phones Blue",
293
+ owner,
277
294
  status,
278
295
  categories: [
279
296
  context.productCategories.add({
@@ -292,6 +309,7 @@ function addHeadPhones(context: ShoppingContext, now: Date) {
292
309
  context.products.add({
293
310
  name: "Jabber Head Phones Black",
294
311
  status,
312
+ owner,
295
313
  categories: [
296
314
  context.productCategories.add({
297
315
  category
@@ -308,6 +326,7 @@ function addHeadPhones(context: ShoppingContext, now: Date) {
308
326
 
309
327
  context.products.add({
310
328
  name: "Jabber Head Phones Blue",
329
+ owner,
311
330
  status,
312
331
  categories: [
313
332
  context.productCategories.add({
@@ -26,7 +26,13 @@ export class OrderEvents extends EntityEvents<Order> {
26
26
  }
27
27
  const { userID } = this.user;
28
28
  // user can only modify placed orders
29
- return query.where({ userID }, (p) => (x) => x.customerID === p.userID);
29
+ return query.where({ userID }, (p) => (x) => x.customerID === p.userID || x.orderItems.some((item) => item.product.ownerID === p.userID));
30
+ }
31
+
32
+ onForeignKeyFilter(filter: ForeignKeyFilter<Order>): IEntityQuery<any> {
33
+ if (filter.is((x) => x.customer)) {
34
+ return filter.read();
35
+ }
30
36
  }
31
37
  }
32
38
 
@@ -52,7 +58,7 @@ export class OrderItemEvents extends EntityEvents<OrderItem> {
52
58
  }
53
59
  const { userID } = this.user;
54
60
  // user can only modify placed orders
55
- return query.where({ userID }, (p) => (x) => x.order.customerID === p.userID);
61
+ return query.where({ userID }, (p) => (x) => x.order.customerID === p.userID || x.product.ownerID === p.userID);
56
62
  }
57
63
 
58
64
  onForeignKeyFilter(filter: ForeignKeyFilter<OrderItem>): IEntityQuery<any> {
@@ -13,6 +13,16 @@ export class UserEvents extends EntityEvents<User> {
13
13
  return null;
14
14
  }
15
15
  const { userID } = this.user;
16
- return query.where({ userID }, (p) => (x) => x.userID === p.userID);
16
+ return query.where({ userID }, (p) => (x) => x.userID === p.userID
17
+ || x.orders.some(
18
+ (op) => op.orderItems.some((oi) => oi.product.ownerID === p.userID)));
19
+ }
20
+
21
+ modify(query: IEntityQuery<User>): IEntityQuery<User> {
22
+ if (this.user.admin) {
23
+ return null;
24
+ }
25
+ const { userID } = this.user;
26
+ return query.where({ userID}, (p) => (x) => x.userID === p.userID);
17
27
  }
18
28
  }
@@ -0,0 +1,19 @@
1
+ import assert from "assert";
2
+ import { TestConfig } from "../../TestConfig.js";
3
+ import { createContext } from "../../model/createContext.js";
4
+ import { ShoppingContext } from "../../model/ShoppingContext.js";
5
+ import Logger from "../../../common/Logger.js";
6
+
7
+ export default async function (this: TestConfig) {
8
+
9
+ const old = await createContext(this.driver);
10
+
11
+ const context = new ShoppingContext(old.driver, void 0, Logger.instance);
12
+ const order = await context.orders.all()
13
+ .where({id: 0}, (p) => (x) => x.orderID > p.id)
14
+ .include((x) => x.orderItems.forEach((oi) => oi.productPrice.product))
15
+ .first();
16
+
17
+ assert.notEqual(null, order);
18
+
19
+ }
@@ -21,8 +21,32 @@ export default async function(this: TestConfig) {
21
21
  } catch(error) {
22
22
 
23
23
  }
24
+
25
+ await getNewOrders.call(this);
26
+
24
27
  }
25
28
 
29
+ async function getNewOrders(this: TestConfig) {
30
+ const scope = ServiceProvider.global.createScope();
31
+ try {
32
+ const user = new UserInfo();
33
+ user.userID = 2;
34
+ scope.add(Logger, Logger.instance);
35
+ scope.add(BaseDriver, this.driver);
36
+ scope.add(UserInfo, user);
37
+ scope.add(ContextEvents, new ShoppingContextEvents());
38
+ const context = scope.create(ShoppingContext);
39
+ context.verifyFilters = true;
40
+
41
+ const order = await context.orders.all().first();
42
+
43
+ order.orderDate = new Date();
44
+ await context.saveChanges();
45
+
46
+ } finally {
47
+ scope.dispose();
48
+ }
49
+ }
26
50
 
27
51
  async function addNewOrder(this: TestConfig, customer: User, userID?) {
28
52
  const scope = ServiceProvider.global.createScope();
@@ -54,6 +78,15 @@ async function addNewOrder(this: TestConfig, customer: User, userID?) {
54
78
 
55
79
  await context.saveChanges();
56
80
 
81
+ // lets filter the orders
82
+
83
+ const f = context.orders.filtered();
84
+ const myOrders = await f.count();
85
+ assert.equal(1, myOrders);
86
+
87
+ const all = await context.orders.all().count();
88
+ assert.notEqual(all, myOrders);
89
+
57
90
  } finally {
58
91
  scope.dispose();
59
92
  }