@expo/entity 0.20.0 → 0.21.0

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.
@@ -1,5 +1,6 @@
1
1
  import EntityQueryContextProvider from './EntityQueryContextProvider';
2
2
  export declare type PostCommitCallback = (...args: any) => Promise<any>;
3
+ export declare type PreCommitCallback = (queryContext: EntityTransactionalQueryContext, ...args: any) => Promise<any>;
3
4
  /**
4
5
  * Entity framework representation of transactional and non-transactional database
5
6
  * query execution units.
@@ -23,10 +24,19 @@ export declare class EntityNonTransactionalQueryContext extends EntityQueryConte
23
24
  export declare class EntityTransactionalQueryContext extends EntityQueryContext {
24
25
  private readonly postCommitInvalidationCallbacks;
25
26
  private readonly postCommitCallbacks;
27
+ private readonly preCommitCallbacks;
28
+ /**
29
+ * Schedule a pre-commit callback. These will be run within the transaction right before it is
30
+ * committed, and will be run in the order specified. Ordering of callbacks scheduled with the
31
+ * same value for the order parameter is undefined within that ordering group.
32
+ * @param callback - callback to schedule
33
+ * @param order - order in which this should be run relative to other scheduled pre-commit callbacks,
34
+ * with higher numbers running later than lower numbers.
35
+ */
36
+ appendPreCommitCallback(callback: PreCommitCallback, order: number): void;
26
37
  /**
27
38
  * Schedule a post-commit cache invalidation callback. These are run before normal
28
39
  * post-commit callbacks in order to have cache consistency in normal post-commit callbacks.
29
- *
30
40
  * @param callback - callback to schedule
31
41
  */
32
42
  appendPostCommitInvalidationCallback(callback: PostCommitCallback): void;
@@ -36,6 +46,7 @@ export declare class EntityTransactionalQueryContext extends EntityQueryContext
36
46
  * @param callback - callback to schedule
37
47
  */
38
48
  appendPostCommitCallback(callback: PostCommitCallback): void;
49
+ runPreCommitCallbacksAsync(): Promise<void>;
39
50
  runPostCommitCallbacksAsync(): Promise<void>;
40
51
  isInTransaction(): boolean;
41
52
  runInTransactionIfNotInTransactionAsync<T>(transactionScope: (queryContext: EntityTransactionalQueryContext) => Promise<T>): Promise<T>;
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.EntityTransactionalQueryContext = exports.EntityNonTransactionalQueryContext = exports.EntityQueryContext = void 0;
7
+ const assert_1 = __importDefault(require("assert"));
4
8
  /**
5
9
  * Entity framework representation of transactional and non-transactional database
6
10
  * query execution units.
@@ -35,11 +39,23 @@ class EntityTransactionalQueryContext extends EntityQueryContext {
35
39
  super(...arguments);
36
40
  this.postCommitInvalidationCallbacks = [];
37
41
  this.postCommitCallbacks = [];
42
+ this.preCommitCallbacks = [];
43
+ }
44
+ /**
45
+ * Schedule a pre-commit callback. These will be run within the transaction right before it is
46
+ * committed, and will be run in the order specified. Ordering of callbacks scheduled with the
47
+ * same value for the order parameter is undefined within that ordering group.
48
+ * @param callback - callback to schedule
49
+ * @param order - order in which this should be run relative to other scheduled pre-commit callbacks,
50
+ * with higher numbers running later than lower numbers.
51
+ */
52
+ appendPreCommitCallback(callback, order) {
53
+ (0, assert_1.default)(order >= Number.MIN_SAFE_INTEGER && order <= Number.MAX_SAFE_INTEGER, `Invalid order specified: ${order}`);
54
+ this.preCommitCallbacks.push({ callback, order });
38
55
  }
39
56
  /**
40
57
  * Schedule a post-commit cache invalidation callback. These are run before normal
41
58
  * post-commit callbacks in order to have cache consistency in normal post-commit callbacks.
42
- *
43
59
  * @param callback - callback to schedule
44
60
  */
45
61
  appendPostCommitInvalidationCallback(callback) {
@@ -53,6 +69,15 @@ class EntityTransactionalQueryContext extends EntityQueryContext {
53
69
  appendPostCommitCallback(callback) {
54
70
  this.postCommitCallbacks.push(callback);
55
71
  }
72
+ async runPreCommitCallbacksAsync() {
73
+ const callbacks = [...this.preCommitCallbacks]
74
+ .sort((a, b) => a.order - b.order)
75
+ .map((c) => c.callback);
76
+ this.preCommitCallbacks.length = 0;
77
+ for (const callback of callbacks) {
78
+ await callback(this);
79
+ }
80
+ }
56
81
  async runPostCommitCallbacksAsync() {
57
82
  const invalidationCallbacks = [...this.postCommitInvalidationCallbacks];
58
83
  this.postCommitInvalidationCallbacks.length = 0;
@@ -1 +1 @@
1
- {"version":3,"file":"EntityQueryContext.js","sourceRoot":"","sources":["../src/EntityQueryContext.ts"],"names":[],"mappings":";;;AAIA;;;;;;GAMG;AACH,MAAsB,kBAAkB;IACtC,YAA6B,cAAmB;QAAnB,mBAAc,GAAd,cAAc,CAAK;IAAG,CAAC;IAIpD,iBAAiB;QACf,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;CAKF;AAZD,gDAYC;AAED,MAAa,kCAAmC,SAAQ,kBAAkB;IACxE,YACE,cAAmB,EACF,0BAAsD;QAEvE,KAAK,CAAC,cAAc,CAAC,CAAC;QAFL,+BAA0B,GAA1B,0BAA0B,CAA4B;IAGzE,CAAC;IAED,eAAe;QACb,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,uCAAuC,CAC3C,gBAA+E;QAE/E,OAAO,MAAM,IAAI,CAAC,0BAA0B,CAAC,qBAAqB,CAAC,gBAAgB,CAAC,CAAC;IACvF,CAAC;CACF;AAjBD,gFAiBC;AAED,MAAa,+BAAgC,SAAQ,kBAAkB;IAAvE;;QACmB,oCAA+B,GAAyB,EAAE,CAAC;QAC3D,wBAAmB,GAAyB,EAAE,CAAC;IAwClE,CAAC;IAtCC;;;;;OAKG;IACI,oCAAoC,CAAC,QAA4B;QACtE,IAAI,CAAC,+BAA+B,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtD,CAAC;IAED;;;;OAIG;IACI,wBAAwB,CAAC,QAA4B;QAC1D,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAEM,KAAK,CAAC,2BAA2B;QACtC,MAAM,qBAAqB,GAAG,CAAC,GAAG,IAAI,CAAC,+BAA+B,CAAC,CAAC;QACxE,IAAI,CAAC,+BAA+B,CAAC,MAAM,GAAG,CAAC,CAAC;QAChD,MAAM,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAEvE,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAChD,IAAI,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC;QACpC,MAAM,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,uCAAuC,CAC3C,gBAA+E;QAE/E,OAAO,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;CACF;AA1CD,0EA0CC"}
1
+ {"version":3,"file":"EntityQueryContext.js","sourceRoot":"","sources":["../src/EntityQueryContext.ts"],"names":[],"mappings":"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"}
@@ -19,6 +19,7 @@ class EntityQueryContextProvider {
19
19
  const [returnedValue, queryContext] = await this.createTransactionRunner()(async (queryInterface) => {
20
20
  const queryContext = new EntityQueryContext_1.EntityTransactionalQueryContext(queryInterface);
21
21
  const result = await transactionScope(queryContext);
22
+ await queryContext.runPreCommitCallbacksAsync();
22
23
  return [result, queryContext];
23
24
  });
24
25
  await queryContext.runPostCommitCallbacksAsync();
@@ -1 +1 @@
1
- {"version":3,"file":"EntityQueryContextProvider.js","sourceRoot":"","sources":["../src/EntityQueryContextProvider.ts"],"names":[],"mappings":";;AAAA,6DAG8B;AAE9B;;GAEG;AACH,MAA8B,0BAA0B;IACtD;;OAEG;IACI,eAAe;QACpB,OAAO,IAAI,uDAAkC,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,IAAI,CAAC,CAAC;IAChF,CAAC;IAcD;;;OAGG;IACH,KAAK,CAAC,qBAAqB,CACzB,gBAA+E;QAE/E,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC,GAAG,MAAM,IAAI,CAAC,uBAAuB,EAErE,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE;YAC3B,MAAM,YAAY,GAAG,IAAI,oDAA+B,CAAC,cAAc,CAAC,CAAC;YACzE,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,YAAY,CAAC,CAAC;YACpD,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,MAAM,YAAY,CAAC,2BAA2B,EAAE,CAAC;QACjD,OAAO,aAAa,CAAC;IACvB,CAAC;CACF;AArCD,6CAqCC"}
1
+ {"version":3,"file":"EntityQueryContextProvider.js","sourceRoot":"","sources":["../src/EntityQueryContextProvider.ts"],"names":[],"mappings":";;AAAA,6DAG8B;AAE9B;;GAEG;AACH,MAA8B,0BAA0B;IACtD;;OAEG;IACI,eAAe;QACpB,OAAO,IAAI,uDAAkC,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,IAAI,CAAC,CAAC;IAChF,CAAC;IAcD;;;OAGG;IACH,KAAK,CAAC,qBAAqB,CACzB,gBAA+E;QAE/E,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC,GAAG,MAAM,IAAI,CAAC,uBAAuB,EAErE,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE;YAC3B,MAAM,YAAY,GAAG,IAAI,oDAA+B,CAAC,cAAc,CAAC,CAAC;YACzE,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,YAAY,CAAC,CAAC;YACpD,MAAM,YAAY,CAAC,0BAA0B,EAAE,CAAC;YAChD,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,MAAM,YAAY,CAAC,2BAA2B,EAAE,CAAC;QACjD,OAAO,aAAa,CAAC;IACvB,CAAC;CACF;AAtCD,6CAsCC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const invariant_1 = __importDefault(require("invariant"));
7
+ const EntityQueryContext_1 = require("../EntityQueryContext");
8
+ const ViewerContext_1 = __importDefault(require("../ViewerContext"));
9
+ const createUnitTestEntityCompanionProvider_1 = require("../utils/testing/createUnitTestEntityCompanionProvider");
10
+ describe(EntityQueryContext_1.EntityQueryContext, () => {
11
+ describe('callbacks', () => {
12
+ it('calls all callbacks, and calls invalidation first', async () => {
13
+ const companionProvider = (0, createUnitTestEntityCompanionProvider_1.createUnitTestEntityCompanionProvider)();
14
+ const viewerContext = new ViewerContext_1.default(companionProvider);
15
+ const preCommitFirstCallback = jest.fn(async () => { });
16
+ const preCommitSecondCallback = jest.fn(async () => { });
17
+ const postCommitInvalidationCallback = jest.fn(async () => {
18
+ (0, invariant_1.default)(preCommitFirstCallback.mock.calls.length === 1, 'preCommit should be called before postCommitInvalidation');
19
+ (0, invariant_1.default)(preCommitSecondCallback.mock.calls.length === 1, 'preCommit should be called before postCommitInvalidation');
20
+ });
21
+ const postCommitCallback = jest.fn(async () => {
22
+ (0, invariant_1.default)(preCommitFirstCallback.mock.calls.length === 1, 'preCommit should be called before postCommit');
23
+ (0, invariant_1.default)(preCommitSecondCallback.mock.calls.length === 1, 'preCommit should be called before postCommit');
24
+ (0, invariant_1.default)(postCommitInvalidationCallback.mock.calls.length === 1, 'postCommitInvalidation should be called before postCommit');
25
+ });
26
+ await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync('postgres', async (queryContext) => {
27
+ queryContext.appendPostCommitCallback(postCommitCallback);
28
+ queryContext.appendPostCommitInvalidationCallback(postCommitInvalidationCallback);
29
+ queryContext.appendPreCommitCallback(preCommitSecondCallback, 2);
30
+ queryContext.appendPreCommitCallback(preCommitFirstCallback, 1);
31
+ });
32
+ expect(preCommitFirstCallback).toHaveBeenCalledTimes(1);
33
+ expect(preCommitSecondCallback).toHaveBeenCalledTimes(1);
34
+ expect(postCommitCallback).toHaveBeenCalledTimes(1);
35
+ expect(postCommitInvalidationCallback).toHaveBeenCalledTimes(1);
36
+ });
37
+ it('prevents transaction from finishing when precommit throws (post commit callbacks are not called)', async () => {
38
+ const companionProvider = (0, createUnitTestEntityCompanionProvider_1.createUnitTestEntityCompanionProvider)();
39
+ const viewerContext = new ViewerContext_1.default(companionProvider);
40
+ const preCommitCallback = jest.fn(async () => {
41
+ throw new Error('wat');
42
+ });
43
+ const postCommitInvalidationCallback = jest.fn(async () => { });
44
+ const postCommitCallback = jest.fn(async () => { });
45
+ await expect(viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync('postgres', async (queryContext) => {
46
+ queryContext.appendPostCommitCallback(postCommitCallback);
47
+ queryContext.appendPostCommitInvalidationCallback(postCommitInvalidationCallback);
48
+ queryContext.appendPreCommitCallback(preCommitCallback, 0);
49
+ })).rejects.toThrowError('wat');
50
+ expect(preCommitCallback).toHaveBeenCalledTimes(1);
51
+ expect(postCommitCallback).toHaveBeenCalledTimes(0);
52
+ expect(postCommitInvalidationCallback).toHaveBeenCalledTimes(0);
53
+ });
54
+ });
55
+ });
56
+ //# sourceMappingURL=EntityQueryContext-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntityQueryContext-test.js","sourceRoot":"","sources":["../../src/__tests__/EntityQueryContext-test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/entity",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "A privacy-first data model",
5
5
  "files": [
6
6
  "build",
@@ -1,6 +1,12 @@
1
+ import assert from 'assert';
2
+
1
3
  import EntityQueryContextProvider from './EntityQueryContextProvider';
2
4
 
3
5
  export type PostCommitCallback = (...args: any) => Promise<any>;
6
+ export type PreCommitCallback = (
7
+ queryContext: EntityTransactionalQueryContext,
8
+ ...args: any
9
+ ) => Promise<any>;
4
10
 
5
11
  /**
6
12
  * Entity framework representation of transactional and non-transactional database
@@ -46,10 +52,27 @@ export class EntityTransactionalQueryContext extends EntityQueryContext {
46
52
  private readonly postCommitInvalidationCallbacks: PostCommitCallback[] = [];
47
53
  private readonly postCommitCallbacks: PostCommitCallback[] = [];
48
54
 
55
+ private readonly preCommitCallbacks: { callback: PreCommitCallback; order: number }[] = [];
56
+
57
+ /**
58
+ * Schedule a pre-commit callback. These will be run within the transaction right before it is
59
+ * committed, and will be run in the order specified. Ordering of callbacks scheduled with the
60
+ * same value for the order parameter is undefined within that ordering group.
61
+ * @param callback - callback to schedule
62
+ * @param order - order in which this should be run relative to other scheduled pre-commit callbacks,
63
+ * with higher numbers running later than lower numbers.
64
+ */
65
+ public appendPreCommitCallback(callback: PreCommitCallback, order: number): void {
66
+ assert(
67
+ order >= Number.MIN_SAFE_INTEGER && order <= Number.MAX_SAFE_INTEGER,
68
+ `Invalid order specified: ${order}`
69
+ );
70
+ this.preCommitCallbacks.push({ callback, order });
71
+ }
72
+
49
73
  /**
50
74
  * Schedule a post-commit cache invalidation callback. These are run before normal
51
75
  * post-commit callbacks in order to have cache consistency in normal post-commit callbacks.
52
- *
53
76
  * @param callback - callback to schedule
54
77
  */
55
78
  public appendPostCommitInvalidationCallback(callback: PostCommitCallback): void {
@@ -65,6 +88,17 @@ export class EntityTransactionalQueryContext extends EntityQueryContext {
65
88
  this.postCommitCallbacks.push(callback);
66
89
  }
67
90
 
91
+ public async runPreCommitCallbacksAsync(): Promise<void> {
92
+ const callbacks = [...this.preCommitCallbacks]
93
+ .sort((a, b) => a.order - b.order)
94
+ .map((c) => c.callback);
95
+ this.preCommitCallbacks.length = 0;
96
+
97
+ for (const callback of callbacks) {
98
+ await callback(this);
99
+ }
100
+ }
101
+
68
102
  public async runPostCommitCallbacksAsync(): Promise<void> {
69
103
  const invalidationCallbacks = [...this.postCommitInvalidationCallbacks];
70
104
  this.postCommitInvalidationCallbacks.length = 0;
@@ -38,6 +38,7 @@ export default abstract class EntityQueryContextProvider {
38
38
  >()(async (queryInterface) => {
39
39
  const queryContext = new EntityTransactionalQueryContext(queryInterface);
40
40
  const result = await transactionScope(queryContext);
41
+ await queryContext.runPreCommitCallbacksAsync();
41
42
  return [result, queryContext];
42
43
  });
43
44
  await queryContext.runPostCommitCallbacksAsync();
@@ -0,0 +1,82 @@
1
+ import invariant from 'invariant';
2
+
3
+ import { EntityQueryContext } from '../EntityQueryContext';
4
+ import ViewerContext from '../ViewerContext';
5
+ import { createUnitTestEntityCompanionProvider } from '../utils/testing/createUnitTestEntityCompanionProvider';
6
+
7
+ describe(EntityQueryContext, () => {
8
+ describe('callbacks', () => {
9
+ it('calls all callbacks, and calls invalidation first', async () => {
10
+ const companionProvider = createUnitTestEntityCompanionProvider();
11
+ const viewerContext = new ViewerContext(companionProvider);
12
+
13
+ const preCommitFirstCallback = jest.fn(async (): Promise<void> => {});
14
+ const preCommitSecondCallback = jest.fn(async (): Promise<void> => {});
15
+ const postCommitInvalidationCallback = jest.fn(async (): Promise<void> => {
16
+ invariant(
17
+ preCommitFirstCallback.mock.calls.length === 1,
18
+ 'preCommit should be called before postCommitInvalidation'
19
+ );
20
+ invariant(
21
+ preCommitSecondCallback.mock.calls.length === 1,
22
+ 'preCommit should be called before postCommitInvalidation'
23
+ );
24
+ });
25
+ const postCommitCallback = jest.fn(async (): Promise<void> => {
26
+ invariant(
27
+ preCommitFirstCallback.mock.calls.length === 1,
28
+ 'preCommit should be called before postCommit'
29
+ );
30
+ invariant(
31
+ preCommitSecondCallback.mock.calls.length === 1,
32
+ 'preCommit should be called before postCommit'
33
+ );
34
+ invariant(
35
+ postCommitInvalidationCallback.mock.calls.length === 1,
36
+ 'postCommitInvalidation should be called before postCommit'
37
+ );
38
+ });
39
+
40
+ await viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
41
+ 'postgres',
42
+ async (queryContext) => {
43
+ queryContext.appendPostCommitCallback(postCommitCallback);
44
+ queryContext.appendPostCommitInvalidationCallback(postCommitInvalidationCallback);
45
+ queryContext.appendPreCommitCallback(preCommitSecondCallback, 2);
46
+ queryContext.appendPreCommitCallback(preCommitFirstCallback, 1);
47
+ }
48
+ );
49
+
50
+ expect(preCommitFirstCallback).toHaveBeenCalledTimes(1);
51
+ expect(preCommitSecondCallback).toHaveBeenCalledTimes(1);
52
+ expect(postCommitCallback).toHaveBeenCalledTimes(1);
53
+ expect(postCommitInvalidationCallback).toHaveBeenCalledTimes(1);
54
+ });
55
+
56
+ it('prevents transaction from finishing when precommit throws (post commit callbacks are not called)', async () => {
57
+ const companionProvider = createUnitTestEntityCompanionProvider();
58
+ const viewerContext = new ViewerContext(companionProvider);
59
+
60
+ const preCommitCallback = jest.fn(async (): Promise<void> => {
61
+ throw new Error('wat');
62
+ });
63
+ const postCommitInvalidationCallback = jest.fn(async (): Promise<void> => {});
64
+ const postCommitCallback = jest.fn(async (): Promise<void> => {});
65
+
66
+ await expect(
67
+ viewerContext.runInTransactionForDatabaseAdaptorFlavorAsync(
68
+ 'postgres',
69
+ async (queryContext) => {
70
+ queryContext.appendPostCommitCallback(postCommitCallback);
71
+ queryContext.appendPostCommitInvalidationCallback(postCommitInvalidationCallback);
72
+ queryContext.appendPreCommitCallback(preCommitCallback, 0);
73
+ }
74
+ )
75
+ ).rejects.toThrowError('wat');
76
+
77
+ expect(preCommitCallback).toHaveBeenCalledTimes(1);
78
+ expect(postCommitCallback).toHaveBeenCalledTimes(0);
79
+ expect(postCommitInvalidationCallback).toHaveBeenCalledTimes(0);
80
+ });
81
+ });
82
+ });