@entity-access/entity-access 1.0.287 → 1.0.289

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 (66) hide show
  1. package/dist/decorators/CheckConstraint.d.ts +4 -0
  2. package/dist/decorators/CheckConstraint.d.ts.map +1 -0
  3. package/dist/decorators/CheckConstraint.js +8 -0
  4. package/dist/decorators/CheckConstraint.js.map +1 -0
  5. package/dist/decorators/ICheckConstraint.d.ts +5 -0
  6. package/dist/decorators/ICheckConstraint.d.ts.map +1 -0
  7. package/dist/decorators/ICheckConstraint.js +3 -0
  8. package/dist/decorators/ICheckConstraint.js.map +1 -0
  9. package/dist/entity-query/EntityType.d.ts +2 -0
  10. package/dist/entity-query/EntityType.d.ts.map +1 -1
  11. package/dist/entity-query/EntityType.js +1 -0
  12. package/dist/entity-query/EntityType.js.map +1 -1
  13. package/dist/migrations/Migrations.d.ts +2 -0
  14. package/dist/migrations/Migrations.d.ts.map +1 -1
  15. package/dist/migrations/Migrations.js +8 -0
  16. package/dist/migrations/Migrations.js.map +1 -1
  17. package/dist/migrations/postgres/PostgresAutomaticMigrations.d.ts +3 -0
  18. package/dist/migrations/postgres/PostgresAutomaticMigrations.d.ts.map +1 -1
  19. package/dist/migrations/postgres/PostgresAutomaticMigrations.js +34 -9
  20. package/dist/migrations/postgres/PostgresAutomaticMigrations.js.map +1 -1
  21. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.d.ts +3 -0
  22. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.d.ts.map +1 -1
  23. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js +33 -9
  24. package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js.map +1 -1
  25. package/dist/model/EntityModel.d.ts.map +1 -1
  26. package/dist/model/EntityModel.js +1 -0
  27. package/dist/model/EntityModel.js.map +1 -1
  28. package/dist/model/EntitySource.js +1 -1
  29. package/dist/model/EntitySource.js.map +1 -1
  30. package/dist/model/verification/VerificationSession.js +1 -1
  31. package/dist/model/verification/VerificationSession.js.map +1 -1
  32. package/dist/tests/db-tests/tests/check-constraint-test.d.ts +3 -0
  33. package/dist/tests/db-tests/tests/check-constraint-test.d.ts.map +1 -0
  34. package/dist/tests/db-tests/tests/check-constraint-test.js +28 -0
  35. package/dist/tests/db-tests/tests/check-constraint-test.js.map +1 -0
  36. package/dist/tests/eternity/throttle-tests.d.ts +3 -0
  37. package/dist/tests/eternity/throttle-tests.d.ts.map +1 -0
  38. package/dist/tests/eternity/throttle-tests.js +86 -0
  39. package/dist/tests/eternity/throttle-tests.js.map +1 -0
  40. package/dist/tests/model/ShoppingContext.d.ts.map +1 -1
  41. package/dist/tests/model/ShoppingContext.js +6 -1
  42. package/dist/tests/model/ShoppingContext.js.map +1 -1
  43. package/dist/tsconfig.tsbuildinfo +1 -1
  44. package/dist/workflows/WorkflowContext.d.ts +5 -1
  45. package/dist/workflows/WorkflowContext.d.ts.map +1 -1
  46. package/dist/workflows/WorkflowContext.js +11 -3
  47. package/dist/workflows/WorkflowContext.js.map +1 -1
  48. package/dist/workflows/WorkflowStorage.d.ts +5 -0
  49. package/dist/workflows/WorkflowStorage.d.ts.map +1 -1
  50. package/dist/workflows/WorkflowStorage.js +28 -0
  51. package/dist/workflows/WorkflowStorage.js.map +1 -1
  52. package/package.json +1 -1
  53. package/src/decorators/CheckConstraint.ts +10 -0
  54. package/src/decorators/ICheckConstraint.ts +4 -0
  55. package/src/entity-query/EntityType.ts +3 -0
  56. package/src/migrations/Migrations.ts +11 -0
  57. package/src/migrations/postgres/PostgresAutomaticMigrations.ts +43 -9
  58. package/src/migrations/sql-server/SqlServerAutomaticMigrations.ts +43 -10
  59. package/src/model/EntityModel.ts +1 -0
  60. package/src/model/EntitySource.ts +1 -1
  61. package/src/model/verification/VerificationSession.ts +1 -1
  62. package/src/tests/db-tests/tests/check-constraint-test.ts +35 -0
  63. package/src/tests/eternity/throttle-tests.ts +100 -0
  64. package/src/tests/model/ShoppingContext.ts +5 -0
  65. package/src/workflows/WorkflowContext.ts +20 -3
  66. package/src/workflows/WorkflowStorage.ts +31 -0
@@ -0,0 +1,100 @@
1
+ import assert from "assert";
2
+ import Inject, { Register, RegisterSingleton, ServiceProvider } from "../../di/di.js";
3
+ import WorkflowContext from "../../workflows/WorkflowContext.js";
4
+ import Workflow, { Activity } from "../../workflows/Workflow.js";
5
+ import WorkflowClock from "../../workflows/WorkflowClock.js";
6
+ import DateTime from "../../types/DateTime.js";
7
+ import { TestConfig } from "../TestConfig.js";
8
+ import { BaseDriver } from "../../drivers/base/BaseDriver.js";
9
+ import WorkflowStorage from "../../workflows/WorkflowStorage.js";
10
+ import TimeSpan from "../../types/TimeSpan.js";
11
+ import sleep from "../../common/sleep.js";
12
+
13
+ class MockClock extends WorkflowClock {
14
+
15
+ public get utcNow(): DateTime {
16
+ return this.time;
17
+ }
18
+
19
+ public set utcNow(v: DateTime) {
20
+ this.time = v;
21
+ }
22
+
23
+ private time: DateTime = DateTime.now;
24
+
25
+ public add(ts: TimeSpan) {
26
+ this.time = this.time.add(ts);
27
+ return this;
28
+ }
29
+ }
30
+
31
+ @RegisterSingleton
32
+ class Mailer {
33
+
34
+ public items: any[] = [];
35
+ }
36
+
37
+ class SendWorkflow extends Workflow<string, string> {
38
+
39
+ public async run(): Promise<any> {
40
+
41
+ await this.delay(TimeSpan.fromHours(1));
42
+
43
+ await this.sendMail("a", "b", "c");
44
+ return "1";
45
+ }
46
+
47
+ @Activity
48
+ public async sendMail(
49
+ from: string,
50
+ to: string,
51
+ message: string,
52
+ @Inject logger?: Mailer) {
53
+ await sleep(10);
54
+ logger.items.push({ from, to, message });
55
+ }
56
+
57
+ }
58
+
59
+ export default async function (this: TestConfig) {
60
+
61
+ const mockClock = new MockClock();
62
+ const mailer = new Mailer();
63
+
64
+ const scope = new ServiceProvider();
65
+ scope.add(WorkflowClock, mockClock);
66
+ scope.add(BaseDriver, this.driver);
67
+ const storage = new WorkflowStorage(this.driver, mockClock);
68
+ scope.add(Mailer, mailer);
69
+ scope.add(WorkflowStorage, storage);
70
+
71
+ const c = new WorkflowContext(storage);
72
+ scope.add(WorkflowContext, c);
73
+
74
+ // this is an important step
75
+ c.register(SendWorkflow);
76
+
77
+ await storage.seed();
78
+
79
+ const id = await c.queue(SendWorkflow, "a", {
80
+ throttle: {
81
+ group: "a",
82
+ maxPerSecond: 1/15
83
+ }
84
+ });
85
+
86
+ const result = await storage.getWorkflow(id);
87
+
88
+ const next = await c.queue(SendWorkflow, "b", {
89
+ throttle: {
90
+ group: "a",
91
+ maxPerSecond: 1/15
92
+ }
93
+ });
94
+
95
+ const resultNext = await storage.getWorkflow(next);
96
+ const span = DateTime.from(resultNext.eta).diff(DateTime.from(result.eta));
97
+ assert(span.totalSeconds > 14);
98
+
99
+ // throw new Error("Preserve");
100
+ }
@@ -7,6 +7,7 @@ import DateTime from "../../types/DateTime.js";
7
7
  import { UserFile } from "./UseFile.js";
8
8
  import Sql from "../../sql/Sql.js";
9
9
  import MultiForeignKeys from "../../decorators/ForeignKey.js";
10
+ import CheckConstraint from "../../decorators/CheckConstraint.js";
10
11
 
11
12
  export const statusPublished = "published";
12
13
 
@@ -285,6 +286,10 @@ export class ProductCategory {
285
286
  }
286
287
 
287
288
  @Table("ProductPrices")
289
+ @CheckConstraint({
290
+ name: "CX_ProductPrices_PositivePrice",
291
+ filter: (x) => x.amount > 0
292
+ })
288
293
  export class ProductPrice {
289
294
 
290
295
  @Column({ key: true, generated: "identity", dataType: "BigInt"})
@@ -138,6 +138,10 @@ export interface IWorkflowQueueParameter {
138
138
  eta?: DateTime;
139
139
  parentID?: string;
140
140
  taskGroup?: string;
141
+ throttle?: {
142
+ group: string;
143
+ maxPerSecond: number;
144
+ }
141
145
  }
142
146
 
143
147
  export interface IWorkflowStartParams {
@@ -189,7 +193,8 @@ export default class WorkflowContext {
189
193
  throwIfExists,
190
194
  eta,
191
195
  parentID,
192
- taskGroup = "default"
196
+ taskGroup = "default",
197
+ throttle
193
198
  }: IWorkflowQueueParameter = {}) {
194
199
  const clock = this.storage.clock;
195
200
  let tries = 1;
@@ -207,10 +212,21 @@ export default class WorkflowContext {
207
212
  throw new EntityAccessError(`ID cannot be more than 400 characters`);
208
213
  }
209
214
 
215
+ const now = align(clock.utcNow);
216
+
217
+ let queued = now;
218
+
219
+ let throttleGroup = null;
220
+
221
+ if (throttle) {
222
+ eta = await this.storage.getNextEta(throttle);
223
+ queued = eta;
224
+ throttleGroup = throttle.group;
225
+ }
226
+
210
227
  // this will ensure even empty workflow !!
211
228
  const schema = WorkflowRegistry.register(type, void 0);
212
229
 
213
- const now = align(clock.utcNow);
214
230
 
215
231
  let lastError = null;
216
232
  while(tries--) {
@@ -229,10 +245,11 @@ export default class WorkflowContext {
229
245
  await this.storage.save({
230
246
  id,
231
247
  taskGroup,
248
+ throttleGroup,
232
249
  name: schema.name,
233
250
  input: JSON.stringify(input),
234
251
  isWorkflow: true,
235
- queued: now,
252
+ queued,
236
253
  updated: now,
237
254
  parentID,
238
255
  eta
@@ -27,6 +27,14 @@ const loadedFromDb = Symbol("loadedFromDB");
27
27
  ],
28
28
  filter: (x) => x.isWorkflow === true
29
29
  })
30
+ @Index({
31
+ name: "IX_Workflows_Throttle_Group",
32
+ columns: [
33
+ { name: (x) => x.throttleGroup, descending: false },
34
+ { name: (x) => x.queued, descending: false }
35
+ ],
36
+ filter: (x) => x.isWorkflow === true && x.throttleGroup !== null
37
+ })
30
38
  export class WorkflowItem {
31
39
 
32
40
  @Column({ dataType: "Char", length: 400, key: true })
@@ -62,6 +70,11 @@ export class WorkflowItem {
62
70
  })
63
71
  public taskGroup: string;
64
72
 
73
+ @Column({
74
+ dataType: "Char", length: 200, nullable: true
75
+ })
76
+ public throttleGroup: string;
77
+
65
78
  @Column({ dataType: "Int", default: () => 0})
66
79
  public priority: number;
67
80
 
@@ -112,6 +125,24 @@ export default class WorkflowStorage {
112
125
 
113
126
  }
114
127
 
128
+ async getNextEta(throttle: { group: string, maxPerSecond: number }) {
129
+
130
+ const db = new WorkflowContext(this.driver);
131
+ const last = await db.workflows.where(throttle, (p) => (x) => x.throttleGroup === p.group
132
+ && x.isWorkflow === true)
133
+ .orderByDescending(void 0, (p) => (x) => x.queued)
134
+ .first();
135
+
136
+ if (last) {
137
+ if (throttle.maxPerSecond <= 0) {
138
+ throttle.maxPerSecond = 1;
139
+ }
140
+ return DateTime.from(last.queued).addSeconds(1 / throttle.maxPerSecond);
141
+ }
142
+
143
+ return DateTime.now;
144
+ }
145
+
115
146
  async getWorkflow(id: string) {
116
147
  const db = new WorkflowContext(this.driver);
117
148
  const r = await db.workflows.where({ id }, (p) => (x) => x.id === p.id && x.isWorkflow === true).first();